[
  {
    "path": ".gitignore",
    "content": "# =============== #\n# Unity generated #\n# =============== #\n[Tt]emp/\n[Oo]bj/\n[Bb]uild\n[Ll]ibrary/\n[Gg]oogle/\n[Ll]ogs/\n[Pp]ackages/\n\nsysinfo.txt\n*.stackdump\nStreamingAssets/\n \n# ===================================== #\n# Visual Studio / MonoDevelop generated #\n# ===================================== #\n[Ee]xported[Oo]bj/\n.vs/\n.idea/\n.vscode/\n/*.userprefs\n/*.csproj\n/*.pidb\n/*.suo\n/*.sln*\n/*.user\n/*.unityproj\n/*.booproj\n/*.pdb\n\n# ============ #\n# OS generated #\n# ============ #\n.DS_Store*\n._*\n.Spotlight-V100\n.Trashes\nIcon?\nehthumbs.db\n[Tt]humbs.db\n*.apk\n"
  },
  {
    "path": "Assets/Plugins/Kcp/Android/libs/armeabi-v7a/libkcp.so.meta",
    "content": "fileFormatVersion: 2\nguid: 8e03e31ef66b1d340bfd9de539878633\ntimeCreated: 1530266293\nlicenseType: Pro\nPluginImporter:\n  serializedVersion: 2\n  iconMap: {}\n  executionOrder: {}\n  isPreloaded: 0\n  isOverridable: 0\n  platformData:\n    data:\n      first:\n        Android: Android\n      second:\n        enabled: 1\n        settings:\n          CPU: ARMv7\n    data:\n      first:\n        Any: \n      second:\n        enabled: 0\n        settings: {}\n    data:\n      first:\n        Editor: Editor\n      second:\n        enabled: 0\n        settings:\n          DefaultValueInitialized: true\n  userData: \n  assetBundleName: \n  assetBundleVariant: \n"
  },
  {
    "path": "Assets/Plugins/Kcp/Android/libs/armeabi-v7a.meta",
    "content": "fileFormatVersion: 2\nguid: 4b9de99f882e1334f88d3fffa436fe26\nfolderAsset: yes\ntimeCreated: 1529980199\nlicenseType: Pro\nDefaultImporter:\n  userData: \n  assetBundleName: \n  assetBundleVariant: \n"
  },
  {
    "path": "Assets/Plugins/Kcp/Android/libs/x86/libkcp.so.meta",
    "content": "fileFormatVersion: 2\nguid: 3869fe202d9b7494cbf94cbd4c9ae4c5\ntimeCreated: 1530266298\nlicenseType: Pro\nPluginImporter:\n  serializedVersion: 2\n  iconMap: {}\n  executionOrder: {}\n  isPreloaded: 0\n  isOverridable: 0\n  platformData:\n    data:\n      first:\n        Android: Android\n      second:\n        enabled: 1\n        settings:\n          CPU: x86\n    data:\n      first:\n        Any: \n      second:\n        enabled: 0\n        settings: {}\n    data:\n      first:\n        Editor: Editor\n      second:\n        enabled: 0\n        settings:\n          DefaultValueInitialized: true\n  userData: \n  assetBundleName: \n  assetBundleVariant: \n"
  },
  {
    "path": "Assets/Plugins/Kcp/Android/libs/x86.meta",
    "content": "fileFormatVersion: 2\nguid: af440bcca3a80614185db5d493411e3e\nfolderAsset: yes\ntimeCreated: 1529980199\nlicenseType: Pro\nDefaultImporter:\n  userData: \n  assetBundleName: \n  assetBundleVariant: \n"
  },
  {
    "path": "Assets/Plugins/Kcp/Android/libs.meta",
    "content": "fileFormatVersion: 2\nguid: e683bde7d2130ef4e9cd605a119c319e\nfolderAsset: yes\ntimeCreated: 1529980199\nlicenseType: Pro\nDefaultImporter:\n  userData: \n  assetBundleName: \n  assetBundleVariant: \n"
  },
  {
    "path": "Assets/Plugins/Kcp/Android.meta",
    "content": "fileFormatVersion: 2\nguid: 89f91d9886713c242b08fc47677c6cd6\nfolderAsset: yes\nDefaultImporter:\n  externalObjects: {}\n  userData: \n  assetBundleName: \n  assetBundleVariant: \n"
  },
  {
    "path": "Assets/Plugins/Kcp/MacOS/kcp.bundle/Contents/MacOS/kcp.meta",
    "content": "fileFormatVersion: 2\nguid: ca685b77470a94b838156da4c499b38a\nDefaultImporter:\n  externalObjects: {}\n  userData: \n  assetBundleName: \n  assetBundleVariant: \n"
  },
  {
    "path": "Assets/Plugins/Kcp/MacOS/kcp.bundle/Contents/MacOS.meta",
    "content": "fileFormatVersion: 2\nguid: 957c90d60724445898ad398879db8b83\nfolderAsset: yes\nDefaultImporter:\n  externalObjects: {}\n  userData: \n  assetBundleName: \n  assetBundleVariant: \n"
  },
  {
    "path": "Assets/Plugins/Kcp/MacOS/kcp.bundle/Contents.meta",
    "content": "fileFormatVersion: 2\nguid: 19bc16a76b8ac49e6bafebcacef13384\nfolderAsset: yes\nDefaultImporter:\n  externalObjects: {}\n  userData: \n  assetBundleName: \n  assetBundleVariant: \n"
  },
  {
    "path": "Assets/Plugins/Kcp/MacOS/kcp.bundle.meta",
    "content": "fileFormatVersion: 2\nguid: eb0cef4b32a23b24688b70b0d9b607e6\nfolderAsset: yes\nPluginImporter:\n  externalObjects: {}\n  serializedVersion: 2\n  iconMap: {}\n  executionOrder: {}\n  isPreloaded: 0\n  isOverridable: 0\n  platformData:\n  - first:\n      '': OSXIntel\n    second:\n      enabled: 1\n      settings: {}\n  - first:\n      '': OSXIntel64\n    second:\n      enabled: 1\n      settings: {}\n  - first:\n      Any: \n    second:\n      enabled: 0\n      settings: {}\n  - first:\n      Editor: Editor\n    second:\n      enabled: 1\n      settings:\n        DefaultValueInitialized: true\n  - first:\n      Standalone: OSXUniversal\n    second:\n      enabled: 1\n      settings: {}\n  userData: \n  assetBundleName: \n  assetBundleVariant: \n"
  },
  {
    "path": "Assets/Plugins/Kcp/MacOS.meta",
    "content": "fileFormatVersion: 2\nguid: 747c6b8c8af2e794da1e88402d7cc394\nfolderAsset: yes\nDefaultImporter:\n  externalObjects: {}\n  userData: \n  assetBundleName: \n  assetBundleVariant: \n"
  },
  {
    "path": "Assets/Plugins/Kcp/iOS/kcp.a.meta",
    "content": "fileFormatVersion: 2\nguid: 81c5e3645249f404986889388c75e6de\nPluginImporter:\n  externalObjects: {}\n  serializedVersion: 2\n  iconMap: {}\n  executionOrder: {}\n  isPreloaded: 0\n  isOverridable: 0\n  platformData:\n  - first:\n      Any: \n    second:\n      enabled: 0\n      settings: {}\n  - first:\n      Editor: Editor\n    second:\n      enabled: 0\n      settings:\n        DefaultValueInitialized: true\n  - first:\n      iPhone: iOS\n    second:\n      enabled: 1\n      settings: {}\n  userData: \n  assetBundleName: \n  assetBundleVariant: \n"
  },
  {
    "path": "Assets/Plugins/Kcp/iOS.meta",
    "content": "fileFormatVersion: 2\nguid: fd0f4e125884b09478cd65e157e73225\nfolderAsset: yes\nDefaultImporter:\n  externalObjects: {}\n  userData: \n  assetBundleName: \n  assetBundleVariant: \n"
  },
  {
    "path": "Assets/Plugins/Kcp/x86/kcp.dll.meta",
    "content": "fileFormatVersion: 2\nguid: 5163e0119c1604a43adbc28ec6fb277e\ntimeCreated: 1530266315\nlicenseType: Pro\nPluginImporter:\n  serializedVersion: 2\n  iconMap: {}\n  executionOrder: {}\n  isPreloaded: 0\n  isOverridable: 0\n  platformData:\n    data:\n      first:\n        '': Any\n      second:\n        enabled: 0\n        settings:\n          Exclude Android: 1\n          Exclude Editor: 1\n          Exclude Linux: 0\n          Exclude Linux64: 0\n          Exclude LinuxUniversal: 0\n          Exclude OSXIntel: 0\n          Exclude OSXIntel64: 0\n          Exclude OSXUniversal: 0\n          Exclude Win: 0\n          Exclude Win64: 1\n    data:\n      first:\n        Android: Android\n      second:\n        enabled: 0\n        settings:\n          CPU: ARMv7\n    data:\n      first:\n        Any: \n      second:\n        enabled: 0\n        settings: {}\n    data:\n      first:\n        Editor: Editor\n      second:\n        enabled: 0\n        settings:\n          CPU: x86\n          DefaultValueInitialized: true\n          OS: AnyOS\n    data:\n      first:\n        Facebook: Win\n      second:\n        enabled: 1\n        settings:\n          CPU: AnyCPU\n    data:\n      first:\n        Facebook: Win64\n      second:\n        enabled: 0\n        settings:\n          CPU: None\n    data:\n      first:\n        Standalone: Linux\n      second:\n        enabled: 1\n        settings:\n          CPU: x86\n    data:\n      first:\n        Standalone: Linux64\n      second:\n        enabled: 1\n        settings:\n          CPU: None\n    data:\n      first:\n        Standalone: LinuxUniversal\n      second:\n        enabled: 1\n        settings:\n          CPU: AnyCPU\n    data:\n      first:\n        Standalone: OSXIntel\n      second:\n        enabled: 1\n        settings:\n          CPU: AnyCPU\n    data:\n      first:\n        Standalone: OSXIntel64\n      second:\n        enabled: 1\n        settings:\n          CPU: None\n    data:\n      first:\n        Standalone: OSXUniversal\n      second:\n        enabled: 1\n        settings:\n          CPU: AnyCPU\n    data:\n      first:\n        Standalone: Win\n      second:\n        enabled: 1\n        settings:\n          CPU: AnyCPU\n    data:\n      first:\n        Standalone: Win64\n      second:\n        enabled: 0\n        settings:\n          CPU: None\n  userData: \n  assetBundleName: \n  assetBundleVariant: \n"
  },
  {
    "path": "Assets/Plugins/Kcp/x86.meta",
    "content": "fileFormatVersion: 2\nguid: 93e339b4ca47c8f4b81c2d9742800ec5\nfolderAsset: yes\nDefaultImporter:\n  externalObjects: {}\n  userData: \n  assetBundleName: \n  assetBundleVariant: \n"
  },
  {
    "path": "Assets/Plugins/Kcp/x86_64/kcp.dll.meta",
    "content": "fileFormatVersion: 2\nguid: 803f7857fdb47b34fbe04eda49b8aa85\nPluginImporter:\n  externalObjects: {}\n  serializedVersion: 2\n  iconMap: {}\n  executionOrder: {}\n  isPreloaded: 0\n  isOverridable: 0\n  platformData:\n  - first:\n      '': Any\n    second:\n      enabled: 0\n      settings:\n        Exclude Android: 1\n        Exclude Editor: 0\n        Exclude Linux: 0\n        Exclude Linux64: 0\n        Exclude LinuxUniversal: 0\n        Exclude OSXIntel: 1\n        Exclude OSXIntel64: 1\n        Exclude OSXUniversal: 0\n        Exclude Win: 1\n        Exclude Win64: 0\n  - first:\n      Android: Android\n    second:\n      enabled: 0\n      settings:\n        CPU: ARMv7\n  - first:\n      Any: \n    second:\n      enabled: 0\n      settings: {}\n  - first:\n      Editor: Editor\n    second:\n      enabled: 1\n      settings:\n        CPU: x86_64\n        DefaultValueInitialized: true\n        OS: Windows\n  - first:\n      Facebook: Win\n    second:\n      enabled: 0\n      settings:\n        CPU: None\n  - first:\n      Facebook: Win64\n    second:\n      enabled: 1\n      settings:\n        CPU: AnyCPU\n  - first:\n      Standalone: Linux\n    second:\n      enabled: 1\n      settings:\n        CPU: None\n  - first:\n      Standalone: Linux64\n    second:\n      enabled: 1\n      settings:\n        CPU: x86_64\n  - first:\n      Standalone: LinuxUniversal\n    second:\n      enabled: 1\n      settings:\n        CPU: AnyCPU\n  - first:\n      Standalone: OSXIntel\n    second:\n      enabled: 0\n      settings:\n        CPU: None\n  - first:\n      Standalone: OSXIntel64\n    second:\n      enabled: 0\n      settings:\n        CPU: AnyCPU\n  - first:\n      Standalone: OSXUniversal\n    second:\n      enabled: 1\n      settings:\n        CPU: None\n  - first:\n      Standalone: Win\n    second:\n      enabled: 0\n      settings:\n        CPU: None\n  - first:\n      Standalone: Win64\n    second:\n      enabled: 1\n      settings:\n        CPU: AnyCPU\n  userData: \n  assetBundleName: \n  assetBundleVariant: \n"
  },
  {
    "path": "Assets/Plugins/Kcp/x86_64.meta",
    "content": "fileFormatVersion: 2\nguid: 06020764b763c6647b21f6933162724f\nfolderAsset: yes\nDefaultImporter:\n  externalObjects: {}\n  userData: \n  assetBundleName: \n  assetBundleVariant: \n"
  },
  {
    "path": "Assets/Plugins/Kcp.meta",
    "content": "fileFormatVersion: 2\nguid: 3c6e92a7004dbcd45bed3397705d35bd\nfolderAsset: yes\nDefaultImporter:\n  externalObjects: {}\n  userData: \n  assetBundleName: \n  assetBundleVariant: \n"
  },
  {
    "path": "Assets/Plugins.meta",
    "content": "fileFormatVersion: 2\nguid: a20cd3483a2a77b4bb8d9638ea3dde5f\nfolderAsset: yes\nDefaultImporter:\n  externalObjects: {}\n  userData: \n  assetBundleName: \n  assetBundleVariant: \n"
  },
  {
    "path": "Assets/Scenes/Client.unity",
    "content": "%YAML 1.1\n%TAG !u! tag:unity3d.com,2011:\n--- !u!29 &1\nOcclusionCullingSettings:\n  m_ObjectHideFlags: 0\n  serializedVersion: 2\n  m_OcclusionBakeSettings:\n    smallestOccluder: 5\n    smallestHole: 0.25\n    backfaceThreshold: 100\n  m_SceneGUID: 00000000000000000000000000000000\n  m_OcclusionCullingData: {fileID: 0}\n--- !u!104 &2\nRenderSettings:\n  m_ObjectHideFlags: 0\n  serializedVersion: 9\n  m_Fog: 0\n  m_FogColor: {r: 0.5, g: 0.5, b: 0.5, a: 1}\n  m_FogMode: 3\n  m_FogDensity: 0.01\n  m_LinearFogStart: 0\n  m_LinearFogEnd: 300\n  m_AmbientSkyColor: {r: 0.212, g: 0.227, b: 0.259, a: 1}\n  m_AmbientEquatorColor: {r: 0.114, g: 0.125, b: 0.133, a: 1}\n  m_AmbientGroundColor: {r: 0.047, g: 0.043, b: 0.035, a: 1}\n  m_AmbientIntensity: 1\n  m_AmbientMode: 3\n  m_SubtractiveShadowColor: {r: 0.42, g: 0.478, b: 0.627, a: 1}\n  m_SkyboxMaterial: {fileID: 0}\n  m_HaloStrength: 0.5\n  m_FlareStrength: 1\n  m_FlareFadeSpeed: 3\n  m_HaloTexture: {fileID: 0}\n  m_SpotCookie: {fileID: 10001, guid: 0000000000000000e000000000000000, type: 0}\n  m_DefaultReflectionMode: 0\n  m_DefaultReflectionResolution: 128\n  m_ReflectionBounces: 1\n  m_ReflectionIntensity: 1\n  m_CustomReflection: {fileID: 0}\n  m_Sun: {fileID: 0}\n  m_IndirectSpecularColor: {r: 0, g: 0, b: 0, a: 1}\n  m_UseRadianceAmbientProbe: 0\n--- !u!157 &3\nLightmapSettings:\n  m_ObjectHideFlags: 0\n  serializedVersion: 11\n  m_GIWorkflowMode: 1\n  m_GISettings:\n    serializedVersion: 2\n    m_BounceScale: 1\n    m_IndirectOutputScale: 1\n    m_AlbedoBoost: 1\n    m_EnvironmentLightingMode: 0\n    m_EnableBakedLightmaps: 0\n    m_EnableRealtimeLightmaps: 0\n  m_LightmapEditorSettings:\n    serializedVersion: 10\n    m_Resolution: 2\n    m_BakeResolution: 40\n    m_AtlasSize: 1024\n    m_AO: 0\n    m_AOMaxDistance: 1\n    m_CompAOExponent: 1\n    m_CompAOExponentDirect: 0\n    m_Padding: 2\n    m_LightmapParameters: {fileID: 0}\n    m_LightmapsBakeMode: 1\n    m_TextureCompression: 1\n    m_FinalGather: 0\n    m_FinalGatherFiltering: 1\n    m_FinalGatherRayCount: 256\n    m_ReflectionCompression: 2\n    m_MixedBakeMode: 2\n    m_BakeBackend: 0\n    m_PVRSampling: 1\n    m_PVRDirectSampleCount: 32\n    m_PVRSampleCount: 500\n    m_PVRBounces: 2\n    m_PVRFilterTypeDirect: 0\n    m_PVRFilterTypeIndirect: 0\n    m_PVRFilterTypeAO: 0\n    m_PVRFilteringMode: 1\n    m_PVRCulling: 1\n    m_PVRFilteringGaussRadiusDirect: 1\n    m_PVRFilteringGaussRadiusIndirect: 5\n    m_PVRFilteringGaussRadiusAO: 2\n    m_PVRFilteringAtrousPositionSigmaDirect: 0.5\n    m_PVRFilteringAtrousPositionSigmaIndirect: 2\n    m_PVRFilteringAtrousPositionSigmaAO: 1\n    m_ShowResolutionOverlay: 1\n  m_LightingDataAsset: {fileID: 0}\n  m_UseShadowmask: 1\n--- !u!196 &4\nNavMeshSettings:\n  serializedVersion: 2\n  m_ObjectHideFlags: 0\n  m_BuildSettings:\n    serializedVersion: 2\n    agentTypeID: 0\n    agentRadius: 0.5\n    agentHeight: 2\n    agentSlope: 45\n    agentClimb: 0.4\n    ledgeDropHeight: 0\n    maxJumpAcrossDistance: 0\n    minRegionArea: 2\n    manualCellSize: 0\n    cellSize: 0.16666667\n    manualTileSize: 0\n    tileSize: 256\n    accuratePlacement: 0\n    debug:\n      m_Flags: 0\n  m_NavMeshData: {fileID: 0}\n--- !u!1 &519420028\nGameObject:\n  m_ObjectHideFlags: 0\n  m_CorrespondingSourceObject: {fileID: 0}\n  m_PrefabInstance: {fileID: 0}\n  m_PrefabAsset: {fileID: 0}\n  serializedVersion: 6\n  m_Component:\n  - component: {fileID: 519420032}\n  - component: {fileID: 519420031}\n  - component: {fileID: 519420029}\n  - component: {fileID: 519420033}\n  - component: {fileID: 519420030}\n  m_Layer: 0\n  m_Name: Main Camera\n  m_TagString: MainCamera\n  m_Icon: {fileID: 0}\n  m_NavMeshLayer: 0\n  m_StaticEditorFlags: 0\n  m_IsActive: 1\n--- !u!81 &519420029\nAudioListener:\n  m_ObjectHideFlags: 0\n  m_CorrespondingSourceObject: {fileID: 0}\n  m_PrefabInstance: {fileID: 0}\n  m_PrefabAsset: {fileID: 0}\n  m_GameObject: {fileID: 519420028}\n  m_Enabled: 1\n--- !u!114 &519420030\nMonoBehaviour:\n  m_ObjectHideFlags: 0\n  m_CorrespondingSourceObject: {fileID: 0}\n  m_PrefabInstance: {fileID: 0}\n  m_PrefabAsset: {fileID: 0}\n  m_GameObject: {fileID: 519420028}\n  m_Enabled: 1\n  m_EditorHideFlags: 0\n  m_Script: {fileID: 11500000, guid: c47d1a2e288e53f4285b397665f9a1dc, type: 3}\n  m_Name: \n  m_EditorClassIdentifier: \n--- !u!20 &519420031\nCamera:\n  m_ObjectHideFlags: 0\n  m_CorrespondingSourceObject: {fileID: 0}\n  m_PrefabInstance: {fileID: 0}\n  m_PrefabAsset: {fileID: 0}\n  m_GameObject: {fileID: 519420028}\n  m_Enabled: 1\n  serializedVersion: 2\n  m_ClearFlags: 2\n  m_BackGroundColor: {r: 0.19215687, g: 0.3019608, b: 0.4745098, a: 0}\n  m_projectionMatrixMode: 1\n  m_SensorSize: {x: 36, y: 24}\n  m_LensShift: {x: 0, y: 0}\n  m_GateFitMode: 2\n  m_FocalLength: 50\n  m_NormalizedViewPortRect:\n    serializedVersion: 2\n    x: 0\n    y: 0\n    width: 1\n    height: 1\n  near clip plane: 0.3\n  far clip plane: 1000\n  field of view: 60\n  orthographic: 1\n  orthographic size: 5\n  m_Depth: -1\n  m_CullingMask:\n    serializedVersion: 2\n    m_Bits: 4294967295\n  m_RenderingPath: -1\n  m_TargetTexture: {fileID: 0}\n  m_TargetDisplay: 0\n  m_TargetEye: 0\n  m_HDR: 1\n  m_AllowMSAA: 0\n  m_AllowDynamicResolution: 0\n  m_ForceIntoRT: 0\n  m_OcclusionCulling: 0\n  m_StereoConvergence: 10\n  m_StereoSeparation: 0.022\n--- !u!4 &519420032\nTransform:\n  m_ObjectHideFlags: 0\n  m_CorrespondingSourceObject: {fileID: 0}\n  m_PrefabInstance: {fileID: 0}\n  m_PrefabAsset: {fileID: 0}\n  m_GameObject: {fileID: 519420028}\n  m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}\n  m_LocalPosition: {x: 0, y: 0, z: -10}\n  m_LocalScale: {x: 1, y: 1, z: 1}\n  m_Children: []\n  m_Father: {fileID: 0}\n  m_RootOrder: 0\n  m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}\n--- !u!114 &519420033\nMonoBehaviour:\n  m_ObjectHideFlags: 0\n  m_CorrespondingSourceObject: {fileID: 0}\n  m_PrefabInstance: {fileID: 0}\n  m_PrefabAsset: {fileID: 0}\n  m_GameObject: {fileID: 519420028}\n  m_Enabled: 1\n  m_EditorHideFlags: 0\n  m_Script: {fileID: 11500000, guid: 151d2db7aab5bc8438bc039443ba0e3b, type: 3}\n  m_Name: \n  m_EditorClassIdentifier: \n  Protocol: 1\n"
  },
  {
    "path": "Assets/Scenes/Client.unity.meta",
    "content": "fileFormatVersion: 2\nguid: 53e18807e282965479d061608139b64b\nDefaultImporter:\n  externalObjects: {}\n  userData: \n  assetBundleName: \n  assetBundleVariant: \n"
  },
  {
    "path": "Assets/Scenes/Server.unity",
    "content": "%YAML 1.1\n%TAG !u! tag:unity3d.com,2011:\n--- !u!29 &1\nOcclusionCullingSettings:\n  m_ObjectHideFlags: 0\n  serializedVersion: 2\n  m_OcclusionBakeSettings:\n    smallestOccluder: 5\n    smallestHole: 0.25\n    backfaceThreshold: 100\n  m_SceneGUID: 00000000000000000000000000000000\n  m_OcclusionCullingData: {fileID: 0}\n--- !u!104 &2\nRenderSettings:\n  m_ObjectHideFlags: 0\n  serializedVersion: 9\n  m_Fog: 0\n  m_FogColor: {r: 0.5, g: 0.5, b: 0.5, a: 1}\n  m_FogMode: 3\n  m_FogDensity: 0.01\n  m_LinearFogStart: 0\n  m_LinearFogEnd: 300\n  m_AmbientSkyColor: {r: 0.212, g: 0.227, b: 0.259, a: 1}\n  m_AmbientEquatorColor: {r: 0.114, g: 0.125, b: 0.133, a: 1}\n  m_AmbientGroundColor: {r: 0.047, g: 0.043, b: 0.035, a: 1}\n  m_AmbientIntensity: 1\n  m_AmbientMode: 3\n  m_SubtractiveShadowColor: {r: 0.42, g: 0.478, b: 0.627, a: 1}\n  m_SkyboxMaterial: {fileID: 0}\n  m_HaloStrength: 0.5\n  m_FlareStrength: 1\n  m_FlareFadeSpeed: 3\n  m_HaloTexture: {fileID: 0}\n  m_SpotCookie: {fileID: 10001, guid: 0000000000000000e000000000000000, type: 0}\n  m_DefaultReflectionMode: 0\n  m_DefaultReflectionResolution: 128\n  m_ReflectionBounces: 1\n  m_ReflectionIntensity: 1\n  m_CustomReflection: {fileID: 0}\n  m_Sun: {fileID: 0}\n  m_IndirectSpecularColor: {r: 0, g: 0, b: 0, a: 1}\n  m_UseRadianceAmbientProbe: 0\n--- !u!157 &3\nLightmapSettings:\n  m_ObjectHideFlags: 0\n  serializedVersion: 12\n  m_GIWorkflowMode: 1\n  m_GISettings:\n    serializedVersion: 2\n    m_BounceScale: 1\n    m_IndirectOutputScale: 1\n    m_AlbedoBoost: 1\n    m_EnvironmentLightingMode: 0\n    m_EnableBakedLightmaps: 0\n    m_EnableRealtimeLightmaps: 0\n  m_LightmapEditorSettings:\n    serializedVersion: 12\n    m_Resolution: 2\n    m_BakeResolution: 40\n    m_AtlasSize: 1024\n    m_AO: 0\n    m_AOMaxDistance: 1\n    m_CompAOExponent: 1\n    m_CompAOExponentDirect: 0\n    m_ExtractAmbientOcclusion: 0\n    m_Padding: 2\n    m_LightmapParameters: {fileID: 0}\n    m_LightmapsBakeMode: 1\n    m_TextureCompression: 1\n    m_FinalGather: 0\n    m_FinalGatherFiltering: 1\n    m_FinalGatherRayCount: 256\n    m_ReflectionCompression: 2\n    m_MixedBakeMode: 2\n    m_BakeBackend: 1\n    m_PVRSampling: 1\n    m_PVRDirectSampleCount: 32\n    m_PVRSampleCount: 512\n    m_PVRBounces: 2\n    m_PVREnvironmentSampleCount: 256\n    m_PVREnvironmentReferencePointCount: 2048\n    m_PVRFilteringMode: 1\n    m_PVRDenoiserTypeDirect: 1\n    m_PVRDenoiserTypeIndirect: 1\n    m_PVRDenoiserTypeAO: 1\n    m_PVRFilterTypeDirect: 0\n    m_PVRFilterTypeIndirect: 0\n    m_PVRFilterTypeAO: 0\n    m_PVREnvironmentMIS: 1\n    m_PVRCulling: 1\n    m_PVRFilteringGaussRadiusDirect: 1\n    m_PVRFilteringGaussRadiusIndirect: 5\n    m_PVRFilteringGaussRadiusAO: 2\n    m_PVRFilteringAtrousPositionSigmaDirect: 0.5\n    m_PVRFilteringAtrousPositionSigmaIndirect: 2\n    m_PVRFilteringAtrousPositionSigmaAO: 1\n    m_ExportTrainingData: 0\n    m_TrainingDataDestination: TrainingData\n    m_LightProbeSampleCountMultiplier: 4\n  m_LightingDataAsset: {fileID: 0}\n  m_LightingSettings: {fileID: 0}\n--- !u!196 &4\nNavMeshSettings:\n  serializedVersion: 2\n  m_ObjectHideFlags: 0\n  m_BuildSettings:\n    serializedVersion: 2\n    agentTypeID: 0\n    agentRadius: 0.5\n    agentHeight: 2\n    agentSlope: 45\n    agentClimb: 0.4\n    ledgeDropHeight: 0\n    maxJumpAcrossDistance: 0\n    minRegionArea: 2\n    manualCellSize: 0\n    cellSize: 0.16666667\n    manualTileSize: 0\n    tileSize: 256\n    accuratePlacement: 0\n    maxJobWorkers: 0\n    preserveTilesOutsideBounds: 0\n    debug:\n      m_Flags: 0\n  m_NavMeshData: {fileID: 0}\n--- !u!1 &2029809421\nGameObject:\n  m_ObjectHideFlags: 0\n  m_CorrespondingSourceObject: {fileID: 0}\n  m_PrefabInstance: {fileID: 0}\n  m_PrefabAsset: {fileID: 0}\n  serializedVersion: 6\n  m_Component:\n  - component: {fileID: 2029809422}\n  - component: {fileID: 2029809424}\n  - component: {fileID: 2029809423}\n  m_Layer: 0\n  m_Name: ServerManager\n  m_TagString: Untagged\n  m_Icon: {fileID: 0}\n  m_NavMeshLayer: 0\n  m_StaticEditorFlags: 0\n  m_IsActive: 1\n--- !u!4 &2029809422\nTransform:\n  m_ObjectHideFlags: 0\n  m_CorrespondingSourceObject: {fileID: 0}\n  m_PrefabInstance: {fileID: 0}\n  m_PrefabAsset: {fileID: 0}\n  m_GameObject: {fileID: 2029809421}\n  m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}\n  m_LocalPosition: {x: 0, y: 0, z: 0}\n  m_LocalScale: {x: 1, y: 1, z: 1}\n  m_ConstrainProportionsScale: 0\n  m_Children: []\n  m_Father: {fileID: 0}\n  m_RootOrder: 0\n  m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}\n--- !u!114 &2029809423\nMonoBehaviour:\n  m_ObjectHideFlags: 0\n  m_CorrespondingSourceObject: {fileID: 0}\n  m_PrefabInstance: {fileID: 0}\n  m_PrefabAsset: {fileID: 0}\n  m_GameObject: {fileID: 2029809421}\n  m_Enabled: 1\n  m_EditorHideFlags: 0\n  m_Script: {fileID: 11500000, guid: 8518a6a6d5ca49ecb048613cc3a03e07, type: 3}\n  m_Name: \n  m_EditorClassIdentifier: \n--- !u!114 &2029809424\nMonoBehaviour:\n  m_ObjectHideFlags: 0\n  m_CorrespondingSourceObject: {fileID: 0}\n  m_PrefabInstance: {fileID: 0}\n  m_PrefabAsset: {fileID: 0}\n  m_GameObject: {fileID: 2029809421}\n  m_Enabled: 1\n  m_EditorHideFlags: 0\n  m_Script: {fileID: 11500000, guid: 84fd225f324f42999874057579220d14, type: 3}\n  m_Name: \n  m_EditorClassIdentifier: \n"
  },
  {
    "path": "Assets/Scenes/Server.unity.meta",
    "content": "fileFormatVersion: 2\nguid: 7a58935d2e567f043835f59be74400f6\nDefaultImporter:\n  externalObjects: {}\n  userData: \n  assetBundleName: \n  assetBundleVariant: \n"
  },
  {
    "path": "Assets/Scenes.meta",
    "content": "fileFormatVersion: 2\nguid: e865c368c8aa84b4783f03af294abb01\nfolderAsset: yes\nDefaultImporter:\n  externalObjects: {}\n  userData: \n  assetBundleName: \n  assetBundleVariant: \n"
  },
  {
    "path": "Assets/Scripts/Helper/ByteHelper.cs",
    "content": "﻿using System.Text;\n\nnamespace UNetwork\n{\n\tpublic static class ByteHelper\n\t{\n\t\tpublic static string ToHex(this byte b)\n\t\t{\n\t\t\treturn b.ToString(\"X2\");\n\t\t}\n\n\t\tpublic static string ToHex(this byte[] bytes)\n\t\t{\n\t\t\tStringBuilder stringBuilder = new StringBuilder();\n\t\t\tforeach (byte b in bytes)\n\t\t\t{\n\t\t\t\tstringBuilder.Append(b.ToString(\"X2\"));\n\t\t\t}\n\t\t\treturn stringBuilder.ToString();\n\t\t}\n\n\t\tpublic static string ToHex(this byte[] bytes, string format)\n\t\t{\n\t\t\tStringBuilder stringBuilder = new StringBuilder();\n\t\t\tforeach (byte b in bytes)\n\t\t\t{\n\t\t\t\tstringBuilder.Append(b.ToString(format));\n\t\t\t}\n\t\t\treturn stringBuilder.ToString();\n\t\t}\n\n\t\tpublic static string ToHex(this byte[] bytes, int offset, int count)\n\t\t{\n\t\t\tStringBuilder stringBuilder = new StringBuilder();\n\t\t\tfor (int i = offset; i < offset + count; ++i)\n\t\t\t{\n\t\t\t\tstringBuilder.Append(bytes[i].ToString(\"X2\"));\n\t\t\t}\n\t\t\treturn stringBuilder.ToString();\n\t\t}\n\n\t\tpublic static string ToStr(this byte[] bytes)\n\t\t{\n\t\t\treturn Encoding.Default.GetString(bytes);\n\t\t}\n\n\t\tpublic static string ToStr(this byte[] bytes, int index, int count)\n\t\t{\n\t\t\treturn Encoding.Default.GetString(bytes, index, count);\n\t\t}\n\n\t\tpublic static string Utf8ToStr(this byte[] bytes)\n\t\t{\n\t\t\treturn Encoding.UTF8.GetString(bytes);\n\t\t}\n\n\t\tpublic static string Utf8ToStr(this byte[] bytes, int index, int count)\n\t\t{\n\t\t\treturn Encoding.UTF8.GetString(bytes, index, count);\n\t\t}\n\n\t\tpublic static void WriteTo(this byte[] bytes, int offset, uint num)\n\t\t{\n\t\t\tbytes[offset] = (byte)(num & 0xff);\n\t\t\tbytes[offset + 1] = (byte)((num & 0xff00) >> 8);\n\t\t\tbytes[offset + 2] = (byte)((num & 0xff0000) >> 16);\n\t\t\tbytes[offset + 3] = (byte)((num & 0xff000000) >> 24);\n\t\t}\n\t\t\n\t\tpublic static void WriteTo(this byte[] bytes, int offset, int num)\n\t\t{\n\t\t\tbytes[offset] = (byte)(num & 0xff);\n\t\t\tbytes[offset + 1] = (byte)((num & 0xff00) >> 8);\n\t\t\tbytes[offset + 2] = (byte)((num & 0xff0000) >> 16);\n\t\t\tbytes[offset + 3] = (byte)((num & 0xff000000) >> 24);\n\t\t}\n\t\t\n\t\tpublic static void WriteTo(this byte[] bytes, int offset, byte num)\n\t\t{\n\t\t\tbytes[offset] = num;\n\t\t}\n\t\t\n\t\tpublic static void WriteTo(this byte[] bytes, int offset, short num)\n\t\t{\n\t\t\tbytes[offset] = (byte)(num & 0xff);\n\t\t\tbytes[offset + 1] = (byte)((num & 0xff00) >> 8);\n\t\t}\n\t\t\n\t\tpublic static void WriteTo(this byte[] bytes, int offset, ushort num)\n\t\t{\n\t\t\tbytes[offset] = (byte)(num & 0xff);\n\t\t\tbytes[offset + 1] = (byte)((num & 0xff00) >> 8);\n\t\t}\n\t}\n}"
  },
  {
    "path": "Assets/Scripts/Helper/ByteHelper.cs.meta",
    "content": "fileFormatVersion: 2\nguid: c31f9c17b9d7c0e45b3b3f77811cb795\nMonoImporter:\n  externalObjects: {}\n  serializedVersion: 2\n  defaultReferences: []\n  executionOrder: 0\n  icon: {instanceID: 0}\n  userData: \n  assetBundleName: \n  assetBundleVariant: \n"
  },
  {
    "path": "Assets/Scripts/Helper/MonoSingleton.cs",
    "content": "using UnityEngine;\n\npublic abstract class MonoSingleton<T> : MonoBehaviour where T : MonoSingleton<T>\n{\n\tprivate static T mInstance = null;\n\n\tpublic static T Instance\n    {\n        get\n        {\n\t\t\tif (mInstance == null)\n            {\n            \tmInstance = GameObject.FindObjectOfType(typeof(T)) as T;\n                if (mInstance == null)\n                {\n                    GameObject go = new GameObject(typeof(T).Name);\n                    mInstance = go.AddComponent<T>();\n                    \n                    GameObject parent = GameObject.Find(\"Boot\");\n                    if (parent != null)\n                    {\n                        go.transform.parent = parent.transform;\n                    }\n                }\n            }\n\n            return mInstance;\n        }\n    }\n\n    /*\n     * 没有任何实现的函数，用于保证MonoSingleton在使用前已创建\n     */\n    public void Startup()\n    {\n\n    }\n\n    private void Awake()\n    {\n        if (mInstance == null)\n        {\n            mInstance = this as T;\n        }\n\n        DontDestroyOnLoad(gameObject);\n        Init();\n    }\n\n    public virtual void Init()\n    {\n\n    }\n\n    public void DestroySelf()\n    {\n        Dispose();\n        mInstance = null;\n        Destroy(gameObject);\n    }\n\n    public virtual void Dispose()\n    {\n\n    }\n\n}"
  },
  {
    "path": "Assets/Scripts/Helper/MonoSingleton.cs.meta",
    "content": "fileFormatVersion: 2\nguid: bbde26f5f28b63743b17d086fd52d70a\nMonoImporter:\n  externalObjects: {}\n  serializedVersion: 2\n  defaultReferences: []\n  executionOrder: 0\n  icon: {instanceID: 0}\n  userData: \n  assetBundleName: \n  assetBundleVariant: \n"
  },
  {
    "path": "Assets/Scripts/Helper/NetworkHelper.cs",
    "content": "﻿using System.Net;\n\nnamespace UNetwork\n{\n\tpublic static class NetworkHelper\n\t{\n\t\tpublic static IPEndPoint ToIPEndPoint(string host, int port)\n\t\t{\n\t\t\treturn new IPEndPoint(IPAddress.Parse(host), port);\n\t\t}\n\n\t\tpublic static IPEndPoint ToIPEndPoint(string address)\n\t\t{\n\t\t\tint index = address.LastIndexOf(':');\n\t\t\tstring host = address.Substring(0, index);\n\t\t\tstring p = address.Substring(index + 1);\n\t\t\tint port = int.Parse(p);\n\t\t\treturn ToIPEndPoint(host, port);\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "Assets/Scripts/Helper/NetworkHelper.cs.meta",
    "content": "fileFormatVersion: 2\nguid: 5f24e42ac059909498f35805b76f9102\ntimeCreated: 1511941046\nlicenseType: Free\nMonoImporter:\n  serializedVersion: 2\n  defaultReferences: []\n  executionOrder: 0\n  icon: {instanceID: 0}\n  userData: \n  assetBundleName: \n  assetBundleVariant: \n"
  },
  {
    "path": "Assets/Scripts/Helper/RandomHelper.cs",
    "content": "﻿using System;\n\nnamespace UNetwork\n{\n\tpublic static class RandomHelper\n\t{\n\t\tprivate static readonly Random random = new Random();\n\n\t\tpublic static UInt64 RandUInt64()\n\t\t{\n\t\t\tvar bytes = new byte[8];\n\t\t\trandom.NextBytes(bytes);\n\t\t\treturn BitConverter.ToUInt64(bytes, 0);\n\t\t}\n\n\t\tpublic static Int64 RandInt64()\n\t\t{\n\t\t\tvar bytes = new byte[8];\n\t\t\trandom.NextBytes(bytes);\n\t\t\treturn BitConverter.ToInt64(bytes, 0);\n\t\t}\n\n\t\t/// <summary>\n\t\t/// 获取lower与Upper之间的随机数\n\t\t/// </summary>\n\t\t/// <param name=\"lower\"></param>\n\t\t/// <param name=\"upper\"></param>\n\t\t/// <returns></returns>\n\t\tpublic static int RandomNumber(int lower, int upper)\n\t\t{\n\t\t\tint value = random.Next(lower, upper);\n\t\t\treturn value;\n\t\t}\n\t}\n}"
  },
  {
    "path": "Assets/Scripts/Helper/RandomHelper.cs.meta",
    "content": "fileFormatVersion: 2\nguid: ec912a7512f681b43bb4cc56873631af\nMonoImporter:\n  externalObjects: {}\n  serializedVersion: 2\n  defaultReferences: []\n  executionOrder: 0\n  icon: {instanceID: 0}\n  userData: \n  assetBundleName: \n  assetBundleVariant: \n"
  },
  {
    "path": "Assets/Scripts/Helper/TimeHelper.cs",
    "content": "﻿using System;\n\nnamespace UNetwork\n{\n\tpublic static class TimeHelper\n\t{\n\t\tprivate static readonly long epoch = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc).Ticks;\n\t\t/// <summary>\n\t\t/// 客户端时间\n\t\t/// </summary>\n\t\t/// <returns></returns>\n\t\tpublic static long ClientNow()\n\t\t{\n\t\t\treturn (DateTime.UtcNow.Ticks - epoch) / 10000;\n\t\t}\n\n\t\tpublic static long ClientNowSeconds()\n\t\t{\n\t\t\treturn (DateTime.UtcNow.Ticks - epoch) / 10000000;\n\t\t}\n\n\t\tpublic static long Now()\n\t\t{\n\t\t\treturn ClientNow();\n\t\t}\n    }\n}"
  },
  {
    "path": "Assets/Scripts/Helper/TimeHelper.cs.meta",
    "content": "fileFormatVersion: 2\nguid: 53e39d24f06688d4d961473c5a977410\nMonoImporter:\n  externalObjects: {}\n  serializedVersion: 2\n  defaultReferences: []\n  executionOrder: 0\n  icon: {instanceID: 0}\n  userData: \n  assetBundleName: \n  assetBundleVariant: \n"
  },
  {
    "path": "Assets/Scripts/Helper.meta",
    "content": "fileFormatVersion: 2\nguid: 3a479f7d4b18e0f439a7ba86124bebe4\nfolderAsset: yes\nDefaultImporter:\n  externalObjects: {}\n  userData: \n  assetBundleName: \n  assetBundleVariant: \n"
  },
  {
    "path": "Assets/Scripts/Network/AChannel.cs",
    "content": "﻿using System;\nusing System.IO;\nusing System.Net;\nusing UnityEngine;\n\nnamespace UNetwork\n{\n    public enum ChannelType\n    {\n        Connect,\n        Accept,\n    }\n\n    public class AChannel\n    {\n        public ChannelType ChannelType { get; }\n\n        public AService Service { get; }\n\n        protected MemoryStream memoryStream;\n        public MemoryStream Stream => this.memoryStream;\n\n        public int Error { get; set; }\n\n        public IPEndPoint RemoteAddress { get; protected set; }\n\n\n        private Action<AChannel, int> connectCallback;\n\n        public event Action<AChannel, int> ConnectCallback\n        {\n            add { this.connectCallback += value; }\n            remove { this.connectCallback -= value; }\n        }\n\n\n        private Action<AChannel, int> errorCallback;\n\n        public event Action<AChannel, int> ErrorCallback\n        {\n            add { this.errorCallback += value; }\n            remove { this.errorCallback -= value; }\n        }\n\n        private Action<MemoryStream> readCallback;\n\n        public event Action<MemoryStream> ReadCallback\n        {\n            add { this.readCallback += value; }\n            remove { this.readCallback -= value; }\n        }\n\n        public void OnConnect(int code)\n        {\n            Debug.Log(\"connect success\");\n            this.connectCallback.Invoke(this, code);\n        }\n\n        protected void OnRead(MemoryStream memoryStream)\n        {\n            this.readCallback.Invoke(memoryStream);\n        }\n\n        protected void OnError(int e)\n        {\n            this.Error = e;\n            this.errorCallback?.Invoke(this, e);\n        }\n\n        protected AChannel(AService service, ChannelType channelType)\n        {\n            this.ChannelType = channelType;\n            this.Service = service;\n        }\n\n        public virtual void Start()\n        {\n        }\n\n        public virtual void Send(MemoryStream stream)\n        {\n        }\n\n        public virtual void Dispose()\n        {\n            this.Service.Dispose();\n        }\n    }\n}"
  },
  {
    "path": "Assets/Scripts/Network/AChannel.cs.meta",
    "content": "fileFormatVersion: 2\nguid: 05a074f965a8c214f8f1f1af1ecf4dcc\ntimeCreated: 1503986857\nlicenseType: Free\nMonoImporter:\n  serializedVersion: 2\n  defaultReferences: []\n  executionOrder: 0\n  icon: {instanceID: 0}\n  userData: \n  assetBundleName: \n  assetBundleVariant: \n"
  },
  {
    "path": "Assets/Scripts/Network/AService.cs",
    "content": "﻿using System;\nusing System.Net;\n\nnamespace UNetwork\n{\n\tpublic enum NetworkProtocol\n\t{\n\t\tKCP,\n\t\tTCP,\n\t\tWebSocket,\n\t}\n\n\tpublic abstract class AService\n\t{\n\t\tpublic abstract AChannel GetChannel();\n\n\t\tpublic abstract AChannel ConnectChannel(IPEndPoint ipEndPoint);\n\t\t\n\t\tpublic abstract AChannel ConnectChannel(string address);\n\n\t\tpublic abstract void Update();\n\n\t\tpublic abstract void Dispose();\n\t}\n}"
  },
  {
    "path": "Assets/Scripts/Network/AService.cs.meta",
    "content": "fileFormatVersion: 2\nguid: b5af0de1c471e6c43989dc0349cc2bba\ntimeCreated: 1503986858\nlicenseType: Free\nMonoImporter:\n  serializedVersion: 2\n  defaultReferences: []\n  executionOrder: 0\n  icon: {instanceID: 0}\n  userData: \n  assetBundleName: \n  assetBundleVariant: \n"
  },
  {
    "path": "Assets/Scripts/Network/ClientManager.cs",
    "content": "﻿using System;\nusing System.Net;\nusing System.Threading;\n\nnamespace UNetwork\n{\n    /// <summary>\n    /// 客户端的 业务逻辑 管理类\n    /// </summary>\n    public class ClientManager : MonoSingleton<ClientManager>, INetworkManager\n    {\n        public AService Service { get; private set; }\n        public Session Session { get; private set; }\n\n        public IMessagePacker MessagePacker { get; set; }\n        public IMessageDispatcher MessageDispatcher { get; set; }\n\n        public Action<int> OnConnect { get; set; }\n        public Action<int> OnError { get; set; }\n        public Action<byte[]> OnMessage { get; set; }\n\n\n        public override void Init()\n        {\n            SynchronizationContext.SetSynchronizationContext(OneThreadSynchronizationContext.Instance);\n        }\n\n        //clinet\n        public void InitService(NetworkProtocol protocol, int packetSize = Packet.PacketSizeLength4)\n        {\n            switch (protocol)\n            {\n                case NetworkProtocol.KCP:\n                    this.Service = new KService() { };\n                    break;\n                case NetworkProtocol.TCP:\n                    this.Service = new TService(packetSize) { };\n                    break;\n                case NetworkProtocol.WebSocket:\n                    this.Service = new WService() { };\n                    break;\n            }\n        }\n\n        /// <summary>\n        /// 创建一个新Session\n        /// </summary>\n        public void Connect(IPEndPoint ipEndPoint)\n        {\n            AChannel channel = this.Service.ConnectChannel(ipEndPoint);\n            Session = new Session(channel);\n            Session.Start(this);\n        }\n\n        /// <summary>\n        /// 创建一个新Session\n        /// </summary>\n        public void Connect(string address)\n        {\n            AChannel channel = this.Service.ConnectChannel(address);\n            Session = new Session(channel);\n            Session.Start(this);\n        }\n\n        public void Connect(string ip, int port)\n        {\n            AChannel channel = this.Service.ConnectChannel(NetworkHelper.ToIPEndPoint(ip, port));\n            Session = new Session(channel);\n            Session.Start(this);\n        }\n\n        public void Update()\n        {\n            OneThreadSynchronizationContext.Instance.Update();\n\n            if (this.Service == null)\n            {\n                return;\n            }\n\n            this.Service.Update();\n        }\n\n        public void Send(byte[] data)\n        {\n            Session.Send(data);\n        }\n\n        public override void Dispose()\n        {\n            Session.Dispose();\n        }\n    }\n}"
  },
  {
    "path": "Assets/Scripts/Network/ClientManager.cs.meta",
    "content": "fileFormatVersion: 2\nguid: 151d2db7aab5bc8438bc039443ba0e3b\ntimeCreated: 1498117616\nlicenseType: Free\nMonoImporter:\n  serializedVersion: 2\n  defaultReferences: []\n  executionOrder: 0\n  icon: {instanceID: 0}\n  userData: \n  assetBundleName: \n  assetBundleVariant: \n"
  },
  {
    "path": "Assets/Scripts/Network/Core/KCP/KCP.cs",
    "content": "// Copyright (C) 2017 prototyped.cn All rights reserved.\n// Distributed under the terms and conditions of the MIT License.\n// See accompanying files LICENSE.\n\nusing System;\nusing System.Diagnostics;\nusing System.Collections.Generic;\n\nnamespace UNetwork\n{\n    public class KCP\n    {\n        public const int IKCP_RTO_NDL = 30;         // no delay min rto\n        public const int IKCP_RTO_MIN = 100;        // normal min rto\n        public const int IKCP_RTO_DEF = 200;\n        public const int IKCP_RTO_MAX = 60000;\n        public const int IKCP_CMD_PUSH = 81;        // cmd: push data\n        public const int IKCP_CMD_ACK = 82;         // cmd: ack\n        public const int IKCP_CMD_WASK = 83;        // cmd: window probe (ask)\n        public const int IKCP_CMD_WINS = 84;        // cmd: window size (tell)\n        public const int IKCP_ASK_SEND = 1;         // need to send IKCP_CMD_WASK\n        public const int IKCP_ASK_TELL = 2;         // need to send IKCP_CMD_WINS\n        public const int IKCP_WND_SND = 32;\n        public const int IKCP_WND_RCV = 32;\n        public const int IKCP_MTU_DEF = 1400;\n        public const int IKCP_ACK_FAST = 3;\n        public const int IKCP_INTERVAL = 100;\n        public const int IKCP_OVERHEAD = 24;\n        public const int IKCP_DEADLINK = 20;\n        public const int IKCP_THRESH_INIT = 2;\n        public const int IKCP_THRESH_MIN = 2;\n        public const int IKCP_PROBE_INIT = 7000;    // 7 secs to probe window size\n        public const int IKCP_PROBE_LIMIT = 120000; // up to 120 secs to probe window\n\n        public const int IKCP_LOG_OUTPUT = 0x1;\n        public const int IKCP_LOG_INPUT = 0x2;\n        public const int IKCP_LOG_SEND = 0x4;\n        public const int IKCP_LOG_RECV = 0x8;\n        public const int IKCP_LOG_IN_DATA = 0x10;\n        public const int IKCP_LOG_IN_ACK = 0x20;\n        public const int IKCP_LOG_IN_PROBE = 0x40;\n        public const int IKCP_LOG_IN_WINS = 0x80;\n        public const int IKCP_LOG_OUT_DATA = 0x100;\n        public const int IKCP_LOG_OUT_ACK = 0x200;\n        public const int IKCP_LOG_OUT_PROBE = 0x400;\n        public const int IKCP_LOG_OUT_WINS = 0x800;\n\n\n        // encode 8 bits unsigned int\n        public static void ikcp_encode8u(byte[] p, int offset, byte c)\n        {\n            p[offset] = c;\n        }\n\n        // decode 8 bits unsigned int\n        public static byte ikcp_decode8u(byte[] p, ref int offset)\n        {\n            return p[offset++];\n        }\n\n        // encode 16 bits unsigned int (lsb)\n        public static void ikcp_encode16u(byte[] p, int offset, UInt16 v)\n        {\n            p[offset] = (byte)(v & 0xFF);\n            p[offset + 1] = (byte)(v >> 8);\n        }\n\n        // decode 16 bits unsigned int (lsb)\n        public static UInt16 ikcp_decode16u(byte[] p, ref int offset)\n        {\n            int pos = offset;\n            offset += 2;\n            return (UInt16)((UInt16)p[pos] | (UInt16)(p[pos + 1] << 8));\n        }\n\n        // encode 32 bits unsigned int (lsb)\n        public static void ikcp_encode32u(byte[] p, int offset, UInt32 l)\n        {\n            p[offset] = (byte)(l & 0xFF);\n            p[offset + 1] = (byte)(l >> 8);\n            p[offset + 2] = (byte)(l >> 16);\n            p[offset + 3] = (byte)(l >> 24);\n        }\n\n        // decode 32 bits unsigned int (lsb)\n        public static UInt32 ikcp_decode32u(byte[] p, ref int offset)\n        {\n            int pos = offset;\n            offset += 4;\n            return ((UInt32)p[pos] | (UInt32)(p[pos + 1] << 8)\n                | (UInt32)(p[pos + 2] << 16) | (UInt32)(p[pos + 3] << 24));\n        }\n\n        public static UInt32 _imin_(UInt32 a, UInt32 b)\n        {\n            return a <= b ? a : b;\n        }\n\n        public static UInt32 _imax_(UInt32 a, UInt32 b)\n        {\n            return a >= b ? a : b;\n        }\n\n        public static UInt32 _ibound_(UInt32 lower, UInt32 middle, UInt32 upper)\n        {\n            return _imin_(_imax_(lower, middle), upper);\n        }\n\n        public static Int32 _itimediff(UInt32 later, UInt32 earlier)\n        {\n            return (Int32)(later - earlier);\n        }\n\n        internal class Segment\n        {\n            internal UInt32 conv = 0;\n            internal UInt32 cmd = 0;\n            internal UInt32 frg = 0;\n            internal UInt32 wnd = 0;\n            internal UInt32 ts = 0;\n            internal UInt32 sn = 0;\n            internal UInt32 una = 0;\n            internal UInt32 resendts = 0;\n            internal UInt32 rto = 0;\n            internal UInt32 faskack = 0;\n            internal UInt32 xmit = 0;\n            internal byte[] data;\n\n            internal Segment(int size = 0)\n            {\n                data = new byte[size];\n            }\n\n            internal void Encode(byte[] ptr, ref int offset)\n            {\n                UInt32 len = (UInt32)data.Length;\n                ikcp_encode32u(ptr, offset, conv);\n                ikcp_encode8u(ptr, offset + 4, (byte)cmd);\n                ikcp_encode8u(ptr, offset + 5, (byte)frg);\n                ikcp_encode16u(ptr, offset + 6, (UInt16)wnd);\n                ikcp_encode32u(ptr, offset + 8, ts);\n                ikcp_encode32u(ptr, offset + 12, sn);\n                ikcp_encode32u(ptr, offset + 16, una);\n                ikcp_encode32u(ptr, offset + 20, len);\n                offset += IKCP_OVERHEAD;\n            }\n\n        }\n\n        UInt32 conv_ = 0;\n        UInt32 mtu_ = 0;\n        UInt32 mss_ = 0;\n        UInt32 state_ = 0;\n\n        UInt32 snd_una_ = 0;\n        UInt32 snd_nxt_ = 0;\n        UInt32 rcv_nxt_ = 0;\n\n        UInt32 ts_recent_ = 0;\n        UInt32 ts_lastack_ = 0;\n        UInt32 ssthresh_ = 0;\n\n        Int32 rx_rttval_ = 0;\n        Int32 rx_srtt_ = 0;\n        Int32 rx_rto_ = 0;\n        Int32 rx_minrto_ = 0;\n\n        UInt32 snd_wnd_ = 0;\n        UInt32 rcv_wnd_ = 0;\n        UInt32 rmt_wnd_ = 0;\n        UInt32 cwnd_ = 0;\n        UInt32 probe_ = 0;\n\n        UInt32 current_ = 0;\n        UInt32 interval_ = 0;\n        UInt32 ts_flush_ = 0;\n        UInt32 xmit_ = 0;\n\n        UInt32 nrcv_buf_ = 0;\n        UInt32 nsnd_buf_ = 0;\n        UInt32 nrcv_que_ = 0;\n        UInt32 nsnd_que_ = 0;\n\n        UInt32 nodelay_ = 0;\n        UInt32 updated_ = 0;\n        UInt32 ts_probe_ = 0;\n        UInt32 probe_wait_ = 0;\n        UInt32 dead_link_ = 0;\n        UInt32 incr_ = 0;\n\n        LinkedList<Segment> snd_queue_;\n        LinkedList<Segment> rcv_queue_;\n        LinkedList<Segment> snd_buf_;\n        LinkedList<Segment> rcv_buf_;\n\n        UInt32[] acklist_;\n        UInt32 ackcount_ = 0;\n        UInt32 ackblock_ = 0;\n\n        byte[] buffer_;\n        object user_;\n\n        Int32 fastresend_ = 0;\n        Int32 nocwnd_ = 0;\n\n        public delegate void OutputDelegate(byte[] data, int size, object user);\n        OutputDelegate output_;\n\n        // create a new kcp control object, 'conv' must equal in two endpoint\n        // from the same connection. 'user' will be passed to the output callback\n        // output callback can be setup like this: 'kcp->output = my_udp_output'\n        public KCP(UInt32 conv, object user)\n        {\n            Debug.Assert(BitConverter.IsLittleEndian); // we only support little endian device\n\n            user_ = user;\n            conv_ = conv;\n            snd_wnd_ = IKCP_WND_SND;\n            rcv_wnd_ = IKCP_WND_RCV;\n            rmt_wnd_ = IKCP_WND_RCV;\n            mtu_ = IKCP_MTU_DEF;\n            mss_ = mtu_ - IKCP_OVERHEAD;\n            rx_rto_ = IKCP_RTO_DEF;\n            rx_minrto_ = IKCP_RTO_MIN;\n            interval_ = IKCP_INTERVAL;\n            ts_flush_ = IKCP_INTERVAL;\n            ssthresh_ = IKCP_THRESH_INIT;\n            dead_link_ = IKCP_DEADLINK;\n            buffer_ = new byte[(mtu_ + IKCP_OVERHEAD) * 3];\n            snd_queue_ = new LinkedList<Segment>();\n            rcv_queue_ = new LinkedList<Segment>();\n            snd_buf_ = new LinkedList<Segment>();\n            rcv_buf_ = new LinkedList<Segment>();\n        }\n\n        // release kcp control object\n        public void Release()\n        {\n            snd_buf_.Clear();\n            rcv_buf_.Clear();\n            snd_queue_.Clear();\n            rcv_queue_.Clear();\n            nrcv_buf_ = 0;\n            nsnd_buf_ = 0;\n            nrcv_que_ = 0;\n            nsnd_que_ = 0;\n            ackblock_ = 0;\n            ackcount_ = 0;\n            buffer_ = null;\n            acklist_ = null;\n        }\n\n        // set output callback, which will be invoked by kcp\n        public void SetOutput(OutputDelegate output)\n        {\n            output_ = output;\n        }\n\n        // user/upper level recv: returns size, returns below zero for EAGAIN\n        public int Recv(byte[] buffer, int offset, int len)\n        {\n            int ispeek = (len < 0 ? 1 : 0);\n            int recover = 0;\n\n            if (rcv_queue_.Count == 0)\n                return -1;\n\n            if (len < 0)\n                len = -len;\n\n            int peeksize = PeekSize();\n            if (peeksize < 0)\n                return -2;\n\n            if (peeksize > len)\n                return -3;\n\n            if (nrcv_que_ >= rcv_wnd_)\n                recover = 1;\n\n            // merge fragment\n            len = 0;\n            LinkedListNode<Segment> next = null;\n            for (var node = rcv_queue_.First; node != null; node = next)\n            {\n                int fragment = 0;\n                var seg = node.Value;\n                next = node.Next;\n                \n                if (buffer != null)\n                {\n                    Buffer.BlockCopy(seg.data, 0, buffer, offset, seg.data.Length);\n                    offset += seg.data.Length;\n                }\n                len += seg.data.Length;\n                fragment = (int)seg.frg;\n\n                Log(IKCP_LOG_RECV, \"recv sn={0}\", seg.sn);\n\n                if (ispeek == 0)\n                {\n                    rcv_queue_.Remove(node);\n                    nrcv_que_--;\n                }\n\n                if (fragment == 0)\n                    break;\n            }\n\n            Debug.Assert(len == peeksize);\n\n            // move available data from rcv_buf -> rcv_queue\n            while (rcv_buf_.Count > 0)\n            {\n                var node = rcv_buf_.First;\n                var seg = node.Value;\n                if (seg.sn == rcv_nxt_ && nrcv_que_ < rcv_wnd_)\n                {\n                    rcv_buf_.Remove(node);\n                    nrcv_buf_--;\n                    rcv_queue_.AddLast(node);\n                    nrcv_que_++;\n                    rcv_nxt_++;\n                }\n                else\n                {\n                    break;\n                }\n            }\n\n            // fast recover\n            if (nrcv_que_ < rcv_wnd_ && recover != 0)\n            {\n                // ready to send back IKCP_CMD_WINS in ikcp_flush\n                // tell remote my window size\n                probe_ |= IKCP_ASK_TELL;\n            }\n\n            return len;\n        }\n\n        // check the size of next message in the recv queue\n        public int PeekSize()\n        {\n            if (rcv_queue_.Count == 0)\n                return -1;\n\n            var node = rcv_queue_.First;\n            var seg = node.Value;\n            if (seg.frg == 0)\n                return seg.data.Length;\n\n            if (nrcv_que_ < seg.frg + 1)\n                return -1;\n\n            int length = 0;\n            for (node = rcv_queue_.First; node != null; node = node.Next)\n            {\n                seg = node.Value;\n                length += seg.data.Length;\n                if (seg.frg == 0)\n                    break;\n            }\n            return length;\n        }\n\n        // user/upper level send, returns below zero for error\n        public int Send(byte[] buffer, int offset, int len)\n        {\n            Debug.Assert(mss_ > 0);\n            if (len < 0)\n                return -1;\n\n            //\n            // not implement streaming mode here as ikcp.c\n            //\n\n            int count = 0;\n            if (len <= (int)mss_)\n                count = 1;\n            else\n                count = (len + (int)mss_ - 1) / (int)mss_;\n\n            if (count > 255) // maximum value `frg` can present\n                return -2;\n\n            if (count == 0)\n                count = 1;\n\n            // fragment\n            for (int i = 0; i < count; i++)\n            {\n                int size = len > (int)mss_ ? (int)mss_ : len;\n                var seg = new Segment(size);\n                if (buffer != null && len > 0)\n                {\n                    Buffer.BlockCopy(buffer, offset, seg.data, 0, size);\n                    offset += size;\n                }\n                seg.frg = (UInt32)(count - i - 1);\n                snd_queue_.AddLast(seg);\n                nsnd_que_++;\n                len -= size;\n            }\n            return 0;\n        }\n\n        // parse ack\n        void UpdateACK(Int32 rtt)\n        {\n            if (rx_srtt_ == 0)\n            {\n                rx_srtt_ = rtt;\n                rx_rttval_ = rtt / 2;\n            }\n            else\n            {\n                Int32 delta = rtt - rx_srtt_;\n                if (delta < 0)\n                    delta = -delta;\n\n                rx_rttval_ = (3 * rx_rttval_ + delta) / 4;\n                rx_srtt_ = (7 * rx_srtt_ + rtt) / 8;\n                if (rx_srtt_ < 1)\n                    rx_srtt_ = 1;\n            }\n\n            var rto = rx_srtt_ + _imax_(interval_, (UInt32)(4 * rx_rttval_));\n            rx_rto_ = (Int32)_ibound_((UInt32)rx_minrto_, (UInt32)rto, IKCP_RTO_MAX);\n        }\n\n        void ShrinkBuf()\n        {\n            var node = snd_buf_.First;\n            if (node != null)\n            {\n                var seg = node.Value;\n                snd_una_ = seg.sn;\n            }\n            else\n            {\n                snd_una_ = snd_nxt_;\n            }\n        }\n\n        void ParseACK(UInt32 sn)\n        {\n            if (_itimediff(sn, snd_una_) < 0 || _itimediff(sn, snd_nxt_) >= 0)\n                return;\n\n            LinkedListNode<Segment> next = null;\n            for (var node = snd_buf_.First; node != null; node = next)\n            {\n                var seg = node.Value;\n                next = node.Next;\n                if (sn == seg.sn)\n                {\n                    snd_buf_.Remove(node);\n                    nsnd_buf_--;\n                    break;\n                }\n                if (_itimediff(sn, seg.sn) < 0)\n                    break;\n            }\n        }\n\n        void ParseUNA(UInt32 una)\n        {\n            LinkedListNode<Segment> next = null;\n            for (var node = snd_buf_.First; node != null; node = next)\n            {\n                var seg = node.Value;\n                next = node.Next;\n                if (_itimediff(una, seg.sn) > 0)\n                {\n                    snd_buf_.Remove(node);\n                    nsnd_buf_--;\n                }\n                else\n                {\n                    break;\n                }\n            }\n        }\n\n        void ParseFastACK(UInt32 sn)\n        {\n            if (_itimediff(sn, snd_una_) < 0 || _itimediff(sn, snd_nxt_) >= 0)\n                return;\n\n            LinkedListNode<Segment> next = null;\n            for (var node = snd_buf_.First; node != null; node = next)\n            {\n                var seg = node.Value;\n                next = node.Next;\n                if (_itimediff(sn, seg.sn) < 0)\n                {\n                    break;\n                }\n                else if (sn != seg.sn)\n                {\n                    seg.faskack++;\n                }\n            }\n        }\n\n        // ack append\n        void ACKPush(UInt32 sn, UInt32 ts)\n        {\n            var newsize = ackcount_ + 1;\n            if (newsize > ackblock_)\n            {\n                UInt32 newblock = 8;\n                for (; newblock < newsize; newblock <<= 1)\n                    ;\n\n                var acklist = new UInt32[newblock * 2];\n                if (acklist_ != null)\n                {\n                    for (var i = 0; i < ackcount_; i++)\n                    {\n                        acklist[i * 2] = acklist_[i * 2];\n                        acklist[i * 2 + 1] = acklist_[i * 2 + 1];\n                    }\n                }\n                acklist_ = acklist;\n                ackblock_ = newblock;\n            }\n            acklist_[ackcount_ * 2] = sn;\n            acklist_[ackcount_ * 2 + 1] = ts;\n            ackcount_++;\n        }\n\n        void ACKGet(int pos, ref UInt32 sn, ref UInt32 ts)\n        {\n            sn = acklist_[pos * 2];\n            ts = acklist_[pos * 2 + 1];\n        }\n\n        // parse data\n        void ParseData(Segment newseg)\n        {\n            UInt32 sn = newseg.sn;\n            int repeat = 0;\n\n            if (_itimediff(sn, rcv_nxt_ + rcv_wnd_) >= 0 ||\n                _itimediff(sn, rcv_nxt_) < 0)\n            {\n                return;\n            }\n\n            LinkedListNode<Segment> node = null;\n            LinkedListNode<Segment> prev = null;\n            for (node = rcv_buf_.Last; node != null; node = prev)\n            {\n                var seg = node.Value;\n                prev = node.Previous;\n                if (seg.sn == sn)\n                {\n                    repeat = 1;\n                    break;\n                }\n                if (_itimediff(sn, seg.sn) > 0) \n                {\n                    break;\n                }\n            }\n            if (repeat == 0)\n            {\n                if (node != null)\n                {\n                    rcv_buf_.AddAfter(node, newseg);\n                }\n                else\n                {\n                    rcv_buf_.AddFirst(newseg);\n                }\n                nrcv_buf_++;\n            }\n\n            // move available data from rcv_buf -> rcv_queue\n            while (rcv_buf_.Count > 0)\n            {\n                node = rcv_buf_.First;\n                var seg = node.Value;\n                if (seg.sn == rcv_nxt_ && nrcv_que_ < rcv_wnd_)\n                {\n                    rcv_buf_.Remove(node);\n                    nrcv_buf_--;\n                    rcv_queue_.AddLast(node);\n                    nrcv_que_++;\n                    rcv_nxt_++;\n                }\n                else\n                {\n                    break;\n                }\n            }\n        }\n\n        // when you received a low level packet (eg. UDP packet), call it\n        public int Input(byte[] data, int offset, int size)\n        {\n            UInt32 maxack = 0;\n            int flag = 0;\n\n            Log(IKCP_LOG_INPUT, \"[RI] {0} bytes\", size);\n\n            if (data == null || size < IKCP_OVERHEAD)\n                return -1;\n\n            while (true)\n            {\n                if (size < IKCP_OVERHEAD)\n                    break;\n\n                UInt32 conv = ikcp_decode32u(data, ref offset);\n                if (conv_ != conv)\n                    return -1;\n                UInt32 cmd = ikcp_decode8u(data, ref offset);\n                UInt32 frg = ikcp_decode8u(data, ref offset);\n                UInt32 wnd = ikcp_decode16u(data, ref offset);\n                UInt32 ts = ikcp_decode32u(data, ref offset);\n                UInt32 sn = ikcp_decode32u(data, ref offset);\n                UInt32 una = ikcp_decode32u(data, ref offset);\n                UInt32 len = ikcp_decode32u(data, ref offset);\n\n                size -= IKCP_OVERHEAD;\n                if (size < len)\n                    return -2;\n\n                if (cmd != IKCP_CMD_PUSH && cmd != IKCP_CMD_ACK &&\n                    cmd != IKCP_CMD_WASK && cmd != IKCP_CMD_WINS)\n                    return -3;\n\n                rmt_wnd_ = wnd;\n                ParseUNA(una);\n                ShrinkBuf();\n\n                if (cmd == IKCP_CMD_ACK)\n                {\n                    if (_itimediff(current_, ts) >= 0)\n                    {\n                        UpdateACK(_itimediff(current_, ts));\n                    }\n                    ParseACK(sn);\n                    ShrinkBuf();\n                    if (flag == 0)\n                    {\n                        flag = 1;\n                        maxack = sn;\n                    }\n                    else\n                    {\n                        if (_itimediff(sn, maxack) > 0)\n                        {\n                            maxack = sn;\n                        }\n                    }\n                    Log(IKCP_LOG_IN_DATA, \"input ack: sn={0} rtt={1} rto={2}\",\n                        sn, _itimediff(current_, ts), rx_rto_);\n                }\n                else if (cmd == IKCP_CMD_PUSH)\n                {\n                    Log(IKCP_LOG_IN_DATA, \"input psh: sn={0} ts={1}\", sn, ts);\n                    if (_itimediff(sn, rcv_nxt_ + rcv_wnd_) < 0)\n                    {\n                        ACKPush(sn, ts);\n                        if (_itimediff(sn, rcv_nxt_) >= 0)\n                        {\n                            var seg = new Segment((int)len);\n                            seg.conv = conv;\n                            seg.cmd = cmd;\n                            seg.frg = frg;\n                            seg.wnd = wnd;\n                            seg.ts = ts;\n                            seg.sn = sn;\n                            seg.una = una;\n                            if (len > 0)\n                            {\n                                Buffer.BlockCopy(data, offset, seg.data, 0, (int)len);\n                            }\n                            ParseData(seg);\n                        }\n                    }\n                }\n                else if (cmd == IKCP_CMD_WASK)\n                {\n                    // ready to send back IKCP_CMD_WINS in ikcp_flush\n                    // tell remote my window size\n                    probe_ |= IKCP_ASK_TELL;\n                    Log(IKCP_LOG_IN_PROBE, \"input probe\");\n                }\n                else if (cmd == IKCP_CMD_WINS)\n                {\n                    // do nothing\n                    Log(IKCP_LOG_IN_WINS, \"input wins: {0}\", wnd);\n                }\n                else\n                {\n                    return -3;\n                }\n\n                offset += (int)len;\n                size -= (int)len;\n            }\n\n            if (flag != 0)\n            {\n                ParseFastACK(maxack);\n            }\n\n            UInt32 unack = snd_una_;\n            if (_itimediff(snd_una_, unack) > 0)\n            {\n                if (cwnd_ < rmt_wnd_)\n                {\n                    if (cwnd_ < ssthresh_)\n                    {\n                        cwnd_++;\n                        incr_ += mss_;\n                    }\n                    else\n                    {\n                        if (incr_ < mss_)\n                            incr_ = mss_;\n                        incr_ += (mss_ * mss_) / incr_ + (mss_ / 16);\n                        if ((cwnd_ + 1) * mss_ <= incr_)\n                            cwnd_++;\n                    }\n                    if (cwnd_ > rmt_wnd_)\n                    {\n                        cwnd_ = rmt_wnd_;\n                        incr_ = rmt_wnd_ * mss_;\n                    }\n                }\n            }\n\n            return 0;\n        }\n\n        int WndUnused()\n        {\n            if (nrcv_que_ < rcv_wnd_)\n                return (int)(rcv_wnd_ - nrcv_que_);\n            return 0;\n        }\n\n        // flush pending data\n        void Flush()\n        {\n            int change = 0;\n            int lost = 0;\n            int offset = 0;\n\n            // 'ikcp_update' haven't been called. \n            if (updated_ == 0)\n                return;\n\n            var seg = new Segment\n            {\n                conv = conv_,\n                cmd = IKCP_CMD_ACK,\n                wnd = (UInt32)WndUnused(),\n                una = rcv_nxt_,\n            };\n\n            // flush acknowledges\n            int count = (int)ackcount_;\n            for (int i = 0; i < count; i++)\n            {\n                if ((offset + IKCP_OVERHEAD) > mtu_)\n                {\n                    output_(buffer_, offset, user_);\n                    offset = 0;\n                }\n                ACKGet(i, ref seg.sn, ref seg.ts);\n                seg.Encode(buffer_, ref offset);\n            }\n\n            ackcount_ = 0;\n\n            // probe window size (if remote window size equals zero)\n            if (rmt_wnd_ == 0)\n            {\n                if (probe_wait_ == 0)\n                {\n                    probe_wait_ = IKCP_PROBE_INIT;\n                    ts_probe_ = current_ + probe_wait_;\n                }\n                else\n                {\n                    if (_itimediff(current_, ts_probe_) >= 0)\n                    {\n                        if (probe_wait_ < IKCP_PROBE_INIT)\n                            probe_wait_ = IKCP_PROBE_INIT;\n                        probe_wait_ += probe_wait_ / 2;\n                        if (probe_wait_ > IKCP_PROBE_LIMIT)\n                            probe_wait_ = IKCP_PROBE_LIMIT;\n                        ts_probe_ = current_ + probe_wait_;\n                        probe_ |= IKCP_ASK_SEND;\n                    }\n                }\n            }\n            else\n            {\n                ts_probe_ = 0;\n                probe_wait_ = 0;\n            }\n\n            // flush window probing commands\n            if ((probe_ & IKCP_ASK_SEND) > 0)\n            {\n                seg.cmd = IKCP_CMD_WASK;\n                if ((offset + IKCP_OVERHEAD) > mtu_)\n                {\n                    output_(buffer_, offset, user_);\n                    offset = 0;\n                }\n                seg.Encode(buffer_, ref offset);\n            }\n\n            // flush window probing commands\n            if ((probe_ & IKCP_ASK_TELL) > 0)\n            {\n                seg.cmd = IKCP_CMD_WINS;\n                if ((offset + IKCP_OVERHEAD) > mtu_)\n                {\n                    output_(buffer_, offset, user_);\n                    offset = 0;\n                }\n                seg.Encode(buffer_, ref offset);\n            }\n\n            probe_ = 0;\n\n            // calculate window size\n            UInt32 cwnd = _imin_(snd_wnd_, rmt_wnd_);\n            if (nocwnd_ == 0)\n                cwnd = _imin_(cwnd_, cwnd);\n\n            // move data from snd_queue to snd_buf\n            while (_itimediff(snd_nxt_, snd_una_ + cwnd) < 0)\n            {\n                if (snd_queue_.Count == 0)\n                    break;\n\n                var node = snd_queue_.First;\n                var newseg = node.Value;\n                snd_queue_.Remove(node);\n                snd_buf_.AddLast(node);\n                nsnd_que_--;\n                nsnd_buf_++;\n\n                newseg.conv = conv_;\n                newseg.cmd = IKCP_CMD_PUSH;\n                newseg.wnd = seg.wnd;\n                newseg.ts = current_;\n                newseg.sn = snd_nxt_++;\n                newseg.una = rcv_nxt_;\n                newseg.resendts = current_;\n                newseg.rto = (UInt32)rx_rto_;\n                newseg.faskack = 0;\n                newseg.xmit = 0;\n            }\n\n            // calculate resent\n            UInt32 resent = (fastresend_ > 0 ? (UInt32)fastresend_ : 0xffffffff);\n            UInt32 rtomin = (nodelay_ == 0 ? (UInt32)(rx_rto_ >> 3) : 0);\n\n            // flush data segments\n            for (var node = snd_buf_.First; node != null; node = node.Next)\n            {\n                var segment = node.Value;\n                int needsend = 0;\n                if (segment.xmit == 0)\n                {\n                    needsend = 1;\n                    segment.xmit++;\n                    segment.rto = (UInt32)rx_rto_;\n                    segment.resendts = current_ + segment.rto + rtomin;\n                }\n                else if (_itimediff(current_, segment.resendts) >= 0)\n                {\n                    needsend = 1;\n                    segment.xmit++;\n                    xmit_++;\n                    if (nodelay_ == 0)\n                        segment.rto += (UInt32)rx_rto_;\n                    else\n                        segment.rto += (UInt32)rx_rto_ / 2;\n                    segment.resendts = current_ + segment.rto;\n                    lost = 1;\n                }\n                else if (segment.faskack >= resent)\n                {\n                    needsend = 1;\n                    segment.xmit++;\n                    segment.faskack = 0;\n                    segment.resendts = current_ + segment.rto;\n                    change++;\n                }\n\n                if (needsend > 0)\n                {\n                    segment.ts = current_;\n                    segment.wnd = seg.wnd;\n                    segment.una = rcv_nxt_;\n\n                    int need = IKCP_OVERHEAD;\n                    if (segment.data != null)\n                        need += segment.data.Length;\n\n                    if (offset + need > mtu_)\n                    {\n                        output_(buffer_, offset, user_);\n                        offset = 0;\n                    }\n                    segment.Encode(buffer_, ref offset);\n                    if (segment.data.Length > 0)\n                    {\n                        Buffer.BlockCopy(segment.data, 0, buffer_, offset, segment.data.Length);\n                        offset += segment.data.Length;\n                    }\n                    if (segment.xmit >= dead_link_)\n                        state_ = 0xffffffff;\n                }\n            }\n\n            // flush remain segments\n            if (offset > 0)\n            {\n                output_(buffer_, offset, user_);\n                offset = 0;\n            }\n\n            // update ssthresh\n            if (change > 0)\n            {\n                UInt32 inflight = snd_nxt_ - snd_una_;\n                ssthresh_ = inflight / 2;\n                if (ssthresh_ < IKCP_THRESH_MIN)\n                    ssthresh_ = IKCP_THRESH_MIN;\n                cwnd_ = ssthresh_ + resent;\n                incr_ = cwnd_ * mss_;\n            }\n\n            if (lost > 0)\n            {\n                ssthresh_ = cwnd / 2;\n                if (ssthresh_ < IKCP_THRESH_MIN)\n                    ssthresh_ = IKCP_THRESH_MIN;\n                cwnd_ = 1;\n                incr_ = mss_;\n            }\n\n            if (cwnd_ < 1)\n            {\n                cwnd_ = 1;\n                incr_ = mss_;\n            }\n        }\n\n        // update state (call it repeatedly, every 10ms-100ms), or you can ask \n        // ikcp_check when to call it again (without ikcp_input/_send calling).\n        // 'current' - current timestamp in millisec. \n        public void Update(UInt32 current)\n        {\n            current_ = current;\n            if (updated_ == 0)\n            {\n                updated_ = 1;\n                ts_flush_ = current;\n            }\n\n            Int32 slap = _itimediff(current_, ts_flush_);\n            if (slap >= 10000 || slap < -10000)\n            {\n                ts_flush_ = current;\n                slap = 0;\n            }\n\n            if (slap >= 0)\n            {\n                ts_flush_ += interval_;\n                if (_itimediff(current_, ts_flush_) >= 0)\n                    ts_flush_ = current_ + interval_;\n\n                Flush();\n            }\n        }\n\n        // Determine when should you invoke ikcp_update:\n        // returns when you should invoke ikcp_update in millisec, if there \n        // is no ikcp_input/_send calling. you can call ikcp_update in that\n        // time, instead of call update repeatly.\n        // Important to reduce unnacessary ikcp_update invoking. use it to \n        // schedule ikcp_update (eg. implementing an epoll-like mechanism, \n        // or optimize ikcp_update when handling massive kcp connections)\n        public UInt32 Check(UInt32 current)\n        {\n            UInt32 ts_flush = ts_flush_;\n            Int32 tm_flush = 0x7fffffff;\n            Int32 tm_packet = 0x7fffffff;\n\n            if (updated_ == 0)\n                return current;\n\n            if (_itimediff(current, ts_flush) >= 10000 || \n                _itimediff(current, ts_flush) < -10000)\n            {\n                ts_flush = current;\n            }\n\n            if (_itimediff(current, ts_flush) >= 0)\n                return current;\n\n            tm_flush = _itimediff(ts_flush, current);\n\n            for (var node = snd_buf_.First; node != null; node = node.Next)\n            {\n                var seg = node.Value;\n                Int32 diff = _itimediff(seg.resendts, current);\n                if (diff <= 0)\n                    return current;\n\n                if (diff < tm_packet)\n                    tm_packet = diff;\n            }\n\n            UInt32 minimal = (UInt32)(tm_packet < tm_flush ? tm_packet : tm_flush);\n            if (minimal >= interval_)\n                minimal = interval_;\n\n            return current + minimal;\n        }\n\n        // change MTU size, default is 1400\n        public int SetMTU(int mtu)\n        {\n            if (mtu < 50 || mtu < IKCP_OVERHEAD)\n                return -1;\n\n            var buffer = new byte[(mtu + IKCP_OVERHEAD) * 3];\n            mtu_ = (UInt32)mtu;\n            mss_ = mtu_ - IKCP_OVERHEAD;\n            buffer_ = buffer;\n            return 0;\n        }\n\n        public int Interval(int interval)\n        {\n            if (interval > 5000)\n                interval = 5000;\n            else if (interval < 10)\n                interval = 10;\n\n            interval_ = (UInt32)interval;\n            return 0;\n        }\n\n        // fastest: ikcp_nodelay(kcp, 1, 20, 2, 1)\n        // nodelay: 0:disable(default), 1:enable\n        // interval: internal update timer interval in millisec, default is 100ms \n        // resend: 0:disable fast resend(default), 1:enable fast resend\n        // nc: 0:normal congestion control(default), 1:disable congestion control\n        public int NoDelay(int nodelay, int interval, int resend, int nc)\n        {\n            if (nodelay >= 0)\n            {\n                nodelay_ = (UInt32)nodelay;\n                if (nodelay > 0)\n                {\n                    rx_minrto_ = IKCP_RTO_NDL;\n                }\n                else\n                {\n                    rx_minrto_ = IKCP_RTO_MIN;\n                }\n            }\n            if (interval >= 0)\n            {\n                if (interval > 5000)\n                    interval = 5000;\n                else if (interval < 10)\n                    interval = 10;\n\n                interval_ = (UInt32)interval;\n            }\n\n            if (resend >= 0)\n                fastresend_ = resend;\n\n            if (nc >= 0)\n                nocwnd_ = nc;\n\n            return 0;\n        }\n\n        // set maximum window size: sndwnd=32, rcvwnd=32 by default\n        public int WndSize(int sndwnd, int rcvwnd)\n        {\n            if (sndwnd > 0)\n                snd_wnd_ = (UInt32)sndwnd;\n            if (rcvwnd > 0)\n                rcv_wnd_ = (UInt32)rcvwnd;\n            return 0;\n        }\n\n        // get how many packet is waiting to be sent\n        public int WaitSnd()\n        {\n            return (int)(nsnd_buf_ + nsnd_que_);\n        }\n\n        // read conv\n        public UInt32 GetConv()\n        {\n            return conv_;\n        }\n\n        public UInt32 GetState()\n        {\n            return state_;\n        }\n\n        public void SetMinRTO(int minrto)\n        {\n            rx_minrto_ = minrto;\n        }\n\n        public void SetFastResend(int resend)\n        {\n            fastresend_ = resend;\n        }\n\n        void Log(int mask, string format, params object[] args)\n        {\n            // Console.WriteLine(mask + String.Format(format, args));\n        }\n    }\n}\n"
  },
  {
    "path": "Assets/Scripts/Network/Core/KCP/KCP.cs.meta",
    "content": "fileFormatVersion: 2\nguid: 20f9afa1793f2d54f811abe0972ada7c\nMonoImporter:\n  externalObjects: {}\n  serializedVersion: 2\n  defaultReferences: []\n  executionOrder: 0\n  icon: {instanceID: 0}\n  userData: \n  assetBundleName: \n  assetBundleVariant: \n"
  },
  {
    "path": "Assets/Scripts/Network/Core/KCP/KChannel.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.IO;\nusing System.Net;\nusing System.Net.Sockets;\nusing UnityEngine;\n#if dynamic_kcp\n\tusing System.Runtime.InteropServices;\n#endif\n\nnamespace UNetwork\n{\n\tpublic struct WaitSendBuffer\n\t{\n\t\tpublic byte[] Bytes;\n\t\tpublic int Length;\n\n\t\tpublic WaitSendBuffer(byte[] bytes, int length)\n\t\t{\n\t\t\tthis.Bytes = bytes;\n\t\t\tthis.Length = length;\n\t\t}\n\t}\n\n#if dynamic_kcp\n\tpublic class KChannel : AChannel\n\t{\n\t\tprivate Socket socket;\n\n\t\tprivate IntPtr kcp;\n\n\t\tprivate readonly Queue<WaitSendBuffer> sendBuffer = new Queue<WaitSendBuffer>();\n\n\t\tprivate bool isConnected;\n\t\t\n\t\tprivate readonly IPEndPoint remoteEndPoint;\n\n\t\tprivate uint lastRecvTime;\n\t\t\n\t\tprivate readonly uint createTime;\n\n\t\tpublic uint LocalConn { get; private set; }\n\t\tpublic uint RemoteConn { get; private set; }\n\n\t\tprivate readonly MemoryStream memoryStream;\n\t\t\n\t\t// connect\n\t\tpublic KChannel(uint localConn, Socket socket, IPEndPoint remoteEndPoint, KService kService) : base(kService, ChannelType.Connect)\n\t\t{\n\t\t\tthis.memoryStream = this.GetService().MemoryStreamManager.GetStream(\"message\", ushort.MaxValue);\n\n\t\t\tthis.LocalConn = localConn;\n\t\t\tthis.socket = socket;\n\t\t\tthis.remoteEndPoint = remoteEndPoint;\n\t\t\tthis.lastRecvTime = kService.TimeNow;\n\t\t\tthis.createTime = kService.TimeNow;\n\t\t\t\n\t\t\tthis.HandleConnnect(localConn);\n\t\t}\n\n\n\t\tpublic void Dispose()\n\t\t{\n\t\t\ttry\n\t\t\t{\n\t\t\t\tif (this.Error == ErrorCode.ERR_Success)\n\t\t\t\t{\n\t\t\t\t\tfor (int i = 0; i < 4; i++)\n\t\t\t\t\t{\n\t\t\t\t\t\tthis.Disconnect();\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\tcatch (Exception)\n\t\t\t{\n\t\t\t\t// ignored\n\t\t\t}\n\n\t\t\tif (this.kcp != IntPtr.Zero)\n\t\t\t{\n\t\t\t\tKcp.KcpRelease(this.kcp);\n\t\t\t\tthis.kcp = IntPtr.Zero;\n\t\t\t}\n\t\t\tthis.socket = null;\n\t\t\tthis.memoryStream.Dispose();\n\t\t}\n\n\t\tpublic override MemoryStream Stream\n\t\t{\n\t\t\tget\n\t\t\t{\n\t\t\t\treturn this.memoryStream;\n\t\t\t}\n\t\t}\n\n\t\tpublic void Disconnect(int error)\n\t\t{\n\t\t\tthis.OnError(error);\n\t\t}\n\n\t\tprivate KService GetService()\n\t\t{\n\t\t\treturn (KService)this.Service;\n\t\t}\n\n\t\tpublic void HandleConnnect(uint remoteConn)\n\t\t{\n\t\t\tif (this.isConnected)\n\t\t\t{\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tthis.RemoteConn = remoteConn;\n\n\t\t\tthis.kcp = Kcp.KcpCreate(this.RemoteConn, new IntPtr(this.LocalConn));\n\t\t\tSetOutput();\n\t\t\tKcp.KcpNodelay(this.kcp, 1, 10, 1, 1);\n\t\t\tKcp.KcpWndsize(this.kcp, 32, 32);\n\t\t\tKcp.KcpSetmtu(this.kcp, 470);\n\n\t\t\tthis.isConnected = true;\n\t\t\tthis.lastRecvTime = this.GetService().TimeNow;\n\t\t\t\n\t\t\tConnect();\n\t\t}\n\n\t\tpublic void Accept()\n\t\t{\n\t\t\tif (this.socket == null)\n\t\t\t{\n\t\t\t\treturn;\n\t\t\t}\n\t\t\t\n\t\t\tuint timeNow = this.GetService().TimeNow;\n\n\t\t\ttry\n\t\t\t{\n\t\t\t\tbyte[] buffer = this.memoryStream.GetBuffer();\n\t\t\t\tbuffer.WriteTo(0, KcpProtocalType.ACK);\n\t\t\t\tbuffer.WriteTo(1, LocalConn);\n\t\t\t\tbuffer.WriteTo(5, RemoteConn);\n\t\t\t\tthis.socket.SendTo(buffer, 0, 9, SocketFlags.None, remoteEndPoint);\n\t\t\t}\n\t\t\tcatch (Exception e)\n\t\t\t{\n\t\t\t\tDebug.LogError(e);\n\t\t\t\tthis.OnError(ErrorCode.ERR_SocketCantSend);\n\t\t\t}\n\t\t}\n\n\t\t/// <summary>\n\t\t/// 发送请求连接消息\n\t\t/// </summary>\n\t\tprivate void Connect()\n\t\t{\n\t\t\ttry\n\t\t\t{\n\t\t\t\tuint timeNow = this.GetService().TimeNow;\n\t\t\t\t\n\t\t\t\tthis.lastRecvTime = timeNow;\n\t\t\t\t\n\t\t\t\tbyte[] buffer = this.memoryStream.GetBuffer();\n\t\t\t\tbuffer.WriteTo(0, KcpProtocalType.SYN);\n\t\t\t\tbuffer.WriteTo(1, this.LocalConn);\n\t\t\t\tthis.socket.SendTo(buffer, 0, 5, SocketFlags.None, remoteEndPoint);\n\t\t\t}\n\t\t\tcatch (Exception e)\n\t\t\t{\n\t\t\t\tDebug.LogError(e);\n\t\t\t\tthis.OnError(ErrorCode.ERR_SocketCantSend);\n\t\t\t}\n\t\t}\n\n\t\t/// <summary>\n\t\t/// 发送请求断开消息\n\t\t/// </summary>\n\t\tprivate void Disconnect()\n\t\t{\n\t\t\tif (this.socket == null)\n\t\t\t{\n\t\t\t\treturn;\n\t\t\t}\n\t\t\ttry\n\t\t\t{\n\t\t\t\tbyte[] buffer = this.memoryStream.GetBuffer();\n\t\t\t\tbuffer.WriteTo(0, KcpProtocalType.FIN);\n\t\t\t\tbuffer.WriteTo(1, this.LocalConn);\n\t\t\t\tbuffer.WriteTo(5, this.RemoteConn);\n\t\t\t\tbuffer.WriteTo(9, (uint)this.Error);\n\t\t\t\tthis.socket.SendTo(buffer, 0, 13, SocketFlags.None, remoteEndPoint);\n\t\t\t}\n\t\t\tcatch (Exception e)\n\t\t\t{\n\t\t\t\tDebug.LogError(e);\n\t\t\t\tthis.OnError(ErrorCode.ERR_SocketCantSend);\n\t\t\t}\n\t\t}\n\n\t\tpublic void Update()\n\t\t{\n\t\t\tuint timeNow = this.GetService().TimeNow;\n\t\t\t\n\t\t\t// 如果还没连接上，发送连接请求\n\t\t\tif (!this.isConnected)\n\t\t\t{\n//\t\t\t\t// 10秒没连接上则报错\n//\t\t\t\tif (timeNow - this.createTime > 10 * 1000)\n//\t\t\t\t{\n//\t\t\t\t\tthis.OnError(ErrorCode.ERR_KcpCantConnect);\n//\t\t\t\t\treturn;\n//\t\t\t\t}\n//\t\t\t\t\n//\t\t\t\tif (timeNow - this.lastRecvTime < 500)\n//\t\t\t\t{\n//\t\t\t\t\treturn;\n//\t\t\t\t}\n\t\t\t}\n\n\t\t\ttry\n\t\t\t{\n\t\t\t\tKcp.KcpUpdate(this.kcp, timeNow);\n\t\t\t}\n\t\t\tcatch (Exception e)\n\t\t\t{\n\t\t\t\tDebug.LogError(e);\n\t\t\t\tthis.OnError(ErrorCode.ERR_SocketError);\n\t\t\t\treturn;\n\t\t\t}\n\n\n\t\t\tif (this.kcp != IntPtr.Zero)\n\t\t\t{\n\t\t\t\tuint nextUpdateTime = Kcp.KcpCheck(this.kcp, timeNow);\n\t\t\t}\n\t\t}\n\n\t\tprivate void HandleSend()\n\t\t{\n\t\t\twhile (true)\n\t\t\t{\n\t\t\t\tif (this.sendBuffer.Count <= 0)\n\t\t\t\t{\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\n\t\t\t\tWaitSendBuffer buffer = this.sendBuffer.Dequeue();\n\t\t\t\tthis.KcpSend(buffer.Bytes, buffer.Length);\n\t\t\t}\n\t\t}\n\n\t\tpublic void HandleRecv(byte[] date, int offset, int length)\n\t\t{\n\t\t\tthis.isConnected = true;\n\t\t\t\n\t\t\tKcp.KcpInput(this.kcp, date, offset, length);\n\n\t\t\twhile (true)\n\t\t\t{\n\t\t\t\tint n = Kcp.KcpPeeksize(this.kcp);\n\t\t\t\tif (n < 0)\n\t\t\t\t{\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tif (n == 0)\n\t\t\t\t{\n\t\t\t\t\tthis.OnError((int)SocketError.NetworkReset);\n\t\t\t\t\treturn;\n\t\t\t\t}\n\n\t\t\t\tbyte[] buffer = this.memoryStream.GetBuffer();\n\t\t\t\tthis.memoryStream.SetLength(n);\n\t\t\t\tthis.memoryStream.Seek(0, SeekOrigin.Begin);\n\t\t\t\tint count = Kcp.KcpRecv(this.kcp, buffer, ushort.MaxValue);\n\t\t\t\tif (n != count)\n\t\t\t\t{\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tif (count <= 0)\n\t\t\t\t{\n\t\t\t\t\treturn;\n\t\t\t\t}\n\n\t\t\t\tthis.lastRecvTime = this.GetService().TimeNow;\n\n\t\t\t\tthis.OnRead(this.memoryStream);\n\t\t\t}\n\t\t}\n\n\t\tpublic override void Start()\n\t\t{\n\t\t}\n\n\t\tpublic void Output(IntPtr bytes, int count)\n\t\t{\n\t\t\ttry\n\t\t\t{\n\t\t\t\tif (count == 0)\n\t\t\t\t{\n\t\t\t\t\tDebug.LogError($\"output 0\");\n\t\t\t\t\treturn;\n\t\t\t\t}\n\n\t\t\t\tbyte[] buffer = this.memoryStream.GetBuffer();\n\t\t\t\tMarshal.Copy(bytes, buffer, 0, count);\n\t\t\t\tthis.socket.SendTo(buffer, 0, count, SocketFlags.None, this.remoteEndPoint);\n\t\t\t}\n\t\t\tcatch (Exception e)\n\t\t\t{\n\t\t\t\tDebug.LogError(e);\n\t\t\t\tthis.OnError(ErrorCode.ERR_SocketCantSend);\n\t\t\t}\n\t\t}\n\t\t\n#if !ENABLE_IL2CPP\n\t\tprivate KcpOutput kcpOutput;\n#endif\n\n\t\tpublic void SetOutput()\n\t\t{\n#if ENABLE_IL2CPP\n\t\t\tKcp.KcpSetoutput(this.kcp, KcpOutput);\n#else\n\t\t\t// 跟上一行一样写法，pc跟linux会出错, 保存防止被GC\n\t\t\tkcpOutput = KcpOutput;\n\t\t\tKcp.KcpSetoutput(this.kcp, kcpOutput);\n#endif\n\t\t}\n\n\n#if ENABLE_IL2CPP\n\t\t[AOT.MonoPInvokeCallback(typeof(KcpOutput))]\n#endif\n\t\tpublic static int KcpOutput(IntPtr bytes, int len, IntPtr kcp, IntPtr user)\n        {\n            KService.Output(bytes, len, user);\n            return len;\n        }\n\n        private void KcpSend(byte[] buffers, int length)\n\t\t{\n\t\t\tKcp.KcpSend(this.kcp, buffers, length);\n\t\t}\n\n\t\tprivate void Send(byte[] buffer, int index, int length)\n\t\t{\n\t\t\tif (isConnected)\n\t\t\t{\n\t\t\t\tthis.KcpSend(buffer, length);\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tthis.sendBuffer.Enqueue(new WaitSendBuffer(buffer, length));\n\t\t}\n\n\t\tpublic override void Send(MemoryStream stream)\n\t\t{\n\t\t\tif (this.kcp != IntPtr.Zero)\n\t\t\t{\n\t\t\t\t// 检查等待发送的消息，如果超出两倍窗口大小，应该断开连接\n\t\t\t\tif (Kcp.KcpWaitsnd(this.kcp) > 256 * 2)\n\t\t\t\t{\n\t\t\t\t\tthis.OnError(ErrorCode.ERR_KcpWaitSendSizeTooLarge);\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tushort size = (ushort)(stream.Length - stream.Position);\n\t\t\tbyte[] bytes = stream.GetBuffer();\n\t\t\tSend(bytes, 0, size);\n\t\t}\n\t}\n#else\n\tpublic class KChannel : AChannel\n\t{\n\t\tprivate Socket socket;\n\n\t\tprivate KCP kcp;\n\n\t\tprivate readonly Queue<WaitSendBuffer> sendBuffer = new Queue<WaitSendBuffer>();\n\n\t\tprivate bool isConnected;\n\t\t\n\t\tprivate readonly IPEndPoint remoteEndPoint;\n\n\t\tprivate uint lastRecvTime;\n\t\t\n\t\tprivate readonly uint createTime;\n\n\t\tpublic uint LocalConn { get; private set; }\n\t\tpublic uint RemoteConn { get; private set; }\n\n\t\t// connect\n\t\tpublic KChannel(uint localConn, Socket socket, IPEndPoint remoteEndPoint, KService kService) : base(kService, ChannelType.Connect)\n\t\t{\n\t\t\tthis.memoryStream = this.GetService().MemoryStreamManager.GetStream(\"message\", ushort.MaxValue);\n\n\t\t\tthis.LocalConn = localConn;\n\t\t\tthis.socket = socket;\n\t\t\tthis.remoteEndPoint = remoteEndPoint;\n\t\t\tthis.lastRecvTime = kService.TimeNow;\n\t\t\tthis.createTime = kService.TimeNow;\n\t\t\t\n\t\t\tthis.HandleConnnect(localConn);\n\t\t}\n\n\n\t\tpublic override void Dispose()\n\t\t{\n\t\t\ttry\n\t\t\t{\n\t\t\t\tif (this.Error == ErrorCode.ERR_Success)\n\t\t\t\t{\n\t\t\t\t\tfor (int i = 0; i < 4; i++)\n\t\t\t\t\t{\n\t\t\t\t\t\tthis.Disconnect();\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\tcatch (Exception)\n\t\t\t{\n\t\t\t\t// ignored\n\t\t\t}\n\n\t\t\tif (this.kcp != null)\n\t\t\t{\n\t\t\t\tkcp.Release();\n\t\t\t\tthis.kcp = null;\n\t\t\t}\n\t\t\tthis.socket = null;\n\t\t\tthis.memoryStream.Dispose();\n\t\t}\n\n\t\tpublic void Disconnect(int error)\n\t\t{\n\t\t\tthis.OnError(error);\n\t\t}\n\n\t\tprivate KService GetService()\n\t\t{\n\t\t\treturn (KService)this.Service;\n\t\t}\n\n\t\tpublic void HandleConnnect(uint remoteConn)\n\t\t{\n\t\t\tif (this.isConnected)\n\t\t\t{\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tthis.RemoteConn = remoteConn;\n\n\t\t\tthis.kcp = new KCP(this.RemoteConn, new IntPtr(this.LocalConn));\n\t\t\tSetOutput();\n\t\t\tkcp.NoDelay(1, 10, 1, 1);\n\t\t\tkcp.WndSize(32, 32);\n\t\t\tkcp.SetMTU(470);\n\n\t\t\tthis.lastRecvTime = this.GetService().TimeNow;\n\t\t\t\n\t\t\tConnect();\n\t\t}\n\n\t\t/// <summary>\n\t\t/// 发送请求连接消息\n\t\t/// </summary>\n\t\tprivate void Connect()\n\t\t{\n\t\t\ttry\n\t\t\t{\n\t\t\t\tuint timeNow = this.GetService().TimeNow;\n\t\t\t\t\n\t\t\t\tthis.lastRecvTime = timeNow;\n\t\t\t\t\n\t\t\t\tbyte[] buffer = this.memoryStream.GetBuffer();\n\t\t\t\tbuffer.WriteTo(0, 4);\n\t\t\t\tbuffer.WriteTo(4, KcpProtocalType.SYN);\n\t\t\t\tSend(buffer,0,6);\n\t\t\t\tHandleSend();\n\t\t\t}\n\t\t\tcatch (Exception e)\n\t\t\t{\n\t\t\t\tDebug.LogError(e);\n\t\t\t\tthis.OnError(ErrorCode.ERR_SocketCantSend);\n\t\t\t}\n\t\t}\n\n\t\t/// <summary>\n\t\t/// 发送请求断开消息\n\t\t/// </summary>\n\t\tprivate void Disconnect()\n\t\t{\n\t\t\tif (this.socket == null)\n\t\t\t{\n\t\t\t\treturn;\n\t\t\t}\n\t\t\ttry\n\t\t\t{\n\t\t\t\tbyte[] buffer = this.memoryStream.GetBuffer();\n\t\t\t\tbuffer.WriteTo(0, 4);\n\t\t\t\tbuffer.WriteTo(4, KcpProtocalType.FIN);\n\t\t\t\tSend(buffer,0,6);\n\t\t\t\tHandleSend();\n\t\t\t}\n\t\t\tcatch (Exception e)\n\t\t\t{\n\t\t\t\tDebug.LogError(e);\n\t\t\t\tthis.OnError(ErrorCode.ERR_SocketCantSend);\n\t\t\t}\n\t\t}\n\n\t\tpublic void Update()\n\t\t{\n\t\t\tuint timeNow = this.GetService().TimeNow;\n\t\t\t\n\t\t\t// 如果还没连接上，发送连接请求\n\t\t\tif (!this.isConnected)\n\t\t\t{\n\t\t\t\t// 10秒没连接上则报错\n\t\t\t\tif (timeNow - this.createTime > 10 * 1000)\n\t\t\t\t{\n\t\t\t\t\tthis.OnError(ErrorCode.ERR_KcpCantConnect);\n\t\t\t\t\treturn;\n\t\t\t\t}\n//\t\t\t\t\n//\t\t\t\tif (timeNow - this.lastRecvTime < 500)\n//\t\t\t\t{\n//\t\t\t\t\treturn;\n//\t\t\t\t}\n\t\t\t}\n\n\t\t\ttry\n\t\t\t{\n\t\t\t\tkcp.Update(timeNow);\n\t\t\t}\n\t\t\tcatch (Exception e)\n\t\t\t{\n\t\t\t\tDebug.LogError(e);\n\t\t\t\tthis.OnError(ErrorCode.ERR_SocketError);\n\t\t\t\treturn;\n\t\t\t}\n\n\n\t\t\tif (this.kcp != null)\n\t\t\t{\n\t\t\t\tuint nextUpdateTime = kcp.Check(timeNow);\n\t\t\t}\n\t\t}\n\n\t\tprivate void HandleSend()\n\t\t{\n\t\t\twhile (true)\n\t\t\t{\n\t\t\t\tif (this.sendBuffer.Count <= 0)\n\t\t\t\t{\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\n\t\t\t\tWaitSendBuffer buffer = this.sendBuffer.Dequeue();\n\t\t\t\tthis.KcpSend(buffer.Bytes, buffer.Length);\n\t\t\t}\n\t\t}\n\n\t\tpublic void HandleRecv(byte[] date, int offset, int length)\n\t\t{\n\t\t\tkcp.Input(date, offset, length);\n\n\t\t\twhile (true)\n\t\t\t{\n\t\t\t\tint n = kcp.PeekSize();\n\t\t\t\tif (n < 0)\n\t\t\t\t{\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tif (n == 0)\n\t\t\t\t{\n\t\t\t\t\tthis.OnError((int)SocketError.NetworkReset);\n\t\t\t\t\treturn;\n\t\t\t\t}\n\n\t\t\t\tbyte[] buffer = this.memoryStream.GetBuffer();\n\t\t\t\tthis.memoryStream.SetLength(n);\n\t\t\t\tthis.memoryStream.Seek(0, SeekOrigin.Begin);\n\t\t\t\tint count = kcp.Recv(buffer,0, ushort.MaxValue);\n\t\t\t\tif (n != count)\n\t\t\t\t{\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tif (count <= 0)\n\t\t\t\t{\n\t\t\t\t\treturn;\n\t\t\t\t}\n\n\t\t\t\tthis.lastRecvTime = this.GetService().TimeNow;\n\n\t\t\t\tif (!isConnected)\n\t\t\t\t{\n\t\t\t\t\tthis.isConnected = true;\n\t\t\t\t\tOnConnect((int)SocketError.Success);\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tthis.OnRead(this.memoryStream);\n\t\t\t\t}\n\t\t\t\t\n\t\t\t}\n\t\t}\n\n\t\tpublic override void Start()\n\t\t{\n\t\t}\n\n\t\tpublic void Output(byte[] bytes, int count)\n\t\t{\n\t\t\ttry\n\t\t\t{\n\t\t\t\tif (count == 0)\n\t\t\t\t{\n\t\t\t\t\tDebug.LogError($\"output 0\");\n\t\t\t\t\treturn;\n\t\t\t\t}\n\n\t\t\t\tbyte[] buffer = this.memoryStream.GetBuffer();\n\t\t\t\t\n\t\t\t\tBuffer.BlockCopy(bytes, 0,buffer, 0, count);\n\t\t\t\tthis.socket.SendTo(buffer, 0, count, SocketFlags.None, this.remoteEndPoint);\n\t\t\t}\n\t\t\tcatch (Exception e)\n\t\t\t{\n\t\t\t\tDebug.LogError(e);\n\t\t\t\tthis.OnError(ErrorCode.ERR_SocketCantSend);\n\t\t\t}\n\t\t}\n\t\t\n\t\tprivate KCP.OutputDelegate kcpOutput;\n\t\tpublic void SetOutput()\n\t\t{\n\t\t\tkcpOutput = KcpOutput;\n\t\t\tkcp.SetOutput(kcpOutput);\n\t\t}\n\n\n\t\tpublic static void KcpOutput(byte[] bytes, int len, object user)\n        {\n            KService.Output(bytes, len, user);\n        }\n\n        private void KcpSend(byte[] buffers, int length)\n\t\t{\n\t\t\tkcp.Send(buffers,0, length);\n\t\t}\n\t\t\n\t\tprivate void Send(byte[] buffer, int index, int length)\n\t\t{\n\t\t\tif (isConnected)\n\t\t\t{\n\t\t\t\tthis.KcpSend(buffer, length);\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tthis.sendBuffer.Enqueue(new WaitSendBuffer(buffer, length));\n\t\t}\n\n\t\tpublic override void Send(MemoryStream stream)\n\t\t{\n\t\t\tif (this.kcp != null)\n\t\t\t{\n\t\t\t\t// 检查等待发送的消息，如果超出两倍窗口大小，应该断开连接\n\t\t\t\tif (kcp.WaitSnd() > 256 * 2)\n\t\t\t\t{\n\t\t\t\t\tthis.OnError(ErrorCode.ERR_KcpWaitSendSizeTooLarge);\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tushort size = (ushort)(stream.Length - stream.Position);\n\t\t\tbyte[] bytes = stream.GetBuffer();\n\t\t\tSend(bytes, 0, size);\n\t\t}\n\t}\n\n#endif\n\t\n}\n"
  },
  {
    "path": "Assets/Scripts/Network/Core/KCP/KChannel.cs.meta",
    "content": "fileFormatVersion: 2\nguid: 5479891425d5a284fb4c8246e1a52d34\ntimeCreated: 1511491627\nlicenseType: Free\nMonoImporter:\n  serializedVersion: 2\n  defaultReferences: []\n  executionOrder: 0\n  icon: {instanceID: 0}\n  userData: \n  assetBundleName: \n  assetBundleVariant: \n"
  },
  {
    "path": "Assets/Scripts/Network/Core/KCP/KService.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Net;\nusing System.Net.Sockets;\nusing Microsoft.IO;\nusing UnityEngine;\n\nnamespace UNetwork\n{\n\tpublic static class KcpProtocalType\n\t{\n\t\tpublic const short SYN = 101;\n\t\tpublic const short ACK = 102;\n\t\tpublic const short FIN = 103;\n\t\tpublic const short MSG = 104;\n\t}\n\n\tpublic sealed class KService : AService\n\t{\n\t\tpublic static KService Instance { get; private set; }\n\n\t\t// KService创建的时间\n\t\tpublic long StartTime{ get; private set; }\n\t\t\n\t\t// 当前时间 - KService创建的时间\n\t\tpublic uint TimeNow { get; private set; }\n\n\t\tprivate Socket socket;\n\n\t\tprivate KChannel channel;\n\t\t\n\t\tprivate readonly byte[] cache = new byte[8192];\n\n\t\tpublic readonly RecyclableMemoryStreamManager MemoryStreamManager = new RecyclableMemoryStreamManager();\n\n\t\tprivate EndPoint ipEndPoint = new IPEndPoint(IPAddress.Any, 0);\n\n\t\tpublic KService()\n\t\t{\n\t\t\tthis.StartTime = TimeHelper.ClientNow();\n\t\t\tthis.TimeNow = (uint)(TimeHelper.ClientNow() - this.StartTime);\n\t\t\tthis.socket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);\n\t\t\t//this.socket.Blocking = false;\n\t\t\tthis.socket.Bind(new IPEndPoint(IPAddress.Any, 0));\n\n\t\t\tInstance = this;\n\t\t}\n\n\t\tpublic override void Dispose()\n\t\t{\n\t\t\tthis.channel.Dispose();\n\t\t\tthis.socket.Close();\n\t\t\tthis.socket = null;\n\t\t\tInstance = null;\n\t\t}\n\n\t\tpublic void Recv()\n\t\t{\n\t\t\tif (this.socket == null)\n\t\t\t{\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\twhile (socket != null && this.socket.Available > 0)\n\t\t\t{\n\t\t\t\tint messageLength = 0;\n\t\t\t\ttry\n\t\t\t\t{\n//\t\t\t\t\tswitch (flag)\n//\t\t\t\t\t{\n//\t\t\t\t\t\tcase KcpProtocalType.ACK: // 连接\n//\t\t\t\t\t\t\tthis.channel.OnConnect((int)SocketError.Success);\n//\t\t\t\t\t\t\tbreak;\n//\t\t\t\t\t\tcase KcpProtocalType.FIN: // 断开\n//\t\t\t\t\t\t\tbreak;\n//\t\t\t\t\t}\n\n\t\t\t\t\tmessageLength = this.socket.ReceiveFrom(this.cache, ref this.ipEndPoint);\n\t\t\t\t\tif (messageLength < 1)\n\t\t\t\t\t{\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\t\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t\tthis.channel.HandleRecv(this.cache, 0, messageLength);\n\t\t\t\t}\n\t\t\t\tcatch (Exception e)\n\t\t\t\t{\n\t\t\t\t\tDebug.LogError(e);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tpublic KChannel GetKChannel()\n\t\t{\n\t\t\treturn (KChannel)channel;\n\t\t}\n\n\t\tpublic override AChannel GetChannel()\n\t\t{\n\t\t\treturn this.channel;\n\t\t}\n\n#if dynamic_kcp\n\t\tpublic static void Output(IntPtr bytes, int count, IntPtr user)\n#else\n\t\tpublic static void Output(byte[] bytes, int count, object user)\n#endif\n\t\t{\n\t\t\tif (Instance == null)\n\t\t\t{\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tKChannel kChannel = Instance.GetKChannel();\n\t\t\tif (kChannel == null)\n\t\t\t{\n\t\t\t\tDebug.LogError($\"not found kchannel, {(uint)user}\");\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tkChannel.Output(bytes, count);\n\t\t}\n\n\t\tpublic override AChannel ConnectChannel(IPEndPoint remoteEndPoint)\n\t\t{\n\t\t\tuint localConn = (uint)RandomHelper.RandomNumber(1000, int.MaxValue);\n\t\t\tthis.channel = new KChannel(localConn, this.socket, remoteEndPoint, this);\n\t\t\treturn channel;\n\t\t}\n\n\t\tpublic override AChannel ConnectChannel(string address)\n\t\t{\n\t\t\tIPEndPoint ipEndPoint2 = NetworkHelper.ToIPEndPoint(address);\n\t\t\treturn this.ConnectChannel(ipEndPoint2);\n\t\t}\n\n\t\tpublic override void Update()\n\t\t{\n\t\t\tif (Instance == null)\n\t\t\t{\n\t\t\t\treturn;\n\t\t\t}\n\t\t\t\n\t\t\tthis.TimeNow = (uint) (TimeHelper.ClientNow() - this.StartTime);\n\n\t\t\tthis.Recv();\n\t\t\t\n\t\t\tthis.channel.Update();\n\t\t}\n\t}\n}"
  },
  {
    "path": "Assets/Scripts/Network/Core/KCP/KService.cs.meta",
    "content": "fileFormatVersion: 2\nguid: 38037f5390cd34e41849290ca1737863\ntimeCreated: 1511491627\nlicenseType: Free\nMonoImporter:\n  serializedVersion: 2\n  defaultReferences: []\n  executionOrder: 0\n  icon: {instanceID: 0}\n  userData: \n  assetBundleName: \n  assetBundleVariant: \n"
  },
  {
    "path": "Assets/Scripts/Network/Core/KCP/KcpDll.cs",
    "content": "﻿using System;\nusing System.Runtime.InteropServices;\n\nnamespace UNetwork\n{\n    public delegate int KcpOutput(IntPtr buf, int len, IntPtr kcp, IntPtr user);\n\n    public class KcpDll\n    {\n#if UNITY_IPHONE && !UNITY_EDITOR\n        const string KcpDLL = \"__Internal\";\n#else\n        const string KcpDLL = \"kcp\";\n#endif\n\n        [DllImport(KcpDLL, CallingConvention=CallingConvention.Cdecl)]\n        private static extern uint ikcp_check(IntPtr kcp, uint current);\n        [DllImport(KcpDLL, CallingConvention=CallingConvention.Cdecl)]\n        private static extern IntPtr ikcp_create(uint conv, IntPtr user);\n        [DllImport(KcpDLL, CallingConvention=CallingConvention.Cdecl)]\n        private static extern void ikcp_flush(IntPtr kcp);\n        [DllImport(KcpDLL, CallingConvention=CallingConvention.Cdecl)]\n        private static extern uint ikcp_getconv(IntPtr ptr);\n        [DllImport(KcpDLL, CallingConvention=CallingConvention.Cdecl)]\n        private static extern int ikcp_input(IntPtr kcp, byte[] data, int offset, int size);\n        [DllImport(KcpDLL, CallingConvention=CallingConvention.Cdecl)]\n        private static extern int ikcp_nodelay(IntPtr kcp, int nodelay, int interval, int resend, int nc);\n        [DllImport(KcpDLL, CallingConvention=CallingConvention.Cdecl)]\n        private static extern int ikcp_peeksize(IntPtr kcp);\n        [DllImport(KcpDLL, CallingConvention=CallingConvention.Cdecl)]\n        private static extern int ikcp_recv(IntPtr kcp, byte[] buffer, int len);\n        [DllImport(KcpDLL, CallingConvention=CallingConvention.Cdecl)]\n        private static extern void ikcp_release(IntPtr kcp);\n        [DllImport(KcpDLL, CallingConvention=CallingConvention.Cdecl)]\n        private static extern int ikcp_send(IntPtr kcp, byte[] buffer, int len);\n        [DllImport(KcpDLL, CallingConvention=CallingConvention.Cdecl)]\n        private static extern void ikcp_setminrto(IntPtr ptr, int minrto);\n        [DllImport(KcpDLL, CallingConvention=CallingConvention.Cdecl)]\n        private static extern int ikcp_setmtu(IntPtr kcp, int mtu);\n        [DllImport(KcpDLL, CallingConvention=CallingConvention.Cdecl)]\n        private static extern void ikcp_setoutput(IntPtr kcp, KcpOutput output);\n        [DllImport(KcpDLL, CallingConvention=CallingConvention.Cdecl)]\n        private static extern void ikcp_update(IntPtr kcp, uint current);\n        [DllImport(KcpDLL, CallingConvention=CallingConvention.Cdecl)]\n        private static extern int ikcp_waitsnd(IntPtr kcp);\n        [DllImport(KcpDLL, CallingConvention=CallingConvention.Cdecl)]\n        private static extern int ikcp_wndsize(IntPtr kcp, int sndwnd, int rcvwnd);\n        \n        public static uint KcpCheck(IntPtr kcp, uint current)\n        {\n            if (kcp == IntPtr.Zero)\n            {\n                throw new Exception($\"kcp error, kcp point is zero\");\n            }\n            return ikcp_check(kcp, current);\n        }\n        \n        public static IntPtr KcpCreate(uint conv, IntPtr user)\n        {\n            return ikcp_create(conv, user);\n        }\n\n        public static void KcpFlush(IntPtr kcp)\n        {\n            if (kcp == IntPtr.Zero)\n            {\n                throw new Exception($\"kcp error, kcp point is zero\");\n            }\n            ikcp_flush(kcp);\n        }\n\n        public static uint KcpGetconv(IntPtr ptr)\n        {\n            if (ptr == IntPtr.Zero)\n            {\n                throw new Exception($\"kcp error, kcp point is zero\");\n            }\n            return ikcp_getconv(ptr);\n        }\n\n        public static int KcpInput(IntPtr kcp, byte[] data, int offset, int size)\n        {\n            if (kcp == IntPtr.Zero)\n            {\n                throw new Exception($\"kcp error, kcp point is zero\");\n            }\n            return ikcp_input(kcp, data, offset, size);\n        }\n\n        public static int KcpNodelay(IntPtr kcp, int nodelay, int interval, int resend, int nc)\n        {\n            if (kcp == IntPtr.Zero)\n            {\n                throw new Exception($\"kcp error, kcp point is zero\");\n            }\n            return ikcp_nodelay(kcp, nodelay, interval, resend, nc);\n        }\n\n        public static int KcpPeeksize(IntPtr kcp)\n        {\n            if (kcp == IntPtr.Zero)\n            {\n                throw new Exception($\"kcp error, kcp point is zero\");\n            }\n            return ikcp_peeksize(kcp);\n        }\n\n        public static int KcpRecv(IntPtr kcp, byte[] buffer, int len)\n        {\n            if (kcp == IntPtr.Zero)\n            {\n                throw new Exception($\"kcp error, kcp point is zero\");\n            }\n            return ikcp_recv(kcp, buffer, len);\n        }\n\n        public static void KcpRelease(IntPtr kcp)\n        {\n            if (kcp == IntPtr.Zero)\n            {\n                throw new Exception($\"kcp error, kcp point is zero\");\n            }\n            ikcp_release(kcp);\n        }\n\n        public static int KcpSend(IntPtr kcp, byte[] buffer, int len)\n        {\n            if (kcp == IntPtr.Zero)\n            {\n                throw new Exception($\"kcp error, kcp point is zero\");\n            }\n            return ikcp_send(kcp, buffer, len);\n        }\n\n        public static void KcpSetminrto(IntPtr kcp, int minrto)\n        {\n            if (kcp == IntPtr.Zero)\n            {\n                throw new Exception($\"kcp error, kcp point is zero\");\n            }\n            ikcp_setminrto(kcp, minrto);\n        }\n\n        public static int KcpSetmtu(IntPtr kcp, int mtu)\n        {\n            if (kcp == IntPtr.Zero)\n            {\n                throw new Exception($\"kcp error, kcp point is zero\");\n            }\n            return ikcp_setmtu(kcp, mtu);\n        }\n\n        public static void KcpSetoutput(IntPtr kcp, KcpOutput output)\n        {\n            if (kcp == IntPtr.Zero)\n            {\n                throw new Exception($\"kcp error, kcp point is zero\");\n            }\n            ikcp_setoutput(kcp, output);\n        }\n\n        public static void KcpUpdate(IntPtr kcp, uint current)\n        {\n            if (kcp == IntPtr.Zero)\n            {\n                throw new Exception($\"kcp error, kcp point is zero\");\n            }\n            ikcp_update(kcp, current);\n        }\n\n        public static int KcpWaitsnd(IntPtr kcp)\n        {\n            if (kcp == IntPtr.Zero)\n            {\n                throw new Exception($\"kcp error, kcp point is zero\");\n            }\n            return ikcp_waitsnd(kcp);\n        }\n\n        public static int KcpWndsize(IntPtr kcp, int sndwnd, int rcvwnd)\n        {\n            if (kcp == IntPtr.Zero)\n            {\n                throw new Exception($\"kcp error, kcp point is zero\");\n            }\n            return ikcp_wndsize(kcp, sndwnd, rcvwnd);\n        }\n    }\n}\n\n"
  },
  {
    "path": "Assets/Scripts/Network/Core/KCP/KcpDll.cs.meta",
    "content": "fileFormatVersion: 2\nguid: 0832a74dcd3e44341bcffd3b1f182419\ntimeCreated: 1511491627\nlicenseType: Free\nMonoImporter:\n  serializedVersion: 2\n  defaultReferences: []\n  executionOrder: 0\n  icon: {instanceID: 0}\n  userData: \n  assetBundleName: \n  assetBundleVariant: \n"
  },
  {
    "path": "Assets/Scripts/Network/Core/KCP.meta",
    "content": "fileFormatVersion: 2\nguid: 80771ba2b140e0f47b8df6dfe75d5c68\nfolderAsset: yes\ntimeCreated: 1511491627\nlicenseType: Free\nDefaultImporter:\n  userData: \n  assetBundleName: \n  assetBundleVariant: \n"
  },
  {
    "path": "Assets/Scripts/Network/Core/TCP/PacketParser.cs",
    "content": "﻿using System;\nusing System.IO;\n\nnamespace UNetwork\n{\n\tpublic enum ParserState\n\t{\n\t\tPacketSize,\n\t\tPacketBody\n\t}\n\t\n\tpublic static class Packet\n\t{\n\t\tpublic const int PacketSizeLength2 = 2;\n\t\tpublic const int PacketSizeLength4 = 4;\n\t\tpublic const int MinPacketSize = 2;\n\t\tpublic const int OpcodeIndex = 0;\n\t\tpublic const int MessageIndex = 2;\n\t}\n\n\tpublic class PacketParser\n\t{\n\t\tprivate readonly CircularBuffer buffer;\n\t\tprivate int packetSize;\n\t\tprivate ParserState state;\n\t\tpublic MemoryStream memoryStream;\n\t\tprivate bool isOK;\n\t\tprivate readonly int packetSizeLength;\n\n\t\tpublic PacketParser(int packetSizeLength, CircularBuffer buffer, MemoryStream memoryStream)\n\t\t{\n\t\t\tthis.packetSizeLength = packetSizeLength;\n\t\t\tthis.buffer = buffer;\n\t\t\tthis.memoryStream = memoryStream;\n\t\t}\n\n\t\tpublic bool Parse()\n\t\t{\n\t\t\tif (this.isOK)\n\t\t\t{\n\t\t\t\treturn true;\n\t\t\t}\n\n\t\t\tbool finish = false;\n\t\t\twhile (!finish)\n\t\t\t{\n\t\t\t\tswitch (this.state)\n\t\t\t\t{\n\t\t\t\t\tcase ParserState.PacketSize:\n\t\t\t\t\t\tif (this.buffer.Length < this.packetSizeLength)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tfinish = true;\n\t\t\t\t\t\t}\n\t\t\t\t\t\telse\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tthis.buffer.Read(this.memoryStream.GetBuffer(), 0, this.packetSizeLength);\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\tswitch (this.packetSizeLength)\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tcase Packet.PacketSizeLength4:\n\t\t\t\t\t\t\t\t\tthis.packetSize = BitConverter.ToInt32(this.memoryStream.GetBuffer(), 0);\n\t\t\t\t\t\t\t\t\tif (this.packetSize > ushort.MaxValue * 16 || this.packetSize < Packet.MinPacketSize)\n\t\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\t\tthrow new Exception($\"recv packet size error, 可能是外网探测端口: {this.packetSize}\");\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\t\tcase Packet.PacketSizeLength2:\n\t\t\t\t\t\t\t\t\tthis.packetSize = BitConverter.ToUInt16(this.memoryStream.GetBuffer(), 0);\n\t\t\t\t\t\t\t\t\tif (this.packetSize > ushort.MaxValue || this.packetSize < Packet.MinPacketSize)\n\t\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\t\tthrow new Exception($\"recv packet size error:, 可能是外网探测端口: {this.packetSize}\");\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\t\tdefault:\n\t\t\t\t\t\t\t\t\tthrow new Exception(\"packet size byte count must be 2 or 4!\");\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tthis.state = ParserState.PacketBody;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase ParserState.PacketBody:\n\t\t\t\t\t\tif (this.buffer.Length < this.packetSize)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tfinish = true;\n\t\t\t\t\t\t}\n\t\t\t\t\t\telse\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tthis.memoryStream.Seek(0, SeekOrigin.Begin);\n\t\t\t\t\t\t\tthis.memoryStream.SetLength(this.packetSize);\n\t\t\t\t\t\t\tbyte[] bytes = this.memoryStream.GetBuffer();\n\t\t\t\t\t\t\tthis.buffer.Read(bytes, 0, this.packetSize);\n\t\t\t\t\t\t\tthis.isOK = true;\n\t\t\t\t\t\t\tthis.state = ParserState.PacketSize;\n\t\t\t\t\t\t\tfinish = true;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn this.isOK;\n\t\t}\n\n\t\tpublic MemoryStream GetPacket()\n\t\t{\n\t\t\tthis.isOK = false;\n\t\t\treturn this.memoryStream;\n\t\t}\n\t}\n}"
  },
  {
    "path": "Assets/Scripts/Network/Core/TCP/PacketParser.cs.meta",
    "content": "fileFormatVersion: 2\nguid: 8b71d12882faac44992e531533491a5f\ntimeCreated: 1503986857\nlicenseType: Free\nMonoImporter:\n  serializedVersion: 2\n  defaultReferences: []\n  executionOrder: 0\n  icon: {instanceID: 0}\n  userData: \n  assetBundleName: \n  assetBundleVariant: \n"
  },
  {
    "path": "Assets/Scripts/Network/Core/TCP/TChannel.cs",
    "content": "﻿using System;\nusing System.IO;\nusing System.Net;\nusing System.Net.Sockets;\nusing UnityEngine;\n\nnamespace UNetwork\n{\n\t/// <summary>\n\t/// 封装Socket,将回调push到主线程处理\n\t/// </summary>\n\tpublic sealed class TChannel: AChannel\n\t{\n\t\tprivate Socket socket;\n\t\tprivate SocketAsyncEventArgs innArgs = new SocketAsyncEventArgs();\n\t\tprivate SocketAsyncEventArgs outArgs = new SocketAsyncEventArgs();\n\n\t\tprivate readonly CircularBuffer recvBuffer = new CircularBuffer();\n\t\tprivate readonly CircularBuffer sendBuffer = new CircularBuffer();\n\n\t\tprivate bool isSending;\n\n\t\tprivate bool isRecving;\n\n\t\tprivate bool isConnected;\n\n\t\tprivate readonly PacketParser parser;\n\n\t\tprivate readonly byte[] packetSizeCache;\n \n\n\t\tpublic TChannel(IPEndPoint ipEndPoint, TService service): base(service, ChannelType.Connect)\n\t\t{\n\t\t\t\n\t\t\tint packetSize = service.PacketSizeLength;\n\t\t\tthis.packetSizeCache = new byte[packetSize];\n\t\t\tthis.memoryStream = service.MemoryStreamManager.GetStream(\"message\", ushort.MaxValue);\n\t\t\t\n\t\t\tthis.socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);\n\t\t\tthis.socket.NoDelay = true;\n\t\t\tthis.parser = new PacketParser(packetSize, this.recvBuffer, this.memoryStream);\n\t\t\tthis.innArgs.Completed += this.OnComplete;\n\t\t\tthis.outArgs.Completed += this.OnComplete;\n\n\t\t\tthis.RemoteAddress = ipEndPoint;\n\n\t\t\tthis.isConnected = false;\n\t\t\tthis.isSending = false;\n\t\t}\n\n\t\tpublic override void Dispose()\n\t\t{\n\t\t\tthis.socket.Close();\n\t\t\tthis.innArgs.Dispose();\n\t\t\tthis.outArgs.Dispose();\n\t\t\tthis.innArgs = null;\n\t\t\tthis.outArgs = null;\n\t\t\tthis.socket = null;\n\t\t\tthis.memoryStream.Dispose();\n\t\t}\n\t\t\n\t\tprivate TService GetService()\n\t\t{\n\t\t\treturn (TService)this.Service;\n\t\t}\n\n\t\tpublic override void Start()\n\t\t{\n\t\t\tif (!this.isConnected)\n\t\t\t{\n\t\t\t\tthis.ConnectAsync(this.RemoteAddress);\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tif (!this.isRecving)\n\t\t\t{\n\t\t\t\tthis.isRecving = true;\n\t\t\t\tthis.StartRecv();\n\t\t\t}\n\t\t}\n\t\t\n\t\tpublic override void Send(MemoryStream stream)\n\t\t{\n\t\t\tswitch (this.GetService().PacketSizeLength)\n\t\t\t{\n\t\t\t\tcase Packet.PacketSizeLength4:\n\t\t\t\t\tif (stream.Length > ushort.MaxValue * 16)\n\t\t\t\t\t{\n\t\t\t\t\t\tthrow new Exception($\"send packet too large: {stream.Length}\");\n\t\t\t\t\t}\n\t\t\t\t\tthis.packetSizeCache.WriteTo(0, (int) stream.Length);\n\t\t\t\t\tbreak;\n\t\t\t\tcase Packet.PacketSizeLength2:\n\t\t\t\t\tif (stream.Length > ushort.MaxValue)\n\t\t\t\t\t{\n\t\t\t\t\t\tthrow new Exception($\"send packet too large: {stream.Length}\");\n\t\t\t\t\t}\n\t\t\t\t\tthis.packetSizeCache.WriteTo(0, (ushort) stream.Length);\n\t\t\t\t\tbreak;\n\t\t\t\tdefault:\n\t\t\t\t\tthrow new Exception(\"packet size must be 2 or 4!\");\n\t\t\t}\n\n\t\t\tthis.sendBuffer.Write(this.packetSizeCache, 0, this.packetSizeCache.Length);\n\t\t\tthis.sendBuffer.Write(stream);\n\t\t}\n\n\t\tprivate void OnComplete(object sender, SocketAsyncEventArgs e)\n\t\t{\n\t\t\tswitch (e.LastOperation)\n\t\t\t{\n\t\t\t\tcase SocketAsyncOperation.Connect:\n\t\t\t\t\tOneThreadSynchronizationContext.Instance.Post(this.OnConnectComplete, e);\n\t\t\t\t\tbreak;\n\t\t\t\tcase SocketAsyncOperation.Receive:\n\t\t\t\t\tOneThreadSynchronizationContext.Instance.Post(this.OnRecvComplete, e);\n\t\t\t\t\tbreak;\n\t\t\t\tcase SocketAsyncOperation.Send:\n\t\t\t\t\tOneThreadSynchronizationContext.Instance.Post(this.OnSendComplete, e);\n\t\t\t\t\tbreak;\n\t\t\t\tcase SocketAsyncOperation.Disconnect:\n\t\t\t\t\tOneThreadSynchronizationContext.Instance.Post(this.OnDisconnectComplete, e);\n\t\t\t\t\tbreak;\n\t\t\t\tdefault:\n\t\t\t\t\tthrow new Exception($\"socket error: {e.LastOperation}\");\n\t\t\t}\n\t\t}\n\n\t\tpublic void ConnectAsync(IPEndPoint ipEndPoint)\n\t\t{\n\t\t\tthis.outArgs.RemoteEndPoint = ipEndPoint;\n\t\t\tif (this.socket.ConnectAsync(this.outArgs))\n\t\t\t{\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tOnConnectComplete(this.outArgs);\n\t\t}\n\n\t\tprivate void OnConnectComplete(object o)\n\t\t{\n\t\t\tif (this.socket == null)\n\t\t\t{\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tSocketAsyncEventArgs e = (SocketAsyncEventArgs) o;\n\t\t\t\n\t\t\tif (e.SocketError != SocketError.Success)\n\t\t\t{\n\t\t\t\tthis.OnError((int)e.SocketError);\t\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\te.RemoteEndPoint = null;\n\t\t\tthis.isConnected = true;\n\t\t\tthis.OnConnect((int)SocketError.Success);\n\t\t\t\n\t\t\tthis.Start();\n\t\t}\n\n\t\tprivate void OnDisconnectComplete(object o)\n\t\t{\n\t\t\tSocketAsyncEventArgs e = (SocketAsyncEventArgs)o;\n\t\t\tthis.OnError((int)e.SocketError);\n\t\t}\n\n\t\tprivate void StartRecv()\n\t\t{\n\t\t\tint size = this.recvBuffer.ChunkSize - this.recvBuffer.LastIndex;\n\t\t\tthis.RecvAsync(this.recvBuffer.Last, this.recvBuffer.LastIndex, size);\n\t\t}\n\n\t\tpublic void RecvAsync(byte[] buffer, int offset, int count)\n\t\t{\n\t\t\ttry\n\t\t\t{\n\t\t\t\tthis.innArgs.SetBuffer(buffer, offset, count);\n\t\t\t}\n\t\t\tcatch (Exception e)\n\t\t\t{\n\t\t\t\tthrow new Exception($\"socket set buffer error: {buffer.Length}, {offset}, {count}\", e);\n\t\t\t}\n\t\t\t\n\t\t\tif (this.socket.ReceiveAsync(this.innArgs))\n\t\t\t{\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tOnRecvComplete(this.innArgs);\n\t\t}\n\n\t\tprivate void OnRecvComplete(object o)\n\t\t{\n\t\t\tif (this.socket == null)\n\t\t\t{\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tSocketAsyncEventArgs e = (SocketAsyncEventArgs) o;\n\n\t\t\tif (e.SocketError != SocketError.Success)\n\t\t\t{\n\t\t\t\tthis.OnError((int)e.SocketError);\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tif (e.BytesTransferred == 0)\n\t\t\t{\n\t\t\t\tthis.OnError(ErrorCode.ERR_PeerDisconnect);\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tthis.recvBuffer.LastIndex += e.BytesTransferred;\n\t\t\tif (this.recvBuffer.LastIndex == this.recvBuffer.ChunkSize)\n\t\t\t{\n\t\t\t\tthis.recvBuffer.AddLast();\n\t\t\t\tthis.recvBuffer.LastIndex = 0;\n\t\t\t}\n\n\t\t\t// 收到消息回调\n\t\t\twhile (true)\n\t\t\t{\n\t\t\t\ttry\n\t\t\t\t{\n\t\t\t\t\tif (!this.parser.Parse())\n\t\t\t\t\t{\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tcatch (Exception ee)\n\t\t\t\t{\n\t\t\t\t\tDebug.LogError($\"ip: {this.RemoteAddress} {ee}\");\n\t\t\t\t\tthis.OnError(ErrorCode.ERR_SocketError);\n\t\t\t\t\treturn;\n\t\t\t\t}\n\n\t\t\t\ttry\n\t\t\t\t{\n\t\t\t\t\tthis.OnRead(this.parser.GetPacket());\n\t\t\t\t}\n\t\t\t\tcatch (Exception ee)\n\t\t\t\t{\n\t\t\t\t\tDebug.LogError(ee);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (this.socket == null)\n\t\t\t{\n\t\t\t\treturn;\n\t\t\t}\n\t\t\t\n\t\t\tthis.StartRecv();\n\t\t}\n\n\t\tpublic bool IsSending => this.isSending;\n\n\t\tpublic void StartSend()\n\t\t{\n\t\t\tif(!this.isConnected)\n\t\t\t{\n\t\t\t\treturn;\n\t\t\t}\n\t\t\t\n\t\t\t// 没有数据需要发送\n\t\t\tif (this.sendBuffer.Length == 0)\n\t\t\t{\n\t\t\t\tthis.isSending = false;\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tthis.isSending = true;\n\n\t\t\tint sendSize = this.sendBuffer.ChunkSize - this.sendBuffer.FirstIndex;\n\t\t\tif (sendSize > this.sendBuffer.Length)\n\t\t\t{\n\t\t\t\tsendSize = (int)this.sendBuffer.Length;\n\t\t\t}\n\n\t\t\tthis.SendAsync(this.sendBuffer.First, this.sendBuffer.FirstIndex, sendSize);\n\t\t}\n\n\t\tpublic void SendAsync(byte[] buffer, int offset, int count)\n\t\t{\n\t\t\ttry\n\t\t\t{\n\t\t\t\tthis.outArgs.SetBuffer(buffer, offset, count);\n\t\t\t}\n\t\t\tcatch (Exception e)\n\t\t\t{\n\t\t\t\tthrow new Exception($\"socket set buffer error: {buffer.Length}, {offset}, {count}\", e);\n\t\t\t}\n\t\t\tif (this.socket.SendAsync(this.outArgs))\n\t\t\t{\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tOnSendComplete(this.outArgs);\n\t\t}\n\n\t\tprivate void OnSendComplete(object o)\n\t\t{\n\t\t\tif (this.socket == null)\n\t\t\t{\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tSocketAsyncEventArgs e = (SocketAsyncEventArgs) o;\n\n\t\t\tif (e.SocketError != SocketError.Success)\n\t\t\t{\n\t\t\t\tthis.OnError((int)e.SocketError);\n\t\t\t\treturn;\n\t\t\t}\n\t\t\t\n\t\t\tif (e.BytesTransferred == 0)\n\t\t\t{\n\t\t\t\tthis.OnError(ErrorCode.ERR_PeerDisconnect);\n\t\t\t\treturn;\n\t\t\t}\n\t\t\t\n\t\t\tthis.sendBuffer.FirstIndex += e.BytesTransferred;\n\t\t\tif (this.sendBuffer.FirstIndex == this.sendBuffer.ChunkSize)\n\t\t\t{\n\t\t\t\tthis.sendBuffer.FirstIndex = 0;\n\t\t\t\tthis.sendBuffer.RemoveFirst();\n\t\t\t}\n\t\t\t\n\t\t\tthis.StartSend();\n\t\t}\n\t}\n}"
  },
  {
    "path": "Assets/Scripts/Network/Core/TCP/TChannel.cs.meta",
    "content": "fileFormatVersion: 2\nguid: 4cacc90d7e561c64a8ea0e9079163bb9\ntimeCreated: 1503986857\nlicenseType: Free\nMonoImporter:\n  serializedVersion: 2\n  defaultReferences: []\n  executionOrder: 0\n  icon: {instanceID: 0}\n  userData: \n  assetBundleName: \n  assetBundleVariant: \n"
  },
  {
    "path": "Assets/Scripts/Network/Core/TCP/TChannelConnector.cs",
    "content": "﻿using System;\nusing System.IO;\nusing System.Net.Sockets;\nusing System.Text;\nusing UnityEngine;\nusing UnityEngine.Events;\n\nnamespace UNetwork\n{\n    /// <summary>\n    /// 管理客户端连接的类\n    /// </summary>\n    public sealed class TClientConnection : AChannel\n    {\n        private int PacketSizeLength;\n\n        private readonly Socket clientSocket;\n        private readonly SocketAsyncEventArgs innArgs = new SocketAsyncEventArgs();\n        private readonly SocketAsyncEventArgs outArgs = new SocketAsyncEventArgs();\n\n        private readonly CircularBuffer recvBuffer = new CircularBuffer();\n        private readonly CircularBuffer sendBuffer = new CircularBuffer();\n\n        private readonly PacketParser parser;\n        private readonly byte[] packetSizeCache;\n\n        private bool isConnected;\n        private bool isSending;\n        private bool isRecving;\n\n        private readonly SessionConnector session;\n        public UnityAction<TClientConnection> OnDisposeCallback;\n\n        public TClientConnection(Socket clientSocket, TServiceServer service) : base(service, ChannelType.Connect)\n        {\n            this.session = new SessionConnector(this);\n\n            this.clientSocket = clientSocket;\n            this.memoryStream = service.MemoryStreamManager.GetStream(\"client\", ushort.MaxValue);\n\n            int packetSize = service.PacketSizeLength;\n            this.PacketSizeLength = packetSize;\n            this.packetSizeCache = new byte[packetSize];\n            this.parser = new PacketParser(packetSize, this.recvBuffer, this.memoryStream);\n\n            this.innArgs.Completed += this.OnComplete;\n            this.outArgs.Completed += this.OnComplete;\n            isConnected = false;\n        }\n\n        public override void Dispose()\n        {\n            OnDisposeCallback?.Invoke(this);\n            this.clientSocket.Close();\n            this.innArgs.Dispose();\n            this.outArgs.Dispose();\n            this.memoryStream.Dispose();\n        }\n\n        public override void Start()\n        {\n            if (!this.isRecving)\n            {\n                this.isRecving = true;\n                this.StartRecv();\n                this.isConnected = true;\n            }\n        }\n\n        public void Update()\n        {\n            if (isSending)\n            {\n                return;\n            }\n\n            try\n            {\n                StartSend();\n            }\n            catch (Exception e)\n            {\n                Debug.LogError(e);\n            }\n        }\n\n        private void OnComplete(object sender, SocketAsyncEventArgs e)\n        {\n            switch (e.LastOperation)\n            {\n                case SocketAsyncOperation.Receive:\n                    OneThreadSynchronizationContext.Instance.Post(this.OnRecvComplete, e);\n                    break;\n                case SocketAsyncOperation.Send:\n                    OneThreadSynchronizationContext.Instance.Post(this.OnSendComplete, e);\n                    break;\n                case SocketAsyncOperation.Disconnect:\n                    OneThreadSynchronizationContext.Instance.Post(this.OnDisconnectComplete, e);\n                    break;\n                default:\n                    throw new Exception($\"socket error: {e.LastOperation}\");\n            }\n        }\n\n        private void OnDisconnectComplete(object o)\n        {\n            SocketAsyncEventArgs e = (SocketAsyncEventArgs)o;\n            this.OnError((int)e.SocketError);\n        }\n\n        private void StartRecv()\n        {\n            int size = this.recvBuffer.ChunkSize - this.recvBuffer.LastIndex;\n            this.RecvAsync(this.recvBuffer.Last, this.recvBuffer.LastIndex, size);\n        }\n\n        public void RecvAsync(byte[] buffer, int offset, int count)\n        {\n            try\n            {\n                this.innArgs.SetBuffer(buffer, offset, count);\n            }\n            catch (Exception e)\n            {\n                throw new Exception($\"socket set buffer error: {buffer.Length}, {offset}, {count}\", e);\n            }\n\n            if (this.clientSocket.ReceiveAsync(this.innArgs))\n            {\n                return;\n            }\n\n            OnRecvComplete(this.innArgs);\n        }\n\n        private void OnRecvComplete(object o)\n        {\n            if (this.clientSocket == null)\n            {\n                return;\n            }\n\n            SocketAsyncEventArgs e = (SocketAsyncEventArgs)o;\n\n            if (e.SocketError != SocketError.Success)\n            {\n                this.OnError((int)e.SocketError);\n                return;\n            }\n\n            if (e.BytesTransferred == 0)\n            {\n                this.OnError(ErrorCode.ERR_PeerDisconnect);\n                return;\n            }\n\n            this.recvBuffer.LastIndex += e.BytesTransferred;\n            if (this.recvBuffer.LastIndex == this.recvBuffer.ChunkSize)\n            {\n                this.recvBuffer.AddLast();\n                this.recvBuffer.LastIndex = 0;\n            }\n\n            // 收到消息回调\n            while (true)\n            {\n                try\n                {\n                    if (!this.parser.Parse())\n                    {\n                        break;\n                    }\n                }\n                catch (Exception ee)\n                {\n                    Debug.LogError($\"Client {clientSocket.RemoteEndPoint} {ee}\");\n                    this.OnError(ErrorCode.ERR_SocketError);\n                    return;\n                }\n\n                try\n                {\n                    this.OnRead(this.parser.GetPacket());\n                }\n                catch (Exception ee)\n                {\n                    Debug.LogError(ee);\n                }\n            }\n\n            this.StartRecv();\n        }\n\n        public override void Send(MemoryStream stream)\n        {\n            switch (PacketSizeLength)\n            {\n                case Packet.PacketSizeLength4:\n                    if (stream.Length > ushort.MaxValue * 16)\n                    {\n                        throw new Exception($\"send packet too large: {stream.Length}\");\n                    }\n\n                    this.packetSizeCache.WriteTo(0, (int)stream.Length);\n                    break;\n                case Packet.PacketSizeLength2:\n                    if (stream.Length > ushort.MaxValue)\n                    {\n                        throw new Exception($\"send packet too large: {stream.Length}\");\n                    }\n\n                    this.packetSizeCache.WriteTo(0, (ushort)stream.Length);\n                    break;\n                default:\n                    throw new Exception(\"packet size must be 2 or 4!\");\n            }\n\n            this.sendBuffer.Write(this.packetSizeCache, 0, this.packetSizeCache.Length);\n            this.sendBuffer.Write(stream);\n        }\n\n        public void StartSend()\n        {\n            if (!this.isConnected)\n            {\n                return;\n            }\n\n            // 没有数据需要发送\n            if (this.sendBuffer.Length == 0)\n            {\n                this.isSending = false;\n                return;\n            }\n\n            if (!this.isSending)\n            {\n                this.isSending = true;\n            }\n\n            int sendSize = this.sendBuffer.ChunkSize - this.sendBuffer.FirstIndex;\n            if (sendSize > this.sendBuffer.Length)\n            {\n                sendSize = (int)this.sendBuffer.Length;\n            }\n\n            this.SendAsync(this.sendBuffer.First, this.sendBuffer.FirstIndex, sendSize);\n        }\n\n        public void SendAsync(byte[] buffer, int offset, int count)\n        {\n            try\n            {\n                this.outArgs.SetBuffer(buffer, offset, count);\n            }\n            catch (Exception e)\n            {\n                throw new Exception($\"socket set buffer error: {buffer.Length}, {offset}, {count}\", e);\n            }\n\n            if (this.clientSocket.SendAsync(this.outArgs))\n            {\n                return;\n            }\n\n            OnSendComplete(this.outArgs);\n        }\n\n        private void OnSendComplete(object o)\n        {\n            SocketAsyncEventArgs e = (SocketAsyncEventArgs)o;\n\n            if (e.SocketError != SocketError.Success)\n            {\n                this.OnError((int)e.SocketError);\n                return;\n            }\n\n            if (e.BytesTransferred == 0)\n            {\n                this.OnError(ErrorCode.ERR_PeerDisconnect);\n                return;\n            }\n\n            this.sendBuffer.FirstIndex += e.BytesTransferred;\n            if (this.sendBuffer.FirstIndex == this.sendBuffer.ChunkSize)\n            {\n                this.sendBuffer.FirstIndex = 0;\n                this.sendBuffer.RemoveFirst();\n            }\n\n            this.StartSend();\n        }\n\n        private new void OnError(int error)\n        {\n            Debug.LogError($\"Socket error: {error}\");\n            this.Dispose();\n        }\n\n        private void OnMessage(byte[] obj)\n        {\n            var msg = Encoding.UTF8.GetString(obj);\n            Debug.Log($\"Receive{obj.Length}\" + msg);\n        }\n    }\n}"
  },
  {
    "path": "Assets/Scripts/Network/Core/TCP/TChannelConnector.cs.meta",
    "content": "fileFormatVersion: 2\nguid: 1740169c35694ff9aa464756e50c4646\ntimeCreated: 1719314153"
  },
  {
    "path": "Assets/Scripts/Network/Core/TCP/TChannelServer.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.IO;\nusing System.Net;\nusing System.Net.Sockets;\nusing UnityEngine;\n\nnamespace UNetwork\n{\n    /// <summary>\n    /// 封装 Server 端Socket监听\n    /// </summary>\n    public sealed class TChannelServer : AChannel\n    {\n        private Socket listener;\n        private List<TClientConnection> clientConnections = new List<TClientConnection>();\n        private SocketAsyncEventArgs acceptArgs = new SocketAsyncEventArgs();\n\n        private readonly PacketParser parser;\n\n        private readonly TServiceServer service;\n\t\t\n        private bool isSending;\n\n        private bool isRecving;\n\n        public TChannelServer(IPEndPoint ipEndPoint, TServiceServer service) : base(service, ChannelType.Accept)\n        {\n            this.service = service;\n            this.memoryStream = service.MemoryStreamManager.GetStream(\"server\", ushort.MaxValue);\n\n            this.listener = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);\n            listener.Bind(ipEndPoint);\n            listener.Listen(10); // 最大挂起连接队列的长度\n            this.listener.NoDelay = true;\n            \n            Debug.Log(\"开启监听：\" + ipEndPoint);\n            this.acceptArgs.Completed += this.OnAcceptComplete;\n        }\n\n        public void Update()\n        {\n            foreach (var connection in clientConnections)\n            {\n                connection.Update();\n            }\n        }\n        \n        public override void Send(MemoryStream stream)\n        {\n            foreach (var connection in clientConnections)\n            {\n                connection.Send(stream);\n            }\n        }\n\n        public override void Dispose()\n        {\n            this.listener.Close();\n            foreach (var clientConnection in this.clientConnections)\n            {\n                clientConnection.Dispose();\n            }\n\n            this.acceptArgs.Dispose();\n            this.listener = null;\n        }\n\n        public override void Start()\n        {\n            this.AcceptAsync();\n        }\n\n        private void AcceptAsync()\n        {\n            acceptArgs.AcceptSocket = null; // Reset AcceptSocket\n            listener.AcceptAsync(acceptArgs);\n        }\n\n        private void OnAcceptComplete(object sender, SocketAsyncEventArgs e)\n        {\n            if (this.listener == null)\n            {\n                return;\n            }\n\n            if (e.SocketError != SocketError.Success)\n            {\n                this.OnError((int)e.SocketError);\n                return;\n            }\n\n            Debug.Log(\"客户端连接成功：\" + e.RemoteEndPoint);\n            Socket clientSocket = e.AcceptSocket;\n\n            TClientConnection clientConnection = new TClientConnection(clientSocket, this.service);\n            clientConnections.Add(clientConnection);\n\n            clientConnection.Start();\n            clientConnection.OnDisposeCallback=OnDisconnect;\n            // Start accepting next client\n            this.AcceptAsync();\n        }\n\n        public void OnDisconnect(TClientConnection connection)\n        {\n            clientConnections.Remove(connection);\n        }\n        \n    }\n}"
  },
  {
    "path": "Assets/Scripts/Network/Core/TCP/TChannelServer.cs.meta",
    "content": "﻿fileFormatVersion: 2\nguid: e1134ac288dc42f2892e29f9f7c754e0\ntimeCreated: 1719304015"
  },
  {
    "path": "Assets/Scripts/Network/Core/TCP/TService.cs",
    "content": "﻿using System;\nusing System.Net;\nusing System.Net.Sockets;\nusing Microsoft.IO;\nusing UnityEngine;\n\nnamespace UNetwork\n{\n\t/// <summary>\n\t/// TChannle的一个封装管理\n\t/// </summary>\n\tpublic sealed class TService : AService\n\t{\n\t\tprivate TChannel channel;\n\n\t\tpublic RecyclableMemoryStreamManager MemoryStreamManager = new RecyclableMemoryStreamManager();\n\t\t\n\t\tpublic int PacketSizeLength { get; }\n\t\t\n\t\tpublic TService(int packetSizeLength)\n\t\t{\n\t\t\tthis.PacketSizeLength = packetSizeLength;\n\t\t}\n\t\t\n\t\tpublic override void Dispose()\n\t\t{\n\t\t\tthis.channel.Dispose();\n\t\t}\n\t\t\n\t\tpublic override AChannel GetChannel()\n\t\t{\n\t\t\treturn channel;\n\t\t}\n\n\t\tpublic override AChannel ConnectChannel(IPEndPoint ipEndPoint)\n\t\t{\n\t\t\tchannel = new TChannel(ipEndPoint, this);\n\t\t\treturn channel;\n\t\t}\n\n\t\tpublic override AChannel ConnectChannel(string address)\n\t\t{\n\t\t\tIPEndPoint ipEndPoint = NetworkHelper.ToIPEndPoint(address);\n\t\t\treturn this.ConnectChannel(ipEndPoint);\n\t\t}\n\n\t\tpublic override void Update()\n\t\t{\n\t\t\tif (channel.IsSending)\n\t\t\t{\n\t\t\t\treturn;\n\t\t\t}\n\t\t\t\n\t\t\ttry\n\t\t\t{\n\t\t\t\tchannel.StartSend();\n\t\t\t}\n\t\t\tcatch (Exception e)\n\t\t\t{\n\t\t\t\tDebug.LogError(e);\n\t\t\t}\n\t\t}\n\t}\n}"
  },
  {
    "path": "Assets/Scripts/Network/Core/TCP/TService.cs.meta",
    "content": "fileFormatVersion: 2\nguid: 9c6cb526a0e4374438cbcbaa70239e4a\ntimeCreated: 1503986857\nlicenseType: Free\nMonoImporter:\n  serializedVersion: 2\n  defaultReferences: []\n  executionOrder: 0\n  icon: {instanceID: 0}\n  userData: \n  assetBundleName: \n  assetBundleVariant: \n"
  },
  {
    "path": "Assets/Scripts/Network/Core/TCP/TServiceServer.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Net;\nusing System.Net.Sockets;\nusing Microsoft.IO;\nusing UnityEngine;\n\nnamespace UNetwork\n{\n\tpublic sealed class TServiceServer : AService\n\t{\n\t\tprivate TChannelServer channel;\n\n\t\tpublic RecyclableMemoryStreamManager MemoryStreamManager = new RecyclableMemoryStreamManager();\n\t\t\n\t\tpublic int PacketSizeLength { get; }\n\n\t\tpublic TServiceServer(int packetSizeLength)\n\t\t{\n\t\t\tthis.PacketSizeLength = packetSizeLength;\n\t\t}\n\t\t\n\t\tpublic override void Dispose()\n\t\t{\n\t\t\tthis.channel.Dispose();\n\t\t}\n\t\t\n\t\tpublic override AChannel GetChannel()\n\t\t{\n\t\t\treturn channel;\n\t\t}\n\n\t\tpublic override AChannel ConnectChannel(IPEndPoint ipEndPoint)\n\t\t{\n\t\t\tchannel = new TChannelServer(ipEndPoint, this);\n\t\t\treturn channel;\n\t\t}\n\n\t\tpublic override AChannel ConnectChannel(string address)\n\t\t{\n\t\t\tIPEndPoint ipEndPoint = NetworkHelper.ToIPEndPoint(address);\n\t\t\treturn this.ConnectChannel(ipEndPoint);\n\t\t}\n\t\tpublic override void Update()\n\t\t{\n\t\t\ttry\n\t\t\t{\n\t\t\t\tchannel.Update();\n\t\t\t}\n\t\t\tcatch (Exception e)\n\t\t\t{\n\t\t\t\tDebug.LogError(e);\n\t\t\t}\n\t\t}\n\t}\n}"
  },
  {
    "path": "Assets/Scripts/Network/Core/TCP/TServiceServer.cs.meta",
    "content": "﻿fileFormatVersion: 2\nguid: a284183e90dc44f4a26c8434514d7da0\ntimeCreated: 1719304034"
  },
  {
    "path": "Assets/Scripts/Network/Core/TCP.meta",
    "content": "fileFormatVersion: 2\nguid: 2b578de151e7f46489a1326f6b38658c\nfolderAsset: yes\ntimeCreated: 1440989101\nlicenseType: Pro\nDefaultImporter:\n  userData: \n  assetBundleName: \n  assetBundleVariant: \n"
  },
  {
    "path": "Assets/Scripts/Network/Core/WebSocket/WChannel.cs",
    "content": "using System;\nusing System.Collections.Generic;\nusing System.IO;\nusing System.Net.WebSockets;\nusing System.Threading;\nusing UnityEngine;\n\nnamespace UNetwork\n{\n    public class WChannel: AChannel\n    {\n        public HttpListenerWebSocketContext WebSocketContext { get; }\n\n        private readonly WebSocket webSocket;\n\n        private readonly Queue<byte[]> queue = new Queue<byte[]>();\n\n        private bool isSending;\n\n        private bool isConnected;\n\n        private readonly MemoryStream recvStream;\n\n        private CancellationTokenSource cancellationTokenSource = new CancellationTokenSource();\n\n        public WChannel(HttpListenerWebSocketContext webSocketContext, AService service): base(service, ChannelType.Accept)\n        {\n            this.WebSocketContext = webSocketContext;\n\n            this.webSocket = webSocketContext.WebSocket;\n\n            this.memoryStream = this.GetService().MemoryStreamManager.GetStream(\"message\", ushort.MaxValue);\n            this.recvStream = this.GetService().MemoryStreamManager.GetStream(\"message\", ushort.MaxValue);\n\n            isConnected = true;\n        }\n\n        public WChannel(WebSocket webSocket, AService service): base(service, ChannelType.Connect)\n        {\n            this.webSocket = webSocket;\n\n            this.memoryStream = this.GetService().MemoryStreamManager.GetStream(\"message\", ushort.MaxValue);\n            this.recvStream = this.GetService().MemoryStreamManager.GetStream(\"message\", ushort.MaxValue);\n\n            isConnected = false;\n        }\n\n        public override void Dispose()\n        {\n            this.cancellationTokenSource.Cancel();\n            this.cancellationTokenSource.Dispose();\n            this.cancellationTokenSource = null;\n\n            this.webSocket.Dispose();\n\n            this.memoryStream.Dispose();\n        }\n\n        public override void Start()\n        {\n            if (!this.isConnected)\n            {\n                return;\n            }\n\n            this.StartRecv();\n            this.StartSend();\n        }\n\n        private WService GetService()\n        {\n            return (WService) this.Service;\n        }\n\n        public async void ConnectAsync(string url)\n        {\n            try\n            {\n                await ((ClientWebSocket) this.webSocket).ConnectAsync(new Uri(url), cancellationTokenSource.Token);\n                isConnected = true;\n                this.Start();\n            }\n            catch (Exception e)\n            {\n                Debug.LogError(e);\n                this.OnError(ErrorCode.ERR_WebsocketConnectError);\n            }\n        }\n\n        public override void Send(MemoryStream stream)\n        {\n            byte[] bytes = new byte[stream.Length];\n            Array.Copy(stream.GetBuffer(), bytes, bytes.Length);\n            this.queue.Enqueue(bytes);\n\n            if (this.isConnected)\n            {\n                this.StartSend();\n            }\n        }\n\n        public async void StartSend()\n        {\n            try\n            {\n                if (this.isSending)\n                {\n                    return;\n                }\n\n                this.isSending = true;\n\n                while (true)\n                {\n                    if (this.queue.Count == 0)\n                    {\n                        this.isSending = false;\n                        return;\n                    }\n\n                    byte[] bytes = this.queue.Dequeue();\n                    try\n                    {\n                        await this.webSocket.SendAsync(new ArraySegment<byte>(bytes, 0, bytes.Length), WebSocketMessageType.Binary, true, cancellationTokenSource.Token);\n                    }\n                    catch (Exception e)\n                    {\n                        Debug.LogError(e);\n                        this.OnError(ErrorCode.ERR_WebsocketSendError);\n                        return;\n                    }\n                }\n            }\n            catch (Exception e)\n            {\n                Debug.LogError(e);\n            }\n        }\n\n        public async void StartRecv()\n        {\n            try\n            {\n                while (true)\n                {\n\n                    WebSocketReceiveResult receiveResult;\n                    int receiveCount = 0;\n                    do\n                    {\n                        receiveResult = await this.webSocket.ReceiveAsync(\n                            new ArraySegment<byte>(this.recvStream.GetBuffer(), receiveCount, this.recvStream.Capacity - receiveCount), \n                            cancellationTokenSource.Token);\n                        receiveCount += receiveResult.Count;\n                    }\n                    while (!receiveResult.EndOfMessage);\n\n                    if (receiveResult.MessageType == WebSocketMessageType.Close)\n                    {\n                        this.OnError(ErrorCode.ERR_WebsocketPeerReset);\n                        return;\n                    }\n\n                    if (receiveResult.Count > ushort.MaxValue)\n                    {\n                        await this.webSocket.CloseAsync(WebSocketCloseStatus.MessageTooBig, $\"message too big: {receiveResult.Count}\",\n                            cancellationTokenSource.Token);\n                        this.OnError(ErrorCode.ERR_WebsocketMessageTooBig);\n                        return;\n                    }\n\n                    this.recvStream.SetLength(receiveResult.Count);\n                    this.OnRead(this.recvStream);\n                }\n            }\n            catch (Exception e)\n            {\n                Debug.LogError(e);\n                this.OnError(ErrorCode.ERR_WebsocketRecvError);\n            }\n        }\n    }\n}"
  },
  {
    "path": "Assets/Scripts/Network/Core/WebSocket/WChannel.cs.meta",
    "content": "fileFormatVersion: 2\nguid: 8e9431d46e7e100479fce069d4c08079\nMonoImporter:\n  externalObjects: {}\n  serializedVersion: 2\n  defaultReferences: []\n  executionOrder: 0\n  icon: {instanceID: 0}\n  userData: \n  assetBundleName: \n  assetBundleVariant: \n"
  },
  {
    "path": "Assets/Scripts/Network/Core/WebSocket/WService.cs",
    "content": "using System;\nusing System.Collections.Generic;\nusing System.Net;\nusing System.Net.WebSockets;\nusing Microsoft.IO;\nusing UnityEngine;\n\nnamespace UNetwork\n{\n    public class WService: AService\n    {\n        private readonly HttpListener httpListener;\n\n        private WChannel channel;\n        \n        public RecyclableMemoryStreamManager MemoryStreamManager = new RecyclableMemoryStreamManager();\n\n        public WService()\n        {\n        }\n        \n        public override AChannel GetChannel()\n        {\n            return channel;\n        }\n\n        public override AChannel ConnectChannel(IPEndPoint ipEndPoint)\n        {\n            throw new NotImplementedException();\n        }\n\n        public override AChannel ConnectChannel(string address)\n        {\n\t\t\tClientWebSocket webSocket = new ClientWebSocket();\n            channel = new WChannel(webSocket, this);\n            channel.ConnectAsync(address);\n            return channel;\n        }\n\n        public override void Update()\n        {\n            \n        }\n\n        public override void Dispose()\n        {\n            \n        }\n        \n    }\n}"
  },
  {
    "path": "Assets/Scripts/Network/Core/WebSocket/WService.cs.meta",
    "content": "fileFormatVersion: 2\nguid: ab08a203cf5da164c86fbc14d09e3a3b\nMonoImporter:\n  externalObjects: {}\n  serializedVersion: 2\n  defaultReferences: []\n  executionOrder: 0\n  icon: {instanceID: 0}\n  userData: \n  assetBundleName: \n  assetBundleVariant: \n"
  },
  {
    "path": "Assets/Scripts/Network/Core/WebSocket.meta",
    "content": "fileFormatVersion: 2\nguid: 02cb26b1214c31847af71b0796e90476\nfolderAsset: yes\nDefaultImporter:\n  externalObjects: {}\n  userData: \n  assetBundleName: \n  assetBundleVariant: \n"
  },
  {
    "path": "Assets/Scripts/Network/Core.meta",
    "content": "fileFormatVersion: 2\nguid: 819d45d33f6b1124e9159ed5ef0bd490\nfolderAsset: yes\nDefaultImporter:\n  externalObjects: {}\n  userData: \n  assetBundleName: \n  assetBundleVariant: \n"
  },
  {
    "path": "Assets/Scripts/Network/INetworkManager.cs",
    "content": "﻿using System;\n\nnamespace UNetwork\n{\n    public interface INetworkManager\n    {\n        public IMessagePacker MessagePacker { get; set; }\n\n        public IMessageDispatcher MessageDispatcher { get; set; }\n\n        public Action<int> OnConnect{ get; set; }\n        public Action<int> OnError{ get; set; }\n        public Action<byte[]> OnMessage{ get; set; }\n    }\n}"
  },
  {
    "path": "Assets/Scripts/Network/INetworkManager.cs.meta",
    "content": "﻿fileFormatVersion: 2\nguid: ec147b2fba3e42f1a6c41924df308bfb\ntimeCreated: 1719310787"
  },
  {
    "path": "Assets/Scripts/Network/Message/Circularbuffer.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.IO;\nusing UnityEngine;\n\nnamespace UNetwork\n{\n    public class CircularBuffer: Stream\n    {\n        public int ChunkSize = 8192;\n\n        private readonly Queue<byte[]> bufferQueue = new Queue<byte[]>();\n\n        private readonly Queue<byte[]> bufferCache = new Queue<byte[]>();\n\n        public int LastIndex { get; set; }\n\n        public int FirstIndex { get; set; }\n\t\t\n        private byte[] lastBuffer;\n\n\t    public CircularBuffer()\n\t    {\n\t\t    this.AddLast();\n\t    }\n\n        public override long Length\n        {\n            get\n            {\n                int c = 0;\n                if (this.bufferQueue.Count == 0)\n                {\n                    c = 0;\n                }\n                else\n                {\n                    c = (this.bufferQueue.Count - 1) * ChunkSize + this.LastIndex - this.FirstIndex;\n                }\n                if (c < 0)\n                {\n                    Debug.LogErrorFormat(\"CircularBuffer count < 0: {0}, {1}, {2}\",this.bufferQueue.Count, this.LastIndex, this.FirstIndex);\n                }\n                return c;\n            }\n        }\n\n        public void AddLast()\n        {\n            byte[] buffer;\n            if (this.bufferCache.Count > 0)\n            {\n                buffer = this.bufferCache.Dequeue();\n            }\n            else\n            {\n                buffer = new byte[ChunkSize];\n            }\n            this.bufferQueue.Enqueue(buffer);\n            this.lastBuffer = buffer;\n        }\n\n        public void RemoveFirst()\n        {\n            this.bufferCache.Enqueue(bufferQueue.Dequeue());\n        }\n\n        public byte[] First\n        {\n            get\n            {\n                if (this.bufferQueue.Count == 0)\n                {\n                    this.AddLast();\n                }\n                return this.bufferQueue.Peek();\n            }\n        }\n\n        public byte[] Last\n        {\n            get\n            {\n                if (this.bufferQueue.Count == 0)\n                {\n                    this.AddLast();\n                }\n                return this.lastBuffer;\n            }\n        }\n\n\t\t/// <summary>\n\t\t/// 从CircularBuffer读到stream中\n\t\t/// </summary>\n\t\t/// <param name=\"stream\"></param>\n\t\t/// <returns></returns>\n\t\tpublic void ReadAsync(Stream stream)\n\t    {\n\t\t    long buffLength = this.Length;\n\t\t\tint sendSize = this.ChunkSize - this.FirstIndex;\n\t\t    if (sendSize > buffLength)\n\t\t    {\n\t\t\t    sendSize = (int)buffLength;\n\t\t    }\n\t\t\t\n\t\t    stream.Write(this.First, this.FirstIndex, sendSize);\n\t\t    \n\t\t    this.FirstIndex += sendSize;\n\t\t    if (this.FirstIndex == this.ChunkSize)\n\t\t    {\n\t\t\t    this.FirstIndex = 0;\n\t\t\t    this.RemoveFirst();\n\t\t    }\n\t\t}\n\n\t    // 从CircularBuffer读到stream\n\t    public void Read(Stream stream, int count)\n\t    {\n\t\t    if (count > this.Length)\n\t\t    {\n\t\t\t    throw new Exception($\"bufferList length < count, {Length} {count}\");\n\t\t    }\n\n\t\t    int alreadyCopyCount = 0;\n\t\t    while (alreadyCopyCount < count)\n\t\t    {\n\t\t\t    int n = count - alreadyCopyCount;\n\t\t\t    if (ChunkSize - this.FirstIndex > n)\n\t\t\t    {\n\t\t\t\t    stream.Write(this.First, this.FirstIndex, n);\n\t\t\t\t    this.FirstIndex += n;\n\t\t\t\t    alreadyCopyCount += n;\n\t\t\t    }\n\t\t\t    else\n\t\t\t    {\n\t\t\t\t    stream.Write(this.First, this.FirstIndex, ChunkSize - this.FirstIndex);\n\t\t\t\t    alreadyCopyCount += ChunkSize - this.FirstIndex;\n\t\t\t\t    this.FirstIndex = 0;\n\t\t\t\t    this.RemoveFirst();\n\t\t\t    }\n\t\t    }\n\t    }\n\t    \n\t    // 从stream写入CircularBuffer\n\t    public void Write(Stream stream)\n\t\t{\n\t\t\tint count = (int)(stream.Length - stream.Position);\n\t\t\t\n\t\t\tint alreadyCopyCount = 0;\n\t\t\twhile (alreadyCopyCount < count)\n\t\t\t{\n\t\t\t\tif (this.LastIndex == ChunkSize)\n\t\t\t\t{\n\t\t\t\t\tthis.AddLast();\n\t\t\t\t\tthis.LastIndex = 0;\n\t\t\t\t}\n\n\t\t\t\tint n = count - alreadyCopyCount;\n\t\t\t\tif (ChunkSize - this.LastIndex > n)\n\t\t\t\t{\n\t\t\t\t\tstream.Read(this.lastBuffer, this.LastIndex, n);\n\t\t\t\t\tthis.LastIndex += count - alreadyCopyCount;\n\t\t\t\t\talreadyCopyCount += n;\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tstream.Read(this.lastBuffer, this.LastIndex, ChunkSize - this.LastIndex);\n\t\t\t\t\talreadyCopyCount += ChunkSize - this.LastIndex;\n\t\t\t\t\tthis.LastIndex = ChunkSize;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t    \n\n\t    /// <summary>\n\t\t///  从stream写入CircularBuffer\n\t\t/// </summary>\n\t\t/// <param name=\"stream\"></param>\n\t\t/// <returns></returns>\n\t\tpublic int WriteAsync(Stream stream)\n\t    {\n\t\t    int size = this.ChunkSize - this.LastIndex;\n\n\t\t    int n = stream.Read(this.Last, this.LastIndex, size);\n//\t\t    int n = await stream.ReadAsync(this.Last, this.LastIndex, size);\n\n\t\t    if (n == 0)\n\t\t    {\n\t\t\t    return 0;\n\t\t    }\n\n\t\t    this.LastIndex += n;\n\n\t\t    if (this.LastIndex == this.ChunkSize)\n\t\t    {\n\t\t\t    this.AddLast();\n\t\t\t    this.LastIndex = 0;\n\t\t    }\n\n\t\t    return n;\n\t    }\n\n\t    // 把CircularBuffer中数据写入buffer\n        public override int Read(byte[] buffer, int offset, int count)\n        {\n\t        if (buffer.Length < offset + count)\n\t        {\n\t\t        throw new Exception($\"bufferList length < coutn, buffer length: {buffer.Length} {offset} {count}\");\n\t        }\n\n\t        long length = this.Length;\n\t\t\tif (length < count)\n            {\n\t            count = (int)length;\n            }\n\n            int alreadyCopyCount = 0;\n            while (alreadyCopyCount < count)\n            {\n                int n = count - alreadyCopyCount;\n\t\t\t\tif (ChunkSize - this.FirstIndex > n)\n                {\n                    Array.Copy(this.First, this.FirstIndex, buffer, alreadyCopyCount + offset, n);\n                    this.FirstIndex += n;\n                    alreadyCopyCount += n;\n                }\n                else\n                {\n                    Array.Copy(this.First, this.FirstIndex, buffer, alreadyCopyCount + offset, ChunkSize - this.FirstIndex);\n                    alreadyCopyCount += ChunkSize - this.FirstIndex;\n                    this.FirstIndex = 0;\n                    this.RemoveFirst();\n                }\n            }\n\n\t        return count;\n        }\n\n\t    // 把buffer写入CircularBuffer中\n        public override void Write(byte[] buffer, int offset, int count)\n        {\n\t        int alreadyCopyCount = 0;\n            while (alreadyCopyCount < count)\n            {\n                if (this.LastIndex == ChunkSize)\n                {\n                    this.AddLast();\n                    this.LastIndex = 0;\n                }\n\n                int n = count - alreadyCopyCount;\n                if (ChunkSize - this.LastIndex > n)\n                {\n                    Array.Copy(buffer, alreadyCopyCount + offset, this.lastBuffer, this.LastIndex, n);\n                    this.LastIndex += count - alreadyCopyCount;\n                    alreadyCopyCount += n;\n                }\n                else\n                {\n                    Array.Copy(buffer, alreadyCopyCount + offset, this.lastBuffer, this.LastIndex, ChunkSize - this.LastIndex);\n                    alreadyCopyCount += ChunkSize - this.LastIndex;\n                    this.LastIndex = ChunkSize;\n                }\n            }\n        }\n\n\t    public override void Flush()\n\t    {\n\t\t    throw new NotImplementedException();\n\t\t}\n\n\t    public override long Seek(long offset, SeekOrigin origin)\n\t    {\n\t\t\tthrow new NotImplementedException();\n\t    }\n\n\t    public override void SetLength(long value)\n\t    {\n\t\t    throw new NotImplementedException();\n\t\t}\n\n\t    public override bool CanRead\n\t    {\n\t\t    get\n\t\t    {\n\t\t\t    return true;\n\t\t    }\n\t    }\n\n\t    public override bool CanSeek\n\t    {\n\t\t    get\n\t\t    {\n\t\t\t    return false;\n\t\t    }\n\t    }\n\n\t    public override bool CanWrite\n\t    {\n\t\t    get\n\t\t    {\n\t\t\t    return true;\n\t\t    }\n\t    }\n\n\t    public override long Position { get; set; }\n    }\n}"
  },
  {
    "path": "Assets/Scripts/Network/Message/Circularbuffer.cs.meta",
    "content": "fileFormatVersion: 2\nguid: ea037025225a9c74b8ddb5349b0c18a6\ntimeCreated: 1503986858\nlicenseType: Free\nMonoImporter:\n  serializedVersion: 2\n  defaultReferences: []\n  executionOrder: 0\n  icon: {instanceID: 0}\n  userData: \n  assetBundleName: \n  assetBundleVariant: \n"
  },
  {
    "path": "Assets/Scripts/Network/Message/ErrorCode.cs",
    "content": "namespace UNetwork\n{\n\tpublic static class ErrorCode\n\t{\n\t\tpublic const int ERR_Success = 0;\n\t\t\n\t\t// 1-11004 是SocketError请看SocketError定义\n\t\t//-----------------------------------\n\t\t// 100000 以上，避免跟SocketError冲突\n\t\tpublic const int ERR_MyErrorCode = 100000;\n\t\tpublic const int ERR_NotFoundActor = 100002;\n\t\tpublic const int ERR_ActorNoMailBoxComponent = 100003;\n\t\tpublic const int ERR_ActorRemove = 100004;\n\t\tpublic const int ERR_PacketParserError = 100005;\n\t\tpublic const int ERR_ConnectGateKeyError = 100006;\n\t\tpublic const int ERR_KcpCantConnect = 102005;\n\t\tpublic const int ERR_KcpChannelTimeout = 102006;\n\t\tpublic const int ERR_KcpRemoteDisconnect = 102007;\n\t\tpublic const int ERR_PeerDisconnect = 102008;\n\t\tpublic const int ERR_SocketCantSend = 102009;\n\t\tpublic const int ERR_SocketError = 102010;\n\t\tpublic const int ERR_KcpWaitSendSizeTooLarge = 102011;\n\n\t\tpublic const int ERR_WebsocketPeerReset = 103001;\n\t\tpublic const int ERR_WebsocketMessageTooBig = 103002;\n\t\tpublic const int ERR_WebsocketError = 103003;\n\t\tpublic const int ERR_WebsocketConnectError = 103004;\n\t\tpublic const int ERR_WebsocketSendError = 103005;\n\t\tpublic const int ERR_WebsocketRecvError = 103006;\n\t\t\n\t\tpublic const int ERR_ReloadFail = 102003;\n\t\t\n\t\t//-----------------------------------\n\t\t// 小于这个Rpc会抛异常，大于这个异常的error需要自己判断处理，也就是说需要处理的错误应该要大于该值\n\t\tpublic const int ERR_Exception = 200000;\n\t\t\n\t\tpublic const int ERR_AccountOrPasswordError = 200102;\n\t}\n}"
  },
  {
    "path": "Assets/Scripts/Network/Message/ErrorCode.cs.meta",
    "content": "fileFormatVersion: 2\nguid: 303bb884f0f9ad24da9b0fe1acbcedaf\ntimeCreated: 1501467234\nlicenseType: Free\nMonoImporter:\n  serializedVersion: 2\n  defaultReferences: []\n  executionOrder: 0\n  icon: {instanceID: 0}\n  userData: \n  assetBundleName: \n  assetBundleVariant: \n"
  },
  {
    "path": "Assets/Scripts/Network/Message/IMessage.cs",
    "content": "﻿namespace UNetwork\n{\n\tpublic interface IMessage\n\t{\n\t}\n\t\n\tpublic interface IRequest: IMessage\n\t{\n\t\tint RpcId { get; set; }\n\t}\n\n\tpublic interface IResponse : IMessage\n\t{\n\t\tint Error { get; set; }\n\t\tstring Message { get; set; }\n\t\tint RpcId { get; set; }\n\t}\n\n\tpublic class ErrorResponse : IResponse\n\t{\n\t\tpublic int Error { get; set; }\n\t\tpublic string Message { get; set; }\n\t\tpublic int RpcId { get; set; }\n\t}\n}"
  },
  {
    "path": "Assets/Scripts/Network/Message/IMessage.cs.meta",
    "content": "fileFormatVersion: 2\nguid: 7f7b01638a2ed1242a9b81b7d6f408eb\nMonoImporter:\n  externalObjects: {}\n  serializedVersion: 2\n  defaultReferences: []\n  executionOrder: 0\n  icon: {instanceID: 0}\n  userData: \n  assetBundleName: \n  assetBundleVariant: \n"
  },
  {
    "path": "Assets/Scripts/Network/Message/IMessageDispatcher.cs",
    "content": "﻿namespace UNetwork\n{\n\tpublic interface IMessageDispatcher\n\t{\n\t\tvoid Dispatch(Session session, byte[] buffer);\n\t}\n}\n"
  },
  {
    "path": "Assets/Scripts/Network/Message/IMessageDispatcher.cs.meta",
    "content": "fileFormatVersion: 2\nguid: 69481855785b4b59834c766e4e2798e1\nMonoImporter:\n  externalObjects: {}\n  serializedVersion: 2\n  defaultReferences: []\n  executionOrder: 0\n  icon: {instanceID: 0}\n  userData: \n  assetBundleName: \n  assetBundleVariant: \n"
  },
  {
    "path": "Assets/Scripts/Network/Message/IMessagePacker.cs",
    "content": "﻿using System;\nusing System.IO;\n\nnamespace UNetwork\n{\n\tpublic interface IMessagePacker\n\t{\n\t\tbyte[] SerializeTo(object obj);\n\t\tvoid SerializeTo(object obj, MemoryStream stream);\n\t\tobject DeserializeFrom(Type type, byte[] bytes, int index, int count);\n\t\tobject DeserializeFrom(object instance, byte[] bytes, int index, int count);\n\t\tobject DeserializeFrom(Type type, MemoryStream stream);\n\t\tobject DeserializeFrom(object instance, MemoryStream stream);\n\t}\n}\n"
  },
  {
    "path": "Assets/Scripts/Network/Message/IMessagePacker.cs.meta",
    "content": "fileFormatVersion: 2\nguid: 09fe3a4882b1b50498860b3f672d1d47\nMonoImporter:\n  externalObjects: {}\n  serializedVersion: 2\n  defaultReferences: []\n  executionOrder: 0\n  icon: {instanceID: 0}\n  userData: \n  assetBundleName: \n  assetBundleVariant: \n"
  },
  {
    "path": "Assets/Scripts/Network/Message/MessageInfo.cs",
    "content": "﻿namespace UNetwork\n{\n\tpublic struct MessageInfo\n\t{\n\t\tpublic ushort Opcode { get; }\n\t\tpublic object Message { get; }\n\n\t\tpublic MessageInfo(ushort opcode, object message)\n\t\t{\n\t\t\tthis.Opcode = opcode;\n\t\t\tthis.Message = message;\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "Assets/Scripts/Network/Message/MessageInfo.cs.meta",
    "content": "fileFormatVersion: 2\nguid: 0b54864d3eb34ee4b90665941ecf5b87\ntimeCreated: 1501467234\nlicenseType: Free\nMonoImporter:\n  serializedVersion: 2\n  defaultReferences: []\n  executionOrder: 0\n  icon: {instanceID: 0}\n  userData: \n  assetBundleName: \n  assetBundleVariant: \n"
  },
  {
    "path": "Assets/Scripts/Network/Message/MessagePool.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\t\t\n\nnamespace UNetwork\n{\n\t// 客户端为了0GC需要消息池，服务端消息需要跨协程不需要消息池\n\tpublic class MessagePool\n\t{\n\t\tpublic static MessagePool Instance { get; } = new MessagePool();\n\n\t\tprivate readonly Dictionary<Type, Queue<object>> dictionary = new Dictionary<Type, Queue<object>>();\n\n\t\tpublic object Fetch(Type type)\n\t\t{\n\n\t\t\tQueue<object> queue;\n\t\t\tif (!this.dictionary.TryGetValue(type, out queue))\n\t\t\t{\n\t\t\t\tqueue = new Queue<object>();\n\t\t\t\tthis.dictionary.Add(type, queue);\n\t\t\t}\n\n\t\t\tobject obj;\n\t\t\tif (queue.Count > 0)\n\t\t\t{\n\t\t\t\tobj = queue.Dequeue();\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tobj = Activator.CreateInstance(type);\n\t\t\t}\n\n\t\t\treturn obj;\n\t\t}\n\n\t\tpublic T Fetch<T>() where T : class\n\t\t{\n\t\t\tT t = (T) this.Fetch(typeof (T));\n\t\t\treturn t;\n\t\t}\n\n\t\tpublic void Recycle(object obj)\n\t\t{\n\t\t\tType type = obj.GetType();\n\t\t\tQueue<object> queue;\n\t\t\tif (!this.dictionary.TryGetValue(type, out queue))\n\t\t\t{\n\t\t\t\tqueue = new Queue<object>();\n\t\t\t\tthis.dictionary.Add(type, queue);\n\t\t\t}\n\n\t\t\tqueue.Enqueue(obj);\n\t\t}\n\t}\n}"
  },
  {
    "path": "Assets/Scripts/Network/Message/MessagePool.cs.meta",
    "content": "fileFormatVersion: 2\nguid: 1d413cb22ac106a439af32933382ce66\nMonoImporter:\n  externalObjects: {}\n  serializedVersion: 2\n  defaultReferences: []\n  executionOrder: 0\n  icon: {instanceID: 0}\n  userData: \n  assetBundleName: \n  assetBundleVariant: \n"
  },
  {
    "path": "Assets/Scripts/Network/Message/Opcode.cs",
    "content": "namespace UNetwork\n{\n\tpublic static partial class Opcode\n\t{\n\t\tpublic const ushort ActorResponse = 1;\n\t\tpublic const ushort FrameMessage = 2;\n\t\tpublic const ushort OneFrameMessage = 3;\n\t}\n}\n"
  },
  {
    "path": "Assets/Scripts/Network/Message/Opcode.cs.meta",
    "content": "fileFormatVersion: 2\nguid: 129811e752b0782479b48ee6a12e1f2b\ntimeCreated: 1517571639\nlicenseType: Free\nMonoImporter:\n  serializedVersion: 2\n  defaultReferences: []\n  executionOrder: 0\n  icon: {instanceID: 0}\n  userData: \n  assetBundleName: \n  assetBundleVariant: \n"
  },
  {
    "path": "Assets/Scripts/Network/Message/OuterMessageDispatcher.cs",
    "content": "﻿using System;\nusing System.Text;\nusing UnityEngine;\n\nnamespace UNetwork\n{\n\tpublic class OuterMessageDispatcher: IMessageDispatcher\n\t{\n\t\tpublic void Dispatch(Session session, byte[] buffer)\n\t\t{\n\t\t\t// ushort opcode = BitConverter.ToUInt16(buffer, Packet.OpcodeIndex);\n\t\t\tstring opcode = Encoding.UTF8.GetString(buffer);\n//\t\t\tobject message = this.Network.MessagePacker.DeserializeFrom(null, memoryStream);\n\t\t\tTestClient.Receive();\n\t\t\tDebug.Log(\"receive msg：\" + opcode);\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "Assets/Scripts/Network/Message/OuterMessageDispatcher.cs.meta",
    "content": "fileFormatVersion: 2\nguid: 1019c6e4151c2374390b6f30c09e0e1f\nMonoImporter:\n  externalObjects: {}\n  serializedVersion: 2\n  defaultReferences: []\n  executionOrder: 0\n  icon: {instanceID: 0}\n  userData: \n  assetBundleName: \n  assetBundleVariant: \n"
  },
  {
    "path": "Assets/Scripts/Network/Message/ProtobufPacker.cs",
    "content": "﻿using System;\nusing System.IO;\nusing System.Runtime.Serialization.Formatters.Binary;\nusing System.Text;\n\nnamespace UNetwork\n{\n\tpublic class ProtobufPacker : IMessagePacker\n\t{\n\t\tpublic byte[] SerializeTo(object obj)\n\t\t{\n\t\t\tBinaryFormatter formatter = new BinaryFormatter();\n\t\t\tMemoryStream rems = new MemoryStream();\n\t\t\tformatter.Serialize(rems, obj);\n\t\t\treturn rems.GetBuffer();\n\t\t}\n\n\t\tpublic void SerializeTo(object obj, MemoryStream stream)\n\t\t{\n\t\t\tBinaryFormatter formatter = new BinaryFormatter();\n\t\t\tformatter.Serialize(stream, obj);\n\t\t}\n\n\t\tpublic object DeserializeFrom(Type type, byte[] bytes, int index, int count)\n\t\t{\n\t\t\treturn null;\n//\t\t\treturn ProtobufHelper.FromBytes(type, bytes, index, count);\n\t\t}\n\n\t\tpublic object DeserializeFrom(object instance, byte[] bytes, int index, int count)\n\t\t{\n\t\t\treturn null;\n//\t\t\treturn ProtobufHelper.FromBytes(instance, bytes, index, count);\n\t\t}\n\n\t\tpublic object DeserializeFrom(Type type, MemoryStream stream)\n\t\t{\n\t\t\tBinaryFormatter formatter = new BinaryFormatter();\n\t\t\treturn formatter.Deserialize(stream);\n\t\t}\n\t\t\n\t\tpublic object DeserializeFrom(object instance, MemoryStream stream)\n\t\t{\n//\t\t\treturn ProtobufHelper.FromStream(instance, stream);\n\t\t\treturn null;\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "Assets/Scripts/Network/Message/ProtobufPacker.cs.meta",
    "content": "fileFormatVersion: 2\nguid: 26802b02461a1454499ebc037e012b5f\nMonoImporter:\n  externalObjects: {}\n  serializedVersion: 2\n  defaultReferences: []\n  executionOrder: 0\n  icon: {instanceID: 0}\n  userData: \n  assetBundleName: \n  assetBundleVariant: \n"
  },
  {
    "path": "Assets/Scripts/Network/Message/RecyclableMemoryStream/Events.cs",
    "content": "﻿// ---------------------------------------------------------------------\n// Copyright (c) 2015 Microsoft\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n// ---------------------------------------------------------------------\n\nnamespace Microsoft.IO\n{\n    using System;\n    using System.Diagnostics.Tracing;\n\n    public sealed partial class RecyclableMemoryStreamManager\n    {\n        [EventSource(Name = \"Microsoft-IO-RecyclableMemoryStream\", Guid = \"{B80CD4E4-890E-468D-9CBA-90EB7C82DFC7}\")]\n        public sealed class Events : EventSource\n        {\n            public static Events Writer = new Events();\n\n            public enum MemoryStreamBufferType\n            {\n                Small,\n                Large\n            }\n\n            public enum MemoryStreamDiscardReason\n            {\n                TooLarge,\n                EnoughFree\n            }\n\n            [Event(1, Level = EventLevel.Verbose)]\n            public void MemoryStreamCreated(Guid guid, string tag, int requestedSize)\n            {\n                if (this.IsEnabled(EventLevel.Verbose, EventKeywords.None))\n                {\n                    WriteEvent(1, guid, tag ?? string.Empty, requestedSize);\n                }\n            }\n\n            [Event(2, Level = EventLevel.Verbose)]\n            public void MemoryStreamDisposed(Guid guid, string tag)\n            {\n                if (this.IsEnabled(EventLevel.Verbose, EventKeywords.None))\n                {\n                    WriteEvent(2, guid, tag ?? string.Empty);\n                }\n            }\n\n            [Event(3, Level = EventLevel.Critical)]\n            public void MemoryStreamDoubleDispose(Guid guid, string tag, string allocationStack, string disposeStack1,\n                                                  string disposeStack2)\n            {\n                if (this.IsEnabled())\n                {\n                    this.WriteEvent(3, guid, tag ?? string.Empty, allocationStack ?? string.Empty,\n                                    disposeStack1 ?? string.Empty, disposeStack2 ?? string.Empty);\n                }\n            }\n\n            [Event(4, Level = EventLevel.Error)]\n            public void MemoryStreamFinalized(Guid guid, string tag, string allocationStack)\n            {\n                if (this.IsEnabled())\n                {\n                    WriteEvent(4, guid, tag ?? string.Empty, allocationStack ?? string.Empty);\n                }\n            }\n\n            [Event(5, Level = EventLevel.Verbose)]\n            public void MemoryStreamToArray(Guid guid, string tag, string stack, int size)\n            {\n                if (this.IsEnabled(EventLevel.Verbose, EventKeywords.None))\n                {\n                    WriteEvent(5, guid, tag ?? string.Empty, stack ?? string.Empty, size);\n                }\n            }\n\n            [Event(6, Level = EventLevel.Informational)]\n            public void MemoryStreamManagerInitialized(int blockSize, int largeBufferMultiple, int maximumBufferSize)\n            {\n                if (this.IsEnabled())\n                {\n                    WriteEvent(6, blockSize, largeBufferMultiple, maximumBufferSize);\n                }\n            }\n\n            [Event(7, Level = EventLevel.Verbose)]\n            public void MemoryStreamNewBlockCreated(long smallPoolInUseBytes)\n            {\n                if (this.IsEnabled(EventLevel.Verbose, EventKeywords.None))\n                {\n                    WriteEvent(7, smallPoolInUseBytes);\n                }\n            }\n\n            [Event(8, Level = EventLevel.Verbose)]\n            public void MemoryStreamNewLargeBufferCreated(int requiredSize, long largePoolInUseBytes)\n            {\n                if (this.IsEnabled(EventLevel.Verbose, EventKeywords.None))\n                {\n                    WriteEvent(8, requiredSize, largePoolInUseBytes);\n                }\n            }\n\n            [Event(9, Level = EventLevel.Verbose)]\n            public void MemoryStreamNonPooledLargeBufferCreated(int requiredSize, string tag, string allocationStack)\n            {\n                if (this.IsEnabled(EventLevel.Verbose, EventKeywords.None))\n                {\n                    WriteEvent(9, requiredSize, tag ?? string.Empty, allocationStack ?? string.Empty);\n                }\n            }\n\n            [Event(10, Level = EventLevel.Warning)]\n            public void MemoryStreamDiscardBuffer(MemoryStreamBufferType bufferType, string tag,\n                                                  MemoryStreamDiscardReason reason)\n            {\n                if (this.IsEnabled())\n                {\n                    WriteEvent(10, bufferType, tag ?? string.Empty, reason);\n                }\n            }\n\n            [Event(11, Level = EventLevel.Error)]\n            public void MemoryStreamOverCapacity(int requestedCapacity, long maxCapacity, string tag,\n                                                 string allocationStack)\n            {\n                if (this.IsEnabled())\n                {\n                    WriteEvent(11, requestedCapacity, maxCapacity, tag ?? string.Empty, allocationStack ?? string.Empty);\n                }\n            }\n        }\n    }\n}\n\n// This is here for .NET frameworks which don't support EventSource. We basically shim bare functionality used above to  \n#if NET40\nnamespace System.Diagnostics.Tracing\n{\n    public enum EventLevel\n    {\n        LogAlways = 0,\n        Critical,\n        Error,\n        Warning,\n        Informational,\n        Verbose,\n    }\n\n    public enum EventKeywords : long\n    {\n        None = 0x0,\n    }\n\n    [AttributeUsage(AttributeTargets.Class)]\n    public sealed class EventSourceAttribute : Attribute\n    {\n        public string Name { get; set; }\n        public string Guid { get; set; }\n    }\n\n    [AttributeUsage(AttributeTargets.Method)]\n    public sealed class EventAttribute : Attribute\n    {\n        public EventAttribute(int id) { }\n\n        public EventLevel Level { get; set; }\n    }\n\n    public class EventSource\n    {\n        public void WriteEvent(params object[] unused)\n        {\n            return;\n        }\n\n        public bool IsEnabled()\n        {\n            return false;\n        }\n\n        public bool IsEnabled(EventLevel level, EventKeywords keywords)\n        {\n            return false;\n        }\n    }\n}\n#endif\n"
  },
  {
    "path": "Assets/Scripts/Network/Message/RecyclableMemoryStream/Events.cs.meta",
    "content": "fileFormatVersion: 2\nguid: 52cd395d4cf275c48b1f8690689d3869\ntimeCreated: 1527234045\nlicenseType: Pro\nMonoImporter:\n  serializedVersion: 2\n  defaultReferences: []\n  executionOrder: 0\n  icon: {instanceID: 0}\n  userData: \n  assetBundleName: \n  assetBundleVariant: \n"
  },
  {
    "path": "Assets/Scripts/Network/Message/RecyclableMemoryStream/OneThreadSynchronizationContext.cs",
    "content": "﻿using System;\nusing System.Collections.Concurrent;\nusing System.Threading;\n\nnamespace UNetwork\n{\n\tpublic class OneThreadSynchronizationContext : SynchronizationContext\n\t{\n\t\tpublic static OneThreadSynchronizationContext Instance { get; } = new OneThreadSynchronizationContext();\n\n\t\tprivate readonly int mainThreadId = Thread.CurrentThread.ManagedThreadId;\n\n\t\t// 线程同步队列,发送接收socket回调都放到该队列,由poll线程统一执行\n\t\tprivate readonly ConcurrentQueue<Action> queue = new ConcurrentQueue<Action>();\n\n\t\tprivate Action a;\n\n\t\tpublic void Update()\n\t\t{\n\t\t\twhile (true)\n\t\t\t{\n\t\t\t\tif (!this.queue.TryDequeue(out a))\n\t\t\t\t{\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\ta();\n\t\t\t}\n\t\t}\n\n\t\tpublic override void Post(SendOrPostCallback callback, object state)\n\t\t{\n\t\t\tif (Thread.CurrentThread.ManagedThreadId == this.mainThreadId)\n\t\t\t{\n\t\t\t\tcallback(state);\n\t\t\t\treturn;\n\t\t\t}\n\t\t\t\n\t\t\tthis.queue.Enqueue(() => { callback(state); });\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "Assets/Scripts/Network/Message/RecyclableMemoryStream/OneThreadSynchronizationContext.cs.meta",
    "content": "fileFormatVersion: 2\nguid: f9b9ed5f7ab66544eb242d9f98cf1a6b\nMonoImporter:\n  externalObjects: {}\n  serializedVersion: 2\n  defaultReferences: []\n  executionOrder: 0\n  icon: {instanceID: 0}\n  userData: \n  assetBundleName: \n  assetBundleVariant: \n"
  },
  {
    "path": "Assets/Scripts/Network/Message/RecyclableMemoryStream/RecyclableMemoryStream.cs",
    "content": "﻿// The MIT License (MIT)\n// \n// Copyright (c) 2015-2016 Microsoft\n// \n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n// \n// The above copyright notice and this permission notice shall be included in all\n// copies or substantial portions of the Software.\n// \n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n// SOFTWARE.\n\nnamespace Microsoft.IO\n{\n    using System;\n    using System.Collections.Generic;\n    using System.Diagnostics.CodeAnalysis;\n    using System.IO;\n    using System.Threading;\n\n    /// <summary>\n    /// MemoryStream implementation that deals with pooling and managing memory streams which use potentially large\n    /// buffers.\n    /// </summary>\n    /// <remarks>\n    /// This class works in tandem with the RecylableMemoryStreamManager to supply MemoryStream\n    /// objects to callers, while avoiding these specific problems:\n    /// 1. LOH allocations - since all large buffers are pooled, they will never incur a Gen2 GC\n    /// 2. Memory waste - A standard memory stream doubles its size when it runs out of room. This\n    /// leads to continual memory growth as each stream approaches the maximum allowed size.\n    /// 3. Memory copying - Each time a MemoryStream grows, all the bytes are copied into new buffers.\n    /// This implementation only copies the bytes when GetBuffer is called.\n    /// 4. Memory fragmentation - By using homogeneous buffer sizes, it ensures that blocks of memory\n    /// can be easily reused.\n    /// \n    /// The stream is implemented on top of a series of uniformly-sized blocks. As the stream's length grows,\n    /// additional blocks are retrieved from the memory manager. It is these blocks that are pooled, not the stream\n    /// object itself.\n    /// \n    /// The biggest wrinkle in this implementation is when GetBuffer() is called. This requires a single \n    /// contiguous buffer. If only a single block is in use, then that block is returned. If multiple blocks \n    /// are in use, we retrieve a larger buffer from the memory manager. These large buffers are also pooled, \n    /// split by size--they are multiples of a chunk size (1 MB by default).\n    /// \n    /// Once a large buffer is assigned to the stream the blocks are NEVER again used for this stream. All operations take place on the \n    /// large buffer. The large buffer can be replaced by a larger buffer from the pool as needed. All blocks and large buffers \n    /// are maintained in the stream until the stream is disposed (unless AggressiveBufferReturn is enabled in the stream manager).\n    /// \n    /// </remarks>\n    public sealed class RecyclableMemoryStream : MemoryStream\n    {\n        private const long MaxStreamLength = Int32.MaxValue;\n\n        private static readonly byte[] emptyArray = new byte[0];\n\n        /// <summary>\n        /// All of these blocks must be the same size\n        /// </summary>\n        private readonly List<byte[]> blocks = new List<byte[]>(1);\n\n        /// <summary>\n        /// This buffer exists so that WriteByte can forward all of its calls to Write\n        /// without creating a new byte[] buffer on every call.\n        /// </summary>\n        private readonly byte[] byteBuffer = new byte[1];\n\n        private readonly Guid id;\n\n        private readonly RecyclableMemoryStreamManager memoryManager;\n\n        private readonly string tag;\n\n        /// <summary>\n        /// This list is used to store buffers once they're replaced by something larger.\n        /// This is for the cases where you have users of this class that may hold onto the buffers longer\n        /// than they should and you want to prevent race conditions which could corrupt the data.\n        /// </summary>\n        private List<byte[]> dirtyBuffers;\n\n        // long to allow Interlocked.Read (for .NET Standard 1.4 compat)\n        private long disposedState;\n\n        /// <summary>\n        /// This is only set by GetBuffer() if the necessary buffer is larger than a single block size, or on\n        /// construction if the caller immediately requests a single large buffer.\n        /// </summary>\n        /// <remarks>If this field is non-null, it contains the concatenation of the bytes found in the individual\n        /// blocks. Once it is created, this (or a larger) largeBuffer will be used for the life of the stream.\n        /// </remarks>\n        private byte[] largeBuffer;\n\n        /// <summary>\n        /// Unique identifier for this stream across it's entire lifetime\n        /// </summary>\n        /// <exception cref=\"ObjectDisposedException\">Object has been disposed</exception>\n        internal Guid Id\n        {\n            get\n            {\n                this.CheckDisposed();\n                return this.id;\n            }\n        }\n\n        /// <summary>\n        /// A temporary identifier for the current usage of this stream.\n        /// </summary>\n        /// <exception cref=\"ObjectDisposedException\">Object has been disposed</exception>\n        internal string Tag\n        {\n            get\n            {\n                this.CheckDisposed();\n                return this.tag;\n            }\n        }\n\n        /// <summary>\n        /// Gets the memory manager being used by this stream.\n        /// </summary>\n        /// <exception cref=\"ObjectDisposedException\">Object has been disposed</exception>\n        internal RecyclableMemoryStreamManager MemoryManager\n        {\n            get\n            {\n                this.CheckDisposed();\n                return this.memoryManager;\n            }\n        }\n\n        /// <summary>\n        /// Callstack of the constructor. It is only set if MemoryManager.GenerateCallStacks is true,\n        /// which should only be in debugging situations.\n        /// </summary>\n        internal string AllocationStack { get; }\n\n        /// <summary>\n        /// Callstack of the Dispose call. It is only set if MemoryManager.GenerateCallStacks is true,\n        /// which should only be in debugging situations.\n        /// </summary>\n        internal string DisposeStack { get; private set; }\n\n        #region Constructors\n        /// <summary>\n        /// Allocate a new RecyclableMemoryStream object.\n        /// </summary>\n        /// <param name=\"memoryManager\">The memory manager</param>\n        public RecyclableMemoryStream(RecyclableMemoryStreamManager memoryManager)\n            : this(memoryManager, null, 0, null) { }\n\n        /// <summary>\n        /// Allocate a new RecyclableMemoryStream object\n        /// </summary>\n        /// <param name=\"memoryManager\">The memory manager</param>\n        /// <param name=\"tag\">A string identifying this stream for logging and debugging purposes</param>\n        public RecyclableMemoryStream(RecyclableMemoryStreamManager memoryManager, string tag)\n            : this(memoryManager, tag, 0, null) { }\n\n        /// <summary>\n        /// Allocate a new RecyclableMemoryStream object\n        /// </summary>\n        /// <param name=\"memoryManager\">The memory manager</param>\n        /// <param name=\"tag\">A string identifying this stream for logging and debugging purposes</param>\n        /// <param name=\"requestedSize\">The initial requested size to prevent future allocations</param>\n        public RecyclableMemoryStream(RecyclableMemoryStreamManager memoryManager, string tag, int requestedSize)\n            : this(memoryManager, tag, requestedSize, null) { }\n\n        /// <summary>\n        /// Allocate a new RecyclableMemoryStream object\n        /// </summary>\n        /// <param name=\"memoryManager\">The memory manager</param>\n        /// <param name=\"tag\">A string identifying this stream for logging and debugging purposes</param>\n        /// <param name=\"requestedSize\">The initial requested size to prevent future allocations</param>\n        /// <param name=\"initialLargeBuffer\">An initial buffer to use. This buffer will be owned by the stream and returned to the memory manager upon Dispose.</param>\n        internal RecyclableMemoryStream(RecyclableMemoryStreamManager memoryManager, string tag, int requestedSize,\n                                        byte[] initialLargeBuffer)\n            : base(emptyArray)\n        {\n            this.memoryManager = memoryManager;\n            this.id = Guid.NewGuid();\n            this.tag = tag;\n\n            if (requestedSize < memoryManager.BlockSize)\n            {\n                requestedSize = memoryManager.BlockSize;\n            }\n\n            if (initialLargeBuffer == null)\n            {\n                this.EnsureCapacity(requestedSize);\n            }\n            else\n            {\n                this.largeBuffer = initialLargeBuffer;\n            }\n\n            if (this.memoryManager.GenerateCallStacks)\n            {\n                this.AllocationStack = Environment.StackTrace;\n            }\n\n            RecyclableMemoryStreamManager.Events.Writer.MemoryStreamCreated(this.id, this.tag, requestedSize);\n            this.memoryManager.ReportStreamCreated();\n        }\n        #endregion\n\n        #region Dispose and Finalize\n        ~RecyclableMemoryStream()\n        {\n            this.Dispose(false);\n        }\n\n        /// <summary>\n        /// Returns the memory used by this stream back to the pool.\n        /// </summary>\n        /// <param name=\"disposing\">Whether we're disposing (true), or being called by the finalizer (false)</param>\n        [SuppressMessage(\"Microsoft.Usage\", \"CA1816:CallGCSuppressFinalizeCorrectly\",\n            Justification = \"We have different disposal semantics, so SuppressFinalize is in a different spot.\")]\n        protected override void Dispose(bool disposing)\n        {\n            if (Interlocked.CompareExchange(ref this.disposedState, 1, 0) != 0)\n            {\n                string doubleDisposeStack = null;\n                if (this.memoryManager.GenerateCallStacks)\n                {\n                    doubleDisposeStack = Environment.StackTrace;\n                }\n\n                RecyclableMemoryStreamManager.Events.Writer.MemoryStreamDoubleDispose(this.id, this.tag,\n                                                                                     this.AllocationStack,\n                                                                                     this.DisposeStack,\n                                                                                     doubleDisposeStack);\n                return;\n            }\n\n            RecyclableMemoryStreamManager.Events.Writer.MemoryStreamDisposed(this.id, this.tag);\n\n            if (this.memoryManager.GenerateCallStacks)\n            {\n                this.DisposeStack = Environment.StackTrace;\n            }\n\n            if (disposing)\n            {\n                this.memoryManager.ReportStreamDisposed();\n\n                GC.SuppressFinalize(this);\n            }\n            else\n            {\n                // We're being finalized.\n\n                RecyclableMemoryStreamManager.Events.Writer.MemoryStreamFinalized(this.id, this.tag, this.AllocationStack);\n\n#if !NETSTANDARD1_4\n                if (AppDomain.CurrentDomain.IsFinalizingForUnload())\n                {\n                    // If we're being finalized because of a shutdown, don't go any further.\n                    // We have no idea what's already been cleaned up. Triggering events may cause\n                    // a crash.\n                    base.Dispose(disposing);\n                    return;\n                }\n#endif\n\n                this.memoryManager.ReportStreamFinalized();\n            }\n\n            this.memoryManager.ReportStreamLength(this.length);\n\n            if (this.largeBuffer != null)\n            {\n                this.memoryManager.ReturnLargeBuffer(this.largeBuffer, this.tag);\n            }\n\n            if (this.dirtyBuffers != null)\n            {\n                foreach (var buffer in this.dirtyBuffers)\n                {\n                    this.memoryManager.ReturnLargeBuffer(buffer, this.tag);\n                }\n            }\n\n            this.memoryManager.ReturnBlocks(this.blocks, this.tag);\n            this.blocks.Clear();\n\n            base.Dispose(disposing);\n        }\n\n        /// <summary>\n        /// Equivalent to Dispose\n        /// </summary>\n#if NETSTANDARD1_4\n        public void Close()\n#else\n        public override void Close()\n#endif\n        {\n            this.Dispose(true);\n        }\n        #endregion\n\n        #region MemoryStream overrides\n        /// <summary>\n        /// Gets or sets the capacity\n        /// </summary>\n        /// <remarks>Capacity is always in multiples of the memory manager's block size, unless\n        /// the large buffer is in use.  Capacity never decreases during a stream's lifetime. \n        /// Explicitly setting the capacity to a lower value than the current value will have no effect. \n        /// This is because the buffers are all pooled by chunks and there's little reason to \n        /// allow stream truncation.\n        /// </remarks>\n        /// <exception cref=\"ObjectDisposedException\">Object has been disposed</exception>\n        public override int Capacity\n        {\n            get\n            {\n                this.CheckDisposed();\n                if (this.largeBuffer != null)\n                {\n                    return this.largeBuffer.Length;\n                }\n\n                long size = (long)this.blocks.Count * this.memoryManager.BlockSize;\n                return (int)Math.Min(int.MaxValue, size);\n            }\n            set\n            {\n                this.CheckDisposed();\n                this.EnsureCapacity(value);\n            }\n        }\n\n        private int length;\n\n        /// <summary>\n        /// Gets the number of bytes written to this stream.\n        /// </summary>\n        /// <exception cref=\"ObjectDisposedException\">Object has been disposed</exception>\n        public override long Length\n        {\n            get\n            {\n                this.CheckDisposed();\n                return this.length;\n            }\n        }\n\n        private int position;\n\n        /// <summary>\n        /// Gets the current position in the stream\n        /// </summary>\n        /// <exception cref=\"ObjectDisposedException\">Object has been disposed</exception>\n        public override long Position\n        {\n            get\n            {\n                this.CheckDisposed();\n                return this.position;\n            }\n            set\n            {\n                this.CheckDisposed();\n                if (value < 0)\n                {\n                    throw new ArgumentOutOfRangeException(\"value\", \"value must be non-negative\");\n                }\n\n                if (value > MaxStreamLength)\n                {\n                    throw new ArgumentOutOfRangeException(\"value\", \"value cannot be more than \" + MaxStreamLength);\n                }\n\n                this.position = (int)value;\n            }\n        }\n\n        /// <summary>\n        /// Whether the stream can currently read\n        /// </summary>\n        public override bool CanRead => !this.Disposed;\n\n        /// <summary>\n        /// Whether the stream can currently seek\n        /// </summary>\n        public override bool CanSeek => !this.Disposed;\n\n        /// <summary>\n        /// Always false\n        /// </summary>\n        public override bool CanTimeout => false;\n\n        /// <summary>\n        /// Whether the stream can currently write\n        /// </summary>\n        public override bool CanWrite => !this.Disposed;\n\n        /// <summary>\n        /// Returns a single buffer containing the contents of the stream.\n        /// The buffer may be longer than the stream length.\n        /// </summary>\n        /// <returns>A byte[] buffer</returns>\n        /// <remarks>IMPORTANT: Doing a Write() after calling GetBuffer() invalidates the buffer. The old buffer is held onto\n        /// until Dispose is called, but the next time GetBuffer() is called, a new buffer from the pool will be required.</remarks>\n        /// <exception cref=\"ObjectDisposedException\">Object has been disposed</exception>\n#if NETSTANDARD1_4\n        public byte[] GetBuffer()\n#else\n        public override byte[] GetBuffer()\n#endif\n        {\n            this.CheckDisposed();\n\n            if (this.largeBuffer != null)\n            {\n                return this.largeBuffer;\n            }\n\n            if (this.blocks.Count == 1)\n            {\n                return this.blocks[0];\n            }\n\n            // Buffer needs to reflect the capacity, not the length, because\n            // it's possible that people will manipulate the buffer directly\n            // and set the length afterward. Capacity sets the expectation\n            // for the size of the buffer.\n            var newBuffer = this.memoryManager.GetLargeBuffer(this.Capacity, this.tag);\n\n            // InternalRead will check for existence of largeBuffer, so make sure we\n            // don't set it until after we've copied the data.\n            this.InternalRead(newBuffer, 0, this.length, 0);\n            this.largeBuffer = newBuffer;\n\n            if (this.blocks.Count > 0 && this.memoryManager.AggressiveBufferReturn)\n            {\n                this.memoryManager.ReturnBlocks(this.blocks, this.tag);\n                this.blocks.Clear();\n            }\n\n            return this.largeBuffer;\n        }\n\n        /// <summary>\n        /// Returns a new array with a copy of the buffer's contents. You should almost certainly be using GetBuffer combined with the Length to \n        /// access the bytes in this stream. Calling ToArray will destroy the benefits of pooled buffers, but it is included\n        /// for the sake of completeness.\n        /// </summary>\n        /// <exception cref=\"ObjectDisposedException\">Object has been disposed</exception>\n#pragma warning disable CS0809\n        [Obsolete(\"This method has degraded performance vs. GetBuffer and should be avoided.\")]\n        public override byte[] ToArray()\n        {\n            this.CheckDisposed();\n            var newBuffer = new byte[this.Length];\n\n            this.InternalRead(newBuffer, 0, this.length, 0);\n            string stack = this.memoryManager.GenerateCallStacks ? Environment.StackTrace : null;\n            RecyclableMemoryStreamManager.Events.Writer.MemoryStreamToArray(this.id, this.tag, stack, 0);\n            this.memoryManager.ReportStreamToArray();\n\n            return newBuffer;\n        }\n#pragma warning restore CS0809\n\n        /// <summary>\n        /// Reads from the current position into the provided buffer\n        /// </summary>\n        /// <param name=\"buffer\">Destination buffer</param>\n        /// <param name=\"offset\">Offset into buffer at which to start placing the read bytes.</param>\n        /// <param name=\"count\">Number of bytes to read.</param>\n        /// <returns>The number of bytes read</returns>\n        /// <exception cref=\"ArgumentNullException\">buffer is null</exception>\n        /// <exception cref=\"ArgumentOutOfRangeException\">offset or count is less than 0</exception>\n        /// <exception cref=\"ArgumentException\">offset subtracted from the buffer length is less than count</exception>\n        /// <exception cref=\"ObjectDisposedException\">Object has been disposed</exception>\n        public override int Read(byte[] buffer, int offset, int count)\n        {\n            return this.SafeRead(buffer, offset, count, ref this.position);\n        }\n\n        /// <summary>\n        /// Reads from the specified position into the provided buffer\n        /// </summary>\n        /// <param name=\"buffer\">Destination buffer</param>\n        /// <param name=\"offset\">Offset into buffer at which to start placing the read bytes.</param>\n        /// <param name=\"count\">Number of bytes to read.</param>\n        /// <param name=\"streamPosition\">Position in the stream to start reading from</param>\n        /// <returns>The number of bytes read</returns>\n        /// <exception cref=\"ArgumentNullException\">buffer is null</exception>\n        /// <exception cref=\"ArgumentOutOfRangeException\">offset or count is less than 0</exception>\n        /// <exception cref=\"ArgumentException\">offset subtracted from the buffer length is less than count</exception>\n        /// <exception cref=\"ObjectDisposedException\">Object has been disposed</exception>\n        public int SafeRead(byte[] buffer, int offset, int count, ref int streamPosition)\n        {\n            this.CheckDisposed();\n            if (buffer == null)\n            {\n                throw new ArgumentNullException(nameof(buffer));\n            }\n\n            if (offset < 0)\n            {\n                throw new ArgumentOutOfRangeException(nameof(offset), \"offset cannot be negative\");\n            }\n\n            if (count < 0)\n            {\n                throw new ArgumentOutOfRangeException(nameof(count), \"count cannot be negative\");\n            }\n\n            if (offset + count > buffer.Length)\n            {\n                throw new ArgumentException(\"buffer length must be at least offset + count\");\n            }\n\n            int amountRead = this.InternalRead(buffer, offset, count, streamPosition);\n            streamPosition += amountRead;\n            return amountRead;\n        }\n\n        /// <summary>\n        /// Writes the buffer to the stream\n        /// </summary>\n        /// <param name=\"buffer\">Source buffer</param>\n        /// <param name=\"offset\">Start position</param>\n        /// <param name=\"count\">Number of bytes to write</param>\n        /// <exception cref=\"ArgumentNullException\">buffer is null</exception>\n        /// <exception cref=\"ArgumentOutOfRangeException\">offset or count is negative</exception>\n        /// <exception cref=\"ArgumentException\">buffer.Length - offset is not less than count</exception>\n        /// <exception cref=\"ObjectDisposedException\">Object has been disposed</exception>\n        public override void Write(byte[] buffer, int offset, int count)\n        {\n            this.CheckDisposed();\n            if (buffer == null)\n            {\n                throw new ArgumentNullException(nameof(buffer));\n            }\n\n            if (offset < 0)\n            {\n                throw new ArgumentOutOfRangeException(nameof(offset), offset,\n                                                      \"Offset must be in the range of 0 - buffer.Length-1\");\n            }\n\n            if (count < 0)\n            {\n                throw new ArgumentOutOfRangeException(nameof(count), count, \"count must be non-negative\");\n            }\n\n            if (count + offset > buffer.Length)\n            {\n                throw new ArgumentException(\"count must be greater than buffer.Length - offset\");\n            }\n\n            int blockSize = this.memoryManager.BlockSize;\n            long end = (long)this.position + count;\n            // Check for overflow\n            if (end > MaxStreamLength)\n            {\n                throw new IOException(\"Maximum capacity exceeded\");\n            }\n\n            long requiredBuffers = (end + blockSize - 1) / blockSize;\n\n            if (requiredBuffers * blockSize > MaxStreamLength)\n            {\n                throw new IOException(\"Maximum capacity exceeded\");\n            }\n\n            this.EnsureCapacity((int)end);\n\n            if (this.largeBuffer == null)\n            {\n                int bytesRemaining = count;\n                int bytesWritten = 0;\n                var blockAndOffset = this.GetBlockAndRelativeOffset(this.position);\n\n                while (bytesRemaining > 0)\n                {\n                    byte[] currentBlock = this.blocks[blockAndOffset.Block];\n                    int remainingInBlock = blockSize - blockAndOffset.Offset;\n                    int amountToWriteInBlock = Math.Min(remainingInBlock, bytesRemaining);\n\n                    Buffer.BlockCopy(buffer, offset + bytesWritten, currentBlock, blockAndOffset.Offset,\n                                     amountToWriteInBlock);\n\n                    bytesRemaining -= amountToWriteInBlock;\n                    bytesWritten += amountToWriteInBlock;\n\n                    ++blockAndOffset.Block;\n                    blockAndOffset.Offset = 0;\n                }\n            }\n            else\n            {\n                Buffer.BlockCopy(buffer, offset, this.largeBuffer, this.position, count);\n            }\n            this.position = (int)end;\n            this.length = Math.Max(this.position, this.length);\n        }\n\n        /// <summary>\n        /// Returns a useful string for debugging. This should not normally be called in actual production code.\n        /// </summary>\n        public override string ToString()\n        {\n            return $\"Id = {this.Id}, Tag = {this.Tag}, Length = {this.Length:N0} bytes\";\n        }\n\n        /// <summary>\n        /// Writes a single byte to the current position in the stream.\n        /// </summary>\n        /// <param name=\"value\">byte value to write</param>\n        /// <exception cref=\"ObjectDisposedException\">Object has been disposed</exception>\n        public override void WriteByte(byte value)\n        {\n            this.CheckDisposed();\n            this.byteBuffer[0] = value;\n            this.Write(this.byteBuffer, 0, 1);\n        }\n\n        /// <summary>\n        /// Reads a single byte from the current position in the stream.\n        /// </summary>\n        /// <returns>The byte at the current position, or -1 if the position is at the end of the stream.</returns>\n        /// <exception cref=\"ObjectDisposedException\">Object has been disposed</exception>\n        public override int ReadByte()\n        {\n            return this.SafeReadByte(ref this.position);\n        }\n\n        /// <summary>\n        /// Reads a single byte from the specified position in the stream.\n        /// </summary>\n        /// <param name=\"streamPosition\">The position in the stream to read from</param>\n        /// <returns>The byte at the current position, or -1 if the position is at the end of the stream.</returns>\n        /// <exception cref=\"ObjectDisposedException\">Object has been disposed</exception>\n        public int SafeReadByte(ref int streamPosition)\n        {\n            this.CheckDisposed();\n            if (streamPosition == this.length)\n            {\n                return -1;\n            }\n            byte value;\n            if (this.largeBuffer == null)\n            {\n                var blockAndOffset = this.GetBlockAndRelativeOffset(streamPosition);\n                value = this.blocks[blockAndOffset.Block][blockAndOffset.Offset];\n            }\n            else\n            {\n                value = this.largeBuffer[streamPosition];\n            }\n            streamPosition++;\n            return value;\n        }\n\n        /// <summary>\n        /// Sets the length of the stream\n        /// </summary>\n        /// <exception cref=\"ArgumentOutOfRangeException\">value is negative or larger than MaxStreamLength</exception>\n        /// <exception cref=\"ObjectDisposedException\">Object has been disposed</exception>\n        public override void SetLength(long value)\n        {\n            this.CheckDisposed();\n            if (value < 0 || value > MaxStreamLength)\n            {\n                throw new ArgumentOutOfRangeException(nameof(value),\n                                                      \"value must be non-negative and at most \" + MaxStreamLength);\n            }\n\n            this.EnsureCapacity((int)value);\n\n            this.length = (int)value;\n            if (this.position > value)\n            {\n                this.position = (int)value;\n            }\n        }\n\n        /// <summary>\n        /// Sets the position to the offset from the seek location\n        /// </summary>\n        /// <param name=\"offset\">How many bytes to move</param>\n        /// <param name=\"loc\">From where</param>\n        /// <returns>The new position</returns>\n        /// <exception cref=\"ObjectDisposedException\">Object has been disposed</exception>\n        /// <exception cref=\"ArgumentOutOfRangeException\">offset is larger than MaxStreamLength</exception>\n        /// <exception cref=\"ArgumentException\">Invalid seek origin</exception>\n        /// <exception cref=\"IOException\">Attempt to set negative position</exception>\n        public override long Seek(long offset, SeekOrigin loc)\n        {\n            this.CheckDisposed();\n            if (offset > MaxStreamLength)\n            {\n                throw new ArgumentOutOfRangeException(nameof(offset), \"offset cannot be larger than \" + MaxStreamLength);\n            }\n\n            int newPosition;\n            switch (loc)\n            {\n            case SeekOrigin.Begin:\n                newPosition = (int)offset;\n                break;\n            case SeekOrigin.Current:\n                newPosition = (int)offset + this.position;\n                break;\n            case SeekOrigin.End:\n                newPosition = (int)offset + this.length;\n                break;\n            default:\n                throw new ArgumentException(\"Invalid seek origin\", nameof(loc));\n            }\n            if (newPosition < 0)\n            {\n                throw new IOException(\"Seek before beginning\");\n            }\n            this.position = newPosition;\n            return this.position;\n        }\n\n        /// <summary>\n        /// Synchronously writes this stream's bytes to the parameter stream.\n        /// </summary>\n        /// <param name=\"stream\">Destination stream</param>\n        /// <remarks>Important: This does a synchronous write, which may not be desired in some situations</remarks>\n        public override void WriteTo(Stream stream)\n        {\n            this.CheckDisposed();\n            if (stream == null)\n            {\n                throw new ArgumentNullException(nameof(stream));\n            }\n\n            if (this.largeBuffer == null)\n            {\n                int currentBlock = 0;\n                int bytesRemaining = this.length;\n\n                while (bytesRemaining > 0)\n                {\n                    int amountToCopy = Math.Min(this.blocks[currentBlock].Length, bytesRemaining);\n                    stream.Write(this.blocks[currentBlock], 0, amountToCopy);\n\n                    bytesRemaining -= amountToCopy;\n\n                    ++currentBlock;\n                }\n            }\n            else\n            {\n                stream.Write(this.largeBuffer, 0, this.length);\n            }\n        }\n        #endregion\n\n        #region Helper Methods\n        private bool Disposed => Interlocked.Read(ref this.disposedState) != 0;\n\n        private void CheckDisposed()\n        {\n            if (this.Disposed)\n            {\n                throw new ObjectDisposedException($\"The stream with Id {this.id} and Tag {this.tag} is disposed.\");\n            }\n        }\n\n        private int InternalRead(byte[] buffer, int offset, int count, int fromPosition)\n        {\n            if (this.length - fromPosition <= 0)\n            {\n                return 0;\n            }\n\n            int amountToCopy;\n\n            if (this.largeBuffer == null)\n            {\n                var blockAndOffset = this.GetBlockAndRelativeOffset(fromPosition);\n                int bytesWritten = 0;\n                int bytesRemaining = Math.Min(count, this.length - fromPosition);\n\n                while (bytesRemaining > 0)\n                {\n                    amountToCopy = Math.Min(this.blocks[blockAndOffset.Block].Length - blockAndOffset.Offset,\n                                                bytesRemaining);\n                    Buffer.BlockCopy(this.blocks[blockAndOffset.Block], blockAndOffset.Offset, buffer,\n                                     bytesWritten + offset, amountToCopy);\n\n                    bytesWritten += amountToCopy;\n                    bytesRemaining -= amountToCopy;\n\n                    ++blockAndOffset.Block;\n                    blockAndOffset.Offset = 0;\n                }\n                return bytesWritten;\n            }\n            amountToCopy = Math.Min(count, this.length - fromPosition);\n            Buffer.BlockCopy(this.largeBuffer, fromPosition, buffer, offset, amountToCopy);\n            return amountToCopy;\n        }\n\n        private struct BlockAndOffset\n        {\n            public int Block;\n            public int Offset;\n\n            public BlockAndOffset(int block, int offset)\n            {\n                this.Block = block;\n                this.Offset = offset;\n            }\n        }\n\n        private BlockAndOffset GetBlockAndRelativeOffset(int offset)\n        {\n            var blockSize = this.memoryManager.BlockSize;\n            return new BlockAndOffset(offset / blockSize, offset % blockSize);\n        }\n\n        private void EnsureCapacity(int newCapacity)\n        {\n            if (newCapacity > this.memoryManager.MaximumStreamCapacity && this.memoryManager.MaximumStreamCapacity > 0)\n            {\n                RecyclableMemoryStreamManager.Events.Writer.MemoryStreamOverCapacity(newCapacity,\n                                                                                    this.memoryManager\n                                                                                        .MaximumStreamCapacity, this.tag,\n                                                                                    this.AllocationStack);\n                throw new InvalidOperationException(\"Requested capacity is too large: \" + newCapacity + \". Limit is \" +\n                                                    this.memoryManager.MaximumStreamCapacity);\n            }\n\n            if (this.largeBuffer != null)\n            {\n                if (newCapacity > this.largeBuffer.Length)\n                {\n                    var newBuffer = this.memoryManager.GetLargeBuffer(newCapacity, this.tag);\n                    this.InternalRead(newBuffer, 0, this.length, 0);\n                    this.ReleaseLargeBuffer();\n                    this.largeBuffer = newBuffer;\n                }\n            }\n            else\n            {\n                while (this.Capacity < newCapacity)\n                {\n                    blocks.Add((this.memoryManager.GetBlock()));\n                }\n            }\n        }\n\n        /// <summary>\n        /// Release the large buffer (either stores it for eventual release or returns it immediately).\n        /// </summary>\n        private void ReleaseLargeBuffer()\n        {\n            if (this.memoryManager.AggressiveBufferReturn)\n            {\n                this.memoryManager.ReturnLargeBuffer(this.largeBuffer, this.tag);\n            }\n            else\n            {\n                if (this.dirtyBuffers == null)\n                {\n                    // We most likely will only ever need space for one\n                    this.dirtyBuffers = new List<byte[]>(1);\n                }\n                this.dirtyBuffers.Add(this.largeBuffer);\n            }\n\n            this.largeBuffer = null;\n        }\n        #endregion\n    }\n}"
  },
  {
    "path": "Assets/Scripts/Network/Message/RecyclableMemoryStream/RecyclableMemoryStream.cs.meta",
    "content": "fileFormatVersion: 2\nguid: 04ecc2633820de7459b8ba26a114f62f\ntimeCreated: 1527234045\nlicenseType: Pro\nMonoImporter:\n  serializedVersion: 2\n  defaultReferences: []\n  executionOrder: 0\n  icon: {instanceID: 0}\n  userData: \n  assetBundleName: \n  assetBundleVariant: \n"
  },
  {
    "path": "Assets/Scripts/Network/Message/RecyclableMemoryStream/RecyclableMemoryStreamManager.cs",
    "content": "﻿// ---------------------------------------------------------------------\n// Copyright (c) 2015-2016 Microsoft\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n// ---------------------------------------------------------------------\n\nnamespace Microsoft.IO\n{\n    using System;\n    using System.Collections.Concurrent;\n    using System.Collections.Generic;\n    using System.Diagnostics.CodeAnalysis;\n    using System.IO;\n    using System.Linq;\n    using System.Threading;\n\n    /// <summary>\n    /// Manages pools of RecyclableMemoryStream objects.\n    /// </summary>\n    /// <remarks>\n    /// There are two pools managed in here. The small pool contains same-sized buffers that are handed to streams\n    /// as they write more data.\n    /// \n    /// For scenarios that need to call GetBuffer(), the large pool contains buffers of various sizes, all\n    /// multiples of LargeBufferMultiple (1 MB by default). They are split by size to avoid overly-wasteful buffer\n    /// usage. There should be far fewer 8 MB buffers than 1 MB buffers, for example.\n    /// </remarks>\n    public partial class RecyclableMemoryStreamManager\n    {\n        /// <summary>\n        /// Generic delegate for handling events without any arguments.\n        /// </summary>\n        public delegate void EventHandler();\n\n        /// <summary>\n        /// Delegate for handling large buffer discard reports.\n        /// </summary>\n        /// <param name=\"reason\">Reason the buffer was discarded.</param>\n        public delegate void LargeBufferDiscardedEventHandler(Events.MemoryStreamDiscardReason reason);\n\n        /// <summary>\n        /// Delegate for handling reports of stream size when streams are allocated\n        /// </summary>\n        /// <param name=\"bytes\">Bytes allocated.</param>\n        public delegate void StreamLengthReportHandler(long bytes);\n\n        /// <summary>\n        /// Delegate for handling periodic reporting of memory use statistics.\n        /// </summary>\n        /// <param name=\"smallPoolInUseBytes\">Bytes currently in use in the small pool.</param>\n        /// <param name=\"smallPoolFreeBytes\">Bytes currently free in the small pool.</param>\n        /// <param name=\"largePoolInUseBytes\">Bytes currently in use in the large pool.</param>\n        /// <param name=\"largePoolFreeBytes\">Bytes currently free in the large pool.</param>\n        public delegate void UsageReportEventHandler(\n            long smallPoolInUseBytes, long smallPoolFreeBytes, long largePoolInUseBytes, long largePoolFreeBytes);\n\n        public const int DefaultBlockSize = 128 * 1024;\n        public const int DefaultLargeBufferMultiple = 1024 * 1024;\n        public const int DefaultMaximumBufferSize = 128 * 1024 * 1024;\n\n        private readonly int blockSize;\n        private readonly long[] largeBufferFreeSize;\n        private readonly long[] largeBufferInUseSize;\n\n        private readonly int largeBufferMultiple;\n\n        /// <summary>\n        /// pools[0] = 1x largeBufferMultiple buffers\n        /// pools[1] = 2x largeBufferMultiple buffers\n        /// etc., up to maximumBufferSize\n        /// </summary>\n        private readonly ConcurrentStack<byte[]>[] largePools;\n\n        private readonly int maximumBufferSize;\n        private readonly ConcurrentStack<byte[]> smallPool;\n\n        private long smallPoolFreeSize;\n        private long smallPoolInUseSize;\n\n        /// <summary>\n        /// Initializes the memory manager with the default block/buffer specifications.\n        /// </summary>\n        public RecyclableMemoryStreamManager()\n            : this(DefaultBlockSize, DefaultLargeBufferMultiple, DefaultMaximumBufferSize) { }\n\n        /// <summary>\n        /// Initializes the memory manager with the given block requiredSize.\n        /// </summary>\n        /// <param name=\"blockSize\">Size of each block that is pooled. Must be > 0.</param>\n        /// <param name=\"largeBufferMultiple\">Each large buffer will be a multiple of this value.</param>\n        /// <param name=\"maximumBufferSize\">Buffers larger than this are not pooled</param>\n        /// <exception cref=\"ArgumentOutOfRangeException\">blockSize is not a positive number, or largeBufferMultiple is not a positive number, or maximumBufferSize is less than blockSize.</exception>\n        /// <exception cref=\"ArgumentException\">maximumBufferSize is not a multiple of largeBufferMultiple</exception>\n        public RecyclableMemoryStreamManager(int blockSize, int largeBufferMultiple, int maximumBufferSize)\n        {\n            if (blockSize <= 0)\n            {\n                throw new ArgumentOutOfRangeException(nameof(blockSize), blockSize, \"blockSize must be a positive number\");\n            }\n\n            if (largeBufferMultiple <= 0)\n            {\n                throw new ArgumentOutOfRangeException(nameof(largeBufferMultiple),\n                                                      \"largeBufferMultiple must be a positive number\");\n            }\n\n            if (maximumBufferSize < blockSize)\n            {\n                throw new ArgumentOutOfRangeException(nameof(maximumBufferSize),\n                                                      \"maximumBufferSize must be at least blockSize\");\n            }\n\n            this.blockSize = blockSize;\n            this.largeBufferMultiple = largeBufferMultiple;\n            this.maximumBufferSize = maximumBufferSize;\n\n            if (!this.IsLargeBufferMultiple(maximumBufferSize))\n            {\n                throw new ArgumentException(\"maximumBufferSize is not a multiple of largeBufferMultiple\",\n                                            nameof(maximumBufferSize));\n            }\n\n            this.smallPool = new ConcurrentStack<byte[]>();\n            var numLargePools = maximumBufferSize / largeBufferMultiple;\n\n            // +1 to store size of bytes in use that are too large to be pooled\n            this.largeBufferInUseSize = new long[numLargePools + 1];\n            this.largeBufferFreeSize = new long[numLargePools];\n\n            this.largePools = new ConcurrentStack<byte[]>[numLargePools];\n\n            for (var i = 0; i < this.largePools.Length; ++i)\n            {\n                this.largePools[i] = new ConcurrentStack<byte[]>();\n            }\n\n            Events.Writer.MemoryStreamManagerInitialized(blockSize, largeBufferMultiple, maximumBufferSize);\n        }\n\n        /// <summary>\n        /// The size of each block. It must be set at creation and cannot be changed.\n        /// </summary>\n        public int BlockSize => this.blockSize;\n\n        /// <summary>\n        /// All buffers are multiples of this number. It must be set at creation and cannot be changed.\n        /// </summary>\n        public int LargeBufferMultiple => this.largeBufferMultiple;\n\n        /// <summary>\n        /// Gets or sets the maximum buffer size.\n        /// </summary>\n        /// <remarks>Any buffer that is returned to the pool that is larger than this will be\n        /// discarded and garbage collected.</remarks>\n        public int MaximumBufferSize => this.maximumBufferSize;\n\n        /// <summary>\n        /// Number of bytes in small pool not currently in use\n        /// </summary>\n        public long SmallPoolFreeSize => this.smallPoolFreeSize;\n\n        /// <summary>\n        /// Number of bytes currently in use by stream from the small pool\n        /// </summary>\n        public long SmallPoolInUseSize => this.smallPoolInUseSize;\n\n        /// <summary>\n        /// Number of bytes in large pool not currently in use\n        /// </summary>\n        public long LargePoolFreeSize => this.largeBufferFreeSize.Sum();\n\n        /// <summary>\n        /// Number of bytes currently in use by streams from the large pool\n        /// </summary>\n        public long LargePoolInUseSize => this.largeBufferInUseSize.Sum();\n\n        /// <summary>\n        /// How many blocks are in the small pool\n        /// </summary>\n        public long SmallBlocksFree => this.smallPool.Count;\n\n        /// <summary>\n        /// How many buffers are in the large pool\n        /// </summary>\n        public long LargeBuffersFree\n        {\n            get\n            {\n                long free = 0;\n                foreach (var pool in this.largePools)\n                {\n                    free += pool.Count;\n                }\n                return free;\n            }\n        }\n\n        /// <summary>\n        /// How many bytes of small free blocks to allow before we start dropping\n        /// those returned to us.\n        /// </summary>\n        public long MaximumFreeSmallPoolBytes { get; set; }\n\n        /// <summary>\n        /// How many bytes of large free buffers to allow before we start dropping\n        /// those returned to us.\n        /// </summary>\n        public long MaximumFreeLargePoolBytes { get; set; }\n\n        /// <summary>\n        /// Maximum stream capacity in bytes. Attempts to set a larger capacity will\n        /// result in an exception.\n        /// </summary>\n        /// <remarks>A value of 0 indicates no limit.</remarks>\n        public long MaximumStreamCapacity { get; set; }\n\n        /// <summary>\n        /// Whether to save callstacks for stream allocations. This can help in debugging.\n        /// It should NEVER be turned on generally in production.\n        /// </summary>\n        public bool GenerateCallStacks { get; set; }\n\n        /// <summary>\n        /// Whether dirty buffers can be immediately returned to the buffer pool. E.g. when GetBuffer() is called on\n        /// a stream and creates a single large buffer, if this setting is enabled, the other blocks will be returned\n        /// to the buffer pool immediately.\n        /// Note when enabling this setting that the user is responsible for ensuring that any buffer previously\n        /// retrieved from a stream which is subsequently modified is not used after modification (as it may no longer\n        /// be valid).\n        /// </summary>\n        public bool AggressiveBufferReturn { get; set; }\n\n        /// <summary>\n        /// Removes and returns a single block from the pool.\n        /// </summary>\n        /// <returns>A byte[] array</returns>\n        internal byte[] GetBlock()\n        {\n            byte[] block;\n            if (!this.smallPool.TryPop(out block))\n            {\n                // We'll add this back to the pool when the stream is disposed\n                // (unless our free pool is too large)\n                block = new byte[this.BlockSize];\n                Events.Writer.MemoryStreamNewBlockCreated(this.smallPoolInUseSize);\n                ReportBlockCreated();\n            }\n            else\n            {\n                Interlocked.Add(ref this.smallPoolFreeSize, -this.BlockSize);\n            }\n\n            Interlocked.Add(ref this.smallPoolInUseSize, this.BlockSize);\n            return block;\n        }\n\n        /// <summary>\n        /// Returns a buffer of arbitrary size from the large buffer pool. This buffer\n        /// will be at least the requiredSize and always be a multiple of largeBufferMultiple.\n        /// </summary>\n        /// <param name=\"requiredSize\">The minimum length of the buffer</param>\n        /// <param name=\"tag\">The tag of the stream returning this buffer, for logging if necessary.</param>\n        /// <returns>A buffer of at least the required size.</returns>\n        internal byte[] GetLargeBuffer(int requiredSize, string tag)\n        {\n            requiredSize = this.RoundToLargeBufferMultiple(requiredSize);\n\n            var poolIndex = requiredSize / this.largeBufferMultiple - 1;\n\n            byte[] buffer;\n            if (poolIndex < this.largePools.Length)\n            {\n                if (!this.largePools[poolIndex].TryPop(out buffer))\n                {\n                    buffer = new byte[requiredSize];\n\n                    Events.Writer.MemoryStreamNewLargeBufferCreated(requiredSize, this.LargePoolInUseSize);\n                    ReportLargeBufferCreated();\n                }\n                else\n                {\n                    Interlocked.Add(ref this.largeBufferFreeSize[poolIndex], -buffer.Length);\n                }\n            }\n            else\n            {\n                // Buffer is too large to pool. They get a new buffer.\n\n                // We still want to track the size, though, and we've reserved a slot\n                // in the end of the inuse array for nonpooled bytes in use.\n                poolIndex = this.largeBufferInUseSize.Length - 1;\n\n                // We still want to round up to reduce heap fragmentation.\n                buffer = new byte[requiredSize];\n                string callStack = null;\n                if (this.GenerateCallStacks)\n                {\n                    // Grab the stack -- we want to know who requires such large buffers\n                    callStack = Environment.StackTrace;\n                }\n                Events.Writer.MemoryStreamNonPooledLargeBufferCreated(requiredSize, tag, callStack);\n                ReportLargeBufferCreated();\n            }\n\n            Interlocked.Add(ref this.largeBufferInUseSize[poolIndex], buffer.Length);\n\n            return buffer;\n        }\n\n        private int RoundToLargeBufferMultiple(int requiredSize)\n        {\n            return ((requiredSize + this.LargeBufferMultiple - 1) / this.LargeBufferMultiple) * this.LargeBufferMultiple;\n        }\n\n        private bool IsLargeBufferMultiple(int value)\n        {\n            return (value != 0) && (value % this.LargeBufferMultiple) == 0;\n        }\n\n        /// <summary>\n        /// Returns the buffer to the large pool\n        /// </summary>\n        /// <param name=\"buffer\">The buffer to return.</param>\n        /// <param name=\"tag\">The tag of the stream returning this buffer, for logging if necessary.</param>\n        /// <exception cref=\"ArgumentNullException\">buffer is null</exception>\n        /// <exception cref=\"ArgumentException\">buffer.Length is not a multiple of LargeBufferMultiple (it did not originate from this pool)</exception>\n        internal void ReturnLargeBuffer(byte[] buffer, string tag)\n        {\n            if (buffer == null)\n            {\n                throw new ArgumentNullException(nameof(buffer));\n            }\n\n            if (!this.IsLargeBufferMultiple(buffer.Length))\n            {\n                throw new ArgumentException(\n                    \"buffer did not originate from this memory manager. The size is not a multiple of \" +\n                    this.LargeBufferMultiple);\n            }\n\n            var poolIndex = buffer.Length / this.largeBufferMultiple - 1;\n\n            if (poolIndex < this.largePools.Length)\n            {\n                if ((this.largePools[poolIndex].Count + 1) * buffer.Length <= this.MaximumFreeLargePoolBytes ||\n                    this.MaximumFreeLargePoolBytes == 0)\n                {\n                    this.largePools[poolIndex].Push(buffer);\n                    Interlocked.Add(ref this.largeBufferFreeSize[poolIndex], buffer.Length);\n                }\n                else\n                {\n                    Events.Writer.MemoryStreamDiscardBuffer(Events.MemoryStreamBufferType.Large, tag,\n                                                           Events.MemoryStreamDiscardReason.EnoughFree);\n                    ReportLargeBufferDiscarded(Events.MemoryStreamDiscardReason.EnoughFree);\n                }\n            }\n            else\n            {\n                // This is a non-poolable buffer, but we still want to track its size for inuse\n                // analysis. We have space in the inuse array for this.\n                poolIndex = this.largeBufferInUseSize.Length - 1;\n\n                Events.Writer.MemoryStreamDiscardBuffer(Events.MemoryStreamBufferType.Large, tag,\n                                                       Events.MemoryStreamDiscardReason.TooLarge);\n                ReportLargeBufferDiscarded(Events.MemoryStreamDiscardReason.TooLarge);\n            }\n\n            Interlocked.Add(ref this.largeBufferInUseSize[poolIndex], -buffer.Length);\n\n            ReportUsageReport(this.smallPoolInUseSize, this.smallPoolFreeSize, this.LargePoolInUseSize,\n                              this.LargePoolFreeSize);\n        }\n\n        /// <summary>\n        /// Returns the blocks to the pool\n        /// </summary>\n        /// <param name=\"blocks\">Collection of blocks to return to the pool</param>\n        /// <param name=\"tag\">The tag of the stream returning these blocks, for logging if necessary.</param>\n        /// <exception cref=\"ArgumentNullException\">blocks is null</exception>\n        /// <exception cref=\"ArgumentException\">blocks contains buffers that are the wrong size (or null) for this memory manager</exception>\n        internal void ReturnBlocks(ICollection<byte[]> blocks, string tag)\n        {\n            if (blocks == null)\n            {\n                throw new ArgumentNullException(nameof(blocks));\n            }\n\n            var bytesToReturn = blocks.Count * this.BlockSize;\n            Interlocked.Add(ref this.smallPoolInUseSize, -bytesToReturn);\n\n            foreach (var block in blocks)\n            {\n                if (block == null || block.Length != this.BlockSize)\n                {\n                    throw new ArgumentException(\"blocks contains buffers that are not BlockSize in length\");\n                }\n            }\n\n            foreach (var block in blocks)\n            {\n                if (this.MaximumFreeSmallPoolBytes == 0 || this.SmallPoolFreeSize < this.MaximumFreeSmallPoolBytes)\n                {\n                    Interlocked.Add(ref this.smallPoolFreeSize, this.BlockSize);\n                    this.smallPool.Push(block);\n                }\n                else\n                {\n                    Events.Writer.MemoryStreamDiscardBuffer(Events.MemoryStreamBufferType.Small, tag,\n                                                           Events.MemoryStreamDiscardReason.EnoughFree);\n                    ReportBlockDiscarded();\n                    break;\n                }\n            }\n\n            ReportUsageReport(this.smallPoolInUseSize, this.smallPoolFreeSize, this.LargePoolInUseSize,\n                              this.LargePoolFreeSize);\n        }\n\n        internal void ReportBlockCreated()\n        {\n            this.BlockCreated?.Invoke();\n        }\n\n        internal void ReportBlockDiscarded()\n        {\n            this.BlockDiscarded?.Invoke();\n        }\n\n        internal void ReportLargeBufferCreated()\n        {\n            this.LargeBufferCreated?.Invoke();\n        }\n\n        internal void ReportLargeBufferDiscarded(Events.MemoryStreamDiscardReason reason)\n        {\n            this.LargeBufferDiscarded?.Invoke(reason);\n        }\n\n        internal void ReportStreamCreated()\n        {\n            this.StreamCreated?.Invoke();\n        }\n\n        internal void ReportStreamDisposed()\n        {\n            this.StreamDisposed?.Invoke();\n        }\n\n        internal void ReportStreamFinalized()\n        {\n            this.StreamFinalized?.Invoke();\n        }\n\n        internal void ReportStreamLength(long bytes)\n        {\n            this.StreamLength?.Invoke(bytes);\n        }\n\n        internal void ReportStreamToArray()\n        {\n            this.StreamConvertedToArray?.Invoke();\n        }\n\n        internal void ReportUsageReport(\n            long smallPoolInUseBytes, long smallPoolFreeBytes, long largePoolInUseBytes, long largePoolFreeBytes)\n        {\n            this.UsageReport?.Invoke(smallPoolInUseBytes, smallPoolFreeBytes, largePoolInUseBytes, largePoolFreeBytes);\n        }\n\n        /// <summary>\n        /// Retrieve a new MemoryStream object with no tag and a default initial capacity.\n        /// </summary>\n        /// <returns>A MemoryStream.</returns>\n        public MemoryStream GetStream()\n        {\n            return new RecyclableMemoryStream(this);\n        }\n\n        /// <summary>\n        /// Retrieve a new MemoryStream object with the given tag and a default initial capacity.\n        /// </summary>\n        /// <param name=\"tag\">A tag which can be used to track the source of the stream.</param>\n        /// <returns>A MemoryStream.</returns>\n        public MemoryStream GetStream(string tag)\n        {\n            return new RecyclableMemoryStream(this, tag);\n        }\n\n        /// <summary>\n        /// Retrieve a new MemoryStream object with the given tag and at least the given capacity.\n        /// </summary>\n        /// <param name=\"tag\">A tag which can be used to track the source of the stream.</param>\n        /// <param name=\"requiredSize\">The minimum desired capacity for the stream.</param>\n        /// <returns>A MemoryStream.</returns>\n        public MemoryStream GetStream(string tag, int requiredSize)\n        {\n            return new RecyclableMemoryStream(this, tag, requiredSize);\n        }\n\n        /// <summary>\n        /// Retrieve a new MemoryStream object with the given tag and at least the given capacity, possibly using\n        /// a single continugous underlying buffer.\n        /// </summary>\n        /// <remarks>Retrieving a MemoryStream which provides a single contiguous buffer can be useful in situations\n        /// where the initial size is known and it is desirable to avoid copying data between the smaller underlying\n        /// buffers to a single large one. This is most helpful when you know that you will always call GetBuffer\n        /// on the underlying stream.</remarks>\n        /// <param name=\"tag\">A tag which can be used to track the source of the stream.</param>\n        /// <param name=\"requiredSize\">The minimum desired capacity for the stream.</param>\n        /// <param name=\"asContiguousBuffer\">Whether to attempt to use a single contiguous buffer.</param>\n        /// <returns>A MemoryStream.</returns>\n        public MemoryStream GetStream(string tag, int requiredSize, bool asContiguousBuffer)\n        {\n            if (!asContiguousBuffer || requiredSize <= this.BlockSize)\n            {\n                return this.GetStream(tag, requiredSize);\n            }\n\n            return new RecyclableMemoryStream(this, tag, requiredSize, this.GetLargeBuffer(requiredSize, tag));\n        }\n\n        /// <summary>\n        /// Retrieve a new MemoryStream object with the given tag and with contents copied from the provided\n        /// buffer. The provided buffer is not wrapped or used after construction.\n        /// </summary>\n        /// <remarks>The new stream's position is set to the beginning of the stream when returned.</remarks>\n        /// <param name=\"tag\">A tag which can be used to track the source of the stream.</param>\n        /// <param name=\"buffer\">The byte buffer to copy data from.</param>\n        /// <param name=\"offset\">The offset from the start of the buffer to copy from.</param>\n        /// <param name=\"count\">The number of bytes to copy from the buffer.</param>\n        /// <returns>A MemoryStream.</returns>\n        [SuppressMessage(\"Microsoft.Reliability\", \"CA2000:Dispose objects before losing scope\")]\n        public MemoryStream GetStream(string tag, byte[] buffer, int offset, int count)\n        {\n            var stream = new RecyclableMemoryStream(this, tag, count);\n            stream.Write(buffer, offset, count);\n            stream.Position = 0;\n            return stream;\n        }\n\n        /// <summary>\n        /// Triggered when a new block is created.\n        /// </summary>\n        public event EventHandler BlockCreated;\n\n        /// <summary>\n        /// Triggered when a new block is created.\n        /// </summary>\n        public event EventHandler BlockDiscarded;\n\n        /// <summary>\n        /// Triggered when a new large buffer is created.\n        /// </summary>\n        public event EventHandler LargeBufferCreated;\n\n        /// <summary>\n        /// Triggered when a new stream is created.\n        /// </summary>\n        public event EventHandler StreamCreated;\n\n        /// <summary>\n        /// Triggered when a stream is disposed.\n        /// </summary>\n        public event EventHandler StreamDisposed;\n\n        /// <summary>\n        /// Triggered when a stream is finalized.\n        /// </summary>\n        public event EventHandler StreamFinalized;\n\n        /// <summary>\n        /// Triggered when a stream is finalized.\n        /// </summary>\n        public event StreamLengthReportHandler StreamLength;\n\n        /// <summary>\n        /// Triggered when a user converts a stream to array.\n        /// </summary>\n        public event EventHandler StreamConvertedToArray;\n\n        /// <summary>\n        /// Triggered when a large buffer is discarded, along with the reason for the discard.\n        /// </summary>\n        public event LargeBufferDiscardedEventHandler LargeBufferDiscarded;\n\n        /// <summary>\n        /// Periodically triggered to report usage statistics.\n        /// </summary>\n        public event UsageReportEventHandler UsageReport;\n    }\n}"
  },
  {
    "path": "Assets/Scripts/Network/Message/RecyclableMemoryStream/RecyclableMemoryStreamManager.cs.meta",
    "content": "fileFormatVersion: 2\nguid: e7f5123bf8a488045a8e48c49b605cc9\ntimeCreated: 1527234045\nlicenseType: Pro\nMonoImporter:\n  serializedVersion: 2\n  defaultReferences: []\n  executionOrder: 0\n  icon: {instanceID: 0}\n  userData: \n  assetBundleName: \n  assetBundleVariant: \n"
  },
  {
    "path": "Assets/Scripts/Network/Message/RecyclableMemoryStream.meta",
    "content": "fileFormatVersion: 2\nguid: ab0cd2080e97f064a9283d5729684c3e\nfolderAsset: yes\nDefaultImporter:\n  externalObjects: {}\n  userData: \n  assetBundleName: \n  assetBundleVariant: \n"
  },
  {
    "path": "Assets/Scripts/Network/Message/Session.cs",
    "content": "﻿using System;\nusing System.IO;\nusing System.Net;\nusing UnityEngine;\n\nnamespace UNetwork\n{\n    public sealed class Session\n    {\n        private AChannel channel;\n\n        private readonly byte[] opcodeBytes = new byte[2];\n\n        public INetworkManager Manager { get;private set; }\n\n        public int Error\n        {\n            get { return this.channel.Error; }\n            set { this.channel.Error = value; }\n        }\n\n        public Session(AChannel aChannel)\n        {\n            this.channel = aChannel;\n\n            channel.ConnectCallback += OnConnect;\n            channel.ErrorCallback += OnError;\n            channel.ReadCallback += OnRead;\n        }\n\n        private void OnConnect(AChannel channel, int code)\n        {\n            if (Manager.OnConnect != null)\n            {\n                Manager.OnConnect.Invoke(0);\n            }\n\n            Debug.Log(\"OnConnect\" + code);\n        }\n\n        private void OnError(AChannel channel, int code)\n        {\n            if (Manager.OnError != null)\n            {\n                Manager.OnError.Invoke(code);\n            }\n\n            Debug.LogError(\"OnError:\" + code);\n            this.Dispose();\n        }\n\n        public void Dispose()\n        {\n            int error = this.channel.Error;\n            if (this.channel.Error != 0)\n            {\n                Debug.LogError($\"session dispose: ErrorCode: {error}, please see ErrorCode.cs!\");\n            }\n\n            this.channel.Dispose();\n        }\n\n        public void Start(INetworkManager manager)\n        {\n            this.channel.Start();\n            this.Manager = manager;\n        }\n\n        public IPEndPoint RemoteAddress\n        {\n            get { return this.channel.RemoteAddress; }\n        }\n\n        public ChannelType ChannelType\n        {\n            get { return this.channel.ChannelType; }\n        }\n\n        public MemoryStream Stream\n        {\n            get { return this.channel.Stream; }\n        }\n\n        private void Run(MemoryStream memoryStream)\n        {\n            memoryStream.Seek(0, SeekOrigin.Begin);\n            var bytes = new byte[memoryStream.Length];\n            memoryStream.Read(bytes, 0, bytes.Length);\n            \n            // Manager.MessageDispatcher.Dispatch(this, memoryStream.GetBuffer());\n            Manager.OnMessage?.Invoke(bytes);\n        }\n\n        public void OnRead(MemoryStream memoryStream)\n        {\n            try\n            {\n                this.Run(memoryStream);\n            }\n            catch (Exception e)\n            {\n                Debug.LogError(e);\n            }\n        }\n\n        public void Send(ushort opcode)\n        {\n            MemoryStream stream = this.Stream;\n            stream.Seek(0, SeekOrigin.Begin);\n            stream.SetLength(Packet.MessageIndex);\n\n            opcodeBytes.WriteTo(0, opcode);\n            Array.Copy(opcodeBytes, 0, stream.GetBuffer(), 0, opcodeBytes.Length);\n\n            this.Send(stream);\n        }\n\n        public void Send(byte[] buffers)\n        {\n            MemoryStream stream = this.Stream;\n            stream.Seek(0, SeekOrigin.Begin);\n            stream.SetLength(buffers.Length);\n\n            Array.Copy(buffers, 0, stream.GetBuffer(), 0, buffers.Length);\n            this.Send(stream);\n        }\n\n        public void Send(MemoryStream stream)\n        {\n            channel.Send(stream);\n        }\n    }\n}"
  },
  {
    "path": "Assets/Scripts/Network/Message/Session.cs.meta",
    "content": "fileFormatVersion: 2\nguid: 73d149d0c325fa24d91a94843bd520b8\ntimeCreated: 1498117617\nlicenseType: Free\nMonoImporter:\n  serializedVersion: 2\n  defaultReferences: []\n  executionOrder: 0\n  icon: {instanceID: 0}\n  userData: \n  assetBundleName: \n  assetBundleVariant: \n"
  },
  {
    "path": "Assets/Scripts/Network/Message/SessionConnector.cs",
    "content": "﻿using System;\nusing System.IO;\nusing System.Net;\nusing UnityEngine;\n\nnamespace UNetwork\n{\n    public sealed class SessionConnector\n    {\n        private AChannel channel;\n\n        public SessionConnector(AChannel aChannel)\n        {\n            this.channel = aChannel;\n\n            channel.ConnectCallback += OnConnect;\n            channel.ErrorCallback += OnError;\n            channel.ReadCallback += OnRead;\n        }\n\n        private void OnConnect(AChannel channel, int code)\n        {\n            Debug.Log(\"OnConnect\" + code);\n        }\n\n        private void OnError(AChannel channel, int code)\n        {\n            Debug.LogError(\"OnError:\" + code);\n        }\n\n        private void Run(MemoryStream memoryStream)\n        {\n            memoryStream.Seek(0, SeekOrigin.Begin);\n            var bytes = new byte[memoryStream.Length];\n            memoryStream.Read(bytes, 0, bytes.Length);\n\n            ServerManager.Instance.OnMessage?.Invoke(bytes);\n        }\n\n        public void OnRead(MemoryStream memoryStream)\n        {\n            try\n            {\n                this.Run(memoryStream);\n            }\n            catch (Exception e)\n            {\n                Debug.LogError(e);\n            }\n        }\n    }\n}"
  },
  {
    "path": "Assets/Scripts/Network/Message/SessionConnector.cs.meta",
    "content": "﻿fileFormatVersion: 2\nguid: 79080751d836414388644ba4afc4410b\ntimeCreated: 1720089809"
  },
  {
    "path": "Assets/Scripts/Network/Message/SessionServer.cs",
    "content": "﻿using System;\nusing System.IO;\nusing System.Net;\nusing UnityEngine;\n\nnamespace UNetwork\n{\n    public sealed class SessionServer\n    {\n        private AChannel channel;\n\n        private readonly byte[] opcodeBytes = new byte[2];\n\n        public INetworkManager Manager { get; set; }\n\n        public int Error\n        {\n            get { return this.channel.Error; }\n            set { this.channel.Error = value; }\n        }\n\n        public SessionServer(AChannel aChannel)\n        {\n            this.channel = aChannel;\n\n            channel.ConnectCallback += OnConnect;\n            channel.ErrorCallback += OnError;\n            channel.ReadCallback += OnRead;\n        }\n\n        private void OnConnect(AChannel channel, int code)\n        {\n            if (Manager.OnConnect != null)\n            {\n                Manager.OnConnect.Invoke(0);\n            }\n\n            Debug.Log(\"OnConnect\" + code);\n        }\n\n        private void OnError(AChannel channel, int code)\n        {\n            if (Manager.OnError != null)\n            {\n                Manager.OnError.Invoke(code);\n            }\n\n            Debug.LogError(\"OnError:\" + code);\n            this.Dispose();\n        }\n\n        public void Dispose()\n        {\n            int error = this.channel.Error;\n            if (this.channel.Error != 0)\n            {\n                Debug.LogError($\"session dispose: ErrorCode: {error}, please see ErrorCode.cs!\");\n            }\n\n            this.channel.Dispose();\n        }\n\n        public void Start(INetworkManager manager)\n        {\n            this.channel.Start();\n            Manager = manager;\n        }\n\n        public IPEndPoint RemoteAddress\n        {\n            get { return this.channel.RemoteAddress; }\n        }\n\n        public ChannelType ChannelType\n        {\n            get { return this.channel.ChannelType; }\n        }\n\n\n        public MemoryStream Stream\n        {\n            get { return this.channel.Stream; }\n        }\n\n        private void Run(MemoryStream memoryStream)\n        {\n            memoryStream.Seek(0, SeekOrigin.Begin);\n            var bytes = new byte[memoryStream.Length];\n            memoryStream.Read(bytes, 0, bytes.Length);\n\n            // Manager.MessageDispatcher.Dispatch(this, memoryStream.GetBuffer());\n            Manager.OnMessage?.Invoke(memoryStream.GetBuffer());\n        }\n\n        public void OnRead(MemoryStream memoryStream)\n        {\n            try\n            {\n                this.Run(memoryStream);\n            }\n            catch (Exception e)\n            {\n                Debug.LogError(e);\n            }\n        }\n\n        public void Send(ushort opcode)\n        {\n            MemoryStream stream = this.Stream;\n            stream.Seek(0, SeekOrigin.Begin);\n            stream.SetLength(Packet.MessageIndex);\n\n            opcodeBytes.WriteTo(0, opcode);\n            Array.Copy(opcodeBytes, 0, stream.GetBuffer(), 0, opcodeBytes.Length);\n\n            this.Send(stream);\n        }\n\n        public void Send(byte[] buffers)\n        {\n            MemoryStream stream = this.Stream;\n            stream.Seek(0, SeekOrigin.Begin);\n            stream.SetLength(buffers.Length);\n\n            Array.Copy(buffers, 0, stream.GetBuffer(), 0, buffers.Length);\n            this.Send(stream);\n        }\n\n        public void Send(MemoryStream stream)\n        {\n            channel.Send(stream);\n        }\n    }\n}"
  },
  {
    "path": "Assets/Scripts/Network/Message/SessionServer.cs.meta",
    "content": "fileFormatVersion: 2\nguid: d72cceac0b794ac596aed9abc9e343b6\ntimeCreated: 1719307380"
  },
  {
    "path": "Assets/Scripts/Network/Message.meta",
    "content": "fileFormatVersion: 2\nguid: 6e2fe20a3053dc440938cf6c166337b2\nfolderAsset: yes\nDefaultImporter:\n  externalObjects: {}\n  userData: \n  assetBundleName: \n  assetBundleVariant: \n"
  },
  {
    "path": "Assets/Scripts/Network/ServerManager.cs",
    "content": "﻿using System;\nusing System.Net;\nusing System.Threading;\n\nnamespace UNetwork\n{\n    /// <summary>\n    /// Server 服务器的 业务逻辑 管理器\n    /// </summary>\n    public class ServerManager : MonoSingleton<ServerManager>, INetworkManager\n    {\n        public AService Service { get; private set; }\n        public SessionServer Session { get; private set; }\n\n        public IMessagePacker MessagePacker { get; set; }\n        public IMessageDispatcher MessageDispatcher { get; set; }\n\n        public Action<int> OnConnect { get; set; }\n        public Action<int> OnError { get; set; }\n        public Action<byte[]> OnMessage { get; set; }\n\n\n        public override void Init()\n        {\n            SynchronizationContext.SetSynchronizationContext(OneThreadSynchronizationContext.Instance);\n        }\n\n        //server\n        public void InitService(NetworkProtocol protocol, int packetSize = Packet.PacketSizeLength4)\n        {\n            switch (protocol)\n            {\n                case NetworkProtocol.TCP:\n                    this.Service = new TServiceServer(packetSize);\n                    break;\n            }\n        }\n\n        /// <summary>\n        /// 创建一个新Session\n        /// </summary>\n        public void Connect(IPEndPoint ipEndPoint)\n        {\n            AChannel channel = this.Service.ConnectChannel(ipEndPoint);\n            Session = new SessionServer(channel);\n            Session.Start(this);\n        }\n\n        /// <summary>\n        /// 创建一个新Session\n        /// </summary>\n        public void Connect(string address)\n        {\n            AChannel channel = this.Service.ConnectChannel(address);\n            Session = new SessionServer(channel);\n            Session.Start(this);\n        }\n\n        public void Connect(string ip, int port)\n        {\n            AChannel channel = this.Service.ConnectChannel(NetworkHelper.ToIPEndPoint(ip, port));\n            Session = new SessionServer(channel);\n            Session.Start(this);\n        }\n\n        public void Update()\n        {\n            OneThreadSynchronizationContext.Instance.Update();\n\n            if (this.Service == null)\n            {\n                return;\n            }\n\n            this.Service.Update();\n        }\n\n        public void Send(byte[] data)\n        {\n            Session.Send(data);\n        }\n\n        public override void Dispose()\n        {\n            Session.Dispose();\n        }\n    }\n}"
  },
  {
    "path": "Assets/Scripts/Network/ServerManager.cs.meta",
    "content": "﻿fileFormatVersion: 2\nguid: 84fd225f324f42999874057579220d14\ntimeCreated: 1719302713"
  },
  {
    "path": "Assets/Scripts/Network.meta",
    "content": "fileFormatVersion: 2\nguid: 89949efe62b53a044928bcf3075aad2f\nfolderAsset: yes\ntimeCreated: 1474943042\nlicenseType: Pro\nDefaultImporter:\n  userData: \n  assetBundleName: \n  assetBundleVariant: \n"
  },
  {
    "path": "Assets/Scripts/TestClient.cs",
    "content": "﻿using System;\nusing System.Text;\nusing UNetwork;\nusing UnityEngine;\n\npublic class TestClient : MonoBehaviour\n{\n    public string ip = \"127.0.0.1\";\n    public int port = 12346;\n\n//    private string address = \"10.200.10.192:3655\";\n    public string sendMessage = \"client\";\n    public static long starttime = 0;\n\n    void Start()\n    {\n        ClientManager client = ClientManager.Instance;\n        client.InitService(NetworkProtocol.TCP);\n        client.MessagePacker = new ProtobufPacker();\n        client.MessageDispatcher = new OuterMessageDispatcher();\n\n        client.Connect(ip,port);\n        client.OnConnect += OnConnect;\n        client.OnError += OnError;\n        client.OnMessage += OnMessage;\n    }\n\n    private void OnMessage(byte[] obj)\n    {\n        var msg = Encoding.UTF8.GetString(obj);\n        Debug.Log($\"Receive{obj.Length}\" + msg);\n    }\n\n    private void OnError(int e)\n    {\n        Debug.LogError(\"网络错误：\" + e);\n    }\n\n    private void OnConnect(int c)\n    {\n        Debug.Log(\"连接成功\");\n    }\n\n    private void Update()\n    {\n        if (Input.GetMouseButtonDown(0))\n        {\n            var data = Encoding.UTF8.GetBytes(sendMessage);\n            Debug.Log($\"Send{data.Length}:\" + sendMessage);\n\n            ClientManager.Instance.Send(data);\n            starttime = GetTimeStamp();\n        }\n    }\n\n    /// <summary>\n    /// 获取时间戳\n    /// </summary>\n    /// <returns></returns>\n    public static long GetTimeStamp()\n    {\n        return new DateTimeOffset(DateTime.UtcNow).ToUnixTimeMilliseconds();\n    }\n\n    public static void Receive()\n    {\n        var inteval = GetTimeStamp() - starttime;\n        Debug.LogWarning(inteval);\n    }\n}"
  },
  {
    "path": "Assets/Scripts/TestClient.cs.meta",
    "content": "fileFormatVersion: 2\nguid: c47d1a2e288e53f4285b397665f9a1dc\nMonoImporter:\n  externalObjects: {}\n  serializedVersion: 2\n  defaultReferences: []\n  executionOrder: 0\n  icon: {instanceID: 0}\n  userData: \n  assetBundleName: \n  assetBundleVariant: \n"
  },
  {
    "path": "Assets/Scripts/TestServer.cs",
    "content": "﻿using System;\nusing System.Text;\nusing UNetwork;\nusing UnityEngine;\n\npublic class TestServer : MonoBehaviour\n{\n    public string ip = \"127.0.0.1\";\n    public int port = 12346;\n    \n    public string sendMessage = \"server\";\n    public static long starttime = 0;\n\n    void Start()\n    {\n        ServerManager client = ServerManager.Instance;\n        client.InitService(NetworkProtocol.TCP);\n        client.MessagePacker = new ProtobufPacker();\n        client.MessageDispatcher = new OuterMessageDispatcher();\n\n        client.Connect(ip, port);\n        client.OnConnect += OnConnect;\n        client.OnError += OnError;\n        client.OnMessage += OnMessage;\n    }\n\n    private void OnMessage(byte[] obj)\n    {\n        var msg = Encoding.UTF8.GetString(obj);\n        Debug.Log(msg);\n    }\n\n    private void OnError(int e)\n    {\n        Debug.LogError(\"网络错误：\" + e);\n    }\n\n    private void OnConnect(int c)\n    {\n        Debug.Log(\"连接成功\");\n    }\n\n    private void Update()\n    {\n        if (Input.GetMouseButtonDown(0))\n        {\n            var data = Encoding.UTF8.GetBytes(sendMessage);\n            Debug.Log($\"Send{data.Length}:\" + sendMessage);\n\n            ServerManager.Instance.Send(data);\n            starttime = GetTimeStamp();\n        }\n    }\n\n    /// <summary>\n    /// 获取时间戳\n    /// </summary>\n    /// <returns></returns>\n    public static long GetTimeStamp()\n    {\n        return new DateTimeOffset(DateTime.UtcNow).ToUnixTimeMilliseconds();\n    }\n\n    public static void Receive()\n    {\n        var inteval = GetTimeStamp() - starttime;\n        Debug.LogWarning(inteval);\n    }\n}"
  },
  {
    "path": "Assets/Scripts/TestServer.cs.meta",
    "content": "﻿fileFormatVersion: 2\nguid: 8518a6a6d5ca49ecb048613cc3a03e07\ntimeCreated: 1719304719"
  },
  {
    "path": "Assets/Scripts.meta",
    "content": "fileFormatVersion: 2\nguid: 68234a28db005584e9d209109316f50f\nfolderAsset: yes\nDefaultImporter:\n  externalObjects: {}\n  userData: \n  assetBundleName: \n  assetBundleVariant: \n"
  },
  {
    "path": "ProjectSettings/AudioManager.asset",
    "content": "%YAML 1.1\n%TAG !u! tag:unity3d.com,2011:\n--- !u!11 &1\nAudioManager:\n  m_ObjectHideFlags: 0\n  m_Volume: 1\n  Rolloff Scale: 1\n  Doppler Factor: 1\n  Default Speaker Mode: 2\n  m_SampleRate: 0\n  m_DSPBufferSize: 1024\n  m_VirtualVoiceCount: 512\n  m_RealVoiceCount: 32\n  m_SpatializerPlugin: \n  m_AmbisonicDecoderPlugin: \n  m_DisableAudio: 0\n  m_VirtualizeEffects: 1\n"
  },
  {
    "path": "ProjectSettings/AutoStreamingSettings.asset",
    "content": "%YAML 1.1\n%TAG !u! tag:unity3d.com,2011:\n--- !u!1200 &1\nAutoStreamingSettings:\n  m_ObjectHideFlags: 0\n  serializedVersion: 2\n  mSearchMode: 15\n  mCustomSearchFile: \n  mTextureSearchString: \n  mMeshSearchString: \n  mTextures: []\n  mAudios: []\n  mMeshes: []\n  mScenes: []\n  mConfigCCD:\n    useCCD: 0\n    cosKey: \n    projectGuid: \n    bucketUuid: \n    bucketName: \n    badgeName: \n"
  },
  {
    "path": "ProjectSettings/ClusterInputManager.asset",
    "content": "%YAML 1.1\n%TAG !u! tag:unity3d.com,2011:\n--- !u!236 &1\nClusterInputManager:\n  m_ObjectHideFlags: 0\n  m_Inputs: []\n"
  },
  {
    "path": "ProjectSettings/DynamicsManager.asset",
    "content": "%YAML 1.1\n%TAG !u! tag:unity3d.com,2011:\n--- !u!55 &1\nPhysicsManager:\n  m_ObjectHideFlags: 0\n  serializedVersion: 8\n  m_Gravity: {x: 0, y: -9.81, z: 0}\n  m_DefaultMaterial: {fileID: 0}\n  m_BounceThreshold: 2\n  m_SleepThreshold: 0.005\n  m_DefaultContactOffset: 0.01\n  m_DefaultSolverIterations: 6\n  m_DefaultSolverVelocityIterations: 1\n  m_QueriesHitBackfaces: 0\n  m_QueriesHitTriggers: 1\n  m_EnableAdaptiveForce: 0\n  m_ClothInterCollisionDistance: 0\n  m_ClothInterCollisionStiffness: 0\n  m_ContactsGeneration: 1\n  m_LayerCollisionMatrix: ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff\n  m_AutoSimulation: 1\n  m_AutoSyncTransforms: 0\n  m_ReuseCollisionCallbacks: 1\n  m_ClothInterCollisionSettingsToggle: 0\n  m_ContactPairsMode: 0\n  m_BroadphaseType: 0\n  m_WorldBounds:\n    m_Center: {x: 0, y: 0, z: 0}\n    m_Extent: {x: 250, y: 250, z: 250}\n  m_WorldSubdivisions: 8\n"
  },
  {
    "path": "ProjectSettings/EditorBuildSettings.asset",
    "content": "%YAML 1.1\n%TAG !u! tag:unity3d.com,2011:\n--- !u!1045 &1\nEditorBuildSettings:\n  m_ObjectHideFlags: 0\n  serializedVersion: 2\n  m_Scenes: []\n  m_configObjects: {}\n"
  },
  {
    "path": "ProjectSettings/EditorSettings.asset",
    "content": "%YAML 1.1\n%TAG !u! tag:unity3d.com,2011:\n--- !u!159 &1\nEditorSettings:\n  m_ObjectHideFlags: 0\n  serializedVersion: 7\n  m_ExternalVersionControlSupport: Visible Meta Files\n  m_SerializationMode: 2\n  m_LineEndingsForNewScripts: 2\n  m_DefaultBehaviorMode: 1\n  m_PrefabRegularEnvironment: {fileID: 0}\n  m_PrefabUIEnvironment: {fileID: 0}\n  m_SpritePackerMode: 4\n  m_SpritePackerPaddingPower: 1\n  m_EtcTextureCompressorBehavior: 1\n  m_EtcTextureFastCompressor: 1\n  m_EtcTextureNormalCompressor: 2\n  m_EtcTextureBestCompressor: 4\n  m_ProjectGenerationIncludedExtensions: txt;xml;fnt;cd;asmdef\n  m_ProjectGenerationRootNamespace: \n  m_CollabEditorSettings:\n    inProgressEnabled: 1\n  m_EnableTextureStreamingInPlayMode: 1\n"
  },
  {
    "path": "ProjectSettings/GraphicsSettings.asset",
    "content": "%YAML 1.1\n%TAG !u! tag:unity3d.com,2011:\n--- !u!30 &1\nGraphicsSettings:\n  m_ObjectHideFlags: 0\n  serializedVersion: 12\n  m_Deferred:\n    m_Mode: 1\n    m_Shader: {fileID: 69, guid: 0000000000000000f000000000000000, type: 0}\n  m_DeferredReflections:\n    m_Mode: 1\n    m_Shader: {fileID: 74, guid: 0000000000000000f000000000000000, type: 0}\n  m_ScreenSpaceShadows:\n    m_Mode: 1\n    m_Shader: {fileID: 64, guid: 0000000000000000f000000000000000, type: 0}\n  m_LegacyDeferred:\n    m_Mode: 1\n    m_Shader: {fileID: 63, guid: 0000000000000000f000000000000000, type: 0}\n  m_DepthNormals:\n    m_Mode: 1\n    m_Shader: {fileID: 62, guid: 0000000000000000f000000000000000, type: 0}\n  m_MotionVectors:\n    m_Mode: 1\n    m_Shader: {fileID: 75, guid: 0000000000000000f000000000000000, type: 0}\n  m_LightHalo:\n    m_Mode: 1\n    m_Shader: {fileID: 105, guid: 0000000000000000f000000000000000, type: 0}\n  m_LensFlare:\n    m_Mode: 1\n    m_Shader: {fileID: 102, guid: 0000000000000000f000000000000000, type: 0}\n  m_AlwaysIncludedShaders:\n  - {fileID: 10753, guid: 0000000000000000f000000000000000, type: 0}\n  - {fileID: 10770, guid: 0000000000000000f000000000000000, type: 0}\n  - {fileID: 10783, guid: 0000000000000000f000000000000000, type: 0}\n  m_PreloadedShaders: []\n  m_SpritesDefaultMaterial: {fileID: 10754, guid: 0000000000000000f000000000000000,\n    type: 0}\n  m_CustomRenderPipeline: {fileID: 0}\n  m_TransparencySortMode: 0\n  m_TransparencySortAxis: {x: 0, y: 0, z: 1}\n  m_DefaultRenderingPath: 1\n  m_DefaultMobileRenderingPath: 1\n  m_TierSettings: []\n  m_LightmapStripping: 0\n  m_FogStripping: 0\n  m_InstancingStripping: 0\n  m_LightmapKeepPlain: 1\n  m_LightmapKeepDirCombined: 1\n  m_LightmapKeepDynamicPlain: 1\n  m_LightmapKeepDynamicDirCombined: 1\n  m_LightmapKeepShadowMask: 1\n  m_LightmapKeepSubtractive: 1\n  m_FogKeepLinear: 1\n  m_FogKeepExp: 1\n  m_FogKeepExp2: 1\n  m_AlbedoSwatchInfos: []\n  m_LightsUseLinearIntensity: 0\n  m_LightsUseColorTemperature: 0\n"
  },
  {
    "path": "ProjectSettings/InputManager.asset",
    "content": "%YAML 1.1\n%TAG !u! tag:unity3d.com,2011:\n--- !u!13 &1\nInputManager:\n  m_ObjectHideFlags: 0\n  serializedVersion: 2\n  m_Axes:\n  - serializedVersion: 3\n    m_Name: Horizontal\n    descriptiveName: \n    descriptiveNegativeName: \n    negativeButton: left\n    positiveButton: right\n    altNegativeButton: a\n    altPositiveButton: d\n    gravity: 3\n    dead: 0.001\n    sensitivity: 3\n    snap: 1\n    invert: 0\n    type: 0\n    axis: 0\n    joyNum: 0\n  - serializedVersion: 3\n    m_Name: Vertical\n    descriptiveName: \n    descriptiveNegativeName: \n    negativeButton: down\n    positiveButton: up\n    altNegativeButton: s\n    altPositiveButton: w\n    gravity: 3\n    dead: 0.001\n    sensitivity: 3\n    snap: 1\n    invert: 0\n    type: 0\n    axis: 0\n    joyNum: 0\n  - serializedVersion: 3\n    m_Name: Fire1\n    descriptiveName: \n    descriptiveNegativeName: \n    negativeButton: \n    positiveButton: left ctrl\n    altNegativeButton: \n    altPositiveButton: mouse 0\n    gravity: 1000\n    dead: 0.001\n    sensitivity: 1000\n    snap: 0\n    invert: 0\n    type: 0\n    axis: 0\n    joyNum: 0\n  - serializedVersion: 3\n    m_Name: Fire2\n    descriptiveName: \n    descriptiveNegativeName: \n    negativeButton: \n    positiveButton: left alt\n    altNegativeButton: \n    altPositiveButton: mouse 1\n    gravity: 1000\n    dead: 0.001\n    sensitivity: 1000\n    snap: 0\n    invert: 0\n    type: 0\n    axis: 0\n    joyNum: 0\n  - serializedVersion: 3\n    m_Name: Fire3\n    descriptiveName: \n    descriptiveNegativeName: \n    negativeButton: \n    positiveButton: left shift\n    altNegativeButton: \n    altPositiveButton: mouse 2\n    gravity: 1000\n    dead: 0.001\n    sensitivity: 1000\n    snap: 0\n    invert: 0\n    type: 0\n    axis: 0\n    joyNum: 0\n  - serializedVersion: 3\n    m_Name: Jump\n    descriptiveName: \n    descriptiveNegativeName: \n    negativeButton: \n    positiveButton: space\n    altNegativeButton: \n    altPositiveButton: \n    gravity: 1000\n    dead: 0.001\n    sensitivity: 1000\n    snap: 0\n    invert: 0\n    type: 0\n    axis: 0\n    joyNum: 0\n  - serializedVersion: 3\n    m_Name: Mouse X\n    descriptiveName: \n    descriptiveNegativeName: \n    negativeButton: \n    positiveButton: \n    altNegativeButton: \n    altPositiveButton: \n    gravity: 0\n    dead: 0\n    sensitivity: 0.1\n    snap: 0\n    invert: 0\n    type: 1\n    axis: 0\n    joyNum: 0\n  - serializedVersion: 3\n    m_Name: Mouse Y\n    descriptiveName: \n    descriptiveNegativeName: \n    negativeButton: \n    positiveButton: \n    altNegativeButton: \n    altPositiveButton: \n    gravity: 0\n    dead: 0\n    sensitivity: 0.1\n    snap: 0\n    invert: 0\n    type: 1\n    axis: 1\n    joyNum: 0\n  - serializedVersion: 3\n    m_Name: Mouse ScrollWheel\n    descriptiveName: \n    descriptiveNegativeName: \n    negativeButton: \n    positiveButton: \n    altNegativeButton: \n    altPositiveButton: \n    gravity: 0\n    dead: 0\n    sensitivity: 0.1\n    snap: 0\n    invert: 0\n    type: 1\n    axis: 2\n    joyNum: 0\n  - serializedVersion: 3\n    m_Name: Horizontal\n    descriptiveName: \n    descriptiveNegativeName: \n    negativeButton: \n    positiveButton: \n    altNegativeButton: \n    altPositiveButton: \n    gravity: 0\n    dead: 0.19\n    sensitivity: 1\n    snap: 0\n    invert: 0\n    type: 2\n    axis: 0\n    joyNum: 0\n  - serializedVersion: 3\n    m_Name: Vertical\n    descriptiveName: \n    descriptiveNegativeName: \n    negativeButton: \n    positiveButton: \n    altNegativeButton: \n    altPositiveButton: \n    gravity: 0\n    dead: 0.19\n    sensitivity: 1\n    snap: 0\n    invert: 1\n    type: 2\n    axis: 1\n    joyNum: 0\n  - serializedVersion: 3\n    m_Name: Fire1\n    descriptiveName: \n    descriptiveNegativeName: \n    negativeButton: \n    positiveButton: joystick button 0\n    altNegativeButton: \n    altPositiveButton: \n    gravity: 1000\n    dead: 0.001\n    sensitivity: 1000\n    snap: 0\n    invert: 0\n    type: 0\n    axis: 0\n    joyNum: 0\n  - serializedVersion: 3\n    m_Name: Fire2\n    descriptiveName: \n    descriptiveNegativeName: \n    negativeButton: \n    positiveButton: joystick button 1\n    altNegativeButton: \n    altPositiveButton: \n    gravity: 1000\n    dead: 0.001\n    sensitivity: 1000\n    snap: 0\n    invert: 0\n    type: 0\n    axis: 0\n    joyNum: 0\n  - serializedVersion: 3\n    m_Name: Fire3\n    descriptiveName: \n    descriptiveNegativeName: \n    negativeButton: \n    positiveButton: joystick button 2\n    altNegativeButton: \n    altPositiveButton: \n    gravity: 1000\n    dead: 0.001\n    sensitivity: 1000\n    snap: 0\n    invert: 0\n    type: 0\n    axis: 0\n    joyNum: 0\n  - serializedVersion: 3\n    m_Name: Jump\n    descriptiveName: \n    descriptiveNegativeName: \n    negativeButton: \n    positiveButton: joystick button 3\n    altNegativeButton: \n    altPositiveButton: \n    gravity: 1000\n    dead: 0.001\n    sensitivity: 1000\n    snap: 0\n    invert: 0\n    type: 0\n    axis: 0\n    joyNum: 0\n  - serializedVersion: 3\n    m_Name: Submit\n    descriptiveName: \n    descriptiveNegativeName: \n    negativeButton: \n    positiveButton: return\n    altNegativeButton: \n    altPositiveButton: joystick button 0\n    gravity: 1000\n    dead: 0.001\n    sensitivity: 1000\n    snap: 0\n    invert: 0\n    type: 0\n    axis: 0\n    joyNum: 0\n  - serializedVersion: 3\n    m_Name: Submit\n    descriptiveName: \n    descriptiveNegativeName: \n    negativeButton: \n    positiveButton: enter\n    altNegativeButton: \n    altPositiveButton: space\n    gravity: 1000\n    dead: 0.001\n    sensitivity: 1000\n    snap: 0\n    invert: 0\n    type: 0\n    axis: 0\n    joyNum: 0\n  - serializedVersion: 3\n    m_Name: Cancel\n    descriptiveName: \n    descriptiveNegativeName: \n    negativeButton: \n    positiveButton: escape\n    altNegativeButton: \n    altPositiveButton: joystick button 1\n    gravity: 1000\n    dead: 0.001\n    sensitivity: 1000\n    snap: 0\n    invert: 0\n    type: 0\n    axis: 0\n    joyNum: 0\n"
  },
  {
    "path": "ProjectSettings/MemorySettings.asset",
    "content": "%YAML 1.1\n%TAG !u! tag:unity3d.com,2011:\n--- !u!387306366 &1\nMemorySettings:\n  m_ObjectHideFlags: 0\n  m_EditorMemorySettings:\n    m_MainAllocatorBlockSize: -1\n    m_ThreadAllocatorBlockSize: -1\n    m_MainGfxBlockSize: -1\n    m_ThreadGfxBlockSize: -1\n    m_CacheBlockSize: -1\n    m_TypetreeBlockSize: -1\n    m_ProfilerBlockSize: -1\n    m_ProfilerEditorBlockSize: -1\n    m_BucketAllocatorGranularity: -1\n    m_BucketAllocatorBucketsCount: -1\n    m_BucketAllocatorBlockSize: -1\n    m_BucketAllocatorBlockCount: -1\n    m_ProfilerBucketAllocatorGranularity: -1\n    m_ProfilerBucketAllocatorBucketsCount: -1\n    m_ProfilerBucketAllocatorBlockSize: -1\n    m_ProfilerBucketAllocatorBlockCount: -1\n    m_TempAllocatorSizeMain: -1\n    m_JobTempAllocatorBlockSize: -1\n    m_BackgroundJobTempAllocatorBlockSize: -1\n    m_JobTempAllocatorReducedBlockSize: -1\n    m_TempAllocatorSizeGIBakingWorker: -1\n    m_TempAllocatorSizeNavMeshWorker: -1\n    m_TempAllocatorSizeAudioWorker: -1\n    m_TempAllocatorSizeCloudWorker: -1\n    m_TempAllocatorSizeGfx: -1\n    m_TempAllocatorSizeJobWorker: -1\n    m_TempAllocatorSizeBackgroundWorker: -1\n    m_TempAllocatorSizePreloadManager: -1\n  m_PlatformMemorySettings: {}\n"
  },
  {
    "path": "ProjectSettings/NavMeshAreas.asset",
    "content": "%YAML 1.1\n%TAG !u! tag:unity3d.com,2011:\n--- !u!126 &1\nNavMeshProjectSettings:\n  m_ObjectHideFlags: 0\n  serializedVersion: 2\n  areas:\n  - name: Walkable\n    cost: 1\n  - name: Not Walkable\n    cost: 1\n  - name: Jump\n    cost: 2\n  - name: \n    cost: 1\n  - name: \n    cost: 1\n  - name: \n    cost: 1\n  - name: \n    cost: 1\n  - name: \n    cost: 1\n  - name: \n    cost: 1\n  - name: \n    cost: 1\n  - name: \n    cost: 1\n  - name: \n    cost: 1\n  - name: \n    cost: 1\n  - name: \n    cost: 1\n  - name: \n    cost: 1\n  - name: \n    cost: 1\n  - name: \n    cost: 1\n  - name: \n    cost: 1\n  - name: \n    cost: 1\n  - name: \n    cost: 1\n  - name: \n    cost: 1\n  - name: \n    cost: 1\n  - name: \n    cost: 1\n  - name: \n    cost: 1\n  - name: \n    cost: 1\n  - name: \n    cost: 1\n  - name: \n    cost: 1\n  - name: \n    cost: 1\n  - name: \n    cost: 1\n  - name: \n    cost: 1\n  - name: \n    cost: 1\n  - name: \n    cost: 1\n  m_LastAgentTypeID: -887442657\n  m_Settings:\n  - serializedVersion: 2\n    agentTypeID: 0\n    agentRadius: 0.5\n    agentHeight: 2\n    agentSlope: 45\n    agentClimb: 0.75\n    ledgeDropHeight: 0\n    maxJumpAcrossDistance: 0\n    minRegionArea: 2\n    manualCellSize: 0\n    cellSize: 0.16666667\n    manualTileSize: 0\n    tileSize: 256\n    accuratePlacement: 0\n    debug:\n      m_Flags: 0\n  m_SettingNames:\n  - Humanoid\n"
  },
  {
    "path": "ProjectSettings/NetworkManager.asset",
    "content": "%YAML 1.1\n%TAG !u! tag:unity3d.com,2011:\n--- !u!149 &1\nNetworkManager:\n  m_ObjectHideFlags: 0\n  m_DebugLevel: 0\n  m_Sendrate: 15\n  m_AssetToPrefab: {}\n"
  },
  {
    "path": "ProjectSettings/PackageManagerSettings.asset",
    "content": "%YAML 1.1\n%TAG !u! tag:unity3d.com,2011:\n--- !u!114 &1\nMonoBehaviour:\n  m_ObjectHideFlags: 61\n  m_CorrespondingSourceObject: {fileID: 0}\n  m_PrefabInstance: {fileID: 0}\n  m_PrefabAsset: {fileID: 0}\n  m_GameObject: {fileID: 0}\n  m_Enabled: 1\n  m_EditorHideFlags: 0\n  m_Script: {fileID: 13964, guid: 0000000000000000e000000000000000, type: 0}\n  m_Name: \n  m_EditorClassIdentifier: \n  m_EnablePreReleasePackages: 0\n  m_EnablePackageDependencies: 0\n  m_AdvancedSettingsExpanded: 1\n  m_ScopedRegistriesSettingsExpanded: 1\n  m_SeeAllPackageVersions: 0\n  oneTimeWarningShown: 0\n  m_Registries:\n  - m_Id: main\n    m_Name: \n    m_Url: https://packages.unity.cn\n    m_Scopes: []\n    m_IsDefault: 1\n    m_Capabilities: 7\n    m_ConfigSource: 0\n  m_UserSelectedRegistryName: \n  m_UserAddingNewScopedRegistry: 0\n  m_RegistryInfoDraft:\n    m_Modified: 0\n    m_ErrorMessage: \n    m_UserModificationsInstanceId: -854\n    m_OriginalInstanceId: -856\n  m_LoadAssets: 0\n"
  },
  {
    "path": "ProjectSettings/Physics2DSettings.asset",
    "content": "%YAML 1.1\n%TAG !u! tag:unity3d.com,2011:\n--- !u!19 &1\nPhysics2DSettings:\n  m_ObjectHideFlags: 0\n  serializedVersion: 4\n  m_Gravity: {x: 0, y: -9.81}\n  m_DefaultMaterial: {fileID: 0}\n  m_VelocityIterations: 8\n  m_PositionIterations: 3\n  m_VelocityThreshold: 1\n  m_MaxLinearCorrection: 0.2\n  m_MaxAngularCorrection: 8\n  m_MaxTranslationSpeed: 100\n  m_MaxRotationSpeed: 360\n  m_BaumgarteScale: 0.2\n  m_BaumgarteTimeOfImpactScale: 0.75\n  m_TimeToSleep: 0.5\n  m_LinearSleepTolerance: 0.01\n  m_AngularSleepTolerance: 2\n  m_DefaultContactOffset: 0.01\n  m_JobOptions:\n    serializedVersion: 2\n    useMultithreading: 0\n    useConsistencySorting: 0\n    m_InterpolationPosesPerJob: 100\n    m_NewContactsPerJob: 30\n    m_CollideContactsPerJob: 100\n    m_ClearFlagsPerJob: 200\n    m_ClearBodyForcesPerJob: 200\n    m_SyncDiscreteFixturesPerJob: 50\n    m_SyncContinuousFixturesPerJob: 50\n    m_FindNearestContactsPerJob: 100\n    m_UpdateTriggerContactsPerJob: 100\n    m_IslandSolverCostThreshold: 100\n    m_IslandSolverBodyCostScale: 1\n    m_IslandSolverContactCostScale: 10\n    m_IslandSolverJointCostScale: 10\n    m_IslandSolverBodiesPerJob: 50\n    m_IslandSolverContactsPerJob: 50\n  m_AutoSimulation: 1\n  m_QueriesHitTriggers: 1\n  m_QueriesStartInColliders: 1\n  m_CallbacksOnDisable: 1\n  m_ReuseCollisionCallbacks: 1\n  m_AutoSyncTransforms: 0\n  m_AlwaysShowColliders: 0\n  m_ShowColliderSleep: 1\n  m_ShowColliderContacts: 0\n  m_ShowColliderAABB: 0\n  m_ContactArrowScale: 0.2\n  m_ColliderAwakeColor: {r: 0.5686275, g: 0.95686275, b: 0.54509807, a: 0.7529412}\n  m_ColliderAsleepColor: {r: 0.5686275, g: 0.95686275, b: 0.54509807, a: 0.36078432}\n  m_ColliderContactColor: {r: 1, g: 0, b: 1, a: 0.6862745}\n  m_ColliderAABBColor: {r: 1, g: 1, b: 0, a: 0.2509804}\n  m_LayerCollisionMatrix: ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff\n"
  },
  {
    "path": "ProjectSettings/PresetManager.asset",
    "content": "%YAML 1.1\n%TAG !u! tag:unity3d.com,2011:\n--- !u!1386491679 &1\nPresetManager:\n  m_ObjectHideFlags: 0\n  m_DefaultList:\n  - type:\n      m_NativeTypeID: 20\n      m_ManagedTypePPtr: {fileID: 0}\n      m_ManagedTypeFallback: \n    defaultPresets:\n    - m_Preset: {fileID: 2655988077585873504, guid: bfcfc320427f8224bbb7a96f3d3aebad,\n        type: 2}\n"
  },
  {
    "path": "ProjectSettings/ProjectSettings.asset",
    "content": "%YAML 1.1\n%TAG !u! tag:unity3d.com,2011:\n--- !u!129 &1\nPlayerSettings:\n  m_ObjectHideFlags: 0\n  serializedVersion: 24\n  productGUID: 773bc0f7adf1dc245bf5c8831827604a\n  AndroidProfiler: 0\n  AndroidFilterTouchesWhenObscured: 0\n  AndroidEnableSustainedPerformanceMode: 0\n  defaultScreenOrientation: 4\n  targetDevice: 2\n  useOnDemandResources: 0\n  accelerometerFrequency: 60\n  companyName: DefaultCompany\n  productName: TestKCP\n  defaultCursor: {fileID: 0}\n  cursorHotspot: {x: 0, y: 0}\n  m_SplashScreenBackgroundColor: {r: 0.13725491, g: 0.12156863, b: 0.1254902, a: 1}\n  m_ShowUnitySplashScreen: 1\n  m_ShowUnitySplashLogo: 1\n  m_SplashScreenOverlayOpacity: 1\n  m_SplashScreenAnimation: 1\n  m_SplashScreenLogoStyle: 1\n  m_SplashScreenDrawMode: 0\n  m_SplashScreenBackgroundAnimationZoom: 1\n  m_SplashScreenLogoAnimationZoom: 1\n  m_SplashScreenBackgroundLandscapeAspect: 1\n  m_SplashScreenBackgroundPortraitAspect: 1\n  m_SplashScreenBackgroundLandscapeUvs:\n    serializedVersion: 2\n    x: 0\n    y: 0\n    width: 1\n    height: 1\n  m_SplashScreenBackgroundPortraitUvs:\n    serializedVersion: 2\n    x: 0\n    y: 0\n    width: 1\n    height: 1\n  m_SplashScreenLogos: []\n  m_VirtualRealitySplashScreen: {fileID: 0}\n  m_HolographicTrackingLossScreen: {fileID: 0}\n  defaultScreenWidth: 1024\n  defaultScreenHeight: 768\n  defaultScreenWidthWeb: 960\n  defaultScreenHeightWeb: 600\n  m_StereoRenderingPath: 0\n  m_ActiveColorSpace: 0\n  unsupportedMSAAFallback: 0\n  m_MTRendering: 1\n  mipStripping: 0\n  numberOfMipsStripped: 0\n  m_StackTraceTypes: 010000000100000001000000010000000100000001000000\n  iosShowActivityIndicatorOnLoading: -1\n  androidShowActivityIndicatorOnLoading: -1\n  iosUseCustomAppBackgroundBehavior: 0\n  iosAllowHTTPDownload: 1\n  allowedAutorotateToPortrait: 1\n  allowedAutorotateToPortraitUpsideDown: 1\n  allowedAutorotateToLandscapeRight: 1\n  allowedAutorotateToLandscapeLeft: 1\n  useOSAutorotation: 1\n  use32BitDisplayBuffer: 1\n  preserveFramebufferAlpha: 0\n  disableDepthAndStencilBuffers: 0\n  androidStartInFullscreen: 1\n  androidRenderOutsideSafeArea: 0\n  androidUseSwappy: 0\n  androidBlitType: 0\n  androidResizableWindow: 0\n  androidDefaultWindowWidth: 1920\n  androidDefaultWindowHeight: 1080\n  androidMinimumWindowWidth: 400\n  androidMinimumWindowHeight: 300\n  androidFullscreenMode: 1\n  androidAutoRotationBehavior: 1\n  defaultIsNativeResolution: 1\n  macRetinaSupport: 1\n  runInBackground: 1\n  captureSingleScreen: 0\n  muteOtherAudioSources: 0\n  Prepare IOS For Recording: 0\n  Force IOS Speakers When Recording: 0\n  deferSystemGesturesMode: 0\n  hideHomeButton: 0\n  submitAnalytics: 1\n  usePlayerLog: 1\n  autoStreaming: 0\n  useAnimationStreaming: 0\n  useFontStreaming: 0\n  autoStreamingId: \n  instantGameAppId: \n  bakeCollisionMeshes: 0\n  forceSingleInstance: 0\n  useFlipModelSwapchain: 1\n  resizableWindow: 0\n  useMacAppStoreValidation: 0\n  macAppStoreCategory: public.app-category.games\n  gpuSkinning: 0\n  xboxPIXTextureCapture: 0\n  xboxEnableAvatar: 0\n  xboxEnableKinect: 0\n  xboxEnableKinectAutoTracking: 0\n  xboxEnableFitness: 0\n  visibleInBackground: 1\n  allowFullscreenSwitch: 1\n  fullscreenMode: 1\n  xboxSpeechDB: 0\n  xboxEnableHeadOrientation: 0\n  xboxEnableGuest: 0\n  xboxEnablePIXSampling: 0\n  metalFramebufferOnly: 0\n  xboxOneResolution: 0\n  xboxOneSResolution: 0\n  xboxOneXResolution: 3\n  xboxOneMonoLoggingLevel: 0\n  xboxOneLoggingLevel: 1\n  xboxOneDisableEsram: 0\n  xboxOneEnableTypeOptimization: 0\n  xboxOnePresentImmediateThreshold: 0\n  switchQueueCommandMemory: 0\n  switchQueueControlMemory: 16384\n  switchQueueComputeMemory: 262144\n  switchNVNShaderPoolsGranularity: 33554432\n  switchNVNDefaultPoolsGranularity: 16777216\n  switchNVNOtherPoolsGranularity: 16777216\n  switchNVNMaxPublicTextureIDCount: 0\n  switchNVNMaxPublicSamplerIDCount: 0\n  switchMaxWorkerMultiple: 8\n  stadiaPresentMode: 0\n  stadiaTargetFramerate: 0\n  vulkanNumSwapchainBuffers: 3\n  vulkanEnableSetSRGBWrite: 0\n  vulkanEnablePreTransform: 0\n  vulkanEnableLateAcquireNextImage: 0\n  vulkanEnableCommandBufferRecycling: 1\n  m_SupportedAspectRatios:\n    4:3: 1\n    5:4: 1\n    16:10: 1\n    16:9: 1\n    Others: 1\n  bundleVersion: 0.1\n  preloadedAssets: []\n  metroInputSource: 0\n  wsaTransparentSwapchain: 0\n  m_HolographicPauseOnTrackingLoss: 1\n  xboxOneDisableKinectGpuReservation: 1\n  xboxOneEnable7thCore: 1\n  vrSettings:\n    enable360StereoCapture: 0\n  isWsaHolographicRemotingEnabled: 0\n  enableFrameTimingStats: 0\n  enableOpenGLProfilerGPURecorders: 1\n  useHDRDisplay: 0\n  D3DHDRBitDepth: 0\n  m_ColorGamuts: 00000000\n  targetPixelDensity: 30\n  resolutionScalingMode: 0\n  resetResolutionOnWindowResize: 0\n  androidSupportedAspectRatio: 1\n  androidMaxAspectRatio: 2.1\n  applicationIdentifier:\n    Standalone: com.Company.ProductName\n  buildNumber:\n    Standalone: 0\n    iPhone: 0\n    tvOS: 0\n  overrideDefaultApplicationIdentifier: 1\n  AndroidBundleVersionCode: 1\n  AndroidMinSdkVersion: 22\n  AndroidTargetSdkVersion: 0\n  AndroidPreferredInstallLocation: 1\n  aotOptions: \n  stripEngineCode: 1\n  iPhoneStrippingLevel: 0\n  iPhoneScriptCallOptimization: 0\n  ForceInternetPermission: 0\n  ForceSDCardPermission: 0\n  CreateWallpaper: 0\n  APKExpansionFiles: 0\n  keepLoadedShadersAlive: 0\n  StripUnusedMeshComponents: 1\n  VertexChannelCompressionMask: 4054\n  iPhoneSdkVersion: 988\n  iOSTargetOSVersionString: 12.0\n  tvOSSdkVersion: 0\n  tvOSRequireExtendedGameController: 0\n  tvOSTargetOSVersionString: 12.0\n  uIPrerenderedIcon: 0\n  uIRequiresPersistentWiFi: 0\n  uIRequiresFullScreen: 1\n  uIStatusBarHidden: 1\n  uIExitOnSuspend: 0\n  uIStatusBarStyle: 0\n  appleTVSplashScreen: {fileID: 0}\n  appleTVSplashScreen2x: {fileID: 0}\n  tvOSSmallIconLayers: []\n  tvOSSmallIconLayers2x: []\n  tvOSLargeIconLayers: []\n  tvOSLargeIconLayers2x: []\n  tvOSTopShelfImageLayers: []\n  tvOSTopShelfImageLayers2x: []\n  tvOSTopShelfImageWideLayers: []\n  tvOSTopShelfImageWideLayers2x: []\n  iOSLaunchScreenType: 0\n  iOSLaunchScreenPortrait: {fileID: 0}\n  iOSLaunchScreenLandscape: {fileID: 0}\n  iOSLaunchScreenBackgroundColor:\n    serializedVersion: 2\n    rgba: 0\n  iOSLaunchScreenFillPct: 100\n  iOSLaunchScreenSize: 100\n  iOSLaunchScreenCustomXibPath: \n  iOSLaunchScreeniPadType: 0\n  iOSLaunchScreeniPadImage: {fileID: 0}\n  iOSLaunchScreeniPadBackgroundColor:\n    serializedVersion: 2\n    rgba: 0\n  iOSLaunchScreeniPadFillPct: 100\n  iOSLaunchScreeniPadSize: 100\n  iOSLaunchScreeniPadCustomXibPath: \n  iOSLaunchScreenCustomStoryboardPath: \n  iOSLaunchScreeniPadCustomStoryboardPath: \n  iOSDeviceRequirements: []\n  iOSURLSchemes: []\n  macOSURLSchemes: []\n  iOSBackgroundModes: 0\n  iOSMetalForceHardShadows: 0\n  metalEditorSupport: 1\n  metalAPIValidation: 1\n  iOSRenderExtraFrameOnPause: 0\n  iosCopyPluginsCodeInsteadOfSymlink: 0\n  appleDeveloperTeamID: \n  iOSManualSigningProvisioningProfileID: \n  tvOSManualSigningProvisioningProfileID: \n  iOSManualSigningProvisioningProfileType: 0\n  tvOSManualSigningProvisioningProfileType: 0\n  appleEnableAutomaticSigning: 0\n  iOSRequireARKit: 0\n  iOSAutomaticallyDetectAndAddCapabilities: 1\n  appleEnableProMotion: 0\n  shaderPrecisionModel: 0\n  clonedFromGUID: 5f34be1353de5cf4398729fda238591b\n  templatePackageId: com.unity.template.2d@1.3.0\n  templateDefaultScene: Assets/Scenes/SampleScene.unity\n  useCustomMainManifest: 0\n  useCustomLauncherManifest: 0\n  useCustomMainGradleTemplate: 0\n  useCustomLauncherGradleManifest: 0\n  useCustomBaseGradleTemplate: 0\n  useCustomGradlePropertiesTemplate: 0\n  useCustomProguardFile: 0\n  AndroidTargetArchitectures: 5\n  AndroidTargetDevices: 0\n  AndroidSplashScreenScale: 0\n  androidSplashScreen: {fileID: 0}\n  AndroidKeystoreName: \n  AndroidKeyaliasName: \n  AndroidBuildApkPerCpuArchitecture: 0\n  AndroidTVCompatibility: 1\n  AndroidIsGame: 1\n  AndroidEnableTango: 0\n  androidEnableBanner: 1\n  androidUseLowAccuracyLocation: 0\n  androidUseCustomKeystore: 0\n  m_AndroidBanners:\n  - width: 320\n    height: 180\n    banner: {fileID: 0}\n  androidGamepadSupportLevel: 0\n  chromeosInputEmulation: 1\n  AndroidMinifyWithR8: 0\n  AndroidMinifyRelease: 0\n  AndroidMinifyDebug: 0\n  AndroidValidateAppBundleSize: 1\n  AndroidAppBundleSizeToValidate: 150\n  m_BuildTargetIcons: []\n  m_BuildTargetPlatformIcons:\n  - m_BuildTarget: Android\n    m_Icons:\n    - m_Textures: []\n      m_Width: 432\n      m_Height: 432\n      m_Kind: 2\n      m_SubKind: \n    - m_Textures: []\n      m_Width: 324\n      m_Height: 324\n      m_Kind: 2\n      m_SubKind: \n    - m_Textures: []\n      m_Width: 216\n      m_Height: 216\n      m_Kind: 2\n      m_SubKind: \n    - m_Textures: []\n      m_Width: 162\n      m_Height: 162\n      m_Kind: 2\n      m_SubKind: \n    - m_Textures: []\n      m_Width: 108\n      m_Height: 108\n      m_Kind: 2\n      m_SubKind: \n    - m_Textures: []\n      m_Width: 81\n      m_Height: 81\n      m_Kind: 2\n      m_SubKind: \n    - m_Textures: []\n      m_Width: 192\n      m_Height: 192\n      m_Kind: 1\n      m_SubKind: \n    - m_Textures: []\n      m_Width: 144\n      m_Height: 144\n      m_Kind: 1\n      m_SubKind: \n    - m_Textures: []\n      m_Width: 96\n      m_Height: 96\n      m_Kind: 1\n      m_SubKind: \n    - m_Textures: []\n      m_Width: 72\n      m_Height: 72\n      m_Kind: 1\n      m_SubKind: \n    - m_Textures: []\n      m_Width: 48\n      m_Height: 48\n      m_Kind: 1\n      m_SubKind: \n    - m_Textures: []\n      m_Width: 36\n      m_Height: 36\n      m_Kind: 1\n      m_SubKind: \n    - m_Textures: []\n      m_Width: 192\n      m_Height: 192\n      m_Kind: 0\n      m_SubKind: \n    - m_Textures: []\n      m_Width: 144\n      m_Height: 144\n      m_Kind: 0\n      m_SubKind: \n    - m_Textures: []\n      m_Width: 96\n      m_Height: 96\n      m_Kind: 0\n      m_SubKind: \n    - m_Textures: []\n      m_Width: 72\n      m_Height: 72\n      m_Kind: 0\n      m_SubKind: \n    - m_Textures: []\n      m_Width: 48\n      m_Height: 48\n      m_Kind: 0\n      m_SubKind: \n    - m_Textures: []\n      m_Width: 36\n      m_Height: 36\n      m_Kind: 0\n      m_SubKind: \n  m_BuildTargetBatching: []\n  m_BuildTargetShaderSettings: []\n  m_BuildTargetGraphicsJobs:\n  - m_BuildTarget: WindowsStandaloneSupport\n    m_GraphicsJobs: 0\n  - m_BuildTarget: MacStandaloneSupport\n    m_GraphicsJobs: 0\n  - m_BuildTarget: LinuxStandaloneSupport\n    m_GraphicsJobs: 0\n  - m_BuildTarget: AndroidPlayer\n    m_GraphicsJobs: 0\n  - m_BuildTarget: iOSSupport\n    m_GraphicsJobs: 0\n  - m_BuildTarget: PS4Player\n    m_GraphicsJobs: 0\n  - m_BuildTarget: PS5Player\n    m_GraphicsJobs: 0\n  - m_BuildTarget: XboxOnePlayer\n    m_GraphicsJobs: 0\n  - m_BuildTarget: GameCoreXboxOneSupport\n    m_GraphicsJobs: 0\n  - m_BuildTarget: GameCoreScarlettSupport\n    m_GraphicsJobs: 0\n  - m_BuildTarget: Switch\n    m_GraphicsJobs: 0\n  - m_BuildTarget: WebGLSupport\n    m_GraphicsJobs: 0\n  - m_BuildTarget: MetroSupport\n    m_GraphicsJobs: 0\n  - m_BuildTarget: AppleTVSupport\n    m_GraphicsJobs: 0\n  - m_BuildTarget: BJMSupport\n    m_GraphicsJobs: 0\n  - m_BuildTarget: LuminSupport\n    m_GraphicsJobs: 0\n  - m_BuildTarget: CloudRendering\n    m_GraphicsJobs: 0\n  - m_BuildTarget: EmbeddedLinux\n    m_GraphicsJobs: 0\n  m_BuildTargetGraphicsJobMode:\n  - m_BuildTarget: PS4Player\n    m_GraphicsJobMode: 0\n  - m_BuildTarget: XboxOnePlayer\n    m_GraphicsJobMode: 0\n  m_BuildTargetGraphicsAPIs:\n  - m_BuildTarget: iOSSupport\n    m_APIs: 10000000\n    m_Automatic: 1\n  - m_BuildTarget: AndroidPlayer\n    m_APIs: 0b00000008000000\n    m_Automatic: 0\n  m_BuildTargetVRSettings: []\n  m_DefaultShaderChunkSizeInMB: 16\n  m_DefaultShaderChunkCount: 0\n  openGLRequireES31: 0\n  openGLRequireES31AEP: 0\n  openGLRequireES32: 0\n  m_TemplateCustomTags: {}\n  mobileMTRendering:\n    Android: 1\n    iPhone: 1\n    tvOS: 1\n  m_BuildTargetGroupLightmapEncodingQuality: []\n  m_BuildTargetGroupLightmapSettings: []\n  m_BuildTargetNormalMapEncoding: []\n  m_BuildTargetDefaultTextureCompressionFormat: []\n  playModeTestRunnerEnabled: 0\n  runPlayModeTestAsEditModeTest: 0\n  actionOnDotNetUnhandledException: 1\n  enableInternalProfiler: 0\n  logObjCUncaughtExceptions: 1\n  enableCrashReportAPI: 0\n  cameraUsageDescription: \n  locationUsageDescription: \n  microphoneUsageDescription: \n  bluetoothUsageDescription: \n  switchNMETAOverride: \n  switchNetLibKey: \n  switchSocketMemoryPoolSize: 6144\n  switchSocketAllocatorPoolSize: 128\n  switchSocketConcurrencyLimit: 14\n  switchScreenResolutionBehavior: 2\n  switchUseCPUProfiler: 0\n  switchEnableFileSystemTrace: 0\n  switchLTOSetting: 0\n  switchApplicationID: 0x01004b9000490000\n  switchNSODependencies: \n  switchTitleNames_0: \n  switchTitleNames_1: \n  switchTitleNames_2: \n  switchTitleNames_3: \n  switchTitleNames_4: \n  switchTitleNames_5: \n  switchTitleNames_6: \n  switchTitleNames_7: \n  switchTitleNames_8: \n  switchTitleNames_9: \n  switchTitleNames_10: \n  switchTitleNames_11: \n  switchTitleNames_12: \n  switchTitleNames_13: \n  switchTitleNames_14: \n  switchTitleNames_15: \n  switchPublisherNames_0: \n  switchPublisherNames_1: \n  switchPublisherNames_2: \n  switchPublisherNames_3: \n  switchPublisherNames_4: \n  switchPublisherNames_5: \n  switchPublisherNames_6: \n  switchPublisherNames_7: \n  switchPublisherNames_8: \n  switchPublisherNames_9: \n  switchPublisherNames_10: \n  switchPublisherNames_11: \n  switchPublisherNames_12: \n  switchPublisherNames_13: \n  switchPublisherNames_14: \n  switchPublisherNames_15: \n  switchIcons_0: {fileID: 0}\n  switchIcons_1: {fileID: 0}\n  switchIcons_2: {fileID: 0}\n  switchIcons_3: {fileID: 0}\n  switchIcons_4: {fileID: 0}\n  switchIcons_5: {fileID: 0}\n  switchIcons_6: {fileID: 0}\n  switchIcons_7: {fileID: 0}\n  switchIcons_8: {fileID: 0}\n  switchIcons_9: {fileID: 0}\n  switchIcons_10: {fileID: 0}\n  switchIcons_11: {fileID: 0}\n  switchIcons_12: {fileID: 0}\n  switchIcons_13: {fileID: 0}\n  switchIcons_14: {fileID: 0}\n  switchIcons_15: {fileID: 0}\n  switchSmallIcons_0: {fileID: 0}\n  switchSmallIcons_1: {fileID: 0}\n  switchSmallIcons_2: {fileID: 0}\n  switchSmallIcons_3: {fileID: 0}\n  switchSmallIcons_4: {fileID: 0}\n  switchSmallIcons_5: {fileID: 0}\n  switchSmallIcons_6: {fileID: 0}\n  switchSmallIcons_7: {fileID: 0}\n  switchSmallIcons_8: {fileID: 0}\n  switchSmallIcons_9: {fileID: 0}\n  switchSmallIcons_10: {fileID: 0}\n  switchSmallIcons_11: {fileID: 0}\n  switchSmallIcons_12: {fileID: 0}\n  switchSmallIcons_13: {fileID: 0}\n  switchSmallIcons_14: {fileID: 0}\n  switchSmallIcons_15: {fileID: 0}\n  switchManualHTML: \n  switchAccessibleURLs: \n  switchLegalInformation: \n  switchMainThreadStackSize: 1048576\n  switchPresenceGroupId: \n  switchLogoHandling: 0\n  switchReleaseVersion: 0\n  switchDisplayVersion: 1.0.0\n  switchStartupUserAccount: 0\n  switchSupportedLanguagesMask: 0\n  switchLogoType: 0\n  switchApplicationErrorCodeCategory: \n  switchUserAccountSaveDataSize: 0\n  switchUserAccountSaveDataJournalSize: 0\n  switchApplicationAttribute: 0\n  switchCardSpecSize: -1\n  switchCardSpecClock: -1\n  switchRatingsMask: 0\n  switchRatingsInt_0: 0\n  switchRatingsInt_1: 0\n  switchRatingsInt_2: 0\n  switchRatingsInt_3: 0\n  switchRatingsInt_4: 0\n  switchRatingsInt_5: 0\n  switchRatingsInt_6: 0\n  switchRatingsInt_7: 0\n  switchRatingsInt_8: 0\n  switchRatingsInt_9: 0\n  switchRatingsInt_10: 0\n  switchRatingsInt_11: 0\n  switchRatingsInt_12: 0\n  switchLocalCommunicationIds_0: \n  switchLocalCommunicationIds_1: \n  switchLocalCommunicationIds_2: \n  switchLocalCommunicationIds_3: \n  switchLocalCommunicationIds_4: \n  switchLocalCommunicationIds_5: \n  switchLocalCommunicationIds_6: \n  switchLocalCommunicationIds_7: \n  switchParentalControl: 0\n  switchAllowsScreenshot: 1\n  switchAllowsVideoCapturing: 1\n  switchAllowsRuntimeAddOnContentInstall: 0\n  switchDataLossConfirmation: 0\n  switchUserAccountLockEnabled: 0\n  switchSystemResourceMemory: 16777216\n  switchSupportedNpadStyles: 3\n  switchNativeFsCacheSize: 32\n  switchIsHoldTypeHorizontal: 0\n  switchSupportedNpadCount: 8\n  switchEnableTouchScreen: 1\n  switchSocketConfigEnabled: 0\n  switchTcpInitialSendBufferSize: 32\n  switchTcpInitialReceiveBufferSize: 64\n  switchTcpAutoSendBufferSizeMax: 256\n  switchTcpAutoReceiveBufferSizeMax: 256\n  switchUdpSendBufferSize: 9\n  switchUdpReceiveBufferSize: 42\n  switchSocketBufferEfficiency: 4\n  switchSocketInitializeEnabled: 1\n  switchNetworkInterfaceManagerInitializeEnabled: 1\n  switchUseNewStyleFilepaths: 0\n  switchUseLegacyFmodPriorities: 1\n  switchUseMicroSleepForYield: 1\n  switchEnableRamDiskSupport: 0\n  switchMicroSleepForYieldTime: 25\n  switchRamDiskSpaceSize: 12\n  ps4NPAgeRating: 12\n  ps4NPTitleSecret: \n  ps4NPTrophyPackPath: \n  ps4ParentalLevel: 11\n  ps4ContentID: ED1633-NPXX51362_00-0000000000000000\n  ps4Category: 0\n  ps4MasterVersion: 01.00\n  ps4AppVersion: 01.00\n  ps4AppType: 0\n  ps4ParamSfxPath: \n  ps4VideoOutPixelFormat: 0\n  ps4VideoOutInitialWidth: 1920\n  ps4VideoOutBaseModeInitialWidth: 1920\n  ps4VideoOutReprojectionRate: 60\n  ps4PronunciationXMLPath: \n  ps4PronunciationSIGPath: \n  ps4BackgroundImagePath: \n  ps4StartupImagePath: \n  ps4StartupImagesFolder: \n  ps4IconImagesFolder: \n  ps4SaveDataImagePath: \n  ps4SdkOverride: \n  ps4BGMPath: \n  ps4ShareFilePath: \n  ps4ShareOverlayImagePath: \n  ps4PrivacyGuardImagePath: \n  ps4ExtraSceSysFile: \n  ps4NPtitleDatPath: \n  ps4RemotePlayKeyAssignment: -1\n  ps4RemotePlayKeyMappingDir: \n  ps4PlayTogetherPlayerCount: 0\n  ps4EnterButtonAssignment: 1\n  ps4ApplicationParam1: 0\n  ps4ApplicationParam2: 0\n  ps4ApplicationParam3: 0\n  ps4ApplicationParam4: 0\n  ps4DownloadDataSize: 0\n  ps4GarlicHeapSize: 2048\n  ps4ProGarlicHeapSize: 2560\n  playerPrefsMaxSize: 32768\n  ps4Passcode: frAQBc8Wsa1xVPfvJcrgRYwTiizs2trQ\n  ps4pnSessions: 1\n  ps4pnPresence: 1\n  ps4pnFriends: 1\n  ps4pnGameCustomData: 1\n  playerPrefsSupport: 0\n  enableApplicationExit: 0\n  resetTempFolder: 1\n  restrictedAudioUsageRights: 0\n  ps4UseResolutionFallback: 0\n  ps4ReprojectionSupport: 0\n  ps4UseAudio3dBackend: 0\n  ps4UseLowGarlicFragmentationMode: 1\n  ps4SocialScreenEnabled: 0\n  ps4ScriptOptimizationLevel: 0\n  ps4Audio3dVirtualSpeakerCount: 14\n  ps4attribCpuUsage: 0\n  ps4PatchPkgPath: \n  ps4PatchLatestPkgPath: \n  ps4PatchChangeinfoPath: \n  ps4PatchDayOne: 0\n  ps4attribUserManagement: 0\n  ps4attribMoveSupport: 0\n  ps4attrib3DSupport: 0\n  ps4attribShareSupport: 0\n  ps4attribExclusiveVR: 0\n  ps4disableAutoHideSplash: 0\n  ps4videoRecordingFeaturesUsed: 0\n  ps4contentSearchFeaturesUsed: 0\n  ps4CompatibilityPS5: 0\n  ps4AllowPS5Detection: 0\n  ps4GPU800MHz: 1\n  ps4attribEyeToEyeDistanceSettingVR: 0\n  ps4IncludedModules: []\n  ps4attribVROutputEnabled: 0\n  monoEnv: \n  splashScreenBackgroundSourceLandscape: {fileID: 0}\n  splashScreenBackgroundSourcePortrait: {fileID: 0}\n  blurSplashScreenBackground: 1\n  spritePackerPolicy: \n  webGLMemorySize: 256\n  webGLExceptionSupport: 1\n  webGLNameFilesAsHashes: 0\n  webGLDataCaching: 1\n  webGLDebugSymbols: 0\n  webGLEmscriptenArgs: \n  webGLModulesDirectory: \n  webGLTemplate: APPLICATION:Default\n  webGLAnalyzeBuildSize: 0\n  webGLUseEmbeddedResources: 0\n  webGLCompressionFormat: 1\n  webGLWasmArithmeticExceptions: 0\n  webGLLinkerTarget: 1\n  webGLThreadsSupport: 0\n  webGLDecompressionFallback: 0\n  webGLPowerPreference: 2\n  scriptingDefineSymbols: {}\n  additionalCompilerArguments: {}\n  platformArchitecture: {}\n  scriptingBackend: {}\n  il2cppCompilerConfiguration: {}\n  managedStrippingLevel:\n    EmbeddedLinux: 1\n    GameCoreScarlett: 1\n    GameCoreXboxOne: 1\n    Lumin: 1\n    Nintendo Switch: 1\n    PS4: 1\n    PS5: 1\n    Stadia: 1\n    WebGL: 1\n    Windows Store Apps: 1\n    XboxOne: 1\n    iPhone: 1\n    tvOS: 1\n  incrementalIl2cppBuild: {}\n  suppressCommonWarnings: 1\n  allowUnsafeCode: 0\n  useDeterministicCompilation: 1\n  enableRoslynAnalyzers: 1\n  additionalIl2CppArgs: \n  scriptingRuntimeVersion: 1\n  gcIncremental: 1\n  assemblyVersionValidation: 1\n  gcWBarrierValidation: 0\n  apiCompatibilityLevelPerPlatform: {}\n  m_RenderingPath: 1\n  m_MobileRenderingPath: 1\n  metroPackageName: Template_2D\n  metroPackageVersion: \n  metroCertificatePath: \n  metroCertificatePassword: \n  metroCertificateSubject: \n  metroCertificateIssuer: \n  metroCertificateNotAfter: 0000000000000000\n  metroApplicationDescription: Template_2D\n  wsaImages: {}\n  metroTileShortName: \n  metroTileShowName: 0\n  metroMediumTileShowName: 0\n  metroLargeTileShowName: 0\n  metroWideTileShowName: 0\n  metroSupportStreamingInstall: 0\n  metroLastRequiredScene: 0\n  metroDefaultTileSize: 1\n  metroTileForegroundText: 2\n  metroTileBackgroundColor: {r: 0.13333334, g: 0.17254902, b: 0.21568628, a: 0}\n  metroSplashScreenBackgroundColor: {r: 0.12941177, g: 0.17254902, b: 0.21568628,\n    a: 1}\n  metroSplashScreenUseBackgroundColor: 0\n  platformCapabilities: {}\n  metroTargetDeviceFamilies: {}\n  metroFTAName: \n  metroFTAFileTypes: []\n  metroProtocolName: \n  vcxProjDefaultLanguage: \n  XboxOneProductId: \n  XboxOneUpdateKey: \n  XboxOneSandboxId: \n  XboxOneContentId: \n  XboxOneTitleId: \n  XboxOneSCId: \n  XboxOneGameOsOverridePath: \n  XboxOnePackagingOverridePath: \n  XboxOneAppManifestOverridePath: \n  XboxOneVersion: 1.0.0.0\n  XboxOnePackageEncryption: 0\n  XboxOnePackageUpdateGranularity: 2\n  XboxOneDescription: \n  XboxOneLanguage:\n  - enus\n  XboxOneCapability: []\n  XboxOneGameRating: {}\n  XboxOneIsContentPackage: 0\n  XboxOneEnhancedXboxCompatibilityMode: 0\n  XboxOneEnableGPUVariability: 1\n  XboxOneSockets: {}\n  XboxOneSplashScreen: {fileID: 0}\n  XboxOneAllowedProductIds: []\n  XboxOnePersistentLocalStorageSize: 0\n  XboxOneXTitleMemory: 8\n  XboxOneOverrideIdentityName: \n  XboxOneOverrideIdentityPublisher: \n  vrEditorSettings: {}\n  cloudServicesEnabled:\n    UNet: 1\n  luminIcon:\n    m_Name: \n    m_ModelFolderPath: \n    m_PortalFolderPath: \n  luminCert:\n    m_CertPath: \n    m_SignPackage: 1\n  luminIsChannelApp: 0\n  luminVersion:\n    m_VersionCode: 1\n    m_VersionName: \n  apiCompatibilityLevel: 6\n  activeInputHandler: 0\n  windowsGamepadBackendHint: 0\n  cloudProjectId: \n  framebufferDepthMemorylessMode: 0\n  qualitySettingsNames: []\n  projectName: \n  organizationId: \n  cloudEnabled: 0\n  legacyClampBlendShapeWeights: 0\n  playerDataPath: \n  forceSRGBBlit: 1\n  virtualTexturingSupportEnabled: 0\n"
  },
  {
    "path": "ProjectSettings/ProjectVersion.txt",
    "content": "m_EditorVersion: 2021.3.36f1c1\nm_EditorVersionWithRevision: 2021.3.36f1c1 (0ec3cfe9a403)\n"
  },
  {
    "path": "ProjectSettings/QualitySettings.asset",
    "content": "%YAML 1.1\n%TAG !u! tag:unity3d.com,2011:\n--- !u!47 &1\nQualitySettings:\n  m_ObjectHideFlags: 0\n  serializedVersion: 5\n  m_CurrentQuality: 3\n  m_QualitySettings:\n  - serializedVersion: 2\n    name: Very Low\n    pixelLightCount: 0\n    shadows: 0\n    shadowResolution: 0\n    shadowProjection: 1\n    shadowCascades: 1\n    shadowDistance: 15\n    shadowNearPlaneOffset: 3\n    shadowCascade2Split: 0.33333334\n    shadowCascade4Split: {x: 0.06666667, y: 0.2, z: 0.46666667}\n    shadowmaskMode: 0\n    blendWeights: 1\n    textureQuality: 1\n    anisotropicTextures: 0\n    antiAliasing: 0\n    softParticles: 0\n    softVegetation: 0\n    realtimeReflectionProbes: 0\n    billboardsFaceCameraPosition: 0\n    vSyncCount: 0\n    lodBias: 0.3\n    maximumLODLevel: 0\n    particleRaycastBudget: 4\n    asyncUploadTimeSlice: 2\n    asyncUploadBufferSize: 16\n    resolutionScalingFixedDPIFactor: 1\n    excludedTargetPlatforms: []\n  - serializedVersion: 2\n    name: Low\n    pixelLightCount: 0\n    shadows: 0\n    shadowResolution: 0\n    shadowProjection: 1\n    shadowCascades: 1\n    shadowDistance: 20\n    shadowNearPlaneOffset: 3\n    shadowCascade2Split: 0.33333334\n    shadowCascade4Split: {x: 0.06666667, y: 0.2, z: 0.46666667}\n    shadowmaskMode: 0\n    blendWeights: 2\n    textureQuality: 0\n    anisotropicTextures: 0\n    antiAliasing: 0\n    softParticles: 0\n    softVegetation: 0\n    realtimeReflectionProbes: 0\n    billboardsFaceCameraPosition: 0\n    vSyncCount: 0\n    lodBias: 0.4\n    maximumLODLevel: 0\n    particleRaycastBudget: 16\n    asyncUploadTimeSlice: 2\n    asyncUploadBufferSize: 16\n    resolutionScalingFixedDPIFactor: 1\n    excludedTargetPlatforms: []\n  - serializedVersion: 2\n    name: Medium\n    pixelLightCount: 1\n    shadows: 0\n    shadowResolution: 0\n    shadowProjection: 1\n    shadowCascades: 1\n    shadowDistance: 20\n    shadowNearPlaneOffset: 3\n    shadowCascade2Split: 0.33333334\n    shadowCascade4Split: {x: 0.06666667, y: 0.2, z: 0.46666667}\n    shadowmaskMode: 0\n    blendWeights: 2\n    textureQuality: 0\n    anisotropicTextures: 0\n    antiAliasing: 0\n    softParticles: 0\n    softVegetation: 0\n    realtimeReflectionProbes: 0\n    billboardsFaceCameraPosition: 0\n    vSyncCount: 1\n    lodBias: 0.7\n    maximumLODLevel: 0\n    particleRaycastBudget: 64\n    asyncUploadTimeSlice: 2\n    asyncUploadBufferSize: 16\n    resolutionScalingFixedDPIFactor: 1\n    excludedTargetPlatforms: []\n  - serializedVersion: 2\n    name: High\n    pixelLightCount: 2\n    shadows: 0\n    shadowResolution: 1\n    shadowProjection: 1\n    shadowCascades: 2\n    shadowDistance: 40\n    shadowNearPlaneOffset: 3\n    shadowCascade2Split: 0.33333334\n    shadowCascade4Split: {x: 0.06666667, y: 0.2, z: 0.46666667}\n    shadowmaskMode: 1\n    blendWeights: 2\n    textureQuality: 0\n    anisotropicTextures: 0\n    antiAliasing: 0\n    softParticles: 0\n    softVegetation: 1\n    realtimeReflectionProbes: 0\n    billboardsFaceCameraPosition: 0\n    vSyncCount: 1\n    lodBias: 1\n    maximumLODLevel: 0\n    particleRaycastBudget: 256\n    asyncUploadTimeSlice: 2\n    asyncUploadBufferSize: 16\n    resolutionScalingFixedDPIFactor: 1\n    excludedTargetPlatforms: []\n  - serializedVersion: 2\n    name: Very High\n    pixelLightCount: 3\n    shadows: 0\n    shadowResolution: 2\n    shadowProjection: 1\n    shadowCascades: 2\n    shadowDistance: 70\n    shadowNearPlaneOffset: 3\n    shadowCascade2Split: 0.33333334\n    shadowCascade4Split: {x: 0.06666667, y: 0.2, z: 0.46666667}\n    shadowmaskMode: 1\n    blendWeights: 4\n    textureQuality: 0\n    anisotropicTextures: 0\n    antiAliasing: 0\n    softParticles: 0\n    softVegetation: 1\n    realtimeReflectionProbes: 0\n    billboardsFaceCameraPosition: 0\n    vSyncCount: 1\n    lodBias: 1.5\n    maximumLODLevel: 0\n    particleRaycastBudget: 1024\n    asyncUploadTimeSlice: 2\n    asyncUploadBufferSize: 16\n    resolutionScalingFixedDPIFactor: 1\n    excludedTargetPlatforms: []\n  - serializedVersion: 2\n    name: Ultra\n    pixelLightCount: 4\n    shadows: 0\n    shadowResolution: 0\n    shadowProjection: 1\n    shadowCascades: 4\n    shadowDistance: 150\n    shadowNearPlaneOffset: 3\n    shadowCascade2Split: 0.33333334\n    shadowCascade4Split: {x: 0.06666667, y: 0.2, z: 0.46666667}\n    shadowmaskMode: 1\n    blendWeights: 4\n    textureQuality: 0\n    anisotropicTextures: 0\n    antiAliasing: 0\n    softParticles: 0\n    softVegetation: 1\n    realtimeReflectionProbes: 0\n    billboardsFaceCameraPosition: 0\n    vSyncCount: 1\n    lodBias: 2\n    maximumLODLevel: 0\n    particleRaycastBudget: 4096\n    asyncUploadTimeSlice: 2\n    asyncUploadBufferSize: 16\n    resolutionScalingFixedDPIFactor: 1\n    excludedTargetPlatforms: []\n  m_PerPlatformDefaultQuality:\n    Android: 2\n    Nintendo 3DS: 5\n    Nintendo Switch: 5\n    PS4: 5\n    PSM: 5\n    PSP2: 2\n    Standalone: 5\n    Tizen: 2\n    WebGL: 3\n    WiiU: 5\n    Windows Store Apps: 5\n    XboxOne: 5\n    iPhone: 2\n    tvOS: 2\n"
  },
  {
    "path": "ProjectSettings/SceneTemplateSettings.json",
    "content": "{\n    \"templatePinStates\": [],\n    \"dependencyTypeInfos\": [\n        {\n            \"userAdded\": false,\n            \"type\": \"UnityEngine.AnimationClip\",\n            \"defaultInstantiationMode\": 0\n        },\n        {\n            \"userAdded\": false,\n            \"type\": \"UnityEditor.Animations.AnimatorController\",\n            \"defaultInstantiationMode\": 0\n        },\n        {\n            \"userAdded\": false,\n            \"type\": \"UnityEngine.AnimatorOverrideController\",\n            \"defaultInstantiationMode\": 0\n        },\n        {\n            \"userAdded\": false,\n            \"type\": \"UnityEditor.Audio.AudioMixerController\",\n            \"defaultInstantiationMode\": 0\n        },\n        {\n            \"userAdded\": false,\n            \"type\": \"UnityEngine.ComputeShader\",\n            \"defaultInstantiationMode\": 1\n        },\n        {\n            \"userAdded\": false,\n            \"type\": \"UnityEngine.Cubemap\",\n            \"defaultInstantiationMode\": 0\n        },\n        {\n            \"userAdded\": false,\n            \"type\": \"UnityEngine.GameObject\",\n            \"defaultInstantiationMode\": 0\n        },\n        {\n            \"userAdded\": false,\n            \"type\": \"UnityEditor.LightingDataAsset\",\n            \"defaultInstantiationMode\": 0\n        },\n        {\n            \"userAdded\": false,\n            \"type\": \"UnityEngine.LightingSettings\",\n            \"defaultInstantiationMode\": 0\n        },\n        {\n            \"userAdded\": false,\n            \"type\": \"UnityEngine.Material\",\n            \"defaultInstantiationMode\": 0\n        },\n        {\n            \"userAdded\": false,\n            \"type\": \"UnityEditor.MonoScript\",\n            \"defaultInstantiationMode\": 1\n        },\n        {\n            \"userAdded\": false,\n            \"type\": \"UnityEngine.PhysicMaterial\",\n            \"defaultInstantiationMode\": 0\n        },\n        {\n            \"userAdded\": false,\n            \"type\": \"UnityEngine.PhysicsMaterial2D\",\n            \"defaultInstantiationMode\": 0\n        },\n        {\n            \"userAdded\": false,\n            \"type\": \"UnityEngine.Rendering.PostProcessing.PostProcessProfile\",\n            \"defaultInstantiationMode\": 0\n        },\n        {\n            \"userAdded\": false,\n            \"type\": \"UnityEngine.Rendering.PostProcessing.PostProcessResources\",\n            \"defaultInstantiationMode\": 0\n        },\n        {\n            \"userAdded\": false,\n            \"type\": \"UnityEngine.Rendering.VolumeProfile\",\n            \"defaultInstantiationMode\": 0\n        },\n        {\n            \"userAdded\": false,\n            \"type\": \"UnityEditor.SceneAsset\",\n            \"defaultInstantiationMode\": 1\n        },\n        {\n            \"userAdded\": false,\n            \"type\": \"UnityEngine.Shader\",\n            \"defaultInstantiationMode\": 1\n        },\n        {\n            \"userAdded\": false,\n            \"type\": \"UnityEngine.ShaderVariantCollection\",\n            \"defaultInstantiationMode\": 1\n        },\n        {\n            \"userAdded\": false,\n            \"type\": \"UnityEngine.Texture\",\n            \"defaultInstantiationMode\": 0\n        },\n        {\n            \"userAdded\": false,\n            \"type\": \"UnityEngine.Texture2D\",\n            \"defaultInstantiationMode\": 0\n        },\n        {\n            \"userAdded\": false,\n            \"type\": \"UnityEngine.Timeline.TimelineAsset\",\n            \"defaultInstantiationMode\": 0\n        }\n    ],\n    \"defaultDependencyTypeInfo\": {\n        \"userAdded\": false,\n        \"type\": \"<default_scene_template_dependencies>\",\n        \"defaultInstantiationMode\": 1\n    },\n    \"newSceneOverride\": 0\n}"
  },
  {
    "path": "ProjectSettings/TagManager.asset",
    "content": "%YAML 1.1\n%TAG !u! tag:unity3d.com,2011:\n--- !u!78 &1\nTagManager:\n  serializedVersion: 2\n  tags: []\n  layers:\n  - Default\n  - TransparentFX\n  - Ignore Raycast\n  - \n  - Water\n  - UI\n  - \n  - \n  - \n  - \n  - \n  - \n  - \n  - \n  - \n  - \n  - \n  - \n  - \n  - \n  - \n  - \n  - \n  - \n  - \n  - \n  - \n  - \n  - \n  - \n  - \n  - \n  m_SortingLayers:\n  - name: Default\n    uniqueID: 0\n    locked: 0\n"
  },
  {
    "path": "ProjectSettings/TimeManager.asset",
    "content": "%YAML 1.1\n%TAG !u! tag:unity3d.com,2011:\n--- !u!5 &1\nTimeManager:\n  m_ObjectHideFlags: 0\n  Fixed Timestep: 0.02\n  Maximum Allowed Timestep: 0.1\n  m_TimeScale: 1\n  Maximum Particle Timestep: 0.03\n"
  },
  {
    "path": "ProjectSettings/UnityConnectSettings.asset",
    "content": "%YAML 1.1\n%TAG !u! tag:unity3d.com,2011:\n--- !u!310 &1\nUnityConnectSettings:\n  m_ObjectHideFlags: 0\n  serializedVersion: 1\n  m_Enabled: 0\n  m_TestMode: 0\n  m_EventOldUrl: https://api.uca.cloud.unity3d.com/v1/events\n  m_EventUrl: https://cdp.cloud.unity3d.com/v1/events\n  m_ConfigUrl: https://config.uca.cloud.unity3d.com\n  m_TestInitMode: 0\n  CrashReportingSettings:\n    m_EventUrl: https://perf-events.cloud.unity3d.com\n    m_Enabled: 0\n    m_LogBufferSize: 10\n    m_CaptureEditorExceptions: 1\n  UnityPurchasingSettings:\n    m_Enabled: 0\n    m_TestMode: 0\n  UnityAnalyticsSettings:\n    m_Enabled: 0\n    m_TestMode: 0\n    m_InitializeOnStartup: 1\n  UnityAdsSettings:\n    m_Enabled: 0\n    m_InitializeOnStartup: 1\n    m_TestMode: 0\n    m_IosGameId: \n    m_AndroidGameId: \n    m_GameIds: {}\n    m_GameId: \n  PerformanceReportingSettings:\n    m_Enabled: 0\n"
  },
  {
    "path": "ProjectSettings/VFXManager.asset",
    "content": "%YAML 1.1\n%TAG !u! tag:unity3d.com,2011:\n--- !u!937362698 &1\nVFXManager:\n  m_ObjectHideFlags: 0\n  m_IndirectShader: {fileID: 0}\n  m_CopyBufferShader: {fileID: 0}\n  m_SortShader: {fileID: 0}\n  m_RenderPipeSettingsPath: \n  m_FixedTimeStep: 0.016666668\n  m_MaxDeltaTime: 0.05\n"
  },
  {
    "path": "ProjectSettings/VersionControlSettings.asset",
    "content": "%YAML 1.1\n%TAG !u! tag:unity3d.com,2011:\n--- !u!890905787 &1\nVersionControlSettings:\n  m_ObjectHideFlags: 0\n  m_Mode: Visible Meta Files\n  m_CollabEditorSettings:\n    inProgressEnabled: 1\n"
  },
  {
    "path": "ProjectSettings/boot.config",
    "content": ""
  },
  {
    "path": "README.md",
    "content": "# kcp-unity\n\nKCP是一种基于UDP的上层协议，项目地址：[KCP – A Fast and Reliable ARQ Protocol](https://github.com/skywind3000/kcp)\n\nkcp就是在udp上再做了一层封装，来实现TCP的效果并且弥补TCP的一些不足。使用kcp的方式就是和服务器连接一个udp连接，然后再udp的通信过程中，使用kcp库对字节进行处理。然后通过kcp拿到数据包。kcp可以保证你拿到的包，一，不会丢包，二，保证数据包的发送顺序和接收顺序一致。这其实就是tcp的功能。但是kcp的速度比tcp要快的多。\n\n## KCP CSharp\n\nunity接入kcp的话，肯定要c#版本的，但是同样是c#版本kcp，也是有区别的，\n\n### 第一种方式：c#移植版\n\n移植版，就是使用c#语言翻译原版c的kcp库。一般这样实现的库有很多，但是质量参差不齐，有的是完美移植，有的可能会遗留一些问题，比如gc太多等\n\n目前用的还比较多的有：\n\n- [kcp-csharp](https://github.com/limpo1989/kcp-csharp): kcp的 csharp移植，同时包含一份回话管理，可以连接上面kcp-go的服务端，且和kcp-go的语法相似。\n- [kcp-csharp](https://github.com/KumoKyaku/KCP): 新版本 Kcp的 csharp移植。线程安全，运行时无alloc，对gc无压力。\n\n### 第二种方式：动态库版\n\n动态库版，就是把原版c的kcp库，进行动态库编译，编译为ios，android，pc，linux等平台的库，然后我们使用C#去封装一个udp，然后调用kcp的动态链接库。\n\n这种方式，好处是可以保留原版库的所有特性，不用考虑优化语法的问题了，而且性能比较好。但是坏处就是编译动态库较麻烦，需要针对所有平台...可想而知\n\n[unity3d构建kcp跨平台动态链接库](https://github.com/smilehao/kcp_bulild)\n\n因此本仓库就是基于以上动态链接库实现的，基于[ET框架](https://github.com/egametang/ET)修改而成。\nET框架中网络有自己的封装，包括了server和client。理解起来稍微有点麻烦，所以我把部分逻辑简化，封装了 一个纯client的。其中tcp和kcp均可直接和服务器(go/java)对接\n\n详情链接：https://passion.blog.csdn.net/article/details/112722271\n\n# 友情链接\n\n[skywind3000/kcp](https://github.com/skywind3000/kcp)\n\n[xtaci/kcp-go](https://github.com/xtaci/kcp-go)\n\n注意事项：\n\ngo版本的kcp的demo：echo.go中默认使用了加密模式\n\nkcp.ListenWithOptions(\"127.0.0.1:12345\", block, 10, 3)\n\n\n这种情况下回合C#端的kcp对接不上。造成死活连接不上的问题\n\n解决方法：\n\n1.改成kcp.ListenWithOptions(\"127.0.0.1:3656\", nil, 0, 0)\n\n2.改成kcp.Listen(\"127.0.0.1:3656\")"
  }
]