update
This commit is contained in:
8
Assets/TapSDK/Core/Standalone/Editor.meta
Normal file
8
Assets/TapSDK/Core/Standalone/Editor.meta
Normal file
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 10883ac51961f49e3aeb26c5723c841e
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,20 @@
|
||||
using System;
|
||||
using UnityEditor.Build.Reporting;
|
||||
using TapSDK.Core.Editor;
|
||||
|
||||
namespace TapSDK.Core.Editor {
|
||||
public class TapCoreStandaloneProcessBuild : SDKLinkProcessBuild {
|
||||
public override int callbackOrder => 0;
|
||||
|
||||
public override string LinkPath => "TapSDK/Core/link.xml";
|
||||
|
||||
public override LinkedAssembly[] LinkedAssemblies => new LinkedAssembly[] {
|
||||
new LinkedAssembly { Fullname = "TapSDK.Core.Runtime" },
|
||||
new LinkedAssembly { Fullname = "TapSDK.Core.Standalone.Runtime" }
|
||||
};
|
||||
|
||||
public override Func<BuildReport, bool> IsTargetPlatform => (report) => {
|
||||
return BuildTargetUtils.IsSupportStandalone(report.summary.platform);
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: c9b26133fd2ab4d9aafdfa492b519e84
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,17 @@
|
||||
{
|
||||
"name": "TapSDK.Core.Standalone.Editor",
|
||||
"references": [
|
||||
"GUID:56f3da7a178484843974054bafe77e73"
|
||||
],
|
||||
"includePlatforms": [
|
||||
"Editor"
|
||||
],
|
||||
"excludePlatforms": [],
|
||||
"allowUnsafeCode": false,
|
||||
"overrideReferences": false,
|
||||
"precompiledReferences": [],
|
||||
"autoReferenced": true,
|
||||
"defineConstraints": [],
|
||||
"versionDefines": [],
|
||||
"noEngineReferences": false
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 223f7c51738354e2cb8b4cb571f7caab
|
||||
AssemblyDefinitionImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
8
Assets/TapSDK/Core/Standalone/Plugins.meta
Normal file
8
Assets/TapSDK/Core/Standalone/Plugins.meta
Normal file
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 39e555e59069c4dc1995634eb234a4bd
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
8
Assets/TapSDK/Core/Standalone/Plugins/macOS.meta
Normal file
8
Assets/TapSDK/Core/Standalone/Plugins/macOS.meta
Normal file
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 28bbe390b7a2d4e1d9e6de4d5865fc52
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
Binary file not shown.
@@ -0,0 +1,80 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 49c295e8cfb414b76aa3bdc07368e922
|
||||
PluginImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
iconMap: {}
|
||||
executionOrder: {}
|
||||
defineConstraints: []
|
||||
isPreloaded: 0
|
||||
isOverridable: 0
|
||||
isExplicitlyReferenced: 0
|
||||
validateReferences: 1
|
||||
platformData:
|
||||
- first:
|
||||
: Any
|
||||
second:
|
||||
enabled: 0
|
||||
settings:
|
||||
Exclude Android: 1
|
||||
Exclude Editor: 0
|
||||
Exclude Linux64: 1
|
||||
Exclude OSXUniversal: 0
|
||||
Exclude Win: 1
|
||||
Exclude Win64: 1
|
||||
Exclude iOS: 1
|
||||
- first:
|
||||
Android: Android
|
||||
second:
|
||||
enabled: 0
|
||||
settings:
|
||||
CPU: ARMv7
|
||||
- first:
|
||||
Any:
|
||||
second:
|
||||
enabled: 0
|
||||
settings: {}
|
||||
- first:
|
||||
Editor: Editor
|
||||
second:
|
||||
enabled: 1
|
||||
settings:
|
||||
CPU: AnyCPU
|
||||
DefaultValueInitialized: true
|
||||
OS: OSX
|
||||
- first:
|
||||
Standalone: Linux64
|
||||
second:
|
||||
enabled: 0
|
||||
settings:
|
||||
CPU: AnyCPU
|
||||
- first:
|
||||
Standalone: OSXUniversal
|
||||
second:
|
||||
enabled: 1
|
||||
settings:
|
||||
CPU: AnyCPU
|
||||
- first:
|
||||
Standalone: Win
|
||||
second:
|
||||
enabled: 0
|
||||
settings:
|
||||
CPU: x86
|
||||
- first:
|
||||
Standalone: Win64
|
||||
second:
|
||||
enabled: 0
|
||||
settings:
|
||||
CPU: x86_64
|
||||
- first:
|
||||
iPhone: iOS
|
||||
second:
|
||||
enabled: 0
|
||||
settings:
|
||||
AddToEmbeddedBinaries: false
|
||||
CPU: AnyCPU
|
||||
CompileFlags:
|
||||
FrameworkDependencies:
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
8
Assets/TapSDK/Core/Standalone/Plugins/x86.meta
Normal file
8
Assets/TapSDK/Core/Standalone/Plugins/x86.meta
Normal file
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 9b86a68828e7ee54f95d29d7d9a077fd
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
BIN
Assets/TapSDK/Core/Standalone/Plugins/x86/tapsdkcore.dll
Normal file
BIN
Assets/TapSDK/Core/Standalone/Plugins/x86/tapsdkcore.dll
Normal file
Binary file not shown.
@@ -0,0 +1,63 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 5bfe079fe35ffa74f814bfa7df1d75f8
|
||||
PluginImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
iconMap: {}
|
||||
executionOrder: {}
|
||||
defineConstraints: []
|
||||
isPreloaded: 0
|
||||
isOverridable: 0
|
||||
isExplicitlyReferenced: 0
|
||||
validateReferences: 1
|
||||
platformData:
|
||||
- first:
|
||||
: Any
|
||||
second:
|
||||
enabled: 0
|
||||
settings:
|
||||
Exclude Editor: 0
|
||||
Exclude Linux64: 0
|
||||
Exclude OSXUniversal: 0
|
||||
Exclude Win: 0
|
||||
Exclude Win64: 0
|
||||
- first:
|
||||
Any:
|
||||
second:
|
||||
enabled: 0
|
||||
settings: {}
|
||||
- first:
|
||||
Editor: Editor
|
||||
second:
|
||||
enabled: 1
|
||||
settings:
|
||||
CPU: x86
|
||||
DefaultValueInitialized: true
|
||||
OS: Windows
|
||||
- first:
|
||||
Standalone: Linux64
|
||||
second:
|
||||
enabled: 1
|
||||
settings:
|
||||
CPU: None
|
||||
- first:
|
||||
Standalone: OSXUniversal
|
||||
second:
|
||||
enabled: 1
|
||||
settings:
|
||||
CPU: x86
|
||||
- first:
|
||||
Standalone: Win
|
||||
second:
|
||||
enabled: 1
|
||||
settings:
|
||||
CPU: AnyCPU
|
||||
- first:
|
||||
Standalone: Win64
|
||||
second:
|
||||
enabled: 1
|
||||
settings:
|
||||
CPU: None
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
8
Assets/TapSDK/Core/Standalone/Plugins/x86_64.meta
Normal file
8
Assets/TapSDK/Core/Standalone/Plugins/x86_64.meta
Normal file
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: a7af5496cfd95834aa62ec556b3dfd83
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
BIN
Assets/TapSDK/Core/Standalone/Plugins/x86_64/tapsdkcore.dll
Normal file
BIN
Assets/TapSDK/Core/Standalone/Plugins/x86_64/tapsdkcore.dll
Normal file
Binary file not shown.
@@ -0,0 +1,63 @@
|
||||
fileFormatVersion: 2
|
||||
guid: f426835f028130348aec138e80a35b77
|
||||
PluginImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
iconMap: {}
|
||||
executionOrder: {}
|
||||
defineConstraints: []
|
||||
isPreloaded: 0
|
||||
isOverridable: 0
|
||||
isExplicitlyReferenced: 0
|
||||
validateReferences: 1
|
||||
platformData:
|
||||
- first:
|
||||
: Any
|
||||
second:
|
||||
enabled: 0
|
||||
settings:
|
||||
Exclude Editor: 0
|
||||
Exclude Linux64: 1
|
||||
Exclude OSXUniversal: 1
|
||||
Exclude Win: 0
|
||||
Exclude Win64: 0
|
||||
- first:
|
||||
Any:
|
||||
second:
|
||||
enabled: 0
|
||||
settings: {}
|
||||
- first:
|
||||
Editor: Editor
|
||||
second:
|
||||
enabled: 1
|
||||
settings:
|
||||
CPU: x86_64
|
||||
DefaultValueInitialized: true
|
||||
OS: Windows
|
||||
- first:
|
||||
Standalone: Linux64
|
||||
second:
|
||||
enabled: 0
|
||||
settings:
|
||||
CPU: None
|
||||
- first:
|
||||
Standalone: OSXUniversal
|
||||
second:
|
||||
enabled: 0
|
||||
settings:
|
||||
CPU: None
|
||||
- first:
|
||||
Standalone: Win
|
||||
second:
|
||||
enabled: 1
|
||||
settings:
|
||||
CPU: None
|
||||
- first:
|
||||
Standalone: Win64
|
||||
second:
|
||||
enabled: 1
|
||||
settings:
|
||||
CPU: x86_64
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
BIN
Assets/TapSDK/Core/Standalone/Plugins/x86_64/taptap_api.dll
Normal file
BIN
Assets/TapSDK/Core/Standalone/Plugins/x86_64/taptap_api.dll
Normal file
Binary file not shown.
@@ -0,0 +1,80 @@
|
||||
fileFormatVersion: 2
|
||||
guid: b6f09ef7ffe9442c3a59f378a3a15005
|
||||
PluginImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
iconMap: {}
|
||||
executionOrder: {}
|
||||
defineConstraints: []
|
||||
isPreloaded: 0
|
||||
isOverridable: 0
|
||||
isExplicitlyReferenced: 0
|
||||
validateReferences: 1
|
||||
platformData:
|
||||
- first:
|
||||
: Any
|
||||
second:
|
||||
enabled: 0
|
||||
settings:
|
||||
Exclude Android: 1
|
||||
Exclude Editor: 0
|
||||
Exclude Linux64: 1
|
||||
Exclude OSXUniversal: 1
|
||||
Exclude Win: 0
|
||||
Exclude Win64: 0
|
||||
Exclude iOS: 1
|
||||
- first:
|
||||
Android: Android
|
||||
second:
|
||||
enabled: 0
|
||||
settings:
|
||||
CPU: ARMv7
|
||||
- first:
|
||||
Any:
|
||||
second:
|
||||
enabled: 0
|
||||
settings: {}
|
||||
- first:
|
||||
Editor: Editor
|
||||
second:
|
||||
enabled: 1
|
||||
settings:
|
||||
CPU: x86_64
|
||||
DefaultValueInitialized: true
|
||||
OS: Windows
|
||||
- first:
|
||||
Standalone: Linux64
|
||||
second:
|
||||
enabled: 0
|
||||
settings:
|
||||
CPU: None
|
||||
- first:
|
||||
Standalone: OSXUniversal
|
||||
second:
|
||||
enabled: 0
|
||||
settings:
|
||||
CPU: None
|
||||
- first:
|
||||
Standalone: Win
|
||||
second:
|
||||
enabled: 1
|
||||
settings:
|
||||
CPU: None
|
||||
- first:
|
||||
Standalone: Win64
|
||||
second:
|
||||
enabled: 1
|
||||
settings:
|
||||
CPU: x86_64
|
||||
- first:
|
||||
iPhone: iOS
|
||||
second:
|
||||
enabled: 0
|
||||
settings:
|
||||
AddToEmbeddedBinaries: false
|
||||
CPU: AnyCPU
|
||||
CompileFlags:
|
||||
FrameworkDependencies:
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
8
Assets/TapSDK/Core/Standalone/Resources.meta
Normal file
8
Assets/TapSDK/Core/Standalone/Resources.meta
Normal file
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: ea0030b0bf2fb40f2900ce45ef0377c6
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
8
Assets/TapSDK/Core/Standalone/Resources/Prefabs.meta
Normal file
8
Assets/TapSDK/Core/Standalone/Resources/Prefabs.meta
Normal file
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 68d6a0d056b944c799867350b4d1f258
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: dee90da2b109147dc8d87fd85190d5c3
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,983 @@
|
||||
%YAML 1.1
|
||||
%TAG !u! tag:unity3d.com,2011:
|
||||
--- !u!1 &2456037518270759131
|
||||
GameObject:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
serializedVersion: 6
|
||||
m_Component:
|
||||
- component: {fileID: 2073536493588250668}
|
||||
- component: {fileID: 9183027028368188416}
|
||||
- component: {fileID: 2102840095578363909}
|
||||
- component: {fileID: 1025808230884409477}
|
||||
m_Layer: 5
|
||||
m_Name: InstallTipBtn
|
||||
m_TagString: Untagged
|
||||
m_Icon: {fileID: 0}
|
||||
m_NavMeshLayer: 0
|
||||
m_StaticEditorFlags: 0
|
||||
m_IsActive: 1
|
||||
--- !u!224 &2073536493588250668
|
||||
RectTransform:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 2456037518270759131}
|
||||
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
|
||||
m_LocalPosition: {x: 0, y: 0, z: 0}
|
||||
m_LocalScale: {x: 1, y: 1, z: 1}
|
||||
m_Children:
|
||||
- {fileID: 8581047488931052277}
|
||||
m_Father: {fileID: 6361964099233889058}
|
||||
m_RootOrder: 6
|
||||
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
|
||||
m_AnchorMin: {x: 0.5, y: 0}
|
||||
m_AnchorMax: {x: 0.5, y: 0}
|
||||
m_AnchoredPosition: {x: 0, y: 32}
|
||||
m_SizeDelta: {x: 260, y: 22}
|
||||
m_Pivot: {x: 0.5, y: 0.5}
|
||||
--- !u!222 &9183027028368188416
|
||||
CanvasRenderer:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 2456037518270759131}
|
||||
m_CullTransparentMesh: 0
|
||||
--- !u!114 &2102840095578363909
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 2456037518270759131}
|
||||
m_Enabled: 1
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: 11500000, guid: fe87c0e1cc204ed48ad3b37840f39efc, type: 3}
|
||||
m_Name:
|
||||
m_EditorClassIdentifier:
|
||||
m_Material: {fileID: 0}
|
||||
m_Color: {r: 1, g: 1, b: 1, a: 1}
|
||||
m_RaycastTarget: 1
|
||||
m_Maskable: 1
|
||||
m_OnCullStateChanged:
|
||||
m_PersistentCalls:
|
||||
m_Calls: []
|
||||
m_Sprite: {fileID: 0}
|
||||
m_Type: 1
|
||||
m_PreserveAspect: 0
|
||||
m_FillCenter: 1
|
||||
m_FillMethod: 4
|
||||
m_FillAmount: 1
|
||||
m_FillClockwise: 1
|
||||
m_FillOrigin: 0
|
||||
m_UseSpriteMesh: 0
|
||||
m_PixelsPerUnitMultiplier: 1
|
||||
--- !u!114 &1025808230884409477
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 2456037518270759131}
|
||||
m_Enabled: 1
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: 11500000, guid: 4e29b1a8efbd4b44bb3f3716e73f07ff, type: 3}
|
||||
m_Name:
|
||||
m_EditorClassIdentifier:
|
||||
m_Navigation:
|
||||
m_Mode: 3
|
||||
m_SelectOnUp: {fileID: 0}
|
||||
m_SelectOnDown: {fileID: 0}
|
||||
m_SelectOnLeft: {fileID: 0}
|
||||
m_SelectOnRight: {fileID: 0}
|
||||
m_Transition: 1
|
||||
m_Colors:
|
||||
m_NormalColor: {r: 1, g: 1, b: 1, a: 1}
|
||||
m_HighlightedColor: {r: 0.9607843, g: 0.9607843, b: 0.9607843, a: 1}
|
||||
m_PressedColor: {r: 0.78431374, g: 0.78431374, b: 0.78431374, a: 1}
|
||||
m_SelectedColor: {r: 0.9607843, g: 0.9607843, b: 0.9607843, a: 1}
|
||||
m_DisabledColor: {r: 0.78431374, g: 0.78431374, b: 0.78431374, a: 0.5019608}
|
||||
m_ColorMultiplier: 1
|
||||
m_FadeDuration: 0.1
|
||||
m_SpriteState:
|
||||
m_HighlightedSprite: {fileID: 0}
|
||||
m_PressedSprite: {fileID: 0}
|
||||
m_SelectedSprite: {fileID: 0}
|
||||
m_DisabledSprite: {fileID: 0}
|
||||
m_AnimationTriggers:
|
||||
m_NormalTrigger: Normal
|
||||
m_HighlightedTrigger: Highlighted
|
||||
m_PressedTrigger: Pressed
|
||||
m_SelectedTrigger: Selected
|
||||
m_DisabledTrigger: Disabled
|
||||
m_Interactable: 1
|
||||
m_TargetGraphic: {fileID: 2102840095578363909}
|
||||
m_OnClick:
|
||||
m_PersistentCalls:
|
||||
m_Calls: []
|
||||
--- !u!1 &2496127260991893935
|
||||
GameObject:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
serializedVersion: 6
|
||||
m_Component:
|
||||
- component: {fileID: 832216409472935358}
|
||||
- component: {fileID: 4931472104156709032}
|
||||
- component: {fileID: 4783731767825643541}
|
||||
m_Layer: 5
|
||||
m_Name: ErrotTip
|
||||
m_TagString: Untagged
|
||||
m_Icon: {fileID: 0}
|
||||
m_NavMeshLayer: 0
|
||||
m_StaticEditorFlags: 0
|
||||
m_IsActive: 1
|
||||
--- !u!224 &832216409472935358
|
||||
RectTransform:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 2496127260991893935}
|
||||
m_LocalRotation: {x: -0, y: -0, z: -0, w: 1}
|
||||
m_LocalPosition: {x: 0, y: 0, z: 0}
|
||||
m_LocalScale: {x: 1, y: 1, z: 1}
|
||||
m_Children: []
|
||||
m_Father: {fileID: 6361964099233889058}
|
||||
m_RootOrder: 4
|
||||
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
|
||||
m_AnchorMin: {x: 0.5, y: 0.5}
|
||||
m_AnchorMax: {x: 0.5, y: 0.5}
|
||||
m_AnchoredPosition: {x: 0, y: 0}
|
||||
m_SizeDelta: {x: 256, y: 56}
|
||||
m_Pivot: {x: 0.5, y: 0.5}
|
||||
--- !u!222 &4931472104156709032
|
||||
CanvasRenderer:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 2496127260991893935}
|
||||
m_CullTransparentMesh: 0
|
||||
--- !u!114 &4783731767825643541
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 2496127260991893935}
|
||||
m_Enabled: 1
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: 11500000, guid: 5f7201a12d95ffc409449d95f23cf332, type: 3}
|
||||
m_Name:
|
||||
m_EditorClassIdentifier:
|
||||
m_Material: {fileID: 0}
|
||||
m_Color: {r: 0.19607843, g: 0.19607843, b: 0.19607843, a: 1}
|
||||
m_RaycastTarget: 1
|
||||
m_Maskable: 1
|
||||
m_OnCullStateChanged:
|
||||
m_PersistentCalls:
|
||||
m_Calls: []
|
||||
m_FontData:
|
||||
m_Font: {fileID: 12800000, guid: c76570f7b9a4942ae84d6491f2669330, type: 3}
|
||||
m_FontSize: 16
|
||||
m_FontStyle: 0
|
||||
m_BestFit: 0
|
||||
m_MinSize: 1
|
||||
m_MaxSize: 40
|
||||
m_Alignment: 1
|
||||
m_AlignByGeometry: 0
|
||||
m_RichText: 1
|
||||
m_HorizontalOverflow: 0
|
||||
m_VerticalOverflow: 1
|
||||
m_LineSpacing: 2
|
||||
m_Text: "\u53D1\u751F\u672A\u77E5\u9519\u8BEF\uFF0C\u8BF7\u4ECE TapTap \u5BA2\u6237\u7AEF\u91CD\u65B0\u542F\u52A8\u6E38\u620F"
|
||||
--- !u!1 &2614750717462274026
|
||||
GameObject:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
serializedVersion: 6
|
||||
m_Component:
|
||||
- component: {fileID: 719222081671675900}
|
||||
- component: {fileID: 6566752454117406737}
|
||||
- component: {fileID: 5456539344680195275}
|
||||
- component: {fileID: 7762865190077089428}
|
||||
m_Layer: 5
|
||||
m_Name: OKButton
|
||||
m_TagString: Untagged
|
||||
m_Icon: {fileID: 0}
|
||||
m_NavMeshLayer: 0
|
||||
m_StaticEditorFlags: 0
|
||||
m_IsActive: 1
|
||||
--- !u!224 &719222081671675900
|
||||
RectTransform:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 2614750717462274026}
|
||||
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
|
||||
m_LocalPosition: {x: 0, y: 0, z: 0}
|
||||
m_LocalScale: {x: 1, y: 1, z: 1}
|
||||
m_Children:
|
||||
- {fileID: 2192333195466092754}
|
||||
m_Father: {fileID: 6361964099233889058}
|
||||
m_RootOrder: 5
|
||||
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
|
||||
m_AnchorMin: {x: 0.5, y: 0}
|
||||
m_AnchorMax: {x: 0.5, y: 0}
|
||||
m_AnchoredPosition: {x: -110, y: 76}
|
||||
m_SizeDelta: {x: 220, y: 36}
|
||||
m_Pivot: {x: 0, y: 0.5}
|
||||
--- !u!222 &6566752454117406737
|
||||
CanvasRenderer:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 2614750717462274026}
|
||||
m_CullTransparentMesh: 1
|
||||
--- !u!114 &5456539344680195275
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 2614750717462274026}
|
||||
m_Enabled: 1
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: 11500000, guid: fe87c0e1cc204ed48ad3b37840f39efc, type: 3}
|
||||
m_Name:
|
||||
m_EditorClassIdentifier:
|
||||
m_Material: {fileID: 0}
|
||||
m_Color: {r: 0, g: 0.8509804, b: 0.77254903, a: 1}
|
||||
m_RaycastTarget: 1
|
||||
m_Maskable: 1
|
||||
m_OnCullStateChanged:
|
||||
m_PersistentCalls:
|
||||
m_Calls: []
|
||||
m_Sprite: {fileID: 21300000, guid: 2386ba664dfbb4993ae59582f6b7a4dc, type: 3}
|
||||
m_Type: 0
|
||||
m_PreserveAspect: 0
|
||||
m_FillCenter: 1
|
||||
m_FillMethod: 4
|
||||
m_FillAmount: 1
|
||||
m_FillClockwise: 1
|
||||
m_FillOrigin: 0
|
||||
m_UseSpriteMesh: 0
|
||||
m_PixelsPerUnitMultiplier: 1
|
||||
--- !u!114 &7762865190077089428
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 2614750717462274026}
|
||||
m_Enabled: 1
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: 11500000, guid: 4e29b1a8efbd4b44bb3f3716e73f07ff, type: 3}
|
||||
m_Name:
|
||||
m_EditorClassIdentifier:
|
||||
m_Navigation:
|
||||
m_Mode: 3
|
||||
m_SelectOnUp: {fileID: 0}
|
||||
m_SelectOnDown: {fileID: 0}
|
||||
m_SelectOnLeft: {fileID: 0}
|
||||
m_SelectOnRight: {fileID: 0}
|
||||
m_Transition: 1
|
||||
m_Colors:
|
||||
m_NormalColor: {r: 1, g: 1, b: 1, a: 1}
|
||||
m_HighlightedColor: {r: 0.9607843, g: 0.9607843, b: 0.9607843, a: 1}
|
||||
m_PressedColor: {r: 0.78431374, g: 0.78431374, b: 0.78431374, a: 1}
|
||||
m_SelectedColor: {r: 0.9607843, g: 0.9607843, b: 0.9607843, a: 1}
|
||||
m_DisabledColor: {r: 0.78431374, g: 0.78431374, b: 0.78431374, a: 0.5019608}
|
||||
m_ColorMultiplier: 1
|
||||
m_FadeDuration: 0.1
|
||||
m_SpriteState:
|
||||
m_HighlightedSprite: {fileID: 0}
|
||||
m_PressedSprite: {fileID: 0}
|
||||
m_SelectedSprite: {fileID: 0}
|
||||
m_DisabledSprite: {fileID: 0}
|
||||
m_AnimationTriggers:
|
||||
m_NormalTrigger: Normal
|
||||
m_HighlightedTrigger: Highlighted
|
||||
m_PressedTrigger: Pressed
|
||||
m_SelectedTrigger: Selected
|
||||
m_DisabledTrigger: Disabled
|
||||
m_Interactable: 1
|
||||
m_TargetGraphic: {fileID: 5456539344680195275}
|
||||
m_OnClick:
|
||||
m_PersistentCalls:
|
||||
m_Calls: []
|
||||
--- !u!1 &3922068344051980923
|
||||
GameObject:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
serializedVersion: 6
|
||||
m_Component:
|
||||
- component: {fileID: 8071375636115987463}
|
||||
- component: {fileID: 6789447465473944001}
|
||||
- component: {fileID: 1901124806566616607}
|
||||
m_Layer: 5
|
||||
m_Name: Blocker
|
||||
m_TagString: Untagged
|
||||
m_Icon: {fileID: 0}
|
||||
m_NavMeshLayer: 0
|
||||
m_StaticEditorFlags: 0
|
||||
m_IsActive: 1
|
||||
--- !u!224 &8071375636115987463
|
||||
RectTransform:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 3922068344051980923}
|
||||
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
|
||||
m_LocalPosition: {x: 0, y: 0, z: 0}
|
||||
m_LocalScale: {x: 1, y: 1, z: 1}
|
||||
m_Children: []
|
||||
m_Father: {fileID: 6361964099233889058}
|
||||
m_RootOrder: 1
|
||||
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
|
||||
m_AnchorMin: {x: 0.5, y: 0.5}
|
||||
m_AnchorMax: {x: 0.5, y: 0.5}
|
||||
m_AnchoredPosition: {x: 0, y: 0}
|
||||
m_SizeDelta: {x: 10000, y: 10000}
|
||||
m_Pivot: {x: 0.5, y: 0.5}
|
||||
--- !u!222 &6789447465473944001
|
||||
CanvasRenderer:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 3922068344051980923}
|
||||
m_CullTransparentMesh: 0
|
||||
--- !u!114 &1901124806566616607
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 3922068344051980923}
|
||||
m_Enabled: 1
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: 11500000, guid: fe87c0e1cc204ed48ad3b37840f39efc, type: 3}
|
||||
m_Name:
|
||||
m_EditorClassIdentifier:
|
||||
m_Material: {fileID: 0}
|
||||
m_Color: {r: 1, g: 1, b: 1, a: 0}
|
||||
m_RaycastTarget: 1
|
||||
m_Maskable: 1
|
||||
m_OnCullStateChanged:
|
||||
m_PersistentCalls:
|
||||
m_Calls: []
|
||||
m_Sprite: {fileID: 0}
|
||||
m_Type: 0
|
||||
m_PreserveAspect: 0
|
||||
m_FillCenter: 1
|
||||
m_FillMethod: 4
|
||||
m_FillAmount: 1
|
||||
m_FillClockwise: 1
|
||||
m_FillOrigin: 0
|
||||
m_UseSpriteMesh: 0
|
||||
m_PixelsPerUnitMultiplier: 1
|
||||
--- !u!1 &4756748923693842436
|
||||
GameObject:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
serializedVersion: 6
|
||||
m_Component:
|
||||
- component: {fileID: 8581047488931052277}
|
||||
- component: {fileID: 5389121682803529379}
|
||||
- component: {fileID: 7315213671180725188}
|
||||
- component: {fileID: 4586140014917180833}
|
||||
m_Layer: 5
|
||||
m_Name: Text
|
||||
m_TagString: Untagged
|
||||
m_Icon: {fileID: 0}
|
||||
m_NavMeshLayer: 0
|
||||
m_StaticEditorFlags: 0
|
||||
m_IsActive: 1
|
||||
--- !u!224 &8581047488931052277
|
||||
RectTransform:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 4756748923693842436}
|
||||
m_LocalRotation: {x: -0, y: -0, z: -0, w: 1}
|
||||
m_LocalPosition: {x: 0, y: 0, z: 0}
|
||||
m_LocalScale: {x: 1, y: 1, z: 1}
|
||||
m_Children: []
|
||||
m_Father: {fileID: 2073536493588250668}
|
||||
m_RootOrder: 0
|
||||
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
|
||||
m_AnchorMin: {x: 0, y: 0}
|
||||
m_AnchorMax: {x: 1, y: 1}
|
||||
m_AnchoredPosition: {x: 0, y: 0}
|
||||
m_SizeDelta: {x: 0, y: 0}
|
||||
m_Pivot: {x: 0.5, y: 0.5}
|
||||
--- !u!222 &5389121682803529379
|
||||
CanvasRenderer:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 4756748923693842436}
|
||||
m_CullTransparentMesh: 0
|
||||
--- !u!114 &7315213671180725188
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 4756748923693842436}
|
||||
m_Enabled: 1
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: 11500000, guid: 5f7201a12d95ffc409449d95f23cf332, type: 3}
|
||||
m_Name:
|
||||
m_EditorClassIdentifier:
|
||||
m_Material: {fileID: 0}
|
||||
m_Color: {r: 0.32156864, g: 0.34117648, b: 0.3647059, a: 1}
|
||||
m_RaycastTarget: 1
|
||||
m_Maskable: 1
|
||||
m_OnCullStateChanged:
|
||||
m_PersistentCalls:
|
||||
m_Calls: []
|
||||
m_FontData:
|
||||
m_Font: {fileID: 10102, guid: 0000000000000000e000000000000000, type: 0}
|
||||
m_FontSize: 14
|
||||
m_FontStyle: 0
|
||||
m_BestFit: 0
|
||||
m_MinSize: 10
|
||||
m_MaxSize: 40
|
||||
m_Alignment: 4
|
||||
m_AlignByGeometry: 0
|
||||
m_RichText: 1
|
||||
m_HorizontalOverflow: 0
|
||||
m_VerticalOverflow: 0
|
||||
m_LineSpacing: 1
|
||||
m_Text: "\u6211\u8FD8\u6CA1\u6709\u5B89\u88C5TapTap\uFF0C\u70B9\u51FB\u524D\u5F80\u4E0B\u8F7D\u5B89\u88C5"
|
||||
--- !u!114 &4586140014917180833
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 4756748923693842436}
|
||||
m_Enabled: 1
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: 11500000, guid: fca03f92079ddaa40a110feb91757557, type: 3}
|
||||
m_Name:
|
||||
m_EditorClassIdentifier:
|
||||
linkText: {fileID: 7315213671180725188}
|
||||
underLineType: 0
|
||||
autoLink: 1
|
||||
--- !u!1 &5185371334061046823
|
||||
GameObject:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
serializedVersion: 6
|
||||
m_Component:
|
||||
- component: {fileID: 4781091973651006734}
|
||||
- component: {fileID: 8079534132318487013}
|
||||
- component: {fileID: 5879139785308660428}
|
||||
- component: {fileID: 7635682526857524625}
|
||||
- component: {fileID: 5731590112282849987}
|
||||
m_Layer: 5
|
||||
m_Name: TapClientConnectTipPanel
|
||||
m_TagString: Untagged
|
||||
m_Icon: {fileID: 0}
|
||||
m_NavMeshLayer: 0
|
||||
m_StaticEditorFlags: 0
|
||||
m_IsActive: 1
|
||||
--- !u!224 &4781091973651006734
|
||||
RectTransform:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 5185371334061046823}
|
||||
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
|
||||
m_LocalPosition: {x: 0, y: 0, z: 0}
|
||||
m_LocalScale: {x: 1, y: 1, z: 1}
|
||||
m_Children:
|
||||
- {fileID: 6361964099233889058}
|
||||
m_Father: {fileID: 0}
|
||||
m_RootOrder: 0
|
||||
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
|
||||
m_AnchorMin: {x: 0.5, y: 0.5}
|
||||
m_AnchorMax: {x: 0.5, y: 0.5}
|
||||
m_AnchoredPosition: {x: 0, y: 0}
|
||||
m_SizeDelta: {x: 600, y: 347}
|
||||
m_Pivot: {x: 0.5, y: 0.5}
|
||||
--- !u!223 &8079534132318487013
|
||||
Canvas:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 5185371334061046823}
|
||||
m_Enabled: 1
|
||||
serializedVersion: 3
|
||||
m_RenderMode: 2
|
||||
m_Camera: {fileID: 0}
|
||||
m_PlaneDistance: 100
|
||||
m_PixelPerfect: 0
|
||||
m_ReceivesEvents: 1
|
||||
m_OverrideSorting: 0
|
||||
m_OverridePixelPerfect: 0
|
||||
m_SortingBucketNormalizedSize: 0
|
||||
m_AdditionalShaderChannelsFlag: 0
|
||||
m_SortingLayerID: 0
|
||||
m_SortingOrder: 0
|
||||
m_TargetDisplay: 0
|
||||
--- !u!225 &5879139785308660428
|
||||
CanvasGroup:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 5185371334061046823}
|
||||
m_Enabled: 1
|
||||
m_Alpha: 1
|
||||
m_Interactable: 1
|
||||
m_BlocksRaycasts: 1
|
||||
m_IgnoreParentGroups: 0
|
||||
--- !u!114 &7635682526857524625
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 5185371334061046823}
|
||||
m_Enabled: 1
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: 11500000, guid: dc42784cf147c0c48a680349fa168899, type: 3}
|
||||
m_Name:
|
||||
m_EditorClassIdentifier:
|
||||
m_IgnoreReversedGraphics: 1
|
||||
m_BlockingObjects: 0
|
||||
m_BlockingMask:
|
||||
serializedVersion: 2
|
||||
m_Bits: 4294967295
|
||||
--- !u!114 &5731590112282849987
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 5185371334061046823}
|
||||
m_Enabled: 1
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: 11500000, guid: 88db6c5941bac46ba91fe65775ad712f, type: 3}
|
||||
m_Name:
|
||||
m_EditorClassIdentifier:
|
||||
canvas: {fileID: 0}
|
||||
canvasGroup: {fileID: 0}
|
||||
panelConfig:
|
||||
animationType: 0
|
||||
toppedOrder: 0
|
||||
installTipButton: {fileID: 1025808230884409477}
|
||||
okButton: {fileID: 7762865190077089428}
|
||||
tipText: {fileID: 4783731767825643541}
|
||||
--- !u!1 &5357233859329253123
|
||||
GameObject:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
serializedVersion: 6
|
||||
m_Component:
|
||||
- component: {fileID: 2192333195466092754}
|
||||
- component: {fileID: 1073151883342619883}
|
||||
- component: {fileID: 5179658266808317134}
|
||||
m_Layer: 5
|
||||
m_Name: Text
|
||||
m_TagString: Untagged
|
||||
m_Icon: {fileID: 0}
|
||||
m_NavMeshLayer: 0
|
||||
m_StaticEditorFlags: 0
|
||||
m_IsActive: 1
|
||||
--- !u!224 &2192333195466092754
|
||||
RectTransform:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 5357233859329253123}
|
||||
m_LocalRotation: {x: -0, y: -0, z: -0, w: 1}
|
||||
m_LocalPosition: {x: 0, y: 0, z: 0}
|
||||
m_LocalScale: {x: 1, y: 1, z: 1}
|
||||
m_Children: []
|
||||
m_Father: {fileID: 719222081671675900}
|
||||
m_RootOrder: 0
|
||||
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
|
||||
m_AnchorMin: {x: 0, y: 0}
|
||||
m_AnchorMax: {x: 1, y: 1}
|
||||
m_AnchoredPosition: {x: 0, y: 0}
|
||||
m_SizeDelta: {x: 0, y: 0}
|
||||
m_Pivot: {x: 0.5, y: 0.5}
|
||||
--- !u!222 &1073151883342619883
|
||||
CanvasRenderer:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 5357233859329253123}
|
||||
m_CullTransparentMesh: 1
|
||||
--- !u!114 &5179658266808317134
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 5357233859329253123}
|
||||
m_Enabled: 1
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: 11500000, guid: 5f7201a12d95ffc409449d95f23cf332, type: 3}
|
||||
m_Name:
|
||||
m_EditorClassIdentifier:
|
||||
m_Material: {fileID: 0}
|
||||
m_Color: {r: 1, g: 1, b: 1, a: 1}
|
||||
m_RaycastTarget: 1
|
||||
m_Maskable: 1
|
||||
m_OnCullStateChanged:
|
||||
m_PersistentCalls:
|
||||
m_Calls: []
|
||||
m_FontData:
|
||||
m_Font: {fileID: 12800000, guid: 5b92beb2e4ac04c1681719225dc6d5fc, type: 3}
|
||||
m_FontSize: 15
|
||||
m_FontStyle: 1
|
||||
m_BestFit: 0
|
||||
m_MinSize: 1
|
||||
m_MaxSize: 40
|
||||
m_Alignment: 4
|
||||
m_AlignByGeometry: 0
|
||||
m_RichText: 1
|
||||
m_HorizontalOverflow: 0
|
||||
m_VerticalOverflow: 0
|
||||
m_LineSpacing: 1
|
||||
m_Text: "\u6211\u77E5\u9053\u4E86"
|
||||
--- !u!1 &5407427821064895557
|
||||
GameObject:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
serializedVersion: 6
|
||||
m_Component:
|
||||
- component: {fileID: 5330526820845008986}
|
||||
- component: {fileID: 2695220490836416828}
|
||||
- component: {fileID: 3204662200658606327}
|
||||
m_Layer: 5
|
||||
m_Name: Image
|
||||
m_TagString: Untagged
|
||||
m_Icon: {fileID: 0}
|
||||
m_NavMeshLayer: 0
|
||||
m_StaticEditorFlags: 0
|
||||
m_IsActive: 1
|
||||
--- !u!224 &5330526820845008986
|
||||
RectTransform:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 5407427821064895557}
|
||||
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
|
||||
m_LocalPosition: {x: 0, y: 0, z: 0}
|
||||
m_LocalScale: {x: 1, y: 1, z: 1}
|
||||
m_Children: []
|
||||
m_Father: {fileID: 6361964099233889058}
|
||||
m_RootOrder: 3
|
||||
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
|
||||
m_AnchorMin: {x: 0.5, y: 1}
|
||||
m_AnchorMax: {x: 0.5, y: 1}
|
||||
m_AnchoredPosition: {x: 0, y: -88}
|
||||
m_SizeDelta: {x: 50, y: 50}
|
||||
m_Pivot: {x: 0.5, y: 0.5}
|
||||
--- !u!222 &2695220490836416828
|
||||
CanvasRenderer:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 5407427821064895557}
|
||||
m_CullTransparentMesh: 0
|
||||
--- !u!114 &3204662200658606327
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 5407427821064895557}
|
||||
m_Enabled: 1
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: 11500000, guid: fe87c0e1cc204ed48ad3b37840f39efc, type: 3}
|
||||
m_Name:
|
||||
m_EditorClassIdentifier:
|
||||
m_Material: {fileID: 0}
|
||||
m_Color: {r: 1, g: 1, b: 1, a: 1}
|
||||
m_RaycastTarget: 1
|
||||
m_Maskable: 1
|
||||
m_OnCullStateChanged:
|
||||
m_PersistentCalls:
|
||||
m_Calls: []
|
||||
m_Sprite: {fileID: 21300000, guid: caf855ae07df04c859a1c5c64ea0802d, type: 3}
|
||||
m_Type: 0
|
||||
m_PreserveAspect: 0
|
||||
m_FillCenter: 1
|
||||
m_FillMethod: 4
|
||||
m_FillAmount: 1
|
||||
m_FillClockwise: 1
|
||||
m_FillOrigin: 0
|
||||
m_UseSpriteMesh: 0
|
||||
m_PixelsPerUnitMultiplier: 1
|
||||
--- !u!1 &7557005277836148278
|
||||
GameObject:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
serializedVersion: 6
|
||||
m_Component:
|
||||
- component: {fileID: 6361964099233889058}
|
||||
- component: {fileID: 3987254139649263377}
|
||||
m_Layer: 5
|
||||
m_Name: Root
|
||||
m_TagString: Untagged
|
||||
m_Icon: {fileID: 0}
|
||||
m_NavMeshLayer: 0
|
||||
m_StaticEditorFlags: 0
|
||||
m_IsActive: 1
|
||||
--- !u!224 &6361964099233889058
|
||||
RectTransform:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 7557005277836148278}
|
||||
m_LocalRotation: {x: -0, y: -0, z: -0, w: 1}
|
||||
m_LocalPosition: {x: 0, y: 0, z: 0}
|
||||
m_LocalScale: {x: 1, y: 1, z: 1}
|
||||
m_Children:
|
||||
- {fileID: 4506720584286079884}
|
||||
- {fileID: 8071375636115987463}
|
||||
- {fileID: 5503233503525070161}
|
||||
- {fileID: 5330526820845008986}
|
||||
- {fileID: 832216409472935358}
|
||||
- {fileID: 719222081671675900}
|
||||
- {fileID: 2073536493588250668}
|
||||
m_Father: {fileID: 4781091973651006734}
|
||||
m_RootOrder: 0
|
||||
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
|
||||
m_AnchorMin: {x: 0, y: 0}
|
||||
m_AnchorMax: {x: 1, y: 1}
|
||||
m_AnchoredPosition: {x: 0, y: 0}
|
||||
m_SizeDelta: {x: 0, y: 0}
|
||||
m_Pivot: {x: 0.5, y: 0.5}
|
||||
--- !u!222 &3987254139649263377
|
||||
CanvasRenderer:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 7557005277836148278}
|
||||
m_CullTransparentMesh: 1
|
||||
--- !u!1 &8135504764682151928
|
||||
GameObject:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
serializedVersion: 6
|
||||
m_Component:
|
||||
- component: {fileID: 5503233503525070161}
|
||||
- component: {fileID: 432106219917414001}
|
||||
- component: {fileID: 8669373154614060998}
|
||||
m_Layer: 5
|
||||
m_Name: TitleText
|
||||
m_TagString: Untagged
|
||||
m_Icon: {fileID: 0}
|
||||
m_NavMeshLayer: 0
|
||||
m_StaticEditorFlags: 0
|
||||
m_IsActive: 1
|
||||
--- !u!224 &5503233503525070161
|
||||
RectTransform:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 8135504764682151928}
|
||||
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
|
||||
m_LocalPosition: {x: 0, y: 0, z: 0}
|
||||
m_LocalScale: {x: 1, y: 1, z: 1}
|
||||
m_Children: []
|
||||
m_Father: {fileID: 6361964099233889058}
|
||||
m_RootOrder: 2
|
||||
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
|
||||
m_AnchorMin: {x: 0.5, y: 1}
|
||||
m_AnchorMax: {x: 0.5, y: 1}
|
||||
m_AnchoredPosition: {x: 0, y: -12}
|
||||
m_SizeDelta: {x: 175.16223, y: 22.939087}
|
||||
m_Pivot: {x: 0.5, y: 1}
|
||||
--- !u!222 &432106219917414001
|
||||
CanvasRenderer:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 8135504764682151928}
|
||||
m_CullTransparentMesh: 1
|
||||
--- !u!114 &8669373154614060998
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 8135504764682151928}
|
||||
m_Enabled: 1
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: 11500000, guid: 5f7201a12d95ffc409449d95f23cf332, type: 3}
|
||||
m_Name:
|
||||
m_EditorClassIdentifier:
|
||||
m_Material: {fileID: 0}
|
||||
m_Color: {r: 0.13333334, g: 0.13333334, b: 0.13333334, a: 1}
|
||||
m_RaycastTarget: 1
|
||||
m_Maskable: 1
|
||||
m_OnCullStateChanged:
|
||||
m_PersistentCalls:
|
||||
m_Calls: []
|
||||
m_FontData:
|
||||
m_Font: {fileID: 12800000, guid: 3b1c92b10dde9426cbbccfbbd9c05cb1, type: 3}
|
||||
m_FontSize: 16
|
||||
m_FontStyle: 0
|
||||
m_BestFit: 0
|
||||
m_MinSize: 1
|
||||
m_MaxSize: 48
|
||||
m_Alignment: 4
|
||||
m_AlignByGeometry: 0
|
||||
m_RichText: 1
|
||||
m_HorizontalOverflow: 0
|
||||
m_VerticalOverflow: 0
|
||||
m_LineSpacing: 1
|
||||
m_Text: "\u6E38\u620F\u542F\u52A8\u5F02\u5E38\u63D0\u793A"
|
||||
--- !u!1 &8277855320559382179
|
||||
GameObject:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
serializedVersion: 6
|
||||
m_Component:
|
||||
- component: {fileID: 4506720584286079884}
|
||||
- component: {fileID: 2020009162116826997}
|
||||
- component: {fileID: 98187547627795137}
|
||||
- component: {fileID: 3607038900518221687}
|
||||
- component: {fileID: 8200218722334513239}
|
||||
m_Layer: 5
|
||||
m_Name: Root (1)
|
||||
m_TagString: Untagged
|
||||
m_Icon: {fileID: 0}
|
||||
m_NavMeshLayer: 0
|
||||
m_StaticEditorFlags: 0
|
||||
m_IsActive: 1
|
||||
--- !u!224 &4506720584286079884
|
||||
RectTransform:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 8277855320559382179}
|
||||
m_LocalRotation: {x: -0, y: -0, z: -0, w: 1}
|
||||
m_LocalPosition: {x: 0, y: 0, z: 0}
|
||||
m_LocalScale: {x: 1, y: 1, z: 1}
|
||||
m_Children: []
|
||||
m_Father: {fileID: 6361964099233889058}
|
||||
m_RootOrder: 0
|
||||
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
|
||||
m_AnchorMin: {x: 0, y: 0}
|
||||
m_AnchorMax: {x: 1, y: 1}
|
||||
m_AnchoredPosition: {x: 0, y: 0}
|
||||
m_SizeDelta: {x: 0, y: 0}
|
||||
m_Pivot: {x: 0.5, y: 0.5}
|
||||
--- !u!222 &2020009162116826997
|
||||
CanvasRenderer:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 8277855320559382179}
|
||||
m_CullTransparentMesh: 1
|
||||
--- !u!114 &98187547627795137
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 8277855320559382179}
|
||||
m_Enabled: 1
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: 11500000, guid: fe87c0e1cc204ed48ad3b37840f39efc, type: 3}
|
||||
m_Name:
|
||||
m_EditorClassIdentifier:
|
||||
m_Material: {fileID: 0}
|
||||
m_Color: {r: 1, g: 1, b: 1, a: 1}
|
||||
m_RaycastTarget: 1
|
||||
m_Maskable: 1
|
||||
m_OnCullStateChanged:
|
||||
m_PersistentCalls:
|
||||
m_Calls: []
|
||||
m_Sprite: {fileID: 21300000, guid: 86b9e58454341479496cd09b34eb515a, type: 3}
|
||||
m_Type: 0
|
||||
m_PreserveAspect: 0
|
||||
m_FillCenter: 1
|
||||
m_FillMethod: 4
|
||||
m_FillAmount: 1
|
||||
m_FillClockwise: 1
|
||||
m_FillOrigin: 0
|
||||
m_UseSpriteMesh: 0
|
||||
m_PixelsPerUnitMultiplier: 1
|
||||
--- !u!114 &3607038900518221687
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 8277855320559382179}
|
||||
m_Enabled: 1
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: 11500000, guid: cfabb0440166ab443bba8876756fdfa9, type: 3}
|
||||
m_Name:
|
||||
m_EditorClassIdentifier:
|
||||
m_EffectColor: {r: 0.9490196, g: 0.9490196, b: 0.9490196, a: 1}
|
||||
m_EffectDistance: {x: -1, y: -2}
|
||||
m_UseGraphicAlpha: 1
|
||||
--- !u!114 &8200218722334513239
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 8277855320559382179}
|
||||
m_Enabled: 1
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: 11500000, guid: cfabb0440166ab443bba8876756fdfa9, type: 3}
|
||||
m_Name:
|
||||
m_EditorClassIdentifier:
|
||||
m_EffectColor: {r: 0.9490196, g: 0.9490196, b: 0.9490196, a: 1}
|
||||
m_EffectDistance: {x: 1, y: 1}
|
||||
m_UseGraphicAlpha: 1
|
||||
@@ -0,0 +1,7 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 09d3bfcab13a8417ab5749aa9b8e6ee3
|
||||
PrefabImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
8
Assets/TapSDK/Core/Standalone/Resources/Texures.meta
Normal file
8
Assets/TapSDK/Core/Standalone/Resources/Texures.meta
Normal file
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 6003e1f9e395e4d4eac9caab4a2574a3
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
BIN
Assets/TapSDK/Core/Standalone/Resources/Texures/TapClientConnectError.png
LFS
Normal file
BIN
Assets/TapSDK/Core/Standalone/Resources/Texures/TapClientConnectError.png
LFS
Normal file
Binary file not shown.
@@ -0,0 +1,128 @@
|
||||
fileFormatVersion: 2
|
||||
guid: caf855ae07df04c859a1c5c64ea0802d
|
||||
TextureImporter:
|
||||
internalIDToNameTable: []
|
||||
externalObjects: {}
|
||||
serializedVersion: 11
|
||||
mipmaps:
|
||||
mipMapMode: 0
|
||||
enableMipMap: 0
|
||||
sRGBTexture: 1
|
||||
linearTexture: 0
|
||||
fadeOut: 0
|
||||
borderMipMap: 0
|
||||
mipMapsPreserveCoverage: 0
|
||||
alphaTestReferenceValue: 0.5
|
||||
mipMapFadeDistanceStart: 1
|
||||
mipMapFadeDistanceEnd: 3
|
||||
bumpmap:
|
||||
convertToNormalMap: 0
|
||||
externalNormalMap: 0
|
||||
heightScale: 0.25
|
||||
normalMapFilter: 0
|
||||
isReadable: 0
|
||||
streamingMipmaps: 0
|
||||
streamingMipmapsPriority: 0
|
||||
grayScaleToAlpha: 0
|
||||
generateCubemap: 6
|
||||
cubemapConvolution: 0
|
||||
seamlessCubemap: 0
|
||||
textureFormat: 1
|
||||
maxTextureSize: 2048
|
||||
textureSettings:
|
||||
serializedVersion: 2
|
||||
filterMode: 1
|
||||
aniso: 1
|
||||
mipBias: 0
|
||||
wrapU: 1
|
||||
wrapV: 1
|
||||
wrapW: 1
|
||||
nPOTScale: 0
|
||||
lightmap: 0
|
||||
compressionQuality: 50
|
||||
spriteMode: 1
|
||||
spriteExtrude: 1
|
||||
spriteMeshType: 1
|
||||
alignment: 0
|
||||
spritePivot: {x: 0.5, y: 0.5}
|
||||
spritePixelsToUnits: 100
|
||||
spriteBorder: {x: 0, y: 0, z: 0, w: 0}
|
||||
spriteGenerateFallbackPhysicsShape: 1
|
||||
alphaUsage: 1
|
||||
alphaIsTransparency: 1
|
||||
spriteTessellationDetail: -1
|
||||
textureType: 8
|
||||
textureShape: 1
|
||||
singleChannelComponent: 0
|
||||
maxTextureSizeSet: 0
|
||||
compressionQualitySet: 0
|
||||
textureFormatSet: 0
|
||||
applyGammaDecoding: 0
|
||||
platformSettings:
|
||||
- serializedVersion: 3
|
||||
buildTarget: DefaultTexturePlatform
|
||||
maxTextureSize: 2048
|
||||
resizeAlgorithm: 0
|
||||
textureFormat: -1
|
||||
textureCompression: 1
|
||||
compressionQuality: 50
|
||||
crunchedCompression: 0
|
||||
allowsAlphaSplitting: 0
|
||||
overridden: 0
|
||||
androidETC2FallbackOverride: 0
|
||||
forceMaximumCompressionQuality_BC6H_BC7: 0
|
||||
- serializedVersion: 3
|
||||
buildTarget: Standalone
|
||||
maxTextureSize: 2048
|
||||
resizeAlgorithm: 0
|
||||
textureFormat: -1
|
||||
textureCompression: 1
|
||||
compressionQuality: 50
|
||||
crunchedCompression: 0
|
||||
allowsAlphaSplitting: 0
|
||||
overridden: 0
|
||||
androidETC2FallbackOverride: 0
|
||||
forceMaximumCompressionQuality_BC6H_BC7: 0
|
||||
- serializedVersion: 3
|
||||
buildTarget: iPhone
|
||||
maxTextureSize: 2048
|
||||
resizeAlgorithm: 0
|
||||
textureFormat: -1
|
||||
textureCompression: 1
|
||||
compressionQuality: 50
|
||||
crunchedCompression: 0
|
||||
allowsAlphaSplitting: 0
|
||||
overridden: 0
|
||||
androidETC2FallbackOverride: 0
|
||||
forceMaximumCompressionQuality_BC6H_BC7: 0
|
||||
- serializedVersion: 3
|
||||
buildTarget: Android
|
||||
maxTextureSize: 2048
|
||||
resizeAlgorithm: 0
|
||||
textureFormat: -1
|
||||
textureCompression: 1
|
||||
compressionQuality: 50
|
||||
crunchedCompression: 0
|
||||
allowsAlphaSplitting: 0
|
||||
overridden: 0
|
||||
androidETC2FallbackOverride: 0
|
||||
forceMaximumCompressionQuality_BC6H_BC7: 0
|
||||
spriteSheet:
|
||||
serializedVersion: 2
|
||||
sprites: []
|
||||
outline: []
|
||||
physicsShape: []
|
||||
bones: []
|
||||
spriteID: 5e97eb03825dee720800000000000000
|
||||
internalID: 0
|
||||
vertices: []
|
||||
indices:
|
||||
edges: []
|
||||
weights: []
|
||||
secondaryTextures: []
|
||||
spritePackingTag:
|
||||
pSDRemoveMatte: 0
|
||||
pSDShowRemoveMatteOption: 0
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
8
Assets/TapSDK/Core/Standalone/Runtime.meta
Normal file
8
Assets/TapSDK/Core/Standalone/Runtime.meta
Normal file
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 8f9383e2313414222b398988ccddf3fb
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
8
Assets/TapSDK/Core/Standalone/Runtime/Internal.meta
Normal file
8
Assets/TapSDK/Core/Standalone/Runtime/Internal.meta
Normal file
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: dcf82a808dfb24329ab66136da3a0220
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
8
Assets/TapSDK/Core/Standalone/Runtime/Internal/Bean.meta
Normal file
8
Assets/TapSDK/Core/Standalone/Runtime/Internal/Bean.meta
Normal file
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: db93f492877c64b2f959c3a04debd60f
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,40 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace TapSDK.Core.Standalone.Internal.Bean
|
||||
{
|
||||
[Serializable]
|
||||
internal class TapGatekeeper
|
||||
{
|
||||
[JsonProperty("switch")]
|
||||
public TapGatekeeperSwitch Switch { get; set; } = new TapGatekeeperSwitch();
|
||||
|
||||
[JsonProperty("urls")]
|
||||
public Dictionary<string, Url> Urls { get; set; }
|
||||
|
||||
[JsonProperty("taptap_app_id")]
|
||||
public int? TapTapAppId { get; set; }
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
internal class TapGatekeeperSwitch
|
||||
{
|
||||
|
||||
[JsonProperty("heartbeat")]
|
||||
public bool Heartbeat { get; set; } = true;
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
internal class Url
|
||||
{
|
||||
[JsonProperty("webview")]
|
||||
public string WebView { get; set; }
|
||||
|
||||
[JsonProperty("browser")]
|
||||
public string Browser { get; set; }
|
||||
|
||||
[JsonProperty("uri")]
|
||||
public string TapUri { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: ba3269645153d4a47b23dd75efdeab5d
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
20
Assets/TapSDK/Core/Standalone/Runtime/Internal/Constants.cs
Normal file
20
Assets/TapSDK/Core/Standalone/Runtime/Internal/Constants.cs
Normal file
@@ -0,0 +1,20 @@
|
||||
|
||||
namespace TapSDK.Core.Standalone.Internal {
|
||||
public static class Constants {
|
||||
public static readonly string EVENT = "event";
|
||||
|
||||
public static readonly string PROPERTY_INITIALIZE_TYPE = "initialise";
|
||||
public static readonly string PROPERTY_UPDATE_TYPE = "update";
|
||||
public static readonly string PROPERTY_ADD_TYPE = "add";
|
||||
|
||||
|
||||
public readonly static string SERVER_URL_CN = "https://e.tapdb.net";
|
||||
public readonly static string SERVER_URL_IO = "https://e.tapdb.ap-sg.tapapis.com";
|
||||
public readonly static string DEVICE_LOGIN = "device_login";
|
||||
public readonly static string USER_LOGIN = "user_login";
|
||||
|
||||
internal static string ClientSettingsFileName = "TapSDKClientSettings";
|
||||
internal static string ClientSettingsEventKey = "ClientSettingsEventKey";
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 240dff90393004fc28bd171ef06f3ea1
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
154
Assets/TapSDK/Core/Standalone/Runtime/Internal/DeviceInfo.cs
Normal file
154
Assets/TapSDK/Core/Standalone/Runtime/Internal/DeviceInfo.cs
Normal file
@@ -0,0 +1,154 @@
|
||||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Globalization;
|
||||
using System.Collections.Generic;
|
||||
using System.Net.NetworkInformation;
|
||||
using UnityEngine;
|
||||
using System.Diagnostics;
|
||||
using TapSDK.Core.Internal.Log;
|
||||
|
||||
namespace TapSDK.Core.Standalone.Internal
|
||||
{
|
||||
public class DeviceInfo
|
||||
{
|
||||
// 缓存网卡地址列表,避免多次调用
|
||||
private static string cachedMacAddressList;
|
||||
|
||||
// 缓存网卡地址,避免多次调用
|
||||
private static string cachedFristMacAddress;
|
||||
|
||||
#if UNITY_EDITOR_OSX || UNITY_STANDALONE_OSX
|
||||
[DllImport("TapDBDeviceInfo", CallingConvention = CallingConvention.Cdecl)]
|
||||
private static extern IntPtr GetDeviceLanguage();
|
||||
#endif
|
||||
|
||||
public static string GetLanguage()
|
||||
{
|
||||
#if UNITY_EDITOR_OSX || UNITY_STANDALONE_OSX
|
||||
return Marshal.PtrToStringAnsi(GetDeviceLanguage());
|
||||
#else
|
||||
return CultureInfo.CurrentUICulture.IetfLanguageTag;
|
||||
#endif
|
||||
}
|
||||
|
||||
#if UNITY_EDITOR_WIN || UNITY_STANDALONE_WIN
|
||||
[DllImport("kernel32.dll")]
|
||||
static extern IntPtr GetCurrentProcess();
|
||||
|
||||
[DllImport("kernel32.dll")]
|
||||
static extern uint GetProcessTimes(IntPtr processHandle,
|
||||
out long creationTime,
|
||||
out long exitTime,
|
||||
out long kernelTime,
|
||||
out long userTime);
|
||||
|
||||
static DateTime GetProcessStartTime()
|
||||
{
|
||||
IntPtr processHandle = GetCurrentProcess();
|
||||
long creationTime;
|
||||
GetProcessTimes(processHandle,
|
||||
out creationTime,
|
||||
out _,
|
||||
out _,
|
||||
out _);
|
||||
|
||||
return DateTime.FromFileTime(creationTime);
|
||||
}
|
||||
#endif
|
||||
|
||||
//安全组提供的设备识别 ID 算法,用于后续数据串联
|
||||
public static string GetLaunchUniqueID()
|
||||
{
|
||||
#if UNITY_EDITOR_WIN || UNITY_STANDALONE_WIN
|
||||
// 获取当前进程对象
|
||||
Process currentProcess = Process.GetCurrentProcess();
|
||||
// 获取进程启动时间
|
||||
DateTime startTime = GetProcessStartTime();
|
||||
return toMd5(startTime.ToFileTime().ToString() + "-" + currentProcess.Id.ToString());
|
||||
#else
|
||||
return "";
|
||||
#endif
|
||||
|
||||
}
|
||||
|
||||
public static void GetMacAddress(out string macAddressList, out string firstMacAddress)
|
||||
{
|
||||
if (
|
||||
!string.IsNullOrEmpty(cachedMacAddressList)
|
||||
&& !string.IsNullOrEmpty(cachedFristMacAddress)
|
||||
)
|
||||
{
|
||||
macAddressList = cachedMacAddressList;
|
||||
firstMacAddress = cachedFristMacAddress;
|
||||
return;
|
||||
}
|
||||
List<string> mac_addrs = new List<string>();
|
||||
|
||||
try
|
||||
{
|
||||
NetworkInterface[] nics = NetworkInterface.GetAllNetworkInterfaces();
|
||||
foreach (NetworkInterface adapter in nics)
|
||||
{
|
||||
string physicalAddress = adapter.GetPhysicalAddress().ToString();
|
||||
if (string.IsNullOrEmpty(physicalAddress))
|
||||
continue;
|
||||
|
||||
physicalAddress = $"\"{physicalAddress}\"";
|
||||
if (mac_addrs.IndexOf(physicalAddress) == -1)
|
||||
mac_addrs.Add(physicalAddress);
|
||||
}
|
||||
// sort
|
||||
mac_addrs.Sort();
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
TapLog.Log("GetMacAddress Exception " + e.Message);
|
||||
}
|
||||
macAddressList = $"[{string.Join(",", mac_addrs)}]";
|
||||
firstMacAddress = mac_addrs.Count > 0 ? mac_addrs[0].Replace("\"", "") : string.Empty;
|
||||
|
||||
cachedMacAddressList = macAddressList;
|
||||
cachedFristMacAddress = firstMacAddress;
|
||||
}
|
||||
|
||||
private static string toMd5(string data)
|
||||
{
|
||||
byte[] buffer = System.Text.Encoding.Default.GetBytes(data);
|
||||
try
|
||||
{
|
||||
System.Security.Cryptography.MD5CryptoServiceProvider chk = new System.Security.Cryptography.MD5CryptoServiceProvider();
|
||||
byte[] some = chk.ComputeHash(buffer);
|
||||
string ret = "";
|
||||
foreach (byte a in some)
|
||||
{
|
||||
if (a < 16)
|
||||
ret += "0" + a.ToString("X");
|
||||
else
|
||||
ret += a.ToString("X");
|
||||
}
|
||||
return ret.ToLower();
|
||||
}
|
||||
catch
|
||||
{
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
public static string RAM
|
||||
{
|
||||
get
|
||||
{
|
||||
return (SystemInfo.systemMemorySize * 1024L * 1024L).ToString();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public static string Local
|
||||
{
|
||||
get
|
||||
{
|
||||
return CultureInfo.CurrentCulture.Name.Replace('-', '_');
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: d118285dce52d4ee38c22f3c1c174569
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
266
Assets/TapSDK/Core/Standalone/Runtime/Internal/EventSender.cs
Normal file
266
Assets/TapSDK/Core/Standalone/Runtime/Internal/EventSender.cs
Normal file
@@ -0,0 +1,266 @@
|
||||
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using TapSDK.Core.Internal.Log;
|
||||
using TapSDK.Core.Standalone.Internal.Http;
|
||||
using UnityEngine;
|
||||
using UnityEngine.Networking;
|
||||
|
||||
namespace TapSDK.Core.Standalone.Internal
|
||||
{
|
||||
public class EventSender
|
||||
{
|
||||
private const string OldEventFilePath = "events.json";
|
||||
|
||||
private readonly TapLog log = new TapLog("TapEvent");
|
||||
private string persistentDataPath = Application.persistentDataPath;
|
||||
|
||||
private Queue<Dictionary<string, object>> eventQueue = new Queue<Dictionary<string, object>>();
|
||||
private TapHttp tapHttp = TapHttp
|
||||
.NewBuilder("TapSDKCore", TapTapSDK.Version)
|
||||
.Sign(TapHttpSign.CreateNoneSign())
|
||||
.Parser(TapHttpParser.CreateEventParser())
|
||||
.Build();
|
||||
|
||||
private const int MaxEvents = 50;
|
||||
private const int MaxBatchSize = 200;
|
||||
private const float SendInterval = 15f;
|
||||
private Timer timer;
|
||||
private DateTime lastSendTime;
|
||||
|
||||
private string domain = Constants.SERVER_URL_CN;
|
||||
|
||||
private int QueueCount => eventQueue.Count;
|
||||
|
||||
public EventSender()
|
||||
{
|
||||
// 设置计时器
|
||||
timer = new Timer(OnTimerElapsed, null, TimeSpan.Zero, TimeSpan.FromSeconds(SendInterval));
|
||||
lastSendTime = DateTime.Now;
|
||||
|
||||
// 初始化 HttpClient
|
||||
var header = new Dictionary<string, string>
|
||||
{
|
||||
{ "User-Agent", $"{TapTapSDK.SDKPlatform}/{TapTapSDK.Version}" }
|
||||
};
|
||||
|
||||
var coreOptions = TapCoreStandalone.coreOptions;
|
||||
if (coreOptions.region == TapTapRegionType.CN)
|
||||
{
|
||||
domain = Constants.SERVER_URL_CN;
|
||||
}
|
||||
else
|
||||
{
|
||||
domain = Constants.SERVER_URL_IO;
|
||||
}
|
||||
|
||||
// 加载未发送的事件
|
||||
LoadEvents();
|
||||
SendEventsAsync(null);
|
||||
}
|
||||
|
||||
public async void SendEventsAsync(Action onSendComplete)
|
||||
{
|
||||
if (eventQueue.Count == 0)
|
||||
{
|
||||
onSendComplete?.Invoke();
|
||||
return;
|
||||
}
|
||||
|
||||
var eventsToSend = new List<Dictionary<string, object>>();
|
||||
for (int i = 0; i < MaxBatchSize && eventQueue.Count > 0; i++)
|
||||
{
|
||||
eventsToSend.Add(eventQueue.Dequeue());
|
||||
}
|
||||
|
||||
var body = new Dictionary<string, object> {
|
||||
{ "data", eventsToSend }
|
||||
};
|
||||
|
||||
var resonse = await tapHttp.PostJsonAsync<Boolean>(path: $"{domain}/v2/batch", json: body);
|
||||
if (resonse.IsSuccess)
|
||||
{
|
||||
// log.Log("Events sent successfully");
|
||||
}
|
||||
else
|
||||
{
|
||||
log.Warning("Failed to send events");
|
||||
// 将事件重新添加到队列
|
||||
foreach (var eventParams in eventsToSend)
|
||||
{
|
||||
eventQueue.Enqueue(eventParams);
|
||||
}
|
||||
}
|
||||
onSendComplete?.Invoke();
|
||||
SaveEvents();
|
||||
}
|
||||
|
||||
public void Send(Dictionary<string, object> eventParams)
|
||||
{
|
||||
// 将事件添加到队列
|
||||
eventQueue.Enqueue(eventParams);
|
||||
SaveEvents();
|
||||
|
||||
// 检查队列大小
|
||||
if (QueueCount >= MaxEvents)
|
||||
{
|
||||
SendEvents();
|
||||
ResetTimer();
|
||||
}
|
||||
}
|
||||
|
||||
private void OnTimerElapsed(object state)
|
||||
{
|
||||
var offset = (DateTime.Now - lastSendTime).TotalSeconds;
|
||||
if (offset >= SendInterval)
|
||||
{
|
||||
SendEvents();
|
||||
ResetTimer();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private void ResetTimer()
|
||||
{
|
||||
timer.Change(TimeSpan.FromSeconds(SendInterval), TimeSpan.FromSeconds(SendInterval));
|
||||
}
|
||||
|
||||
private string GetEventCacheFileName(){
|
||||
if (TapTapSDK.taptapSdkOptions != null
|
||||
&& !string.IsNullOrEmpty(TapTapSDK.taptapSdkOptions.clientId)){
|
||||
return "events_" + TapTapSDK.taptapSdkOptions.clientId + ".json";
|
||||
}
|
||||
return OldEventFilePath;
|
||||
}
|
||||
|
||||
private void LoadEvents()
|
||||
{
|
||||
string filePath = Path.Combine(persistentDataPath, GetEventCacheFileName());
|
||||
if(!File.Exists(filePath)){
|
||||
string oldFilePath = Path.Combine(persistentDataPath, OldEventFilePath);
|
||||
// 兼容旧版本文件
|
||||
if (File.Exists(oldFilePath)) {
|
||||
File.Move(oldFilePath, filePath);
|
||||
}
|
||||
}
|
||||
|
||||
if (File.Exists(filePath))
|
||||
{
|
||||
string jsonData = File.ReadAllText(filePath);
|
||||
if (string.IsNullOrEmpty(jsonData))
|
||||
{
|
||||
return;
|
||||
}
|
||||
var savedEvents = ConvertToListOfDictionaries(Json.Deserialize(jsonData));
|
||||
if (savedEvents == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
foreach (var eventParams in savedEvents)
|
||||
{
|
||||
eventQueue.Enqueue(eventParams);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void SaveEvents()
|
||||
{
|
||||
try
|
||||
{
|
||||
if (eventQueue == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var eventList = eventQueue.ToList();
|
||||
string jsonData = Json.Serialize(eventList);
|
||||
|
||||
if (string.IsNullOrEmpty(GetEventCacheFileName()))
|
||||
{
|
||||
TapLog.Error("EventFilePath is null or empty");
|
||||
return;
|
||||
}
|
||||
|
||||
string filePath = Path.Combine(persistentDataPath, GetEventCacheFileName());
|
||||
|
||||
if (string.IsNullOrEmpty(filePath))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
File.WriteAllText(filePath, jsonData);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
TapLog.Error("SaveEvents Exception - " + ex.Message);
|
||||
}
|
||||
}
|
||||
|
||||
public void SendEvents()
|
||||
{
|
||||
SendEventsAsync(() => lastSendTime = DateTime.Now);
|
||||
}
|
||||
|
||||
private Dictionary<string, object> ConvertToDictionary(Dictionary<string, object> original)
|
||||
{
|
||||
var result = new Dictionary<string, object>();
|
||||
foreach (var keyValuePair in original)
|
||||
{
|
||||
if (keyValuePair.Value is Dictionary<string, object> nestedDictionary)
|
||||
{
|
||||
result[keyValuePair.Key] = ConvertToDictionary(nestedDictionary);
|
||||
}
|
||||
else if (keyValuePair.Value is List<object> nestedList)
|
||||
{
|
||||
result[keyValuePair.Key] = ConvertToListOfDictionaries(nestedList);
|
||||
}
|
||||
else
|
||||
{
|
||||
result[keyValuePair.Key] = keyValuePair.Value;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
private List<Dictionary<string, object>> ConvertToListOfDictionaries(object deserializedData)
|
||||
{
|
||||
if (deserializedData is List<object> list)
|
||||
{
|
||||
var result = new List<Dictionary<string, object>>();
|
||||
foreach (var item in list)
|
||||
{
|
||||
if (item is Dictionary<string, object> dictionary)
|
||||
{
|
||||
result.Add(ConvertToDictionary(dictionary));
|
||||
}
|
||||
else
|
||||
{
|
||||
return null; // 数据格式不匹配
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
return null; // 数据格式不匹配
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
private class Serialization<T>
|
||||
{
|
||||
public List<T> items;
|
||||
public Serialization(List<T> items)
|
||||
{
|
||||
this.items = items;
|
||||
}
|
||||
|
||||
public List<T> ToList()
|
||||
{
|
||||
return items;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 0a0c17dc1a104465bb5542984656d6fc
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
8
Assets/TapSDK/Core/Standalone/Runtime/Internal/Http.meta
Normal file
8
Assets/TapSDK/Core/Standalone/Runtime/Internal/Http.meta
Normal file
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 928b028f42aae4ded9b34d5c4c6536d1
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
407
Assets/TapSDK/Core/Standalone/Runtime/Internal/Http/TapHttp.cs
Normal file
407
Assets/TapSDK/Core/Standalone/Runtime/Internal/Http/TapHttp.cs
Normal file
@@ -0,0 +1,407 @@
|
||||
using System.Net.Http;
|
||||
|
||||
namespace TapSDK.Core.Standalone.Internal.Http
|
||||
{
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Net.Http.Headers;
|
||||
using System.Net.NetworkInformation;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using Newtonsoft.Json;
|
||||
using TapSDK.Core.Internal.Log;
|
||||
using UnityEngine;
|
||||
|
||||
public class TapHttp
|
||||
{
|
||||
|
||||
private static readonly string TAG = "Http";
|
||||
|
||||
private static readonly int MAX_GET_RETRY_COUNT = 3;
|
||||
|
||||
internal static readonly long CONNECT_TIMEOUT_MILLIS = 10 * 1000L;
|
||||
internal static readonly long READ_TIMEOUT_MILLIS = 5 * 1000L;
|
||||
internal static readonly long WRITE_TIMEOUT_MILLIS = 5 * 1000L;
|
||||
|
||||
public static readonly string HOST_CN = "https://tapsdk.tapapis.cn";
|
||||
public static readonly string HOST_IO = "https://tapsdk.tapapis.com";
|
||||
|
||||
private static HttpClient client = GetHttpClient();
|
||||
|
||||
private readonly TapHttpConfig httpConfig;
|
||||
|
||||
private readonly TapLog log = new TapLog(TAG);
|
||||
|
||||
private TapHttp() { }
|
||||
|
||||
internal TapHttp(TapHttpConfig httpConfig)
|
||||
{
|
||||
this.httpConfig = httpConfig;
|
||||
}
|
||||
|
||||
public static TapHttpBuilder NewBuilder(string moduleName, string moduleVersion)
|
||||
{
|
||||
return new TapHttpBuilder(moduleName, moduleVersion);
|
||||
}
|
||||
|
||||
private static HttpClient GetHttpClient()
|
||||
{
|
||||
var handler = new HttpClientHandler { UseCookies = false };
|
||||
HttpClient client = new HttpClient(handler){
|
||||
// 默认超时 10 秒
|
||||
Timeout = TimeSpan.FromMilliseconds(CONNECT_TIMEOUT_MILLIS)
|
||||
};
|
||||
return client;
|
||||
}
|
||||
|
||||
public async void PostJson<T>(
|
||||
string url,
|
||||
Dictionary<string, string> headers = null,
|
||||
Dictionary<string, string> queryParams = null,
|
||||
Dictionary<string, object> json = null,
|
||||
bool enableAuthorization = false,
|
||||
ITapHttpRetryStrategy retryStrategy = null,
|
||||
Action<T> onSuccess = null,
|
||||
Action<AbsTapHttpException> onFailure = null)
|
||||
{
|
||||
TapHttpResult<T> tapHttpResult = await PostJsonAsync<T>(
|
||||
path: url,
|
||||
headers: headers,
|
||||
queryParams: queryParams,
|
||||
json: json,
|
||||
enableAuthorization: enableAuthorization,
|
||||
retryStrategy: retryStrategy
|
||||
);
|
||||
if (tapHttpResult.IsSuccess)
|
||||
{
|
||||
onSuccess?.Invoke(tapHttpResult.Data);
|
||||
}
|
||||
else
|
||||
{
|
||||
onFailure?.Invoke(tapHttpResult.HttpException);
|
||||
}
|
||||
}
|
||||
|
||||
public async void PostForm<T>(
|
||||
string url,
|
||||
Dictionary<string, string> headers = null,
|
||||
Dictionary<string, string> queryParams = null,
|
||||
Dictionary<string, string> form = null,
|
||||
bool enableAuthorization = false,
|
||||
ITapHttpRetryStrategy retryStrategy = null,
|
||||
Action<T> onSuccess = null,
|
||||
Action<AbsTapHttpException> onFailure = null)
|
||||
{
|
||||
TapHttpResult<T> tapHttpResult = await PostFormAsync<T>(
|
||||
url: url,
|
||||
headers: headers,
|
||||
queryParams: queryParams,
|
||||
form: form,
|
||||
enableAuthorization: enableAuthorization,
|
||||
retryStrategy: retryStrategy
|
||||
);
|
||||
if (tapHttpResult.IsSuccess)
|
||||
{
|
||||
onSuccess?.Invoke(tapHttpResult.Data);
|
||||
}
|
||||
else
|
||||
{
|
||||
onFailure?.Invoke(tapHttpResult.HttpException);
|
||||
}
|
||||
}
|
||||
|
||||
public async void Get<T>(
|
||||
string url,
|
||||
Dictionary<string, string> headers = null,
|
||||
Dictionary<string, string> queryParams = null,
|
||||
bool enableAuthorization = false,
|
||||
ITapHttpRetryStrategy retryStrategy = null,
|
||||
Action<T> onSuccess = null,
|
||||
Action<Exception> onFailure = null
|
||||
)
|
||||
{
|
||||
TapHttpResult<T> tapHttpResult = await GetAsync<T>(
|
||||
url: url,
|
||||
headers: headers,
|
||||
queryParams: queryParams,
|
||||
enableAuthorization: enableAuthorization,
|
||||
retryStrategy: retryStrategy
|
||||
);
|
||||
if (tapHttpResult.IsSuccess)
|
||||
{
|
||||
onSuccess?.Invoke(tapHttpResult.Data);
|
||||
}
|
||||
else
|
||||
{
|
||||
onFailure?.Invoke(tapHttpResult.HttpException);
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<TapHttpResult<T>> GetAsync<T>(
|
||||
string url,
|
||||
Dictionary<string, string> headers = null,
|
||||
Dictionary<string, string> queryParams = null,
|
||||
bool enableAuthorization = false,
|
||||
ITapHttpRetryStrategy retryStrategy = null
|
||||
)
|
||||
{
|
||||
if (retryStrategy == null)
|
||||
{
|
||||
retryStrategy = TapHttpRetryStrategy.CreateDefault(TapHttpBackoffStrategy.CreateFixed(MAX_GET_RETRY_COUNT));
|
||||
}
|
||||
return await Request<T>(
|
||||
url: url,
|
||||
method: HttpMethod.Get,
|
||||
enableAuthorization: enableAuthorization,
|
||||
retryStrategy: retryStrategy,
|
||||
headers: headers,
|
||||
queryParams: queryParams
|
||||
);
|
||||
}
|
||||
|
||||
public async Task<TapHttpResult<T>> PostJsonAsync<T>(
|
||||
string path,
|
||||
Dictionary<string, string> headers = null,
|
||||
Dictionary<string, string> queryParams = null,
|
||||
object json = null,
|
||||
bool enableAuthorization = false,
|
||||
ITapHttpRetryStrategy retryStrategy = null
|
||||
)
|
||||
{
|
||||
if (retryStrategy == null)
|
||||
{
|
||||
retryStrategy = TapHttpRetryStrategy.CreateDefault(TapHttpBackoffStrategy.CreateNone());
|
||||
}
|
||||
string jsonStr = null;
|
||||
if (json != null)
|
||||
{
|
||||
jsonStr = await Task.Run(() => JsonConvert.SerializeObject(json));
|
||||
}
|
||||
return await Request<T>(
|
||||
url: path,
|
||||
method: HttpMethod.Post,
|
||||
enableAuthorization: enableAuthorization,
|
||||
retryStrategy: retryStrategy,
|
||||
headers: headers,
|
||||
queryParams: queryParams,
|
||||
body: jsonStr
|
||||
);
|
||||
}
|
||||
|
||||
public async Task<TapHttpResult<T>> PostFormAsync<T>(
|
||||
string url,
|
||||
Dictionary<string, string> headers = null,
|
||||
Dictionary<string, string> queryParams = null,
|
||||
Dictionary<string, string> form = null,
|
||||
bool enableAuthorization = false,
|
||||
ITapHttpRetryStrategy retryStrategy = null
|
||||
)
|
||||
{
|
||||
if (retryStrategy == null)
|
||||
{
|
||||
retryStrategy = TapHttpRetryStrategy.CreateDefault(TapHttpBackoffStrategy.CreateNone());
|
||||
}
|
||||
return await Request<T>(
|
||||
url: url,
|
||||
method: HttpMethod.Post,
|
||||
enableAuthorization: enableAuthorization,
|
||||
retryStrategy: retryStrategy,
|
||||
headers: headers,
|
||||
queryParams: queryParams,
|
||||
body: form
|
||||
);
|
||||
}
|
||||
|
||||
private async Task<TapHttpResult<T>> Request<T>(
|
||||
string url,
|
||||
HttpMethod method,
|
||||
bool enableAuthorization,
|
||||
ITapHttpRetryStrategy retryStrategy,
|
||||
Dictionary<string, string> headers = null,
|
||||
Dictionary<string, string> queryParams = null,
|
||||
object body = null
|
||||
)
|
||||
{
|
||||
TapHttpResult<T> tapHttpResult;
|
||||
long nextRetryMillis;
|
||||
do
|
||||
{
|
||||
tapHttpResult = await RequestInner<T>(url, method, enableAuthorization, headers, queryParams, body);
|
||||
|
||||
if (tapHttpResult.IsSuccess)
|
||||
{
|
||||
return tapHttpResult;
|
||||
}
|
||||
|
||||
nextRetryMillis = retryStrategy.NextRetryMillis(tapHttpResult.HttpException);
|
||||
if (nextRetryMillis > 0)
|
||||
{
|
||||
log.Log($"Request failed, retry after {nextRetryMillis} ms");
|
||||
await Task.Delay(TimeSpan.FromMilliseconds(nextRetryMillis));
|
||||
}
|
||||
|
||||
} while (nextRetryMillis >= 0L);
|
||||
|
||||
return tapHttpResult;
|
||||
}
|
||||
|
||||
private async Task<TapHttpResult<T>> RequestInner<T>(
|
||||
string path,
|
||||
HttpMethod method,
|
||||
bool enableAuthorization,
|
||||
Dictionary<string, string> headers = null,
|
||||
Dictionary<string, string> queryParams = null,
|
||||
object body = null
|
||||
)
|
||||
{
|
||||
if(!CheckNetworkConnection()){
|
||||
return TapHttpResult<T>.NetworkError(new TapHttpNetworkErrorException("network error"));
|
||||
}else{
|
||||
TapLog.Log("current network is connected");
|
||||
}
|
||||
// 处理查询参数
|
||||
Dictionary<string, string> allQueryParams = new Dictionary<string, string>();
|
||||
if (queryParams != null)
|
||||
{
|
||||
foreach (var param in queryParams)
|
||||
{
|
||||
allQueryParams[param.Key] = param.Value;
|
||||
}
|
||||
}
|
||||
var fixedQueryParams = httpConfig.Sign.GetFixedQueryParams();
|
||||
if (fixedQueryParams != null)
|
||||
{
|
||||
foreach (var param in fixedQueryParams)
|
||||
{
|
||||
allQueryParams[param.Key] = param.Value;
|
||||
}
|
||||
}
|
||||
string host = HOST_CN;
|
||||
if (httpConfig.Domain != null)
|
||||
{
|
||||
host = httpConfig.Domain;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (TapCoreStandalone.coreOptions.region == TapTapRegionType.CN)
|
||||
{
|
||||
host = HOST_CN;
|
||||
}
|
||||
else if (TapCoreStandalone.coreOptions.region == TapTapRegionType.Overseas)
|
||||
{
|
||||
host = HOST_IO;
|
||||
}
|
||||
}
|
||||
// 拼接查询参数
|
||||
UriBuilder uriBuilder;
|
||||
if (path.StartsWith("http://") || path.StartsWith("https://"))
|
||||
{
|
||||
uriBuilder = new UriBuilder(path);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!path.StartsWith("/"))
|
||||
{
|
||||
path = "/" + path;
|
||||
}
|
||||
uriBuilder = new UriBuilder(uri: $"{host}{path}");
|
||||
}
|
||||
|
||||
if (allQueryParams.Count > 0)
|
||||
{
|
||||
var queryBuilder = new StringBuilder();
|
||||
foreach (var param in allQueryParams)
|
||||
{
|
||||
// 使用 Uri.EscapeDataString 来编码参数的键和值
|
||||
queryBuilder.Append($"{Uri.EscapeDataString(param.Key)}={Uri.EscapeDataString(param.Value)}&");
|
||||
}
|
||||
// 移除末尾的 '&'
|
||||
queryBuilder.Length -= 1;
|
||||
|
||||
uriBuilder.Query = queryBuilder.ToString();
|
||||
}
|
||||
|
||||
var requestUri = uriBuilder.Uri;
|
||||
|
||||
// 创建 HttpRequestMessage
|
||||
var request = new HttpRequestMessage
|
||||
{
|
||||
Method = method,
|
||||
RequestUri = requestUri
|
||||
};
|
||||
|
||||
// 处理请求头
|
||||
Dictionary<string, string> allHeaders = new Dictionary<string, string>();
|
||||
if (headers != null)
|
||||
{
|
||||
foreach (var header in headers)
|
||||
{
|
||||
allHeaders[header.Key] = header.Value;
|
||||
}
|
||||
}
|
||||
Dictionary<string, string> fixedHeaders = httpConfig.Sign.GetFixedHeaders(requestUri.ToString(), method, httpConfig.ModuleName, httpConfig.ModuleVersion, enableAuthorization);
|
||||
if (fixedHeaders != null)
|
||||
{
|
||||
foreach (var header in fixedHeaders)
|
||||
{
|
||||
allHeaders[header.Key] = header.Value;
|
||||
}
|
||||
}
|
||||
// 添加请求头
|
||||
if (allHeaders != null)
|
||||
{
|
||||
foreach (var header in allHeaders)
|
||||
{
|
||||
request.Headers.TryAddWithoutValidation(header.Key, header.Value.ToString());
|
||||
}
|
||||
}
|
||||
|
||||
// 根据请求类型设置请求体
|
||||
if (method == HttpMethod.Post || method == HttpMethod.Put)
|
||||
{
|
||||
if (body != null)
|
||||
{
|
||||
if (body is string jsonBody) // 处理 JSON 数据
|
||||
{
|
||||
StringContent requestContent = new StringContent(jsonBody);
|
||||
requestContent.Headers.ContentType = new MediaTypeHeaderValue("application/json");
|
||||
request.Content = requestContent;
|
||||
}
|
||||
else if (body is Dictionary<string, string> formData) // 处理 Form 数据
|
||||
{
|
||||
request.Content = new FormUrlEncodedContent(formData);
|
||||
}
|
||||
}
|
||||
}
|
||||
// 签算
|
||||
httpConfig.Sign.Sign(request);
|
||||
try
|
||||
{
|
||||
if (TapTapSDK.taptapSdkOptions.enableLog)
|
||||
{
|
||||
TapHttpUtils.PrintRequest(client, request);
|
||||
}
|
||||
// 发送请求
|
||||
HttpResponseMessage response = await client.SendAsync(request);
|
||||
if (TapTapSDK.taptapSdkOptions.enableLog)
|
||||
{
|
||||
TapHttpUtils.PrintResponse(response);
|
||||
}
|
||||
// 解析响应
|
||||
return await httpConfig.Parser.Parse<T>(response);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
// 捕获并处理请求异常
|
||||
return TapHttpResult<T>.UnknownFailure(new TapHttpUnknownException(ex));
|
||||
}
|
||||
}
|
||||
|
||||
// 判断网络连接状态
|
||||
private bool CheckNetworkConnection()
|
||||
{
|
||||
return Application.internetReachability != NetworkReachability.NotReachable;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: d2dd246dfad224e9aae739da577b7087
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,92 @@
|
||||
using System;
|
||||
|
||||
namespace TapSDK.Core.Standalone.Internal.Http
|
||||
{
|
||||
public class TapHttpBuilder
|
||||
{
|
||||
private readonly string moduleName;
|
||||
private readonly string moduleVersion;
|
||||
|
||||
private string domain = null;
|
||||
|
||||
private ITapHttpSign sign = null;
|
||||
private ITapHttpParser parser = null;
|
||||
|
||||
private long connectTimeoutMillis = TapHttp.CONNECT_TIMEOUT_MILLIS;
|
||||
private long readTimeoutMillis = TapHttp.READ_TIMEOUT_MILLIS;
|
||||
private long writeTimeoutMillis = TapHttp.WRITE_TIMEOUT_MILLIS;
|
||||
|
||||
public TapHttpBuilder(string moduleName, string moduleVersion)
|
||||
{
|
||||
this.moduleName = moduleName ?? throw new ArgumentNullException(nameof(moduleName));
|
||||
this.moduleVersion = moduleVersion ?? throw new ArgumentNullException(nameof(moduleVersion));
|
||||
}
|
||||
|
||||
public TapHttp Build()
|
||||
{
|
||||
TapHttpConfig httpConfig = new TapHttpConfig
|
||||
{
|
||||
ModuleName = moduleName,
|
||||
ModuleVersion = moduleVersion,
|
||||
Domain = domain,
|
||||
Sign = sign ?? TapHttpSign.CreateDefaultSign(),
|
||||
Parser = parser ?? TapHttpParser.CreateDefaultParser(),
|
||||
ConnectTimeoutMillis = connectTimeoutMillis,
|
||||
ReadTimeoutMillis = readTimeoutMillis,
|
||||
WriteTimeoutMillis = writeTimeoutMillis,
|
||||
};
|
||||
return new TapHttp(httpConfig);
|
||||
}
|
||||
|
||||
public TapHttpBuilder ConnectTimeout(long connectTimeoutMillis)
|
||||
{
|
||||
this.connectTimeoutMillis = connectTimeoutMillis;
|
||||
return this;
|
||||
}
|
||||
|
||||
public TapHttpBuilder ReadTimeout(long readTimeoutMillis)
|
||||
{
|
||||
this.readTimeoutMillis = readTimeoutMillis;
|
||||
return this;
|
||||
}
|
||||
|
||||
public TapHttpBuilder WriteTimeout(long writeTimeoutMillis)
|
||||
{
|
||||
this.writeTimeoutMillis = writeTimeoutMillis;
|
||||
return this;
|
||||
}
|
||||
|
||||
public TapHttpBuilder Domain(string domain)
|
||||
{
|
||||
this.domain = domain;
|
||||
return this;
|
||||
}
|
||||
|
||||
public TapHttpBuilder Sign(ITapHttpSign sign)
|
||||
{
|
||||
this.sign = sign;
|
||||
return this;
|
||||
}
|
||||
|
||||
public TapHttpBuilder Parser(ITapHttpParser parser)
|
||||
{
|
||||
this.parser = parser;
|
||||
return this;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
internal class TapHttpConfig
|
||||
{
|
||||
public string ModuleName { get; set; }
|
||||
public string ModuleVersion { get; set; }
|
||||
public string Domain { get; set; }
|
||||
public ITapHttpSign Sign { get; set; }
|
||||
public ITapHttpParser Parser { get; set; }
|
||||
public ITapHttpParser RetryStrategy { get; set; }
|
||||
public long ConnectTimeoutMillis { get; set; }
|
||||
public long ReadTimeoutMillis { get; set; }
|
||||
public long WriteTimeoutMillis { get; set; }
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: ecc8edfa34fdd462a83db234c7940cec
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,114 @@
|
||||
|
||||
namespace TapSDK.Core.Standalone.Internal.Http
|
||||
{
|
||||
/// <summary>
|
||||
/// HTTP 错误常量类。
|
||||
/// </summary>
|
||||
public static class TapHttpErrorConstants
|
||||
{
|
||||
|
||||
// --------------------------------------------------------------------------------------------
|
||||
// 下面的错误信息是服务器端 [data.error] 字段的枚举
|
||||
// --------------------------------------------------------------------------------------------
|
||||
|
||||
/// <summary>
|
||||
/// 400 请阅读:[通用 API 人机验证协议](https://xindong.atlassian.net/wiki/spaces/TAP/pages/66032081)。
|
||||
/// </summary>
|
||||
public const string ERROR_CAPTCHA_NEEDS = "captcha.needs";
|
||||
|
||||
/// <summary>
|
||||
/// 400 人机验证未通过。
|
||||
/// </summary>
|
||||
public const string ERROR_CAPTCHA_FAILED = "captcha.failed";
|
||||
|
||||
/// <summary>
|
||||
/// 403 用户冻结。
|
||||
/// </summary>
|
||||
public const string ERROR_USER_IS_DEACTIVATED = "user_is_deactivated";
|
||||
|
||||
/// <summary>
|
||||
/// 400 请求缺少某个必需参数,包含一个不支持的参数或参数值,或者格式不正确。
|
||||
/// </summary>
|
||||
public const string ERROR_INVALID_REQUEST = "invalid_request";
|
||||
|
||||
/// <summary>
|
||||
/// 404 请求失败,请求所希望得到的资源未被在服务器上发现。在参数相同的情况下,不应该重复请求。
|
||||
/// </summary>
|
||||
public const string ERROR_NOT_FOUND = "not_found";
|
||||
|
||||
/// <summary>
|
||||
/// 403 用户没有对当前动作的权限,引导重新身份验证并不能提供任何帮助,而且这个请求也不应该被重复提交。
|
||||
/// </summary>
|
||||
public const string ERROR_FORBIDDEN = "forbidden";
|
||||
|
||||
/// <summary>
|
||||
/// 500 服务器出现异常情况,可稍等后重新尝试请求,但需有尝试上限,建议最多 3 次,如一直失败,则中断并告知用户。
|
||||
/// </summary>
|
||||
public const string ERROR_SERVER_ERROR = "server_error";
|
||||
|
||||
/// <summary>
|
||||
/// 400 客户端时间不正确,应请求服务器时间重新构造。
|
||||
/// </summary>
|
||||
public const string ERROR_INVALID_TIME = "invalid_time";
|
||||
|
||||
/// <summary>
|
||||
/// 400 请求是重放的。
|
||||
/// </summary>
|
||||
public const string ERROR_REPLAY_ATTACKS = "replay_attacks";
|
||||
|
||||
/// <summary>
|
||||
/// 401 client_id、client_secret 参数无效。
|
||||
/// </summary>
|
||||
public const string ERROR_INVALID_CLIENT = "invalid_client";
|
||||
|
||||
/// <summary>
|
||||
/// 400 提供的 Access Grant 是无效的、过期的或已撤销的,例如: Device Code 无效(一个设备授权码只能使用一次)等。
|
||||
/// </summary>
|
||||
public const string ERROR_INVALID_GRANT = "invalid_grant";
|
||||
|
||||
/// <summary>
|
||||
/// 400 服务器不支持 grant_type 参数的值。
|
||||
/// </summary>
|
||||
public const string ERROR_UNSUPPORTED_GRANT_TYPE = "unsupported_grant_type";
|
||||
|
||||
/// <summary>
|
||||
/// 400 服务器不支持 response_type 参数的值。
|
||||
/// </summary>
|
||||
public const string ERROR_UNSUPPORTED_RESPONSE_TYPE = "unsupported_response_type";
|
||||
|
||||
/// <summary>
|
||||
/// 400 服务器不支持 secret_type 参数的值。
|
||||
/// </summary>
|
||||
public const string ERROR_UNSUPPORTED_SECRET_TYPE = "unsupported_secret_type";
|
||||
|
||||
/// <summary>
|
||||
/// 400 Device Flow 中,设备通过 Device Code 换取 Access Token 的接口过于频繁。
|
||||
/// </summary>
|
||||
public const string ERROR_SLOW_DOWN = "slow_down";
|
||||
|
||||
/// <summary>
|
||||
/// 429 登录尝试次数过多,请稍后重试,用于 password 模式下出错次数过多。
|
||||
/// </summary>
|
||||
public const string ERROR_TOO_MANY_LOGIN_ATTEMPTS = "too_many_login_attempts";
|
||||
|
||||
/// <summary>
|
||||
/// 401 授权服务器拒绝请求,这个状态出现在拿着 token 请求用户资源时,如出现,客户端应退出本地的用户登录信息,引导用户重新登录。
|
||||
/// </summary>
|
||||
public const string ERROR_ACCESS_DENIED = "access_denied";
|
||||
|
||||
/// <summary>
|
||||
/// 401 认证内容无效 grant_type 为 password 的模式下,用户名或密码错误。
|
||||
/// </summary>
|
||||
public const string ERROR_INVALID_CREDENTIALS = "invalid_credentials";
|
||||
|
||||
/// <summary>
|
||||
/// 400 Device Flow 中,用户还没有对 Device Code 完成授权操作,按 interval 要求频率继续轮询,直到 expires_in 过期。
|
||||
/// </summary>
|
||||
public const string ERROR_AUTHORIZATION_PENDING = "authorization_pending";
|
||||
|
||||
/// <summary>
|
||||
/// 服务端业务异常,如:防沉迷 token 失效(code=20000)。
|
||||
/// </summary>
|
||||
public const string ERROR_BUSINESS_ERROR = "business_code_error";
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: a1ac950db03b74f6a8e85a3023f72fd6
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,79 @@
|
||||
using System;
|
||||
using System.Net;
|
||||
|
||||
namespace TapSDK.Core.Standalone.Internal.Http
|
||||
{
|
||||
public abstract class AbsTapHttpException : Exception
|
||||
{
|
||||
public AbsTapHttpException(string message) : base(message)
|
||||
{
|
||||
}
|
||||
|
||||
protected AbsTapHttpException(string message, Exception e) : base(message, e)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
public class TapHttpUnknownException : AbsTapHttpException
|
||||
{
|
||||
public TapHttpUnknownException(Exception e) : base("Unknown error", e)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
public class TapHttpNetworkErrorException : AbsTapHttpException
|
||||
{
|
||||
public TapHttpNetworkErrorException(string msg) : base("network error")
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
public class TapHttpInvalidResponseException : AbsTapHttpException
|
||||
{
|
||||
public HttpStatusCode StatusCode { get; }
|
||||
|
||||
public TapHttpInvalidResponseException(HttpStatusCode statusCode, string message)
|
||||
: base(message)
|
||||
{
|
||||
StatusCode = statusCode;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 表示 TapSDK 中与 Http 相关的服务端返回的错误信息。
|
||||
/// </summary>
|
||||
public class TapHttpServerException : AbsTapHttpException
|
||||
{
|
||||
/// <summary>
|
||||
/// 获取服务器返回的 HTTP 状态码。
|
||||
/// </summary>
|
||||
public HttpStatusCode StatusCode { get; }
|
||||
|
||||
/// <summary>
|
||||
/// 获取服务器返回的 Response。
|
||||
/// </summary>
|
||||
public TapHttpResponse TapHttpResponse { get; }
|
||||
|
||||
/// <summary>
|
||||
/// 获取服务器返回的 Response Error Data。
|
||||
/// </summary>
|
||||
public TapHttpErrorData ErrorData { get; }
|
||||
|
||||
/// <summary>
|
||||
/// 初始化 <see cref="TapHttpServerException"/> 类的新实例。
|
||||
/// </summary>
|
||||
/// <param name="statusCode">服务器返回的 HTTP 状态码。</param>
|
||||
/// <param name="code">与异常相关的自定义错误代码。</param>
|
||||
/// <param name="msg">错误消息。</param>
|
||||
/// <param name="error">服务器返回的错误标识符。</param>
|
||||
/// <param name="errorDescription">错误的详细描述。</param>
|
||||
public TapHttpServerException(HttpStatusCode statusCode, TapHttpResponse tapHttpResponse, TapHttpErrorData tapHttpErrorData)
|
||||
: base(tapHttpErrorData.Msg)
|
||||
{
|
||||
StatusCode = statusCode;
|
||||
TapHttpResponse = tapHttpResponse ?? throw new ArgumentNullException(nameof(tapHttpResponse));
|
||||
ErrorData = tapHttpErrorData ?? throw new ArgumentNullException(nameof(tapHttpErrorData));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: d3f8dc471e1b04052ba0833a4dec7cee
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,136 @@
|
||||
using System;
|
||||
using System.Net;
|
||||
using System.Net.Http;
|
||||
using System.Threading.Tasks;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace TapSDK.Core.Standalone.Internal.Http
|
||||
{
|
||||
/// <summary>
|
||||
/// 表示一个 HTTP 解析器的接口。
|
||||
/// Represents an HTTP parser interface.
|
||||
/// </summary>
|
||||
public interface ITapHttpParser
|
||||
{
|
||||
/// <summary>
|
||||
/// 解析 HTTP 响应。
|
||||
/// Parses the HTTP response.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">解析后返回的对象类型。The type of the object to return after parsing.</typeparam>
|
||||
/// <param name="response">HTTP 响应消息。The HTTP response message.</param>
|
||||
/// <returns>解析结果。The parsing result.</returns>
|
||||
Task<TapHttpResult<T>> Parse<T>(HttpResponseMessage response);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 提供 HTTP 解析功能的类。
|
||||
/// Class that provides HTTP parsing functionality.
|
||||
/// </summary>
|
||||
public class TapHttpParser
|
||||
{
|
||||
/// <summary>
|
||||
/// 创建默认的 HTTP 解析器。
|
||||
/// Creates a default HTTP parser.
|
||||
/// </summary>
|
||||
/// <returns>返回一个实现了 ITapHttpParser 接口的解析器。Returns a parser that implements the ITapHttpParser interface.</returns>
|
||||
public static ITapHttpParser CreateDefaultParser()
|
||||
{
|
||||
return new Default();
|
||||
}
|
||||
|
||||
public static ITapHttpParser CreateEventParser()
|
||||
{
|
||||
return new Event();
|
||||
}
|
||||
|
||||
private class Event : ITapHttpParser
|
||||
{
|
||||
public Task<TapHttpResult<T>> Parse<T>(HttpResponseMessage response)
|
||||
{
|
||||
_ = response ?? throw new ArgumentNullException(nameof(response));
|
||||
HttpStatusCode statusCode = response.StatusCode;
|
||||
if (statusCode >= HttpStatusCode.OK && statusCode < HttpStatusCode.MultipleChoices)
|
||||
{
|
||||
return Task.FromResult(TapHttpResult<T>.Success(default));
|
||||
}
|
||||
else
|
||||
{
|
||||
return Task.FromResult(TapHttpResult<T>.UnknownFailure(default));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private class Default : ITapHttpParser
|
||||
{
|
||||
/// <summary>
|
||||
/// 解析 HTTP 响应。
|
||||
/// Parses the HTTP response.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">解析后返回的对象类型。The type of the object to return after parsing.</typeparam>
|
||||
/// <param name="response">HTTP 响应消息。The HTTP response message.</param>
|
||||
/// <returns>解析结果。The parsing result.</returns>
|
||||
public async Task<TapHttpResult<T>> Parse<T>(HttpResponseMessage response)
|
||||
{
|
||||
_ = response ?? throw new ArgumentNullException(nameof(response));
|
||||
HttpStatusCode statusCode = response.StatusCode;
|
||||
// 读取响应内容
|
||||
// Read the response content
|
||||
string content = await response.Content.ReadAsStringAsync();
|
||||
|
||||
// 处理响应
|
||||
// Handle the response
|
||||
TapHttpResponse httpResponse;
|
||||
try
|
||||
{
|
||||
httpResponse = JsonConvert.DeserializeObject<TapHttpResponse>(content);
|
||||
// 设置当前服务端返回的事件戳
|
||||
if (httpResponse.Now > 0){
|
||||
TapHttpTime.ResetLastServerTime(httpResponse.Now);
|
||||
}
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
return TapHttpResult<T>.InvalidResponseFailure(new TapHttpInvalidResponseException(statusCode, "Failed to parse TapHttpResponse"));
|
||||
}
|
||||
if (httpResponse == null)
|
||||
{
|
||||
return TapHttpResult<T>.InvalidResponseFailure(new TapHttpInvalidResponseException(statusCode, "TapHttpResponse is null"));
|
||||
}
|
||||
|
||||
if (httpResponse.Success)
|
||||
{
|
||||
// 修正时间
|
||||
// Fix the time
|
||||
TapHttpTime.FixTime(httpResponse.Now);
|
||||
if (httpResponse.Data == null)
|
||||
{
|
||||
return TapHttpResult<T>.InvalidResponseFailure(new TapHttpInvalidResponseException(statusCode, "TapHttpResponse.Data is null"));
|
||||
}
|
||||
try
|
||||
{
|
||||
T data = httpResponse.Data.ToObject<T>();
|
||||
if (data == null)
|
||||
{
|
||||
return TapHttpResult<T>.InvalidResponseFailure(new TapHttpInvalidResponseException(statusCode, "TapHttpResponse.Data is null"));
|
||||
}
|
||||
return TapHttpResult<T>.Success(data);
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
return TapHttpResult<T>.InvalidResponseFailure(new TapHttpInvalidResponseException(statusCode, "Failed to parse TapHttpResponse.Data"));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
TapHttpErrorData httpErrorData = httpResponse.Data?.ToObject<TapHttpErrorData>();
|
||||
if (httpErrorData == null)
|
||||
{
|
||||
return TapHttpResult<T>.InvalidResponseFailure(new TapHttpInvalidResponseException(statusCode, "TapHttpErrorData is null"));
|
||||
}
|
||||
return TapHttpResult<T>.ServerFailure(new TapHttpServerException((HttpStatusCode)statusCode, httpResponse, httpErrorData));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: ab2c195f6902f4a6da00ffc255b8378d
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,37 @@
|
||||
using System;
|
||||
using Newtonsoft.Json;
|
||||
using Newtonsoft.Json.Linq;
|
||||
|
||||
namespace TapSDK.Core.Standalone.Internal.Http
|
||||
{
|
||||
|
||||
[Serializable]
|
||||
public class TapHttpResponse
|
||||
{
|
||||
[JsonProperty("data")]
|
||||
public JObject Data { get; private set; }
|
||||
|
||||
[JsonProperty("success")]
|
||||
public bool Success { get; private set; }
|
||||
|
||||
[JsonProperty("now")]
|
||||
public int Now { get; private set; }
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
public class TapHttpErrorData
|
||||
{
|
||||
[JsonProperty("code")]
|
||||
public int Code { get; private set; }
|
||||
|
||||
[JsonProperty("msg")]
|
||||
public string Msg { get; private set; }
|
||||
|
||||
[JsonProperty("error")]
|
||||
public string Error { get; private set; }
|
||||
|
||||
[JsonProperty("error_description")]
|
||||
public string ErrorDescription { get; private set; }
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 1c88fae64652a4577a0f71aafa0f468a
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,99 @@
|
||||
using System;
|
||||
using System.Net;
|
||||
|
||||
namespace TapSDK.Core.Standalone.Internal.Http
|
||||
{
|
||||
/// <summary>
|
||||
/// 表示 HTTP 错误类型的枚举。
|
||||
/// </summary>
|
||||
|
||||
/// <summary>
|
||||
/// 表示 TapSDK 中 HTTP 请求的结果。
|
||||
/// </summary>
|
||||
public class TapHttpResult<T>
|
||||
{
|
||||
/// <summary>
|
||||
/// 指示请求是否成功。
|
||||
/// </summary>
|
||||
public bool IsSuccess { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// HTTP 请求的响应内容。
|
||||
/// </summary>
|
||||
public T Data { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// 错误类型,区分网络错误、客户端错误、服务器错误等。
|
||||
/// </summary>
|
||||
public AbsTapHttpException HttpException { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// 私有构造函数,防止直接实例化。
|
||||
/// </summary>
|
||||
private TapHttpResult()
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 创建一个成功的 HTTP 请求结果。
|
||||
/// </summary>
|
||||
/// <param name="response">HTTP 响应的内容。</param>
|
||||
/// <returns>TapHttpResult 对象,表示成功的请求。</returns>
|
||||
public static TapHttpResult<T> Success(T data)
|
||||
{
|
||||
return new TapHttpResult<T>
|
||||
{
|
||||
IsSuccess = true,
|
||||
Data = data,
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 创建一个失败的 HTTP 请求结果,通常是网络错误或客户端错误。
|
||||
/// </summary>
|
||||
/// <param name="errorType">错误类型,例如网络错误或客户端错误。</param>
|
||||
/// <param name="exception">异常对象,用于传递错误详情(可选)。</param>
|
||||
/// <returns>TapHttpResult 对象,表示失败的请求。</returns>
|
||||
public static TapHttpResult<T> InvalidResponseFailure(TapHttpInvalidResponseException exception)
|
||||
{
|
||||
return new TapHttpResult<T>
|
||||
{
|
||||
IsSuccess = false,
|
||||
HttpException = exception
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 创建一个服务端返回的错误结果。
|
||||
/// </summary>
|
||||
/// <param name="httpException">包含详细服务端错误信息的异常对象。</param>
|
||||
/// <returns>TapHttpResult 对象,表示服务端错误的请求。</returns>
|
||||
public static TapHttpResult<T> ServerFailure(TapHttpServerException httpException)
|
||||
{
|
||||
return new TapHttpResult<T>
|
||||
{
|
||||
IsSuccess = false,
|
||||
HttpException = httpException,
|
||||
};
|
||||
}
|
||||
|
||||
public static TapHttpResult<T> UnknownFailure(TapHttpUnknownException httpException)
|
||||
{
|
||||
return new TapHttpResult<T>
|
||||
{
|
||||
IsSuccess = false,
|
||||
HttpException = httpException,
|
||||
};
|
||||
}
|
||||
|
||||
public static TapHttpResult<T> NetworkError(TapHttpNetworkErrorException httpException)
|
||||
{
|
||||
return new TapHttpResult<T>
|
||||
{
|
||||
IsSuccess = false,
|
||||
HttpException = httpException,
|
||||
};
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 4c7f303ab4c084015a8c2b75cb268c04
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,237 @@
|
||||
using System;
|
||||
using System.Net;
|
||||
using System.Threading;
|
||||
|
||||
namespace TapSDK.Core.Standalone.Internal.Http
|
||||
{
|
||||
/// <summary>
|
||||
/// 重试策略接口。
|
||||
/// </summary>
|
||||
public interface ITapHttpRetryStrategy
|
||||
{
|
||||
/// <summary>
|
||||
/// 获取下次重试的时间(毫秒)。
|
||||
/// </summary>
|
||||
/// <param name="errorType">错误类型。</param>
|
||||
/// <param name="e">异常信息。</param>
|
||||
/// <returns>下次重试的时间(毫秒),如果不重试返回 -1。</returns>
|
||||
long NextRetryMillis(AbsTapHttpException e);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 后退策略接口。
|
||||
/// </summary>
|
||||
public interface ITapHttpBackoffStrategy
|
||||
{
|
||||
/// <summary>
|
||||
/// 获取下一个后退的时间(毫秒)。
|
||||
/// </summary>
|
||||
/// <returns>下一个后退的时间(毫秒)。</returns>
|
||||
long NextBackoffMillis();
|
||||
|
||||
/// <summary>
|
||||
/// 判断是否可以重试无效时间。
|
||||
/// </summary>
|
||||
/// <returns>如果可以重试返回 true,否则返回 false。</returns>
|
||||
bool CanInvalidTimeRetry();
|
||||
|
||||
/// <summary>
|
||||
/// 重置策略状态。
|
||||
/// </summary>
|
||||
void Reset();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// HTTP 重试策略实现。
|
||||
/// </summary>
|
||||
public class TapHttpRetryStrategy
|
||||
{
|
||||
/// <summary>
|
||||
/// 创建默认重试策略。
|
||||
/// </summary>
|
||||
/// <param name="backoffStrategy">后退策略。</param>
|
||||
/// <returns>默认重试策略。</returns>
|
||||
public static ITapHttpRetryStrategy CreateDefault(ITapHttpBackoffStrategy backoffStrategy)
|
||||
{
|
||||
return new Default(backoffStrategy);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 创建不重试策略。
|
||||
/// </summary>
|
||||
/// <returns>不重试策略。</returns>
|
||||
public static ITapHttpRetryStrategy CreateNone()
|
||||
{
|
||||
return new None();
|
||||
}
|
||||
|
||||
private class None : ITapHttpRetryStrategy
|
||||
{
|
||||
public long NextRetryMillis(AbsTapHttpException e)
|
||||
{
|
||||
// 不重试返回 -1
|
||||
return -1L;
|
||||
}
|
||||
}
|
||||
|
||||
private class Default : ITapHttpRetryStrategy
|
||||
{
|
||||
private readonly ITapHttpBackoffStrategy backoffStrategy;
|
||||
|
||||
public Default(ITapHttpBackoffStrategy backoffStrategy)
|
||||
{
|
||||
this.backoffStrategy = backoffStrategy;
|
||||
}
|
||||
|
||||
public long NextRetryMillis(AbsTapHttpException e)
|
||||
{
|
||||
long nextRetryMillis = -1L;
|
||||
if (e is TapHttpServerException se)
|
||||
{
|
||||
// 处理服务器错误状态码
|
||||
if (se.StatusCode >= HttpStatusCode.InternalServerError && se.StatusCode <= (HttpStatusCode)599)
|
||||
{
|
||||
nextRetryMillis = backoffStrategy.NextBackoffMillis();
|
||||
}
|
||||
else if (TapHttpErrorConstants.ERROR_INVALID_TIME.Equals(se.ErrorData.Error))
|
||||
{
|
||||
// 修复时间并判断是否可以重试
|
||||
TapHttpTime.FixTime(se.TapHttpResponse.Now);
|
||||
if (backoffStrategy.CanInvalidTimeRetry())
|
||||
{
|
||||
nextRetryMillis = 0L; // 立马重试
|
||||
}
|
||||
}
|
||||
else if (TapHttpErrorConstants.ERROR_SERVER_ERROR.Equals(se.ErrorData.Error))
|
||||
{
|
||||
nextRetryMillis = backoffStrategy.NextBackoffMillis();
|
||||
}
|
||||
}
|
||||
else if (e is TapHttpInvalidResponseException ie)
|
||||
{
|
||||
if (ie.StatusCode >= HttpStatusCode.InternalServerError && ie.StatusCode <= (HttpStatusCode)599)
|
||||
{
|
||||
nextRetryMillis = backoffStrategy.NextBackoffMillis();
|
||||
}
|
||||
}
|
||||
return nextRetryMillis;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// HTTP 后退策略实现。
|
||||
/// </summary>
|
||||
public class TapHttpBackoffStrategy
|
||||
{
|
||||
/// <summary>
|
||||
/// 创建固定后退策略。
|
||||
/// </summary>
|
||||
/// <param name="maxCount">最大重试次数。</param>
|
||||
/// <returns>固定后退策略。</returns>
|
||||
public static ITapHttpBackoffStrategy CreateFixed(int maxCount)
|
||||
{
|
||||
return new Fixed(maxCount);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 创建指数后退策略。
|
||||
/// </summary>
|
||||
/// <returns>指数后退策略。</returns>
|
||||
public static ITapHttpBackoffStrategy CreateExponential()
|
||||
{
|
||||
return new Exponential();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 创建不后退策略。
|
||||
/// </summary>
|
||||
/// <returns>不后退策略。</returns>
|
||||
public static ITapHttpBackoffStrategy CreateNone()
|
||||
{
|
||||
return new None();
|
||||
}
|
||||
|
||||
private abstract class Base : ITapHttpBackoffStrategy
|
||||
{
|
||||
protected int CanTimeDeltaRetry = 1;
|
||||
|
||||
public abstract long NextBackoffMillis();
|
||||
|
||||
public abstract void Reset();
|
||||
|
||||
/// <summary>
|
||||
/// 判断是否可以重试无效时间。
|
||||
/// </summary>
|
||||
/// <returns>如果可以重试返回 true,否则返回 false。</returns>
|
||||
public bool CanInvalidTimeRetry()
|
||||
{
|
||||
return Interlocked.CompareExchange(ref CanTimeDeltaRetry, 0, 1) == 1;
|
||||
}
|
||||
}
|
||||
|
||||
private class Fixed : Base
|
||||
{
|
||||
private readonly int _maxCount;
|
||||
private int CurrentCount = 0;
|
||||
|
||||
public Fixed(int maxCount)
|
||||
{
|
||||
_maxCount = maxCount;
|
||||
}
|
||||
|
||||
public override long NextBackoffMillis()
|
||||
{
|
||||
if (++CurrentCount < _maxCount)
|
||||
{
|
||||
return 100L; // 固定的重试时间 100ms
|
||||
}
|
||||
return -1L; // 达到最大重试次数,返回 -1
|
||||
}
|
||||
|
||||
public override void Reset()
|
||||
{
|
||||
CurrentCount = 0;
|
||||
Interlocked.Exchange(ref CanTimeDeltaRetry, 1);
|
||||
}
|
||||
}
|
||||
|
||||
private class Exponential : Base
|
||||
{
|
||||
private static readonly long INIT_INTERVAL_MILLIS = 2 * 1000L; // 初始时间 2 秒
|
||||
private static readonly long MAX_INTERVAL_MILLIS = 600 * 1000L; // 最大时间 600 秒
|
||||
private static readonly int MULTIPLIER = 2; // 指数倍数
|
||||
|
||||
private long CurrentIntervalMillis = INIT_INTERVAL_MILLIS;
|
||||
|
||||
public override long NextBackoffMillis()
|
||||
{
|
||||
if (CurrentIntervalMillis * MULTIPLIER > MAX_INTERVAL_MILLIS)
|
||||
{
|
||||
return MAX_INTERVAL_MILLIS; // 返回最大时间
|
||||
}
|
||||
CurrentIntervalMillis *= MULTIPLIER; // 增加当前时间
|
||||
return CurrentIntervalMillis;
|
||||
}
|
||||
|
||||
public override void Reset()
|
||||
{
|
||||
CurrentIntervalMillis = INIT_INTERVAL_MILLIS / MULTIPLIER; // 重置当前时间
|
||||
Interlocked.Exchange(ref CanTimeDeltaRetry, 1);
|
||||
}
|
||||
}
|
||||
|
||||
private class None : Base
|
||||
{
|
||||
public override long NextBackoffMillis()
|
||||
{
|
||||
return -1L; // 不后退,返回 -1
|
||||
}
|
||||
|
||||
public override void Reset()
|
||||
{
|
||||
Interlocked.Exchange(ref CanTimeDeltaRetry, 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 88883d9f22f9042c19eb909edcc7f851
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,141 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Net.Http;
|
||||
using System.Security.Cryptography;
|
||||
using System.Text;
|
||||
|
||||
namespace TapSDK.Core.Standalone.Internal.Http
|
||||
{
|
||||
|
||||
/// <summary>
|
||||
/// 定义 HTTP 签名相关操作的接口。
|
||||
/// </summary>
|
||||
public interface ITapHttpSign
|
||||
{
|
||||
/// <summary>
|
||||
/// 获取固定的 HTTP 请求头信息。
|
||||
/// </summary>
|
||||
/// <returns>返回包含固定请求头的字典。</returns>
|
||||
Dictionary<string, string> GetFixedHeaders(string url, HttpMethod method, string moduleName, string moduleVersion, bool enableAuthorization);
|
||||
|
||||
/// <summary>
|
||||
/// 获取固定的查询参数。
|
||||
/// </summary>
|
||||
/// <returns>返回包含固定查询参数的字典。</returns>
|
||||
Dictionary<string, string> GetFixedQueryParams();
|
||||
|
||||
/// <summary>
|
||||
/// 对 HTTP 请求数据进行签名处理。
|
||||
/// </summary>
|
||||
/// <param name="signData">包含请求数据的 <see cref="TapHttpSignData"/> 对象。</param>
|
||||
void Sign(HttpRequestMessage signData);
|
||||
}
|
||||
|
||||
public class TapHttpSign
|
||||
{
|
||||
public static ITapHttpSign CreateDefaultSign()
|
||||
{
|
||||
return new Default();
|
||||
}
|
||||
|
||||
public static ITapHttpSign CreateNoneSign()
|
||||
{
|
||||
return new None();
|
||||
}
|
||||
|
||||
private class Default : ITapHttpSign
|
||||
{
|
||||
public Dictionary<string, string> GetFixedHeaders(string url, HttpMethod method, string moduleName, string moduleVersion, bool enableAuthorization)
|
||||
{
|
||||
_ = method ?? throw new ArgumentNullException(nameof(method));
|
||||
_ = moduleName ?? throw new ArgumentNullException(nameof(moduleName));
|
||||
_ = moduleVersion ?? throw new ArgumentNullException(nameof(moduleVersion));
|
||||
|
||||
if (method == HttpMethod.Post || method == HttpMethod.Get)
|
||||
{
|
||||
Dictionary<string, string> headers = new Dictionary<string, string>
|
||||
{
|
||||
{ "X-Tap-PN", "TapSDK" },
|
||||
{ "X-Tap-Device-Id", TapHttpUtils.GenerateDeviceId()},
|
||||
{ "X-Tap-Platform", "PC"},
|
||||
{ "X-Tap-SDK-Module", moduleName},
|
||||
{ "X-Tap-SDK-Module-Version", moduleVersion},
|
||||
{ "X-Tap-SDK-Artifact", "Unity"},
|
||||
{ "X-Tap-Ts", TapHttpUtils.GenerateTime()},
|
||||
{ "X-Tap-Nonce", TapHttpUtils.GenerateNonce()},
|
||||
{ "X-Tap-Lang", TapHttpUtils.GenerateLanguage()},
|
||||
{ "User-Agent", TapHttpUtils.GenerateUserAgent()},
|
||||
};
|
||||
string currentUserId = TapCoreStandalone.User?.Id;
|
||||
if(currentUserId != null && currentUserId.Length > 0) {
|
||||
headers.Add("X-Tap-SDK-Game-User-Id", currentUserId);
|
||||
}
|
||||
if (enableAuthorization)
|
||||
{
|
||||
string authorization = TapHttpUtils.GenerateAuthorization(url, method.ToString());
|
||||
if (authorization != null)
|
||||
{
|
||||
headers.Add("Authorization", authorization);
|
||||
}
|
||||
}
|
||||
return headers;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public Dictionary<string, string> GetFixedQueryParams()
|
||||
{
|
||||
return new Dictionary<string, string>
|
||||
{
|
||||
{ "client_id", TapCoreStandalone.coreOptions.clientId }
|
||||
};
|
||||
}
|
||||
|
||||
public async void Sign(HttpRequestMessage requestMessage)
|
||||
{
|
||||
_ = requestMessage ?? throw new ArgumentNullException(nameof(requestMessage));
|
||||
|
||||
string clientToken = TapCoreStandalone.coreOptions.clientToken;
|
||||
string methodPart = requestMessage.Method.Method;
|
||||
string urlPathAndQueryPart = requestMessage.RequestUri.PathAndQuery;
|
||||
var headerKeys = requestMessage.Headers
|
||||
.Where(h => h.Key.StartsWith("x-tap-", StringComparison.OrdinalIgnoreCase))
|
||||
.OrderBy(h => h.Key.ToLowerInvariant())
|
||||
.Select(h => $"{h.Key.ToLowerInvariant()}:{string.Join(",", h.Value)}")
|
||||
.ToList();
|
||||
string headersPart = string.Join("\n", headerKeys);
|
||||
string bodyPart = string.Empty;
|
||||
if (requestMessage.Content != null)
|
||||
{
|
||||
bodyPart = await requestMessage.Content.ReadAsStringAsync();
|
||||
}
|
||||
string signParts = methodPart + "\n" + urlPathAndQueryPart + "\n" + headersPart + "\n" + bodyPart + "\n";
|
||||
using (var hmac = new HMACSHA256(Encoding.UTF8.GetBytes(clientToken)))
|
||||
{
|
||||
byte[] hash = hmac.ComputeHash(Encoding.UTF8.GetBytes(signParts));
|
||||
string sign = Convert.ToBase64String(hash);
|
||||
requestMessage.Headers.Add("X-Tap-Sign", sign);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private class None : ITapHttpSign
|
||||
{
|
||||
public Dictionary<string, string> GetFixedHeaders(string url, HttpMethod method, string moduleName, string moduleVersion, bool enableAuthorization)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
public Dictionary<string, string> GetFixedQueryParams()
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
public void Sign(HttpRequestMessage signData)
|
||||
{
|
||||
// do nothing
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: a87a746ca4bed45e09d0997c60660752
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,196 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Net.Http;
|
||||
using System.Text;
|
||||
using TapSDK.Core.Internal.Log;
|
||||
using TapSDK.Core.Standalone.Internal.Service;
|
||||
using UnityEngine;
|
||||
|
||||
namespace TapSDK.Core.Standalone.Internal.Http
|
||||
{
|
||||
public static class TapHttpTime
|
||||
{
|
||||
private static int timeOffset = 0;
|
||||
private static void SetTimeOffset(int offset)
|
||||
{
|
||||
timeOffset = offset;
|
||||
}
|
||||
|
||||
// 获取当前时间的秒级时间戳
|
||||
public static int GetCurrentTime()
|
||||
{
|
||||
DateTime epochStart = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc);
|
||||
TimeSpan timeSpan = DateTime.UtcNow - epochStart;
|
||||
return (int)timeSpan.TotalSeconds + timeOffset;
|
||||
}
|
||||
|
||||
public static void FixTime(int time)
|
||||
{
|
||||
if (time == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
SetTimeOffset(time - (int)(DateTime.UtcNow - new DateTime(1970, 1, 1)).TotalSeconds);
|
||||
}
|
||||
|
||||
|
||||
// 服务端同步的时间戳
|
||||
private static long LastServerTime = 0;
|
||||
// 设置服务端时间时,当前应用启动时间
|
||||
private static long LastStartUpTime = 0 ;
|
||||
internal static void ResetLastServerTime(long time){
|
||||
LastServerTime = time;
|
||||
LastStartUpTime = (long) Time.realtimeSinceStartup;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 根据服务端时间获取当前时间戳,单位:秒
|
||||
/// </summary>
|
||||
/// <returns>当前时间戳,当服务端时间未设置过时,返回值为 0</returns>
|
||||
public static long GetCurrentServerTime(){
|
||||
if(LastServerTime == 0){
|
||||
return 0;
|
||||
}
|
||||
long startUpTime = (long) Time.realtimeSinceStartup;
|
||||
return LastServerTime + startUpTime - LastStartUpTime;
|
||||
}
|
||||
}
|
||||
|
||||
public static class TapHttpUtils
|
||||
{
|
||||
|
||||
private static readonly TapLog tapLog = new TapLog("Http");
|
||||
|
||||
internal static string GenerateNonce()
|
||||
{
|
||||
string chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
|
||||
char[] nonce = new char[10];
|
||||
for (int i = 0; i < 10; i++)
|
||||
{
|
||||
nonce[i] = chars[UnityEngine.Random.Range(0, chars.Length)];
|
||||
}
|
||||
return new string(nonce);
|
||||
}
|
||||
|
||||
public static string GenerateUserAgent()
|
||||
{
|
||||
return $"TapSDK-Unity/{TapTapSDK.Version}";
|
||||
}
|
||||
|
||||
internal static string GenerateTime()
|
||||
{
|
||||
return TapHttpTime.GetCurrentTime().ToString();
|
||||
}
|
||||
|
||||
internal static string GenerateLanguage()
|
||||
{
|
||||
return TapLocalizeManager.GetCurrentLanguageString();
|
||||
}
|
||||
|
||||
internal static string GenerateDeviceId()
|
||||
{
|
||||
return SystemInfo.deviceUniqueIdentifier;
|
||||
}
|
||||
|
||||
internal static string GenerateAuthorization(string url, string method)
|
||||
{
|
||||
Type interfaceType = typeof(ITapLoginService);
|
||||
Type[] initTaskTypes = AppDomain.CurrentDomain.GetAssemblies()
|
||||
.Where(asssembly =>
|
||||
{
|
||||
string fullName = asssembly.GetName().FullName;
|
||||
return fullName.StartsWith("TapSDK.Login.Standalone.Runtime");
|
||||
})
|
||||
.SelectMany(assembly => assembly.GetTypes())
|
||||
.Where(clazz =>
|
||||
{
|
||||
return interfaceType.IsAssignableFrom(clazz) && clazz.IsClass;
|
||||
})
|
||||
.ToArray();
|
||||
if (initTaskTypes.Length != 1)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
try
|
||||
{
|
||||
ITapLoginService tapLoginService = Activator.CreateInstance(initTaskTypes[0]) as ITapLoginService;
|
||||
string authorization = tapLoginService.ObtainAuthorizationAsync(url, method);
|
||||
return authorization;
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
TapLog.Error("e = " + e);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public static void PrintRequest(HttpClient client, HttpRequestMessage request)
|
||||
{
|
||||
if (client == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
if (request == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.AppendLine("=== HTTP Request Start ===");
|
||||
sb.AppendLine($"URL: {request.RequestUri}");
|
||||
sb.AppendLine($"Method: {request.Method}");
|
||||
sb.AppendLine($"Headers: ");
|
||||
foreach (var header in client.DefaultRequestHeaders)
|
||||
{
|
||||
sb.AppendLine($"\t{header.Key}: {string.Join(",", header.Value.ToArray())}");
|
||||
}
|
||||
foreach (var header in request.Headers)
|
||||
{
|
||||
sb.AppendLine($"\t{header.Key}: {string.Join(",", header.Value.ToArray())}");
|
||||
}
|
||||
if (request.Content != null)
|
||||
{
|
||||
foreach (var header in request.Content.Headers)
|
||||
{
|
||||
sb.AppendLine($"\t{header.Key}: {string.Join(",", header.Value.ToArray())}");
|
||||
}
|
||||
}
|
||||
string contentString = null;
|
||||
try
|
||||
{
|
||||
contentString = request.Content.ReadAsStringAsync().Result;
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
}
|
||||
if (!string.IsNullOrEmpty(contentString))
|
||||
{
|
||||
sb.AppendLine($"Content: \n{contentString}");
|
||||
}
|
||||
sb.AppendLine("=== HTTP Request End ===");
|
||||
tapLog.Log($"HTTP Request [{request.RequestUri.PathAndQuery}]", sb.ToString());
|
||||
}
|
||||
|
||||
public static void PrintResponse(HttpResponseMessage response)
|
||||
{
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.AppendLine("=== HTTP Response Start ===");
|
||||
sb.AppendLine($"URL: {response.RequestMessage.RequestUri}");
|
||||
sb.AppendLine($"Status Code: {response.StatusCode}");
|
||||
string contentString = null;
|
||||
try
|
||||
{
|
||||
contentString = response.Content.ReadAsStringAsync().Result;
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
}
|
||||
if (!string.IsNullOrEmpty(contentString))
|
||||
{
|
||||
sb.AppendLine($"Content: {contentString}");
|
||||
}
|
||||
sb.AppendLine("=== HTTP Response End ===");
|
||||
tapLog.Log($"HTTP Response [{response.RequestMessage.RequestUri.PathAndQuery}]", sb.ToString());
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 0fcfd5c4fd9c24270b68ffa8cd4f93a9
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
43
Assets/TapSDK/Core/Standalone/Runtime/Internal/Identity.cs
Normal file
43
Assets/TapSDK/Core/Standalone/Runtime/Internal/Identity.cs
Normal file
@@ -0,0 +1,43 @@
|
||||
using System;
|
||||
using UnityEngine;
|
||||
|
||||
namespace TapSDK.Core.Standalone.Internal {
|
||||
public class Identity {
|
||||
public static readonly string DEVICE_ID_KEY = "tapdb_unique_id";
|
||||
public static readonly string PERSISTENT_ID_KEY = "tapdb_persist_id";
|
||||
public static readonly string INSTALLATION_ID_KEY = "tapdb_install_id";
|
||||
|
||||
public static string DeviceId {
|
||||
get {
|
||||
string deviceId = TapCoreStandalone.Prefs.Get<string>(DEVICE_ID_KEY);
|
||||
if (string.IsNullOrWhiteSpace(deviceId)) {
|
||||
deviceId = SystemInfo.deviceUniqueIdentifier;
|
||||
TapCoreStandalone.Prefs.Set(DEVICE_ID_KEY, deviceId);
|
||||
}
|
||||
return deviceId;
|
||||
}
|
||||
}
|
||||
|
||||
public static string PersistentId {
|
||||
get {
|
||||
string persistentId = TapCoreStandalone.Prefs.Get<string>(PERSISTENT_ID_KEY);
|
||||
if (string.IsNullOrWhiteSpace(persistentId)) {
|
||||
persistentId = Guid.NewGuid().ToString();
|
||||
TapCoreStandalone.Prefs.Set(PERSISTENT_ID_KEY, persistentId);
|
||||
}
|
||||
return persistentId;
|
||||
}
|
||||
}
|
||||
|
||||
public static string InstallationId {
|
||||
get {
|
||||
string installationId = TapCoreStandalone.Prefs.Get<string>(INSTALLATION_ID_KEY);
|
||||
if (string.IsNullOrWhiteSpace(installationId)) {
|
||||
installationId = Guid.NewGuid().ToString();
|
||||
TapCoreStandalone.Prefs.Set(INSTALLATION_ID_KEY, installationId);
|
||||
}
|
||||
return installationId;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 2dafe2e45386e48e797494ee95c9a4d3
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 93aaba645bba840eb88fe51d138c10fb
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,121 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Newtonsoft.Json;
|
||||
using TapSDK.Core.Internal.Log;
|
||||
|
||||
namespace TapSDK.Core.Standalone.Internal.Openlog
|
||||
{
|
||||
public class TapAppDurationStandalone
|
||||
{
|
||||
private const string MODULE_NAME = "app_duration";
|
||||
|
||||
private static readonly TapLog log = new TapLog("AppDuration");
|
||||
|
||||
public static void Enable()
|
||||
{
|
||||
#if UNITY_STANDALONE
|
||||
// log.Log("Enable");
|
||||
List<string> modules = new List<string> { MODULE_NAME };
|
||||
TapOpenlogWrapper.TdkEnableModules(JsonConvert.SerializeObject(modules));
|
||||
#endif
|
||||
}
|
||||
|
||||
public static void Disable()
|
||||
{
|
||||
#if UNITY_STANDALONE
|
||||
// log.Log("Disable");
|
||||
List<string> modules = new List<string> { MODULE_NAME };
|
||||
TapOpenlogWrapper.TdkDisableModules(JsonConvert.SerializeObject(modules));
|
||||
#endif
|
||||
}
|
||||
|
||||
public static void SetExtraAppDurationParams(Dictionary<string, string> param)
|
||||
{
|
||||
#if UNITY_STANDALONE
|
||||
log.Log("SetExtraAppDurationParams param: ", JsonConvert.SerializeObject(param));
|
||||
if (param == null)
|
||||
{
|
||||
log.Warning("SetExtraAppDurationParams param is null");
|
||||
return;
|
||||
}
|
||||
TapOpenlogWrapper.TdkSetExtraAppDurationParams(JsonConvert.SerializeObject(param));
|
||||
#endif
|
||||
}
|
||||
|
||||
public static void OnLogin(string openId)
|
||||
{
|
||||
#if UNITY_STANDALONE
|
||||
log.Log("OnLogin openId: " + openId);
|
||||
if (openId == null)
|
||||
{
|
||||
log.Warning("OnLogin openId is null");
|
||||
return;
|
||||
}
|
||||
Dictionary<string, string> userInfo = new Dictionary<string, string>
|
||||
{
|
||||
{TapOpenlogParamConstants.PARAM_OPEN_ID, openId}
|
||||
};
|
||||
TapOpenlogWrapper.TdkOnLogin(JsonConvert.SerializeObject(userInfo));
|
||||
#endif
|
||||
}
|
||||
|
||||
public static void OnLogout()
|
||||
{
|
||||
#if UNITY_STANDALONE
|
||||
log.Log("OnLogout");
|
||||
TapOpenlogWrapper.TdkOnLogout();
|
||||
#endif
|
||||
}
|
||||
|
||||
public static void OnForeground()
|
||||
{
|
||||
#if UNITY_STANDALONE
|
||||
log.Log("OnForeground");
|
||||
TapOpenlogWrapper.TdkOnForeground();
|
||||
#endif
|
||||
}
|
||||
|
||||
public static void OnBackground()
|
||||
{
|
||||
#if UNITY_STANDALONE
|
||||
log.Log("OnBackground");
|
||||
TapOpenlogWrapper.TdkOnBackground();
|
||||
#endif
|
||||
}
|
||||
|
||||
public static string Version()
|
||||
{
|
||||
|
||||
string v = null;
|
||||
#if UNITY_STANDALONE
|
||||
try
|
||||
{
|
||||
v = TapOpenlogWrapper.GetTdkVersion();
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
log.Error("GetVersion error: " + e.Message);
|
||||
}
|
||||
log.Log("Version: " + v);
|
||||
#endif
|
||||
return v;
|
||||
}
|
||||
|
||||
public static string GitCommit()
|
||||
{
|
||||
string v = null;
|
||||
#if UNITY_STANDALONE
|
||||
try
|
||||
{
|
||||
v = TapOpenlogWrapper.GetTdkGitCommit();
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
log.Error("GitCommit error: " + e.Message);
|
||||
}
|
||||
log.Log("GitCommit: " + v);
|
||||
#endif
|
||||
return v;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: dd48e24007af84b75a1587cb282be168
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,107 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace TapSDK.Core.Standalone.Internal.Openlog
|
||||
{
|
||||
internal class TapCoreTracker
|
||||
{
|
||||
|
||||
private const string ACTION_INIT = "init";
|
||||
private const string ACTION_START = "start";
|
||||
private const string ACTION_SUCCESS = "success";
|
||||
private const string ACTION_FAIL = "fail";
|
||||
private const string ACTION_CANCEL = "cancel";
|
||||
|
||||
internal static string SUCCESS_TYPE_RESTART = "restart";
|
||||
internal static string SUCCESS_TYPE_INIT = "init";
|
||||
|
||||
internal static string METHOD_LAUNCHER = "isLaunchedFromTapTapPC";
|
||||
|
||||
private static TapCoreTracker instance;
|
||||
|
||||
private TapOpenlogStandalone openlog;
|
||||
|
||||
private TapCoreTracker()
|
||||
{
|
||||
openlog = new TapOpenlogStandalone("TapSDKCore", TapTapSDK.Version);
|
||||
}
|
||||
|
||||
public static TapCoreTracker Instance
|
||||
{
|
||||
get
|
||||
{
|
||||
if (instance == null)
|
||||
{
|
||||
instance = new TapCoreTracker();
|
||||
}
|
||||
return instance;
|
||||
}
|
||||
}
|
||||
|
||||
internal void TrackInit()
|
||||
{
|
||||
ReportLog(ACTION_INIT);
|
||||
}
|
||||
|
||||
internal void TrackStart(string funcNace, string seesionId)
|
||||
{
|
||||
Dictionary<string, string> parameters = new Dictionary<string, string>
|
||||
{
|
||||
{ "func_name", funcNace },
|
||||
{ "session_id", seesionId },
|
||||
};
|
||||
ReportLog(ACTION_START, new Dictionary<string, string>()
|
||||
{
|
||||
{ "args", JsonConvert.SerializeObject(parameters) }
|
||||
});
|
||||
}
|
||||
|
||||
internal void TrackSuccess(string funcNace, string seesionId, string successType)
|
||||
{
|
||||
Dictionary<string, string> parameters = new Dictionary<string, string>
|
||||
{
|
||||
{ "func_name", funcNace },
|
||||
{ "session_id", seesionId },
|
||||
{ "launch_success_type", successType }
|
||||
};
|
||||
ReportLog(ACTION_SUCCESS, new Dictionary<string, string>()
|
||||
{
|
||||
{ "args", JsonConvert.SerializeObject(parameters) }
|
||||
});
|
||||
}
|
||||
|
||||
internal void TrackCancel(string funcNace, string seesionId)
|
||||
{
|
||||
Dictionary<string, string> parameters = new Dictionary<string, string>
|
||||
{
|
||||
{ "func_name", funcNace },
|
||||
{ "session_id", seesionId },
|
||||
};
|
||||
ReportLog(ACTION_CANCEL, new Dictionary<string, string>()
|
||||
{
|
||||
{ "args", JsonConvert.SerializeObject(parameters) }
|
||||
});
|
||||
}
|
||||
|
||||
internal void TrackFailure(string funcNace, string seesionId, int errorCode = -1, string errorMessage = null)
|
||||
{
|
||||
Dictionary<string, string> parameters = new Dictionary<string, string>
|
||||
{
|
||||
{ "func_name", funcNace },
|
||||
{ "session_id", seesionId },
|
||||
{ "error_code", errorCode.ToString() },
|
||||
{ "error_msg", errorMessage }
|
||||
};
|
||||
ReportLog(ACTION_FAIL, new Dictionary<string, string>()
|
||||
{
|
||||
{ "args", JsonConvert.SerializeObject(parameters) }
|
||||
});
|
||||
}
|
||||
|
||||
private void ReportLog(string action, Dictionary<string, string> parameters = null)
|
||||
{
|
||||
openlog.LogBusiness(action, parameters);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 105a74e032b41b84a841ec52a2cdbb87
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,102 @@
|
||||
namespace TapSDK.Core.Standalone.Internal.Openlog
|
||||
{
|
||||
internal class TapOpenlogParamConstants
|
||||
{
|
||||
public const string PARAM_ACTION = "action";
|
||||
// 该条日志的唯一标识 d
|
||||
public const string PARAM_T_LOG_ID = "t_log_id";
|
||||
|
||||
// 客户端时区,eg:Asia/Shanghai d
|
||||
public const string PARAM_TIMEZONE = "timezone";
|
||||
|
||||
// 客户端生成的时间戳,毫秒级 d
|
||||
public const string PARAM_TIMESTAMP = "timestamp";
|
||||
|
||||
// 应用包名 g
|
||||
public const string PARAM_APP_PACKAGE_NAME = "app_package_name";
|
||||
|
||||
// 应用版本字符串 g
|
||||
public const string PARAM_APP_VERSION = "app_version";
|
||||
|
||||
// 应用版本(数字) g
|
||||
public const string PARAM_APP_VERSION_CODE = "app_version_code";
|
||||
|
||||
// 固定一个枚举值: TapSDK g
|
||||
public const string PARAM_PN = "pn";
|
||||
|
||||
// SDK接入项目具体模块枚举值 d
|
||||
public const string PARAM_TAPSDK_PROJECT = "tapsdk_project";
|
||||
|
||||
// SDK 模块版本号 d
|
||||
public const string PARAM_TAPSDK_VERSION = "tapsdk_version";
|
||||
|
||||
// SDK 产物类型 d
|
||||
public const string PARAM_TAPSDK_ARTIFACT = "tapsdk_artifact";
|
||||
|
||||
// SDK 运行平台 g
|
||||
public const string PARAM_PLATFORM = "platform";
|
||||
|
||||
// SDK设置的地区,例如 zh_CN d
|
||||
public const string PARAM_SDK_LOCALE = "sdk_locale";
|
||||
|
||||
// 游戏账号 ID(非角色 ID)d
|
||||
public const string PARAM_GAME_USER_ID = "game_user_id";
|
||||
|
||||
// SDK生成的设备全局唯一标识 d
|
||||
public const string PARAM_GID = "gid";
|
||||
|
||||
// SDK生成的设备唯一标识 d
|
||||
public const string PARAM_DEVICE_ID = "device_id";
|
||||
|
||||
// SDK生成的设备一次安装的唯一标识 d
|
||||
public const string PARAM_INSTALL_UUID = "install_uuid";
|
||||
|
||||
// 设备品牌,eg: Xiaomi d
|
||||
public const string PARAM_DV = "dv";
|
||||
|
||||
// 设备品牌型号,eg:21051182C d
|
||||
public const string PARAM_MD = "md";
|
||||
|
||||
// 设备CPU型号,eg:arm64-v8a d
|
||||
public const string PARAM_CPU = "cpu";
|
||||
|
||||
// 支持 CPU 架构,eg:arm64-v8a d
|
||||
public const string PARAM_CPU_ABIS = "cpu_abis";
|
||||
|
||||
// 设备操作系统 d
|
||||
public const string PARAM_OS = "os";
|
||||
|
||||
// 设备操作系统版本 d
|
||||
public const string PARAM_SV = "sv";
|
||||
|
||||
// 物理设备真实屏幕分辨率宽 g
|
||||
public const string PARAM_WIDTH = "width";
|
||||
|
||||
// 物理设备真实屏幕分辨率高 g
|
||||
public const string PARAM_HEIGHT = "height";
|
||||
|
||||
// 设备可用存储空间(磁盘),单位B d
|
||||
public const string PARAM_ROM = "rom";
|
||||
|
||||
// 设备可用内存,单位B d
|
||||
public const string PARAM_RAM = "ram";
|
||||
|
||||
// 设备总存储空间(磁盘),单位B g
|
||||
public const string PARAM_TOTAL_ROM = "total_rom";
|
||||
|
||||
// 设备总内存,单位B g
|
||||
public const string PARAM_TOTAL_RAM = "total_ram";
|
||||
|
||||
// 芯片型号,eg:Qualcomm Technologies, Inc SM7250 g
|
||||
public const string PARAM_HARDWARE = "hardware";
|
||||
|
||||
// taptap的用户ID的外显ID(加密)d
|
||||
public const string PARAM_OPEN_ID = "open_id";
|
||||
|
||||
// 网络类型,eg:wifi, mobile
|
||||
public const string PARAM_NETWORK_TYPE = "network_type";
|
||||
|
||||
// SDK进程粒度的本地日志 session_id
|
||||
public const string PARAM_P_SESSION_ID = "p_session_id";
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: cef15b6df966846ab9959061ba833aa9
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,289 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Runtime.InteropServices;
|
||||
using AOT;
|
||||
using Newtonsoft.Json;
|
||||
using TapSDK.Core.Internal.Log;
|
||||
using TapSDK.Core.Internal.Utils;
|
||||
using TapSDK.Core.Standalone.Internal.Http;
|
||||
using UnityEngine;
|
||||
#if UNITY_STANDALONE
|
||||
using static TapSDK.Core.Standalone.Internal.Openlog.TapOpenlogWrapper;
|
||||
#endif
|
||||
|
||||
namespace TapSDK.Core.Standalone.Internal.Openlog
|
||||
{
|
||||
public class TapOpenlogStandalone
|
||||
{
|
||||
public static string openid = "";
|
||||
private static readonly Dictionary<string, string> generalParameter =
|
||||
new Dictionary<string, string>();
|
||||
private static readonly Dictionary<string, System.Object> openlogStartParameter =
|
||||
new Dictionary<string, System.Object>();
|
||||
private readonly string sdkProjectName;
|
||||
private readonly string sdkProjectVersion;
|
||||
private static readonly bool isRND = false;
|
||||
private static readonly TapLog log = new TapLog(module: "Openlog");
|
||||
#if UNITY_STANDALONE
|
||||
private static readonly CommonVariablesGetter commonVariablesGetter =
|
||||
new CommonVariablesGetter(GetCommonVariables);
|
||||
private static readonly FreeStringCallback freeString = new FreeStringCallback(FreeString);
|
||||
#endif
|
||||
|
||||
public static void Init()
|
||||
{
|
||||
#if UNITY_STANDALONE
|
||||
InitGeneralParameter();
|
||||
InitOpenlogStartParameter();
|
||||
// 初始化 C++ SDK 日志回调,将日志桥接到 Unity Debug.Log
|
||||
InitLogger(TapCoreStandalone.coreOptions.enableLog ? 1 : 6);
|
||||
string openlogStartStr = JsonConvert.SerializeObject(openlogStartParameter);
|
||||
int result = TdkOnAppStarted(openlogStartStr, commonVariablesGetter, freeString);
|
||||
BindWindowChange();
|
||||
#endif
|
||||
}
|
||||
|
||||
[MonoPInvokeCallback(typeof(Action))]
|
||||
private static IntPtr GetCommonVariables()
|
||||
{
|
||||
Dictionary<string, string> dynamicProperties = InflateDynamicProperties();
|
||||
string jsonStr = JsonConvert.SerializeObject(dynamicProperties);
|
||||
return Marshal.StringToHGlobalAnsi(jsonStr);
|
||||
}
|
||||
|
||||
[MonoPInvokeCallback(typeof(Action))]
|
||||
private static void FreeString(IntPtr intPtr)
|
||||
{
|
||||
if (intPtr != IntPtr.Zero)
|
||||
{
|
||||
Marshal.FreeHGlobal(intPtr);
|
||||
}
|
||||
}
|
||||
|
||||
private static void BindWindowChange()
|
||||
{
|
||||
#if UNITY_STANDALONE
|
||||
EventManager.AddListener(
|
||||
EventManager.OnApplicationPause,
|
||||
(isPause) =>
|
||||
{
|
||||
var isPauseBool = (bool)isPause;
|
||||
if (isPauseBool)
|
||||
{
|
||||
TdkOnBackground();
|
||||
}
|
||||
else
|
||||
{
|
||||
TdkOnForeground();
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
EventManager.AddListener(
|
||||
EventManager.OnApplicationQuit,
|
||||
(quit) =>
|
||||
{
|
||||
TdkOnAppStopped();
|
||||
}
|
||||
);
|
||||
EventManager.AddListener(
|
||||
EventManager.OnComplianceUserChanged,
|
||||
(userInfo) =>
|
||||
{
|
||||
TdkSetExtraAppDurationParams(userInfo.ToString());
|
||||
}
|
||||
);
|
||||
#endif
|
||||
}
|
||||
|
||||
public TapOpenlogStandalone(string sdkProjectName, string sdkProjectVersion)
|
||||
{
|
||||
this.sdkProjectName = sdkProjectName;
|
||||
this.sdkProjectVersion = sdkProjectVersion;
|
||||
}
|
||||
|
||||
public void LogBusiness(string action, Dictionary<string, string> properties = null)
|
||||
{
|
||||
#if UNITY_STANDALONE
|
||||
if (properties == null)
|
||||
{
|
||||
properties = new Dictionary<string, string>();
|
||||
}
|
||||
properties[TapOpenlogParamConstants.PARAM_TAPSDK_PROJECT] = sdkProjectName;
|
||||
properties[TapOpenlogParamConstants.PARAM_TAPSDK_VERSION] = sdkProjectVersion;
|
||||
properties[TapOpenlogParamConstants.PARAM_ACTION] = action;
|
||||
string propertiesStr = JsonConvert.SerializeObject(properties);
|
||||
TdkOpenLog("tapsdk", propertiesStr);
|
||||
#endif
|
||||
}
|
||||
|
||||
public void LogTechnology(string action, Dictionary<string, string> properties = null)
|
||||
{
|
||||
#if UNITY_STANDALONE
|
||||
if (
|
||||
TapCoreStandalone.coreOptions.region == TapTapRegionType.CN
|
||||
&& !"TapPayment".Equals(sdkProjectName)
|
||||
)
|
||||
{
|
||||
// 国内非支付SDK不上报技术日志
|
||||
return;
|
||||
}
|
||||
Dictionary<string, string> content = InflateDynamicProperties();
|
||||
content[TapOpenlogParamConstants.PARAM_TAPSDK_PROJECT] = sdkProjectName;
|
||||
content[TapOpenlogParamConstants.PARAM_TAPSDK_VERSION] = sdkProjectVersion;
|
||||
content[TapOpenlogParamConstants.PARAM_ACTION] = action;
|
||||
if (properties != null)
|
||||
{
|
||||
content["args"] = JsonConvert.SerializeObject(properties);
|
||||
}
|
||||
string propertiesStr = JsonConvert.SerializeObject(content);
|
||||
TdkOpenLog("tapsdk-apm", propertiesStr);
|
||||
#endif
|
||||
}
|
||||
|
||||
private static void InitOpenlogStartParameter()
|
||||
{
|
||||
if (isRND)
|
||||
{
|
||||
openlogStartParameter[TapOpenlogStartParamConstants.PARAM_REGION] = 2;
|
||||
}
|
||||
else
|
||||
{
|
||||
openlogStartParameter[TapOpenlogStartParamConstants.PARAM_REGION] =
|
||||
TapCoreStandalone.coreOptions.region;
|
||||
}
|
||||
openlogStartParameter[TapOpenlogStartParamConstants.PARAM_LOG_TO_CONSOLE] =
|
||||
TapCoreStandalone.coreOptions.enableLog ? 1 : 0;
|
||||
openlogStartParameter[TapOpenlogStartParamConstants.PARAM_LOG_LEVEL] = 1;
|
||||
string openLogDirPath = Path.Combine(Application.persistentDataPath, "OpenlogData");
|
||||
if (
|
||||
TapTapSDK.taptapSdkOptions != null
|
||||
&& !string.IsNullOrEmpty(TapTapSDK.taptapSdkOptions.clientId)
|
||||
)
|
||||
{
|
||||
openLogDirPath = Path.Combine(
|
||||
Application.persistentDataPath,
|
||||
"OpenlogData_" + TapTapSDK.taptapSdkOptions.clientId
|
||||
);
|
||||
if (!Directory.Exists(openLogDirPath))
|
||||
{
|
||||
string oldPath = Path.Combine(Application.persistentDataPath, "OpenlogData");
|
||||
if (Directory.Exists(oldPath))
|
||||
{
|
||||
Directory.Move(oldPath, openLogDirPath);
|
||||
}
|
||||
}
|
||||
}
|
||||
openlogStartParameter[TapOpenlogStartParamConstants.PARAM_DATA_DIR] = openLogDirPath;
|
||||
openlogStartParameter[TapOpenlogStartParamConstants.PARAM_ENV] = "local";
|
||||
openlogStartParameter[TapOpenlogStartParamConstants.PARAM_PLATFORM] = "PC";
|
||||
openlogStartParameter[TapOpenlogStartParamConstants.PARAM_UA] =
|
||||
TapHttpUtils.GenerateUserAgent();
|
||||
openlogStartParameter[TapOpenlogStartParamConstants.PARAM_CLIENT_ID] = TapCoreStandalone
|
||||
.coreOptions
|
||||
.clientId;
|
||||
openlogStartParameter[TapOpenlogStartParamConstants.PARAM_CLIENT_TOKEN] =
|
||||
TapCoreStandalone.coreOptions.clientToken;
|
||||
openlogStartParameter[TapOpenlogStartParamConstants.PARAM_COMMON] = generalParameter;
|
||||
openlogStartParameter[TapOpenlogStartParamConstants.PARAM_APP_DURATION] =
|
||||
new Dictionary<string, string>()
|
||||
{
|
||||
{ TapOpenlogParamConstants.PARAM_TAPSDK_VERSION, TapTapSDK.Version },
|
||||
};
|
||||
}
|
||||
|
||||
private static void InitGeneralParameter()
|
||||
{
|
||||
// 应用包名
|
||||
generalParameter[TapOpenlogParamConstants.PARAM_APP_PACKAGE_NAME] =
|
||||
Application.identifier;
|
||||
// 应用版本字符串
|
||||
generalParameter[TapOpenlogParamConstants.PARAM_APP_VERSION] = Application.version;
|
||||
// 应用版本(数字)
|
||||
generalParameter[TapOpenlogParamConstants.PARAM_APP_VERSION_CODE] = "";
|
||||
// 固定一个枚举值: TapSDK
|
||||
generalParameter[TapOpenlogParamConstants.PARAM_PN] = "TapSDK";
|
||||
// SDK生成的设备唯一标识
|
||||
generalParameter[TapOpenlogParamConstants.PARAM_DEVICE_ID] =
|
||||
SystemInfo.deviceUniqueIdentifier;
|
||||
// SDK生成的设备一次安装的唯一标识
|
||||
generalParameter[TapOpenlogParamConstants.PARAM_INSTALL_UUID] = Identity.InstallationId;
|
||||
// 设备品牌,eg: Xiaomi
|
||||
generalParameter[TapOpenlogParamConstants.PARAM_DV] = "";
|
||||
// 设备品牌型号,eg:21051182C
|
||||
generalParameter[TapOpenlogParamConstants.PARAM_MD] = SystemInfo.deviceModel;
|
||||
// 设备CPU型号,eg:arm64-v8a
|
||||
generalParameter[TapOpenlogParamConstants.PARAM_CPU] = "";
|
||||
// 支持 CPU 架构,eg:arm64-v8a
|
||||
generalParameter[TapOpenlogParamConstants.PARAM_CPU_ABIS] = "";
|
||||
// 设备操作系统
|
||||
generalParameter[TapOpenlogParamConstants.PARAM_OS] =
|
||||
SystemInfo.operatingSystemFamily.ToString();
|
||||
// 设备操作系统版本
|
||||
generalParameter[TapOpenlogParamConstants.PARAM_SV] = SystemInfo.operatingSystem;
|
||||
// 物理设备真实屏幕分辨率宽
|
||||
generalParameter[TapOpenlogParamConstants.PARAM_WIDTH] =
|
||||
Screen.currentResolution.width.ToString();
|
||||
// 物理设备真实屏幕分辨率高
|
||||
generalParameter[TapOpenlogParamConstants.PARAM_HEIGHT] =
|
||||
Screen.currentResolution.height.ToString();
|
||||
// 设备总存储空间(磁盘),单位B
|
||||
generalParameter[TapOpenlogParamConstants.PARAM_TOTAL_ROM] = "";
|
||||
// 设备总内存,单位B
|
||||
generalParameter[TapOpenlogParamConstants.PARAM_TOTAL_RAM] = DeviceInfo.RAM;
|
||||
// 芯片型号,eg:Qualcomm Technologies, Inc SM7250
|
||||
generalParameter[TapOpenlogParamConstants.PARAM_HARDWARE] = SystemInfo.processorType;
|
||||
// SDK设置的地区,例如 zh_CN
|
||||
generalParameter[TapOpenlogParamConstants.PARAM_SDK_LOCALE] =
|
||||
TapLocalizeManager.GetCurrentLanguageString();
|
||||
// taptap的用户ID的外显ID(加密)
|
||||
generalParameter[TapOpenlogParamConstants.PARAM_OPEN_ID] = openid;
|
||||
}
|
||||
|
||||
private static Dictionary<string, string> InflateDynamicProperties()
|
||||
{
|
||||
Dictionary<string, string> props = new Dictionary<string, string>();
|
||||
// 客户端时区,eg:Asia/Shanghai
|
||||
props[TapOpenlogParamConstants.PARAM_TIMEZONE] = "";
|
||||
// SDK 产物类型
|
||||
props[TapOpenlogParamConstants.PARAM_TAPSDK_ARTIFACT] = "Unity";
|
||||
// 游戏账号 ID(非角色 ID)
|
||||
props[TapOpenlogParamConstants.PARAM_GAME_USER_ID] = TapCoreStandalone.User.Id ?? "";
|
||||
// taptap的用户ID的外显ID(加密)
|
||||
props[TapOpenlogParamConstants.PARAM_OPEN_ID] = openid ?? "";
|
||||
// SDK生成的设备全局唯一标识
|
||||
props[TapOpenlogParamConstants.PARAM_GID] = "";
|
||||
// 设备可用存储空间(磁盘),单位B
|
||||
props[TapOpenlogParamConstants.PARAM_ROM] = "0";
|
||||
// 设备可用内存,单位B
|
||||
props[TapOpenlogParamConstants.PARAM_RAM] = "0";
|
||||
// 网络类型,eg:wifi, mobile
|
||||
props[TapOpenlogParamConstants.PARAM_NETWORK_TYPE] = "";
|
||||
// SDK设置的地区,例如 zh_CN
|
||||
props[TapOpenlogParamConstants.PARAM_SDK_LOCALE] =
|
||||
TapLocalizeManager.GetCurrentLanguageString();
|
||||
return props;
|
||||
}
|
||||
|
||||
internal static void LogBusiness(
|
||||
string sdkProjectName,
|
||||
string sdkProjectVersion,
|
||||
string action,
|
||||
Dictionary<string, string> properties = null
|
||||
)
|
||||
{
|
||||
#if UNITY_STANDALONE
|
||||
if (properties == null)
|
||||
{
|
||||
properties = new Dictionary<string, string>();
|
||||
}
|
||||
properties[TapOpenlogParamConstants.PARAM_TAPSDK_PROJECT] = sdkProjectName;
|
||||
properties[TapOpenlogParamConstants.PARAM_TAPSDK_VERSION] = sdkProjectVersion;
|
||||
properties[TapOpenlogParamConstants.PARAM_ACTION] = action;
|
||||
string propertiesStr = JsonConvert.SerializeObject(properties);
|
||||
TdkOpenLog("tapsdk", propertiesStr);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 8ca3a4d34396f4bd6946aa219459ad60
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,23 @@
|
||||
namespace TapSDK.Core.Standalone.Internal.Openlog
|
||||
{
|
||||
internal class TapOpenlogStartParamConstants
|
||||
{
|
||||
public const string PARAM_REGION = "region";
|
||||
public const string PARAM_LOG_TO_CONSOLE = "log_to_console";
|
||||
public const string PARAM_LOG_LEVEL = "log_level";
|
||||
public const string PARAM_DATA_DIR = "data_dir";
|
||||
public const string PARAM_ENV = "env";
|
||||
public const string PARAM_PLATFORM = "platform";
|
||||
public const string PARAM_UA = "ua";
|
||||
public const string PARAM_CLIENT_ID = "client_id";
|
||||
public const string PARAM_CLIENT_TOKEN = "client_token";
|
||||
public const string PARAM_MODULES = "modules";
|
||||
|
||||
// Common parameters
|
||||
public const string PARAM_COMMON = "common";
|
||||
|
||||
// App duration parameters
|
||||
public const string PARAM_APP_DURATION = "app_duration";
|
||||
public const string PARAM_TAPSDK_VERSION = "tapsdk_version";
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 57bde0c60960d4f769713d2e12930f75
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,222 @@
|
||||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
using AOT;
|
||||
using UnityEngine;
|
||||
|
||||
namespace TapSDK.Core.Standalone.Internal.Openlog
|
||||
{
|
||||
internal class TapOpenlogWrapper
|
||||
{
|
||||
#if UNITY_STANDALONE_WIN || UNITY_EDITOR_WIN
|
||||
internal const string DllName = "tapsdkcore";
|
||||
#elif UNITY_STANDALONE_OSX || UNITY_EDITOR_OSX
|
||||
internal const string DllName = "libtapsdkcorecpp";
|
||||
#elif UNITY_STANDALONE_LINUX || UNITY_EDITOR_LINUX
|
||||
internal const string DllName = "libtapsdkcorecpp";
|
||||
#endif
|
||||
|
||||
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||
internal delegate void TapSdkCppLogWriterDelegate(
|
||||
int logLevel,
|
||||
string codeLocation,
|
||||
string logTag,
|
||||
string logMessage
|
||||
);
|
||||
|
||||
[DllImport(DllName, CallingConvention = CallingConvention.Cdecl)]
|
||||
internal static extern void TapSdkCppInitLogger(int logLevel, TapSdkCppLogWriterDelegate logWriter);
|
||||
|
||||
private static readonly TapSdkCppLogWriterDelegate logWriterDelegate = LogWriterCallback;
|
||||
|
||||
[MonoPInvokeCallback(typeof(TapSdkCppLogWriterDelegate))]
|
||||
private static void LogWriterCallback(int logLevel, string codeLocation, string logTag, string logMessage)
|
||||
{
|
||||
var msg = $"[TapSDK-{logTag}] [{codeLocation}] {logMessage}";
|
||||
switch (logLevel)
|
||||
{
|
||||
case 1: // trace
|
||||
case 2: // debug
|
||||
Debug.Log(msg);
|
||||
break;
|
||||
case 3: // info
|
||||
Debug.Log(msg);
|
||||
break;
|
||||
case 4: // warn
|
||||
Debug.LogWarning(msg);
|
||||
break;
|
||||
default: // error
|
||||
Debug.LogError(msg);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
internal static void InitLogger(int logLevel)
|
||||
{
|
||||
TapSdkCppInitLogger(logLevel, logWriterDelegate);
|
||||
}
|
||||
|
||||
/**
|
||||
* 初始化接口,只需要调用一次。
|
||||
*
|
||||
* cfg 初始化配置,JSON 格式:
|
||||
*
|
||||
{
|
||||
"region": 2,
|
||||
"log_to_console": 1,
|
||||
"log_level": 1,
|
||||
"data_dir": "/tmp",
|
||||
"env": "local",
|
||||
"platform": "abc",
|
||||
"ua": "TapSDK-Android/3.28.0",
|
||||
"client_id": "***",
|
||||
"client_token": "***",
|
||||
"modules": [
|
||||
"app_duration"
|
||||
],
|
||||
"common": {
|
||||
"pn": "TapSDK",
|
||||
"app_version_code": "123",
|
||||
"app_version": "1.2.3",
|
||||
"app_package_name": "",
|
||||
"install_uuid": "",
|
||||
"device_id": "123456",
|
||||
"caid": "",
|
||||
"dv": "",
|
||||
"md": "",
|
||||
"hardware": "",
|
||||
"cpu": "",
|
||||
"cpu_abis": "",
|
||||
"os": "android",
|
||||
"sv": "",
|
||||
"width": "",
|
||||
"height": "",
|
||||
"total_rom": "",
|
||||
"total_ram": "",
|
||||
"open_id": "",
|
||||
"tds_user_id": "",
|
||||
"sdk_locale": ""
|
||||
}
|
||||
"app_duration": {
|
||||
"tapsdk_version": ""
|
||||
}
|
||||
}
|
||||
*
|
||||
* - log_level 取值:1 Trace、2 Debug、3 Info、4 Warn、5 Error、6 完全不输出
|
||||
* - region 取值:0 国内、1 海外、2 RND
|
||||
* - modules 取值:app_duration(开启时长埋点)。如果 modules 不传,或者为空,
|
||||
* 则仅开启 OpenLog 功能,不会上报游戏时长
|
||||
*
|
||||
* commonVariablesGetter 用于获取运行时会发生变化的公参
|
||||
*
|
||||
* 成功返回 0,失败返回 -1
|
||||
*/
|
||||
[DllImport(DllName, CallingConvention = CallingConvention.Cdecl)]
|
||||
internal static extern int TdkOnAppStarted(string cfg, CommonVariablesGetter commonVariablesGetter,
|
||||
FreeStringCallback freeString);
|
||||
|
||||
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||
internal delegate IntPtr CommonVariablesGetter();
|
||||
|
||||
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||
internal delegate void FreeStringCallback(IntPtr intPtr);
|
||||
|
||||
/**
|
||||
* App 退出时调用,只需要调用一次。
|
||||
*/
|
||||
[DllImport(DllName)]
|
||||
internal static extern void TdkOnAppStopped();
|
||||
|
||||
/**
|
||||
* 启用功能模块,如:app_duration
|
||||
*
|
||||
* modules JSON 数组,如:["app_duration"]
|
||||
*
|
||||
*/
|
||||
[DllImport(DllName)]
|
||||
internal static extern void TdkEnableModules(string modules);
|
||||
|
||||
/**
|
||||
* 禁用功能模块,如:app_duration
|
||||
*
|
||||
* modules JSON 数组,如:["app_duration"]
|
||||
*
|
||||
*/
|
||||
[DllImport(DllName)]
|
||||
internal static extern void TdkDisableModules(string modules);
|
||||
|
||||
/**
|
||||
* 需要发送埋点日志时调用。
|
||||
* SDK 内部会整合初始化接口传入的公参、extraArgsFunc 返回的公参,以及 log 里的业务参数,
|
||||
* 然后再发送到 OpenLog 服务端
|
||||
*
|
||||
* logstore 如:tapsdk、tapsdk-apm
|
||||
* log 埋点日志,仅需传递业务参数,JSON 格式:{"action":"xxx", "open_id":"yyy","tds_user_id":"zzz"}
|
||||
*/
|
||||
[DllImport(DllName)]
|
||||
internal static extern void TdkOpenLog(string logStore, string logContent);
|
||||
|
||||
/**
|
||||
* 用户登录成功时调用。
|
||||
*
|
||||
* userInfo 用户信息,JSON 格式:{"open_id":"","tds_user_id":""}
|
||||
*/
|
||||
[DllImport(DllName)]
|
||||
internal static extern void TdkOnLogin(string userInfo);
|
||||
|
||||
/**
|
||||
* 用户登出时调用。
|
||||
*/
|
||||
[DllImport(DllName)]
|
||||
internal static extern void TdkOnLogout();
|
||||
|
||||
/**
|
||||
* 用户切到后台时调用。
|
||||
*/
|
||||
[DllImport(DllName)]
|
||||
internal static extern void TdkOnForeground();
|
||||
|
||||
/**
|
||||
* 游戏切回前台时调用。
|
||||
*/
|
||||
[DllImport(DllName)]
|
||||
internal static extern void TdkOnBackground();
|
||||
|
||||
/**
|
||||
* 设置额外的时长模块日志参数,JSON K/V 格式。每次调用这个接口时,会用最新得到的参数,替换原有参数。
|
||||
*
|
||||
* params 额外日志参数,JSON 格式:{"K1":"V1","K2":"V2","K3":"V3"}
|
||||
*/
|
||||
[DllImport(DllName)]
|
||||
internal static extern void TdkSetExtraAppDurationParams(string paramsJson);
|
||||
|
||||
/**
|
||||
* 设置日志等级
|
||||
*/
|
||||
[DllImport(DllName)]
|
||||
internal static extern void TdkSetLogLevel(int logLevel, int logToConsole);
|
||||
|
||||
/**
|
||||
* 代码版本,如:1.2.5
|
||||
*/
|
||||
[DllImport(DllName)]
|
||||
internal static extern IntPtr TdkVersion();
|
||||
|
||||
/**
|
||||
* git commit 版本,如:98f5d81a0fdcab9a755878b3e825c2cb510e5196
|
||||
*/
|
||||
[DllImport(DllName)]
|
||||
internal static extern IntPtr TdkGitCommit();
|
||||
|
||||
// 用于返回 TdkVersion 的封装方法
|
||||
public static string GetTdkVersion()
|
||||
{
|
||||
return Marshal.PtrToStringAnsi(TdkVersion());
|
||||
}
|
||||
|
||||
// 用于返回 TdkGitCommit 的封装方法
|
||||
public static string GetTdkGitCommit()
|
||||
{
|
||||
return Marshal.PtrToStringAnsi(TdkGitCommit());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: f7931ada3fb5ee14ca44265af3983618
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,53 @@
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
|
||||
namespace TapSDK.Core.Standalone.Internal {
|
||||
public class PlayRecorder {
|
||||
internal static readonly string PLAYED_DURATION_KEY = "tapdb_played_duration";
|
||||
|
||||
/// <summary>
|
||||
/// 记录间隔
|
||||
/// </summary>
|
||||
private const int RECORD_INTERVAL = 2 * 1000;
|
||||
|
||||
private CancellationTokenSource cts;
|
||||
|
||||
/// <summary>
|
||||
/// 启动记录
|
||||
/// </summary>
|
||||
public async void Start() {
|
||||
if (cts != null && !cts.IsCancellationRequested) {
|
||||
cts.Cancel();
|
||||
}
|
||||
|
||||
cts = new CancellationTokenSource();
|
||||
while (Application.isPlaying) {
|
||||
try {
|
||||
await Task.Delay(RECORD_INTERVAL, cts.Token);
|
||||
} catch (TaskCanceledException) {
|
||||
break;
|
||||
}
|
||||
|
||||
// 保存用户游玩时长
|
||||
TapCoreStandalone.Prefs.AddOrUpdate(PLAYED_DURATION_KEY,
|
||||
2L,
|
||||
(k, v) => (long)v + 2);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 结束记录并上报
|
||||
/// </summary>
|
||||
public void Stop() {
|
||||
cts?.Cancel();
|
||||
if (TapCoreStandalone.Prefs.TryRemove(PLAYED_DURATION_KEY, out long duration)) {
|
||||
Dictionary<string, object> props = new Dictionary<string, object> {
|
||||
{ "duration", duration }
|
||||
};
|
||||
TapEventStandalone.Tracker?.TrackEvent("play_game", props, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: c289811a1dbcd41be9707de8e51a0e21
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
97
Assets/TapSDK/Core/Standalone/Runtime/Internal/Prefs.cs
Normal file
97
Assets/TapSDK/Core/Standalone/Runtime/Internal/Prefs.cs
Normal file
@@ -0,0 +1,97 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Threading;
|
||||
using System.Linq;
|
||||
using System.IO;
|
||||
using UnityEngine;
|
||||
using TapSDK.Core;
|
||||
using TapSDK.Core.Internal.Log;
|
||||
|
||||
namespace TapSDK.Core.Standalone.Internal {
|
||||
public class Prefs {
|
||||
internal static readonly string OLD_PERSISTENT_FILE_NAME = "tapdb_storage_v2";
|
||||
|
||||
private string persistentFilePath;
|
||||
|
||||
private readonly ConcurrentDictionary<string, object> data;
|
||||
|
||||
private readonly Thread persistThread;
|
||||
|
||||
private readonly AutoResetEvent persistEvent;
|
||||
|
||||
public Prefs() {
|
||||
string newCacheFileName = OLD_PERSISTENT_FILE_NAME;
|
||||
if( TapTapSDK.taptapSdkOptions != null && !string.IsNullOrEmpty(TapTapSDK.taptapSdkOptions.clientId)) {
|
||||
newCacheFileName = OLD_PERSISTENT_FILE_NAME + "_" + TapTapSDK.taptapSdkOptions.clientId;
|
||||
}
|
||||
persistentFilePath = Path.Combine(Application.persistentDataPath, newCacheFileName);
|
||||
// 兼容旧版缓存文件
|
||||
if( !File.Exists(persistentFilePath)) {
|
||||
string oldPath = Path.Combine(Application.persistentDataPath, OLD_PERSISTENT_FILE_NAME);
|
||||
if (File.Exists(oldPath)){
|
||||
File.Move(oldPath, persistentFilePath);
|
||||
}
|
||||
}
|
||||
if (File.Exists(persistentFilePath)) {
|
||||
try {
|
||||
string json = File.ReadAllText(persistentFilePath);
|
||||
Dictionary<string, object> jsonData = Json.Deserialize(json) as Dictionary<string, object>;
|
||||
data = new ConcurrentDictionary<string, object>(jsonData);
|
||||
} catch (Exception e) {
|
||||
TapLog.Error(e.Message);
|
||||
File.Delete(persistentFilePath);
|
||||
}
|
||||
}
|
||||
if (data == null) {
|
||||
data = new ConcurrentDictionary<string, object>();
|
||||
}
|
||||
persistEvent = new AutoResetEvent(false);
|
||||
persistThread = new Thread(PersistProc) {
|
||||
IsBackground = true
|
||||
};
|
||||
persistThread.Start();
|
||||
}
|
||||
|
||||
public T Get<T>(string key) {
|
||||
if (data.TryGetValue(key, out object val)) {
|
||||
return (T)val;
|
||||
}
|
||||
return default;
|
||||
}
|
||||
|
||||
public void Set<T>(string key, T value) {
|
||||
data[key] = value;
|
||||
persistEvent.Set();
|
||||
}
|
||||
|
||||
public bool TryRemove<T>(string key, out T val) {
|
||||
if (data.TryRemove(key, out object v)) {
|
||||
val = (T)v;
|
||||
persistEvent.Set();
|
||||
return true;
|
||||
}
|
||||
val = default;
|
||||
return false;
|
||||
}
|
||||
|
||||
public void AddOrUpdate(string key, object addValue, Func<string, object, object> updateValueFactory) {
|
||||
data.AddOrUpdate(key, addValue, updateValueFactory);
|
||||
persistEvent.Set();
|
||||
}
|
||||
|
||||
private void PersistProc() {
|
||||
while (true) {
|
||||
persistEvent.WaitOne();
|
||||
try {
|
||||
Dictionary<string, object> dict = data.ToArray()
|
||||
.ToDictionary(kv => kv.Key, kv => kv.Value);
|
||||
string json = Json.Serialize(dict);
|
||||
File.WriteAllText(persistentFilePath, json);
|
||||
} catch (Exception e) {
|
||||
TapLog.Error(e.Message);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
11
Assets/TapSDK/Core/Standalone/Runtime/Internal/Prefs.cs.meta
Normal file
11
Assets/TapSDK/Core/Standalone/Runtime/Internal/Prefs.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: d9465a7fca8d2451b94b37902d875c5d
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,143 @@
|
||||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Text;
|
||||
using TapSDK.Core.Internal.Log;
|
||||
|
||||
namespace TapSDK.Core.Standalone.Internal
|
||||
{
|
||||
internal enum TapSDKInitResult
|
||||
{
|
||||
// 初始化成功
|
||||
OK = 0,
|
||||
|
||||
// 其他错误
|
||||
FailedGeneric = 1,
|
||||
|
||||
// 未找到 TapTap,用户可能未安装,请引导用户下载安装 TapTap
|
||||
NoPlatform = 2,
|
||||
|
||||
// 已安装 TapTap,游戏未通过 TapTap 启动
|
||||
NotLaunchedByPlatform = 3,
|
||||
|
||||
// 平台版本不匹配,请引导用户升级 TapTap 与游戏至最新版本,再重新运行游戏
|
||||
PlatformVersionMismatch = 4,
|
||||
|
||||
// SDK 本地执行时未知错误
|
||||
Unknown = -1,
|
||||
};
|
||||
|
||||
internal enum TapEventID
|
||||
{
|
||||
SystemStateChanged = 1, // TapTap 客户端运行状态事件监听
|
||||
}
|
||||
|
||||
// 系统事件类型
|
||||
internal enum SystemState
|
||||
{
|
||||
Unknown = 0, // 未知
|
||||
Online = 1, // 在线
|
||||
Offline = 2, // 离线
|
||||
Shutdown = 3, // 退出
|
||||
};
|
||||
|
||||
// 授权返回结果结构体
|
||||
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
|
||||
internal struct SystemStateResponse
|
||||
{
|
||||
public int state; // 运行状态
|
||||
}
|
||||
|
||||
public class TapClientBridge
|
||||
{
|
||||
#if UNITY_STANDALONE_WIN
|
||||
public const string DLL_NAME = "taptap_api";
|
||||
#endif
|
||||
|
||||
#if UNITY_STANDALONE_WIN
|
||||
[DllImport(DLL_NAME, CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)]
|
||||
internal static extern bool TapSDK_RestartAppIfNecessary(
|
||||
[MarshalAs(UnmanagedType.LPStr)] string clientId
|
||||
);
|
||||
|
||||
[DllImport(DLL_NAME, CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)]
|
||||
internal static extern int TapSDK_Init(
|
||||
StringBuilder errMsg,
|
||||
[MarshalAs(UnmanagedType.LPStr)] string pubKey
|
||||
);
|
||||
|
||||
[DllImport(DLL_NAME, CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)]
|
||||
internal static extern void TapSDK_Shutdown();
|
||||
|
||||
// 定义与 C 兼容的委托
|
||||
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||
public delegate void CallbackDelegate(int id, IntPtr userData);
|
||||
|
||||
// 系统状态返回结果结构体
|
||||
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
|
||||
internal struct SystemStateResponse
|
||||
{
|
||||
public SystemState state; // 枚举直接映射
|
||||
}
|
||||
|
||||
[DllImport(DLL_NAME, CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)]
|
||||
public static extern void TapSDK_RegisterCallback(int callbackId, IntPtr callback);
|
||||
|
||||
[DllImport(DLL_NAME, CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)]
|
||||
internal static extern void TapSDK_RunCallbacks();
|
||||
|
||||
[DllImport(DLL_NAME, CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)]
|
||||
public static extern void TapSDK_UnregisterCallback(int callbackId, IntPtr callback);
|
||||
|
||||
[DllImport(DLL_NAME, CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)]
|
||||
internal static extern bool TapUser_GetOpenID(StringBuilder openId);
|
||||
|
||||
[DllImport(DLL_NAME, CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)]
|
||||
internal static extern bool TapSDK_GetClientID(StringBuilder clientId);
|
||||
|
||||
// 初始化检查
|
||||
internal static int CheckInitState(out string errMessage, string key)
|
||||
{
|
||||
StringBuilder errMsgBuffer = new StringBuilder(1024); // 分配 1024 字节缓冲区
|
||||
int result = TapSDK_Init(errMsgBuffer, key);
|
||||
errMessage = errMsgBuffer.ToString();
|
||||
TapLog.Log("CheckInitState result = " + result);
|
||||
return result;
|
||||
}
|
||||
|
||||
internal static bool GetTapUserOpenId(out string openId)
|
||||
{
|
||||
StringBuilder openIdBuffer = new StringBuilder(256); // 分配一个足够大的缓冲区
|
||||
bool success = TapUser_GetOpenID(openIdBuffer); // 调用 C 函数
|
||||
openId = openIdBuffer.ToString();
|
||||
return success;
|
||||
}
|
||||
|
||||
internal static bool GetClientId(out string clientId)
|
||||
{
|
||||
StringBuilder clientIDBuffer = new StringBuilder(256); // 分配一个足够大的缓冲区
|
||||
bool success = TapSDK_GetClientID(clientIDBuffer); // 调用 C 函数
|
||||
clientId = clientIDBuffer.ToString();
|
||||
return success;
|
||||
}
|
||||
|
||||
private static CallbackDelegate _systemStateCallbackInstance;
|
||||
internal static void RegisterSystemStateCallback(CallbackDelegate callback)
|
||||
{
|
||||
IntPtr funcPtr = Marshal.GetFunctionPointerForDelegate(callback);
|
||||
if (_systemStateCallbackInstance != null)
|
||||
{
|
||||
UnRegisterSystemStateCallback(_systemStateCallbackInstance);
|
||||
}
|
||||
_systemStateCallbackInstance = callback;
|
||||
TapSDK_RegisterCallback((int)TapEventID.SystemStateChanged, funcPtr);
|
||||
}
|
||||
|
||||
internal static void UnRegisterSystemStateCallback(CallbackDelegate callback)
|
||||
{
|
||||
IntPtr funcPtr = Marshal.GetFunctionPointerForDelegate(callback);
|
||||
TapSDK_UnregisterCallback((int)TapEventID.SystemStateChanged, funcPtr);
|
||||
_systemStateCallbackInstance = null;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 350ab3505547544ccb1c7b08617026e5
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,35 @@
|
||||
|
||||
using TapSDK.Core.Internal.Log;
|
||||
using UnityEngine;
|
||||
|
||||
namespace TapSDK.Core.Standalone.Internal
|
||||
{
|
||||
|
||||
internal class TapClientBridgePoll : MonoBehaviour
|
||||
{
|
||||
static readonly string TAP_CLIENT_POLL_NAME = "TapClientBridgePoll";
|
||||
|
||||
static TapClientBridgePoll current;
|
||||
|
||||
|
||||
internal static void StartUp()
|
||||
{
|
||||
TapLog.Log("TapClientBridgePoll StartUp " );
|
||||
if (current == null)
|
||||
{
|
||||
GameObject pollGo = new GameObject(TAP_CLIENT_POLL_NAME);
|
||||
DontDestroyOnLoad(pollGo);
|
||||
current = pollGo.AddComponent<TapClientBridgePoll>();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private void Update()
|
||||
{
|
||||
#if UNITY_STANDALONE_WIN
|
||||
TapClientBridge.TapSDK_RunCallbacks();
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: c81a3a82ec1f94dd39440d2a1421e4d9
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,128 @@
|
||||
using System.Threading;
|
||||
using TapSDK.Core.Internal.Utils;
|
||||
using UnityEngine;
|
||||
|
||||
namespace TapSDK.Core.Standalone
|
||||
{
|
||||
public class TapLocalizeManager
|
||||
{
|
||||
private static volatile TapLocalizeManager _instance;
|
||||
private static readonly object ObjLock = new object();
|
||||
|
||||
public static TapLocalizeManager Instance
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_instance != null) return _instance;
|
||||
lock (ObjLock)
|
||||
{
|
||||
if (_instance == null)
|
||||
{
|
||||
_instance = new TapLocalizeManager();
|
||||
}
|
||||
}
|
||||
|
||||
return _instance;
|
||||
}
|
||||
}
|
||||
|
||||
private bool _regionIsCn;
|
||||
|
||||
public static void SetCurrentRegion(bool isCn)
|
||||
{
|
||||
Instance._regionIsCn = isCn;
|
||||
}
|
||||
|
||||
private TapTapLanguageType _language = TapTapLanguageType.Auto;
|
||||
|
||||
public static void SetCurrentLanguage(TapTapLanguageType language)
|
||||
{
|
||||
Instance._language = language;
|
||||
}
|
||||
|
||||
public static TapTapLanguageType GetCurrentLanguage()
|
||||
{
|
||||
return Instance._language != TapTapLanguageType.Auto ? Instance._language : GetSystemLanguage();
|
||||
}
|
||||
|
||||
public static string GetCurrentLanguageString() {
|
||||
TapTapLanguageType lang = GetCurrentLanguage();
|
||||
switch (lang) {
|
||||
case TapTapLanguageType.zh_Hans:
|
||||
return "zh_CN";
|
||||
case TapTapLanguageType.en:
|
||||
return "en_US";
|
||||
case TapTapLanguageType.zh_Hant:
|
||||
return "zh_TW";
|
||||
case TapTapLanguageType.ja:
|
||||
return "ja_JP";
|
||||
case TapTapLanguageType.ko:
|
||||
return "ko_KR";
|
||||
case TapTapLanguageType.th:
|
||||
return "th_TH";
|
||||
case TapTapLanguageType.id:
|
||||
return "id_ID";
|
||||
case TapTapLanguageType.de:
|
||||
return "de";
|
||||
case TapTapLanguageType.es:
|
||||
return "es_ES";
|
||||
case TapTapLanguageType.fr:
|
||||
return "fr";
|
||||
case TapTapLanguageType.pt:
|
||||
return "pt_PT";
|
||||
case TapTapLanguageType.ru:
|
||||
return "ru";
|
||||
case TapTapLanguageType.tr:
|
||||
return "tr";
|
||||
case TapTapLanguageType.vi:
|
||||
return "vi_VN";
|
||||
default:
|
||||
return Instance._regionIsCn ? "zh_CN" : "en_US";
|
||||
}
|
||||
}
|
||||
|
||||
public static string GetCurrentLanguageString2() {
|
||||
return GetCurrentLanguageString().Replace("_", "-");
|
||||
}
|
||||
|
||||
private static TapTapLanguageType GetSystemLanguage()
|
||||
{
|
||||
var lang = TapTapLanguageType.Auto;
|
||||
// Application.systemLanguage 必须在主线程访问,所以这里需要使用 TapLoom 确保调用线程
|
||||
var defaultSystemLanguage = Instance._regionIsCn ? SystemLanguage.ChineseSimplified : SystemLanguage.English;
|
||||
var sysLanguage = TapLoom.RunOnMainThreadSync(
|
||||
() => Application.systemLanguage,
|
||||
defaultSystemLanguage
|
||||
);
|
||||
switch (sysLanguage)
|
||||
{
|
||||
case SystemLanguage.ChineseSimplified:
|
||||
lang = TapTapLanguageType.zh_Hans;
|
||||
break;
|
||||
case SystemLanguage.English:
|
||||
lang = TapTapLanguageType.en;
|
||||
break;
|
||||
case SystemLanguage.ChineseTraditional:
|
||||
lang = TapTapLanguageType.zh_Hant;
|
||||
break;
|
||||
case SystemLanguage.Japanese:
|
||||
lang = TapTapLanguageType.ja;
|
||||
break;
|
||||
case SystemLanguage.Korean:
|
||||
lang = TapTapLanguageType.ko;
|
||||
break;
|
||||
case SystemLanguage.Thai:
|
||||
lang = TapTapLanguageType.th;
|
||||
break;
|
||||
case SystemLanguage.Indonesian:
|
||||
lang = TapTapLanguageType.id;
|
||||
break;
|
||||
default:
|
||||
lang = Instance._regionIsCn ? TapTapLanguageType.zh_Hans : TapTapLanguageType.en;
|
||||
break;
|
||||
}
|
||||
|
||||
return lang;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 973ae2f9a5cf94b3cb97d5d27ffcae75
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
283
Assets/TapSDK/Core/Standalone/Runtime/Internal/Tracker.cs
Normal file
283
Assets/TapSDK/Core/Standalone/Runtime/Internal/Tracker.cs
Normal file
@@ -0,0 +1,283 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using TapSDK.Core.Standalone;
|
||||
using System.Threading.Tasks;
|
||||
using UnityEngine;
|
||||
using TapSDK.Core.Internal.Utils;
|
||||
using TapSDK.Core.Internal.Log;
|
||||
|
||||
namespace TapSDK.Core.Standalone.Internal {
|
||||
public class Tracker {
|
||||
|
||||
private Dictionary<string, object> customProps;
|
||||
|
||||
private Dictionary<string, object> basicProps;
|
||||
private Dictionary<string, object> commonProps;
|
||||
|
||||
private EventSender sender;
|
||||
private IDynamicProperties dynamicPropsDelegate;
|
||||
|
||||
private TapTapEventOptions eventOptions;
|
||||
|
||||
private static string session_uuid = generateUUID();
|
||||
|
||||
public void Init(TapTapEventOptions eventOptions) {
|
||||
basicProps = new Dictionary<string, object>();
|
||||
commonProps = new Dictionary<string, object>();
|
||||
this.eventOptions = eventOptions;
|
||||
customProps = Json.Deserialize(eventOptions.propertiesJson) as Dictionary<string, object>;
|
||||
sender = new EventSender();
|
||||
|
||||
InitBasicProps();
|
||||
|
||||
Dictionary<string, object> props = new Dictionary<string, object>(basicProps);
|
||||
TrackEvent(Constants.DEVICE_LOGIN, props, true);
|
||||
|
||||
}
|
||||
|
||||
public void AddCommonProperty(string key, object value) {
|
||||
commonProps[key] = value;
|
||||
}
|
||||
|
||||
public void AddCommon(Dictionary<string, object> properties) {
|
||||
foreach (KeyValuePair<string, object> kv in properties) {
|
||||
commonProps[kv.Key] = kv.Value;
|
||||
}
|
||||
}
|
||||
public void ClearCommonProperty(string key) {
|
||||
commonProps.Remove(key);
|
||||
}
|
||||
public void ClearCommonProperties(string[] keys) {
|
||||
foreach (string key in keys) {
|
||||
commonProps.Remove(key);
|
||||
}
|
||||
}
|
||||
public void ClearAllCommonProperties() {
|
||||
commonProps.Clear();
|
||||
}
|
||||
|
||||
public void RegisterDynamicPropsDelegate(IDynamicProperties dynamicPropsDelegate) {
|
||||
this.dynamicPropsDelegate = dynamicPropsDelegate;
|
||||
}
|
||||
|
||||
public void LogPurchasedEvent(string orderID, string productName, Int64 amount, string currencyType, string paymentMethod, string properties){
|
||||
var prop = Json.Deserialize(properties) as Dictionary<string, object>;
|
||||
|
||||
var data = new Dictionary<string, object> {
|
||||
{ "order_id", orderID },
|
||||
{ "product", productName },
|
||||
{ "amount", amount },
|
||||
{ "currency_type", currencyType },
|
||||
{ "payment", paymentMethod }
|
||||
};
|
||||
if (prop != null) {
|
||||
foreach (KeyValuePair<string, object> kv in prop) {
|
||||
data[kv.Key] = kv.Value;
|
||||
}
|
||||
}
|
||||
TrackEvent("charge", data);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 上报事件
|
||||
/// </summary>
|
||||
/// <param name="name"></param>
|
||||
/// <param name="properties"></param>
|
||||
/// <param name="isAutomationlly">是否为自动事件</param>
|
||||
public void TrackEvent(string name, Dictionary<string, object> properties = null, bool isAutomationlly = false) {
|
||||
|
||||
Dictionary<string, object> props = new Dictionary<string, object>(basicProps);
|
||||
|
||||
if (commonProps != null) {
|
||||
foreach (KeyValuePair<string, object> kv in commonProps) {
|
||||
props[kv.Key] = kv.Value;
|
||||
}
|
||||
}
|
||||
|
||||
Dictionary<string, object> dynamicProps = dynamicPropsDelegate?.GetDynamicProperties();
|
||||
TapLog.Log("dynamicProps: " + dynamicProps);
|
||||
if (dynamicProps != null) {
|
||||
foreach (KeyValuePair<string, object> kv in dynamicProps) {
|
||||
props[kv.Key] = kv.Value;
|
||||
}
|
||||
}
|
||||
|
||||
if (name == Constants.DEVICE_LOGIN) { // Device login 事件带上初始化时的自定义属性
|
||||
TapLog.Log("customProps: " + customProps);
|
||||
if (customProps != null) {
|
||||
foreach (KeyValuePair<string, object> kv in customProps) {
|
||||
props[kv.Key] = kv.Value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
props["t_log_id"] = generateUUID();
|
||||
// 时间戳,毫秒级
|
||||
props["timestamp"] = DateTimeOffset.Now.ToUnixTimeMilliseconds();
|
||||
var open_id = OpenID;
|
||||
if (!string.IsNullOrWhiteSpace(open_id)) {
|
||||
props["open_id"] = open_id;
|
||||
}
|
||||
|
||||
TapLog.Log("properties: " + properties);
|
||||
if (properties != null) {
|
||||
foreach (KeyValuePair<string, object> kv in properties) {
|
||||
props[kv.Key] = kv.Value;
|
||||
}
|
||||
}
|
||||
|
||||
props["is_automatically_log"] = isAutomationlly ? "true" : "false";
|
||||
|
||||
var language = TapLocalizeManager.GetCurrentLanguageString();
|
||||
props["sdk_locale"] = language;
|
||||
props["lang_system"] = DeviceInfo.GetLanguage();
|
||||
|
||||
Dictionary<string, object> data = new Dictionary<string, object> {
|
||||
{ "client_id", TapCoreStandalone.coreOptions.clientId },
|
||||
{ "type", "track" },
|
||||
{ "name", name },
|
||||
{ "device_id", Identity.DeviceId },
|
||||
{ "properties", props },
|
||||
};
|
||||
if (!string.IsNullOrWhiteSpace(TapCoreStandalone.User.Id)) {
|
||||
data["user_id"] = TapCoreStandalone.User.Id;
|
||||
}
|
||||
|
||||
sender.Send(data);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 上报设备属性变化
|
||||
/// </summary>
|
||||
/// <param name="type"></param>
|
||||
/// <param name="properties"></param>
|
||||
public void TrackDeviceProperties(string type, Dictionary<string, object> properties) {
|
||||
if (string.IsNullOrWhiteSpace(Identity.DeviceId)) {
|
||||
TapLog.Error("DeviceId is NULL.");
|
||||
return;
|
||||
}
|
||||
|
||||
Dictionary<string, object> baseProps = new Dictionary<string, object> {
|
||||
{ "device_id", Identity.DeviceId }
|
||||
};
|
||||
_ = TrackPropertiesAsync(type, baseProps, properties);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 上报玩家属性变化
|
||||
/// </summary>
|
||||
public void TrackUserProperties(string type, Dictionary<string, object> properties) {
|
||||
string userId = TapCoreStandalone.User.Id;
|
||||
if (string.IsNullOrWhiteSpace(userId)) {
|
||||
TapLog.Error("UserId is NULL.");
|
||||
return;
|
||||
}
|
||||
|
||||
Dictionary<string, object> baseProps = new Dictionary<string, object> {
|
||||
{ "user_id", userId }
|
||||
};
|
||||
_ = TrackPropertiesAsync(type, baseProps, properties);
|
||||
}
|
||||
|
||||
private Task TrackPropertiesAsync(string type,
|
||||
Dictionary<string, object> basicProps, Dictionary<string, object> properties)
|
||||
{
|
||||
if (!IsInitialized) {
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
if (properties == null) {
|
||||
properties = new Dictionary<string, object>();
|
||||
}
|
||||
properties["sdk_version"] = TapTapSDK.Version;
|
||||
|
||||
Dictionary<string, object> data = new Dictionary<string, object>(basicProps) {
|
||||
{ "client_id", TapCoreStandalone.coreOptions.clientId },
|
||||
{ "type", type },
|
||||
{ "properties", properties }
|
||||
};
|
||||
|
||||
sender.Send(data);
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
private void InitBasicProps() {
|
||||
DeviceInfo.GetMacAddress(out string macList, out string firstMac);
|
||||
basicProps = new Dictionary<string, object> {
|
||||
{ "os", OS },
|
||||
{ "md", SystemInfo.deviceModel },
|
||||
{ "sv", SystemInfo.operatingSystem },
|
||||
{ "pn", "TapSDK" },
|
||||
{ "tapsdk_project", "TapSDKCore" },
|
||||
{ "session_uuid", session_uuid },
|
||||
{ "install_uuid", Identity.InstallationId },
|
||||
{ "persist_uuid", Identity.PersistentId },
|
||||
{ "ram", DeviceInfo.RAM },
|
||||
{ "rom", "0" },
|
||||
{ "width", Screen.currentResolution.width },
|
||||
{ "height", Screen.currentResolution.height },
|
||||
{ "provider", "unknown" },
|
||||
{ "app_version", TapCoreStandalone.coreOptions.gameVersion ?? Application.version },
|
||||
{ "sdk_version", TapTapSDK.Version },
|
||||
{ "network_type", Network },
|
||||
{ "channel", eventOptions.channel },
|
||||
{ "mac_list", macList },
|
||||
{ "first_mac", firstMac },
|
||||
{ "device_id5", DeviceInfo.GetLaunchUniqueID() }
|
||||
};
|
||||
}
|
||||
private string OS {
|
||||
get {
|
||||
switch (SystemInfo.operatingSystemFamily) {
|
||||
case OperatingSystemFamily.Windows:
|
||||
return "Windows";
|
||||
case OperatingSystemFamily.MacOSX:
|
||||
return "Mac";
|
||||
case OperatingSystemFamily.Linux:
|
||||
return "Linux";
|
||||
default:
|
||||
return "Unknown";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private string Network {
|
||||
get {
|
||||
switch (Application.internetReachability) {
|
||||
case NetworkReachability.ReachableViaCarrierDataNetwork:
|
||||
return "3";
|
||||
case NetworkReachability.ReachableViaLocalAreaNetwork:
|
||||
return "2";
|
||||
default:
|
||||
return "Unknown";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private bool IsInitialized {
|
||||
get {
|
||||
if (string.IsNullOrWhiteSpace(TapCoreStandalone.coreOptions.clientId)) {
|
||||
TapLog.Error("MUST be initialized.");
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
private static string generateUUID() {
|
||||
return Guid.NewGuid().ToString();
|
||||
}
|
||||
|
||||
private static string OpenID {
|
||||
get {
|
||||
IOpenIDProvider provider = BridgeUtils.CreateBridgeImplementation(typeof(IOpenIDProvider),
|
||||
"TapSDK.Login") as IOpenIDProvider;
|
||||
return provider?.GetOpenID();
|
||||
}
|
||||
}
|
||||
|
||||
public interface IDynamicProperties {
|
||||
Dictionary<string, object> GetDynamicProperties();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: ed9eafce641cf411196eb3308eceef1a
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
8
Assets/TapSDK/Core/Standalone/Runtime/Internal/UI.meta
Normal file
8
Assets/TapSDK/Core/Standalone/Runtime/Internal/UI.meta
Normal file
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: fba24ccb71fdc4129bae01af96a234a3
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,59 @@
|
||||
using System;
|
||||
using UnityEngine.UI;
|
||||
using UnityEngine;
|
||||
using TapSDK.UI;
|
||||
|
||||
namespace TapSDK.Core.Standalone.Internal {
|
||||
public class TapClientConnectTipController : BasePanelController
|
||||
{
|
||||
public Button installTipButton;
|
||||
public Button okButton;
|
||||
|
||||
public Text tipText;
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// bind ugui components for every panel
|
||||
/// </summary>
|
||||
protected override void BindComponents()
|
||||
{
|
||||
okButton = transform.Find("Root/OKButton").GetComponent<Button>();
|
||||
installTipButton = transform.Find("Root/InstallTipBtn").GetComponent<Button>();
|
||||
}
|
||||
|
||||
protected override void OnLoadSuccess()
|
||||
{
|
||||
base.OnLoadSuccess();
|
||||
|
||||
installTipButton.onClick.AddListener(OnInstallButtonClicked);
|
||||
okButton.onClick.AddListener(OnOKButtonClicked);
|
||||
}
|
||||
|
||||
internal void Show(int errorType)
|
||||
{
|
||||
if (errorType == (int)TapSDKInitResult.NoPlatform){
|
||||
tipText.text = "获取游戏信息失败,请下载 TapTap 客户端后重新启动游戏";
|
||||
}else if (errorType == (int)TapSDKInitResult.NotLaunchedByPlatform){
|
||||
tipText.text = "获取游戏信息失败,请从 TapTap 客户端重新启动游戏";
|
||||
}else if (errorType == (int)TapSDKInitResult.Unknown){
|
||||
tipText.text = "本地发生未知错误,请从 TapTap 客户端重新启动游戏";
|
||||
}else if (errorType == (int)TapSDKInitResult.PlatformVersionMismatch){
|
||||
tipText.text = "客户端版本不匹配,请将 TapTap 客户端升级到最新版本";
|
||||
}else {
|
||||
tipText.text = "发生未知错误,请从 TapTap 客户端重新启动游戏";
|
||||
}
|
||||
}
|
||||
|
||||
private void OnOKButtonClicked()
|
||||
{
|
||||
Close();
|
||||
Application.Quit();
|
||||
}
|
||||
|
||||
private void OnInstallButtonClicked()
|
||||
{
|
||||
Application.OpenURL("https://www.taptap.cn/mobile?utm_medium=coop&utm_source=pc_toStart");
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 88db6c5941bac46ba91fe65775ad712f
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,76 @@
|
||||
using UnityEngine;
|
||||
using System.Collections;
|
||||
using UnityEngine.UI;
|
||||
|
||||
namespace TapSDK.Core.Standalone.Internal {
|
||||
public enum UnderLineType
|
||||
{
|
||||
//画线位置
|
||||
Bottom = 0,
|
||||
Center
|
||||
}
|
||||
|
||||
//文本编辑扩展
|
||||
public class TapUnderLineText : MonoBehaviour
|
||||
{
|
||||
public Text linkText;
|
||||
public UnderLineType underLineType;
|
||||
public bool autoLink = true;
|
||||
private string underLineText = "_";
|
||||
|
||||
private void Awake()
|
||||
{
|
||||
if (underLineType == UnderLineType.Bottom)
|
||||
{
|
||||
underLineText = "_";
|
||||
}
|
||||
else
|
||||
{
|
||||
underLineText = "-";
|
||||
}
|
||||
}
|
||||
|
||||
private void Start()
|
||||
{
|
||||
if (autoLink)
|
||||
CreateLink(linkText, null);
|
||||
}
|
||||
|
||||
public void CreateLink(Text text, UnityEngine.Events.UnityAction onClickBtn = null)
|
||||
{
|
||||
if (text == null)
|
||||
return;
|
||||
|
||||
//克隆Text,获得相同的属性
|
||||
Text underline = Instantiate(text) as Text;
|
||||
underline.name = "Underline";
|
||||
underline.transform.SetParent(text.transform);
|
||||
underline.transform.localScale = Vector3.one;
|
||||
RectTransform rt = underline.rectTransform;
|
||||
//设置下划线坐标和位置
|
||||
rt.anchoredPosition3D = Vector3.zero;
|
||||
rt.offsetMax = Vector2.zero;
|
||||
rt.offsetMin = Vector2.zero;
|
||||
rt.anchorMax = Vector2.one;
|
||||
rt.anchorMin = Vector2.zero;
|
||||
underline.text = underLineText;
|
||||
float perlineWidth = underline.preferredWidth; //单个下划线宽度
|
||||
float width = text.preferredWidth;
|
||||
int lineCount = (int)Mathf.Round(width / perlineWidth);
|
||||
for (int i = 1; i < lineCount; i++)
|
||||
{
|
||||
underline.text += underLineText;
|
||||
}
|
||||
if (onClickBtn != null)
|
||||
{
|
||||
var btn = text.gameObject.AddComponent<Button>();
|
||||
btn.onClick.AddListener(onClickBtn);
|
||||
}
|
||||
var underLine = underline.GetComponent<TapUnderLineText>();
|
||||
if (underLine)
|
||||
{
|
||||
underLine.autoLink = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: fca03f92079ddaa40a110feb91757557
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
50
Assets/TapSDK/Core/Standalone/Runtime/Internal/User.cs
Normal file
50
Assets/TapSDK/Core/Standalone/Runtime/Internal/User.cs
Normal file
@@ -0,0 +1,50 @@
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace TapSDK.Core.Standalone.Internal {
|
||||
public class User {
|
||||
internal static readonly string USER_ID_KEY = "tapdb_played_duration_user_id";
|
||||
|
||||
internal string Id {
|
||||
get => id;
|
||||
set {
|
||||
id = value;
|
||||
TapCoreStandalone.Prefs.Set(USER_ID_KEY, id);
|
||||
}
|
||||
}
|
||||
|
||||
private string id;
|
||||
|
||||
private readonly PlayRecorder playRecorder;
|
||||
|
||||
internal User() {
|
||||
playRecorder = new PlayRecorder();
|
||||
}
|
||||
|
||||
internal void Login(string userId, Dictionary<string, object> props = null) {
|
||||
// 先执行旧用户登出逻辑
|
||||
Id = TapCoreStandalone.Prefs.Get<string>(USER_ID_KEY);
|
||||
if (!string.IsNullOrWhiteSpace(Id)) {
|
||||
Logout();
|
||||
}
|
||||
|
||||
// 再执行新用户登录逻辑
|
||||
Id = userId;
|
||||
|
||||
TapEventStandalone.Tracker?.TrackEvent(Constants.USER_LOGIN, props, true);
|
||||
|
||||
|
||||
Dictionary<string, object> updateProps = new Dictionary<string, object> {
|
||||
{ "has_user", true },
|
||||
};
|
||||
TapEventStandalone.Tracker?.TrackDeviceProperties(Constants.PROPERTY_UPDATE_TYPE, updateProps);
|
||||
|
||||
playRecorder.Start();
|
||||
}
|
||||
|
||||
internal void Logout() {
|
||||
playRecorder.Stop();
|
||||
|
||||
Id = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
11
Assets/TapSDK/Core/Standalone/Runtime/Internal/User.cs.meta
Normal file
11
Assets/TapSDK/Core/Standalone/Runtime/Internal/User.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 910b81931513342b08384bc8ade66afb
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 92adce826e16c4263aa1d3a4e5664306
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,9 @@
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace TapSDK.Core.Standalone.Internal.Service
|
||||
{
|
||||
public interface ITapLoginService
|
||||
{
|
||||
string ObtainAuthorizationAsync(string url, string method);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 53ad0b57811aa4067a6bbdbc700ddd63
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
8
Assets/TapSDK/Core/Standalone/Runtime/Public.meta
Normal file
8
Assets/TapSDK/Core/Standalone/Runtime/Public.meta
Normal file
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 75cc82e9a12884d5c9ac299e6695f804
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,378 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using TapSDK.Core.Internal.Log;
|
||||
using TapSDK.Core.Internal.Utils;
|
||||
using TapSDK.Core.Standalone.Internal;
|
||||
using TapSDK.Core.Standalone.Internal.Openlog;
|
||||
using TapSDK.UI;
|
||||
using UnityEditor;
|
||||
using UnityEngine;
|
||||
|
||||
namespace TapSDK.Core.Standalone
|
||||
{
|
||||
#if UNITY_STANDALONE_WIN
|
||||
public class TapClientStandalone
|
||||
{
|
||||
// 是否是渠道服游戏包
|
||||
private static bool isChannelPackage = false;
|
||||
|
||||
// -1 未执行 0 失败 1 成功
|
||||
private static int lastIsLaunchedFromTapTapPCResult = -1;
|
||||
private static bool isRuningIsLaunchedFromTapTapPC = false;
|
||||
|
||||
// 当为渠道游戏包时,与启动器的初始化校验结果
|
||||
private static TapInitResult tapInitResult;
|
||||
|
||||
// <summary>
|
||||
// 校验游戏是否通过启动器唤起,建立与启动器通讯
|
||||
//</summary>
|
||||
public static async Task<bool> IsLaunchedFromTapTapPC()
|
||||
{
|
||||
// 正在执行中
|
||||
if (isRuningIsLaunchedFromTapTapPC)
|
||||
{
|
||||
UIManager.Instance.OpenToast(
|
||||
"IsLaunchedFromTapTapPC 正在执行,请勿重复调用",
|
||||
UIManager.GeneralToastLevel.Error
|
||||
);
|
||||
TapLog.Error("IsLaunchedFromTapTapPC 正在执行,请勿重复调用");
|
||||
return false;
|
||||
}
|
||||
// 多次执行时返回上一次结果
|
||||
if (lastIsLaunchedFromTapTapPCResult != -1)
|
||||
{
|
||||
TapLog.Log(
|
||||
"IsLaunchedFromTapTapPC duplicate invoke return "
|
||||
+ lastIsLaunchedFromTapTapPCResult
|
||||
);
|
||||
return lastIsLaunchedFromTapTapPCResult > 0;
|
||||
}
|
||||
|
||||
isChannelPackage = true;
|
||||
TapTapSdkOptions coreOptions = TapCoreStandalone.coreOptions;
|
||||
if (coreOptions == null)
|
||||
{
|
||||
UIManager.Instance.OpenToast(
|
||||
"IsLaunchedFromTapTapPC 调用必须在初始化之后",
|
||||
UIManager.GeneralToastLevel.Error
|
||||
);
|
||||
TapLog.Error("IsLaunchedFromTapTapPC 调用必须在初始化之后");
|
||||
return false;
|
||||
}
|
||||
string clientId = coreOptions.clientId;
|
||||
string pubKey = coreOptions.clientPublicKey;
|
||||
if (string.IsNullOrEmpty(clientId) || string.IsNullOrEmpty(pubKey))
|
||||
{
|
||||
UIManager.Instance.OpenToast(
|
||||
"clientId 及 TapPubKey 参数都不能为空, clientId ="
|
||||
+ clientId
|
||||
+ ", TapPubKey = "
|
||||
+ pubKey,
|
||||
UIManager.GeneralToastLevel.Error
|
||||
);
|
||||
TapLog.Error(
|
||||
"clientId 或 TapPubKey 无效, clientId = " + clientId + ", TapPubKey = " + pubKey
|
||||
);
|
||||
return false;
|
||||
}
|
||||
isRuningIsLaunchedFromTapTapPC = true;
|
||||
|
||||
string sessionId = Guid.NewGuid().ToString();
|
||||
TapCoreTracker.Instance.TrackStart(TapCoreTracker.METHOD_LAUNCHER, sessionId);
|
||||
try
|
||||
{
|
||||
TapInitResult result = await RunClientBridgeMethod(clientId, pubKey);
|
||||
TapLog.Log(
|
||||
"check startupWithClientBridge finished thread = "
|
||||
+ Thread.CurrentThread.ManagedThreadId
|
||||
);
|
||||
isRuningIsLaunchedFromTapTapPC = false;
|
||||
if (result.needQuitGame)
|
||||
{
|
||||
lastIsLaunchedFromTapTapPCResult = 0;
|
||||
TapCoreTracker.Instance.TrackSuccess(
|
||||
TapCoreTracker.METHOD_LAUNCHER,
|
||||
sessionId,
|
||||
TapCoreTracker.SUCCESS_TYPE_RESTART
|
||||
);
|
||||
TapLog.Log("IsLaunchedFromTapTapPC Quit game");
|
||||
#if UNITY_EDITOR
|
||||
TapLog.Log(
|
||||
$"本地测试文件配置错误,请确保 taptap_client_id.txt 文件拷贝到 {EditorApplication.applicationPath} 同级目录下"
|
||||
);
|
||||
EditorApplication.ExitPlaymode();
|
||||
#else
|
||||
Application.Quit();
|
||||
#endif
|
||||
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (result.result == (int)TapSDKInitResult.OK)
|
||||
{
|
||||
string currentClientId;
|
||||
bool isFetchClientIdSuccess = TapClientBridge.GetClientId(
|
||||
out currentClientId
|
||||
);
|
||||
TapLog.Log("IsLaunchedFromTapTapPC get clientId = " + currentClientId);
|
||||
if (
|
||||
isFetchClientIdSuccess
|
||||
&& !string.IsNullOrEmpty(currentClientId)
|
||||
&& currentClientId != clientId
|
||||
)
|
||||
{
|
||||
UIManager.Instance.OpenToast(
|
||||
"SDK 中配置的 clientId = "
|
||||
+ clientId
|
||||
+ "与 Tap 启动器中"
|
||||
+ currentClientId
|
||||
+ "不一致",
|
||||
UIManager.GeneralToastLevel.Error
|
||||
);
|
||||
TapLog.Error(
|
||||
"SDK 中配置的 clientId = "
|
||||
+ clientId
|
||||
+ "与 Tap 启动器中"
|
||||
+ currentClientId
|
||||
+ "不一致"
|
||||
);
|
||||
TapCoreTracker.Instance.TrackFailure(
|
||||
TapCoreTracker.METHOD_LAUNCHER,
|
||||
sessionId,
|
||||
-1,
|
||||
"SDK 中配置的 clientId = "
|
||||
+ clientId
|
||||
+ "与 Tap 启动器中"
|
||||
+ currentClientId
|
||||
+ "不一致"
|
||||
);
|
||||
lastIsLaunchedFromTapTapPCResult = 0;
|
||||
return false;
|
||||
}
|
||||
string openId;
|
||||
bool fetchOpenIdSuccess = TapClientBridge.GetTapUserOpenId(out openId);
|
||||
if (fetchOpenIdSuccess)
|
||||
{
|
||||
TapLog.Log("IsLaunchedFromTapTapPC get openId = " + openId);
|
||||
EventManager.TriggerEvent(
|
||||
EventManager.IsLaunchedFromTapTapPCFinished,
|
||||
openId
|
||||
);
|
||||
}
|
||||
else
|
||||
{
|
||||
TapLog.Log("IsLaunchedFromTapTapPC get openId failed");
|
||||
}
|
||||
lastIsLaunchedFromTapTapPCResult = 1;
|
||||
TapClientBridgePoll.StartUp();
|
||||
TapCoreTracker.Instance.TrackSuccess(
|
||||
TapCoreTracker.METHOD_LAUNCHER,
|
||||
sessionId,
|
||||
TapCoreTracker.SUCCESS_TYPE_INIT
|
||||
);
|
||||
TapLog.Log("IsLaunchedFromTapTapPC check success");
|
||||
// 如果开发者已经注册了监听客户端运行状态,此时添加对应回调
|
||||
if (
|
||||
taptapPCStateChangeListeners != null
|
||||
&& taptapPCStateChangeListeners.Count > 0
|
||||
&& !hasRegisterSystemListener
|
||||
)
|
||||
{
|
||||
TapClientBridge.RegisterSystemStateCallback(TapTapPCStateDelegate);
|
||||
hasRegisterSystemListener = true;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
TapCoreTracker.Instance.TrackFailure(
|
||||
TapCoreTracker.METHOD_LAUNCHER,
|
||||
sessionId,
|
||||
(int)result.result,
|
||||
result.errorMsg ?? ""
|
||||
);
|
||||
lastIsLaunchedFromTapTapPCResult = 0;
|
||||
TapLog.Log(
|
||||
"IsLaunchedFromTapTapPC show TapClient tip Pannel "
|
||||
+ result.result
|
||||
+ " , error = "
|
||||
+ result.errorMsg
|
||||
);
|
||||
string tipPannelPath = "Prefabs/TapClient/TapClientConnectTipPanel";
|
||||
if (Resources.Load<GameObject>(tipPannelPath) != null)
|
||||
{
|
||||
var pannel = UIManager.Instance.OpenUI<TapClientConnectTipController>(
|
||||
tipPannelPath
|
||||
);
|
||||
pannel.Show(result.result);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
lastIsLaunchedFromTapTapPCResult = 0;
|
||||
TapCoreTracker.Instance.TrackFailure(
|
||||
TapCoreTracker.METHOD_LAUNCHER,
|
||||
sessionId,
|
||||
(int)TapSDKInitResult.Unknown,
|
||||
e.Message ?? ""
|
||||
);
|
||||
|
||||
TapLog.Log(
|
||||
"IsLaunchedFromTapTapPC check exception = " + e.Message + " \n" + e.StackTrace
|
||||
);
|
||||
string tipPannelPath = "Prefabs/TapClient/TapClientConnectTipPanel";
|
||||
if (Resources.Load<GameObject>(tipPannelPath) != null)
|
||||
{
|
||||
var pannel = UIManager.Instance.OpenUI<TapClientConnectTipController>(
|
||||
tipPannelPath
|
||||
);
|
||||
pannel.Show((int)TapSDKInitResult.Unknown);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private static async Task<TapInitResult> RunClientBridgeMethod(
|
||||
string clientId,
|
||||
string pubKey
|
||||
)
|
||||
{
|
||||
TaskCompletionSource<TapInitResult> task = new TaskCompletionSource<TapInitResult>();
|
||||
try
|
||||
{
|
||||
await Task.Run(() =>
|
||||
{
|
||||
TapLog.Log(
|
||||
"check startupWithClientBridge start thread = "
|
||||
+ Thread.CurrentThread.ManagedThreadId
|
||||
);
|
||||
bool needQuitGame = TapClientBridge.TapSDK_RestartAppIfNecessary(clientId);
|
||||
TapLog.Log(
|
||||
"RunClientBridgeMethodWithTimeout invoke TapSDK_RestartAppIfNecessary result = "
|
||||
+ needQuitGame
|
||||
);
|
||||
TapLog.Log(
|
||||
"RunClientBridgeMethodWithTimeout invoke TapSDK_RestartAppIfNecessary finished "
|
||||
);
|
||||
if (needQuitGame)
|
||||
{
|
||||
tapInitResult = new TapInitResult(needQuitGame);
|
||||
}
|
||||
else
|
||||
{
|
||||
string outputError;
|
||||
int tapSDKInitResult = TapClientBridge.CheckInitState(
|
||||
out outputError,
|
||||
pubKey
|
||||
);
|
||||
TapLog.Log(
|
||||
"RunClientBridgeMethodWithTimeout invoke CheckInitState result = "
|
||||
+ tapSDKInitResult
|
||||
+ ", error = "
|
||||
+ outputError
|
||||
);
|
||||
tapInitResult = new TapInitResult(tapSDKInitResult, outputError);
|
||||
}
|
||||
task.TrySetResult(tapInitResult);
|
||||
});
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
TapLog.Log("RunClientBridgeMethodWithTimeout invoke C 方法出错!" + ex.Message);
|
||||
task.TrySetException(ex);
|
||||
}
|
||||
return await task.Task;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 是否需要从启动器登录
|
||||
/// </summary>
|
||||
public static bool IsNeedLoginByTapClient()
|
||||
{
|
||||
return isChannelPackage;
|
||||
}
|
||||
|
||||
public static bool isPassedInLaunchedFromTapTapPCCheck()
|
||||
{
|
||||
return lastIsLaunchedFromTapTapPCResult > 0;
|
||||
}
|
||||
|
||||
private static HashSet<Action<int>> taptapPCStateChangeListeners;
|
||||
private static volatile bool hasRegisterSystemListener = false;
|
||||
/// <summary>
|
||||
/// 设置 TapPC 客户端状态监听
|
||||
/// </summary>
|
||||
internal static void RegisterTapTapPCStateChangeListener(Action<int> action)
|
||||
{
|
||||
if (taptapPCStateChangeListeners == null)
|
||||
{
|
||||
taptapPCStateChangeListeners = new HashSet<Action<int>>();
|
||||
}
|
||||
taptapPCStateChangeListeners.Add(action);
|
||||
if (isPassedInLaunchedFromTapTapPCCheck() && !hasRegisterSystemListener)
|
||||
{
|
||||
TapClientBridge.RegisterSystemStateCallback(TapTapPCStateDelegate);
|
||||
hasRegisterSystemListener = true;
|
||||
}
|
||||
}
|
||||
|
||||
internal static void UnRegisterTapTapPCStateChangeListener(Action<int> action)
|
||||
{
|
||||
if (taptapPCStateChangeListeners != null && taptapPCStateChangeListeners.Count > 0)
|
||||
{
|
||||
taptapPCStateChangeListeners.Remove(action);
|
||||
if (taptapPCStateChangeListeners.Count == 0)
|
||||
{
|
||||
TapClientBridge.UnRegisterSystemStateCallback(TapTapPCStateDelegate);
|
||||
hasRegisterSystemListener = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[AOT.MonoPInvokeCallback(typeof(TapClientBridge.CallbackDelegate))]
|
||||
static void TapTapPCStateDelegate(int id, IntPtr userData)
|
||||
{
|
||||
if (id == (int)TapEventID.SystemStateChanged)
|
||||
{
|
||||
SystemStateResponse response = Marshal.PtrToStructure<SystemStateResponse>(
|
||||
userData
|
||||
);
|
||||
if (taptapPCStateChangeListeners != null)
|
||||
{
|
||||
foreach (var listener in taptapPCStateChangeListeners)
|
||||
{
|
||||
listener(response.state);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 初始化校验结果
|
||||
private class TapInitResult
|
||||
{
|
||||
internal int result;
|
||||
internal string errorMsg;
|
||||
|
||||
internal bool needQuitGame = false;
|
||||
|
||||
public TapInitResult(int result, string errorMsg)
|
||||
{
|
||||
this.result = result;
|
||||
this.errorMsg = errorMsg;
|
||||
}
|
||||
|
||||
public TapInitResult(bool needQuitGame)
|
||||
{
|
||||
this.needQuitGame = needQuitGame;
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 0e4bd11b0b39e402f994b87ae7f21ef3
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,270 @@
|
||||
using TapSDK.Core.Internal;
|
||||
using UnityEngine;
|
||||
using TapSDK.Core.Standalone.Internal;
|
||||
using System.Collections.Generic;
|
||||
using UnityEditor;
|
||||
using System.IO;
|
||||
using TapSDK.Core.Internal.Utils;
|
||||
using TapSDK.Core.Standalone.Internal.Openlog;
|
||||
using TapSDK.Core.Internal.Log;
|
||||
using TapSDK.Core.Standalone.Internal.Http;
|
||||
using Newtonsoft.Json;
|
||||
using TapSDK.Core.Standalone.Internal.Bean;
|
||||
using System.Threading.Tasks;
|
||||
using System;
|
||||
using System.Threading;
|
||||
using TapSDK.UI;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace TapSDK.Core.Standalone
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents the standalone implementation of the TapCore SDK.
|
||||
/// </summary>
|
||||
public class TapCoreStandalone : ITapCorePlatform
|
||||
{
|
||||
internal static Prefs Prefs;
|
||||
internal static User User;
|
||||
internal static TapTapSdkOptions coreOptions;
|
||||
|
||||
// client 信息是否匹配
|
||||
internal static bool isClientInfoMatched = true;
|
||||
|
||||
internal static TapGatekeeper gatekeeperData = new TapGatekeeper();
|
||||
|
||||
private readonly TapHttp tapHttp = TapHttp.NewBuilder("TapSDKCore", TapTapSDK.Version).Build();
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="TapCoreStandalone"/> class.
|
||||
/// </summary>
|
||||
public TapCoreStandalone()
|
||||
{
|
||||
// Instantiate modules
|
||||
User = new User();
|
||||
TapLoom.Initialize();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes the TapCore SDK with the specified options.
|
||||
/// </summary>
|
||||
/// <param name="options">The TapCore SDK options.</param>
|
||||
public void Init(TapTapSdkOptions options)
|
||||
{
|
||||
Init(options, null);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes the TapCore SDK with the specified core options and additional options.
|
||||
/// </summary>
|
||||
/// <param name="coreOption">The TapCore SDK core options.</param>
|
||||
/// <param name="otherOptions">Additional TapCore SDK options.</param>
|
||||
public void Init(TapTapSdkOptions coreOption, TapTapSdkBaseOptions[] otherOptions)
|
||||
{
|
||||
if (coreOption.clientId == null || coreOption.clientId.Length == 0)
|
||||
{
|
||||
TapVerifyInitStateUtils.ShowVerifyErrorMsg("clientId 不能为空", "clientId 不能为空");
|
||||
return;
|
||||
}
|
||||
if (coreOption.clientToken == null || coreOption.clientToken.Length == 0)
|
||||
{
|
||||
TapVerifyInitStateUtils.ShowVerifyErrorMsg("clientToken 不能为空", "clientToken 不能为空");
|
||||
return;
|
||||
}
|
||||
|
||||
TapLog.Log("SDK Init Options : ", "coreOption : " + JsonConvert.SerializeObject(coreOption) + "\notherOptions : " + JsonConvert.SerializeObject(otherOptions));
|
||||
coreOptions = coreOption;
|
||||
// 设置区域与语言
|
||||
TapLocalizeManager.SetCurrentRegion(coreOption.region == TapTapRegionType.CN);
|
||||
TapLocalizeManager.SetCurrentLanguage(coreOption.preferredLanguage);
|
||||
if (Prefs == null)
|
||||
{
|
||||
Prefs = new Prefs();
|
||||
}
|
||||
TapOpenlogStandalone.Init();
|
||||
|
||||
var path = Path.Combine(Application.persistentDataPath, Constants.ClientSettingsFileName + "_" + coreOption.clientId + ".json");
|
||||
// 兼容旧版文件
|
||||
if (!File.Exists(path))
|
||||
{
|
||||
var oldPath = Path.Combine(Application.persistentDataPath, Constants.ClientSettingsFileName + ".json");
|
||||
if (File.Exists(oldPath))
|
||||
{
|
||||
File.Move(oldPath, path);
|
||||
}
|
||||
}
|
||||
if (File.Exists(path))
|
||||
{
|
||||
var clientSettings = File.ReadAllText(path);
|
||||
// TapLog.Log("本地 clientSettings: " + clientSettings);
|
||||
try
|
||||
{
|
||||
TapGatekeeper tapGatekeeper = JsonConvert.DeserializeObject<TapGatekeeper>(clientSettings);
|
||||
if (tapGatekeeper.Switch?.Heartbeat == true)
|
||||
{
|
||||
TapAppDurationStandalone.Enable();
|
||||
}
|
||||
else
|
||||
{
|
||||
TapAppDurationStandalone.Disable();
|
||||
}
|
||||
gatekeeperData = tapGatekeeper;
|
||||
}
|
||||
catch (System.Exception e)
|
||||
{
|
||||
TapLog.Warning("TriggerEvent error: " + e.Message);
|
||||
}
|
||||
}
|
||||
|
||||
requestClientSetting();
|
||||
}
|
||||
|
||||
public void UpdateLanguage(TapTapLanguageType language)
|
||||
{
|
||||
if (coreOptions == null)
|
||||
{
|
||||
TapLog.Log("coreOptions is null");
|
||||
return;
|
||||
}
|
||||
TapLog.Log("UpdateLanguage called with language: " + language);
|
||||
coreOptions.preferredLanguage = language;
|
||||
TapLocalizeManager.SetCurrentLanguage(language);
|
||||
}
|
||||
|
||||
public static string getGatekeeperConfigUrl(string key)
|
||||
{
|
||||
if (gatekeeperData != null)
|
||||
{
|
||||
var urlsData = gatekeeperData.Urls;
|
||||
if (urlsData != null && urlsData.ContainsKey(key))
|
||||
{
|
||||
var keyData = urlsData[key];
|
||||
if (keyData != null)
|
||||
{
|
||||
return keyData.Browser;
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private void requestClientSetting()
|
||||
{
|
||||
// 使用 httpclient 请求 /sdk-core/v1/gatekeeper 获取配置
|
||||
#if UNITY_EDITOR
|
||||
var bundleIdentifier = PlayerSettings.applicationIdentifier;
|
||||
#else
|
||||
var bundleIdentifier = Application.identifier;
|
||||
#endif
|
||||
var path = "sdk-core/v1/gatekeeper";
|
||||
var body = new Dictionary<string, object> {
|
||||
{ "platform", "pc" },
|
||||
{ "bundle_id", bundleIdentifier }
|
||||
};
|
||||
|
||||
tapHttp.PostJson<TapGatekeeper>(
|
||||
url: path,
|
||||
json: body,
|
||||
onSuccess: (data) =>
|
||||
{
|
||||
if (data.Switch?.Heartbeat == true)
|
||||
{
|
||||
TapAppDurationStandalone.Enable();
|
||||
}
|
||||
else
|
||||
{
|
||||
TapAppDurationStandalone.Disable();
|
||||
}
|
||||
gatekeeperData = data;
|
||||
// 把 data 存储在本地
|
||||
saveClientSettings(data);
|
||||
// 发通知
|
||||
EventManager.TriggerEvent(Constants.ClientSettingsEventKey, data);
|
||||
},
|
||||
onFailure: (error) =>
|
||||
{
|
||||
if (error is TapHttpServerException se)
|
||||
{
|
||||
if (TapHttpErrorConstants.ERROR_INVALID_CLIENT.Equals(se.ErrorData.Error))
|
||||
{
|
||||
isClientInfoMatched = false;
|
||||
TapLog.Error("Init Failed", se.ErrorData.ErrorDescription);
|
||||
TapMessage.ShowMessage(se.ErrorData.Msg, TapMessage.Position.bottom, TapMessage.Time.twoSecond);
|
||||
}
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
private void saveClientSettings(TapGatekeeper settings)
|
||||
{
|
||||
string json = JsonConvert.SerializeObject(settings);
|
||||
File.WriteAllText(Path.Combine(Application.persistentDataPath, Constants.ClientSettingsFileName + "_" + TapTapSDK.taptapSdkOptions.clientId + ".json"), json);
|
||||
}
|
||||
|
||||
|
||||
public static bool CheckInitState()
|
||||
{
|
||||
// 未初始化
|
||||
if (coreOptions == null || coreOptions.clientId == null || coreOptions.clientId.Length == 0
|
||||
|| coreOptions.clientToken == null || coreOptions.clientToken.Length == 0)
|
||||
{
|
||||
TapVerifyInitStateUtils.ShowVerifyErrorMsg("当前应用还未初始化", "当前应用还未初始化: 请在调用 SDK 业务接口前,先调用 TapTapSDK.Init 接口");
|
||||
return false;
|
||||
}
|
||||
// 应用信息不匹配
|
||||
if (isClientInfoMatched == false)
|
||||
{
|
||||
TapVerifyInitStateUtils.ShowVerifyErrorMsg("当前应用初始化信息错误", "当前应用初始化信息错误: 请在 TapTap 开发者中心检查当前应用调用初始化接口设置的 clientId 、clientToken 是否匹配");
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// 获取当前用户设置的 DB userID
|
||||
public static string GetCurrentUserId()
|
||||
{
|
||||
return User?.Id;
|
||||
}
|
||||
|
||||
|
||||
// <summary>
|
||||
// 校验游戏是否通过启动器唤起,建立与启动器通讯
|
||||
//</summary>
|
||||
public async Task<bool> IsLaunchedFromTapTapPC()
|
||||
{
|
||||
#if UNITY_STANDALONE_WIN
|
||||
return await TapClientStandalone.IsLaunchedFromTapTapPC();
|
||||
#else
|
||||
throw new System.NotImplementedException();
|
||||
#endif
|
||||
}
|
||||
|
||||
public void SendOpenLog(
|
||||
string project,
|
||||
string version,
|
||||
string action,
|
||||
Dictionary<string, string> properties
|
||||
)
|
||||
{
|
||||
TapOpenlogStandalone.LogBusiness(project, version, action, properties);
|
||||
}
|
||||
|
||||
#if UNITY_STANDALONE_WIN
|
||||
public void RegisterTapTapPCStateChangeListener(Action<int> action)
|
||||
{
|
||||
TapClientStandalone.RegisterTapTapPCStateChangeListener(action);
|
||||
}
|
||||
|
||||
public void UnRegisterTapTapPCStateChangeListener(Action<int> action)
|
||||
{
|
||||
TapClientStandalone.UnRegisterTapTapPCStateChangeListener(action);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
public interface IOpenIDProvider
|
||||
{
|
||||
string GetOpenID();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: e53020dc505c04b53a9fe5365cae8114
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,431 @@
|
||||
using System;
|
||||
using TapSDK.Core.Internal;
|
||||
using TapSDK.Core.Standalone.Internal;
|
||||
using System.Collections.Generic;
|
||||
using System.Text.RegularExpressions;
|
||||
using UnityEngine;
|
||||
using TapSDK.Core.Internal.Utils;
|
||||
using TapSDK.Core.Internal.Log;
|
||||
|
||||
namespace TapSDK.Core.Standalone
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents the standalone implementation of the Tap event.
|
||||
/// </summary>
|
||||
public class TapEventStandalone : ITapEventPlatform
|
||||
{
|
||||
internal static Tracker Tracker;
|
||||
private readonly User User = TapCoreStandalone.User;
|
||||
|
||||
private TapTapEventOptions eventOptions;
|
||||
|
||||
public void Init(TapTapEventOptions eventOptions)
|
||||
{
|
||||
this.eventOptions = eventOptions;
|
||||
if (eventOptions == null || !eventOptions.enableTapTapEvent)
|
||||
{
|
||||
return;
|
||||
}
|
||||
Tracker = new Tracker();
|
||||
Tracker.Init(eventOptions);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the user ID for tracking events.
|
||||
/// </summary>
|
||||
/// <param name="userID">The user ID to set.</param>
|
||||
public void SetUserID(string userID)
|
||||
{
|
||||
if (!CheckInitAndEnableState())
|
||||
{
|
||||
return;
|
||||
}
|
||||
SetUserID(userID, null);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the user ID and additional properties for tracking events.
|
||||
/// </summary>
|
||||
/// <param name="userID">The user ID to set.</param>
|
||||
/// <param name="properties">Additional properties to associate with the user.</param>
|
||||
public void SetUserID(string userID, string properties)
|
||||
{
|
||||
if (!CheckInitAndEnableState())
|
||||
{
|
||||
return;
|
||||
}
|
||||
if (!IsValidUserID(userID))
|
||||
{
|
||||
TapLog.Error("Invalid user ID, length should be 1-160 and only contains a-zA-Z0-9_+/=.,:");
|
||||
return;
|
||||
}
|
||||
|
||||
Dictionary<string, object> prop = Json.Deserialize(properties) as Dictionary<string, object>;
|
||||
User.Login(userID, filterProperties(prop));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Clears the current user.
|
||||
/// </summary>
|
||||
public void ClearUser()
|
||||
{
|
||||
if (!CheckInitAndEnableState())
|
||||
{
|
||||
return;
|
||||
}
|
||||
User.Logout();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the device ID.
|
||||
/// </summary>
|
||||
/// <returns>The device ID.</returns>
|
||||
public string GetDeviceId()
|
||||
{
|
||||
if (!CheckInitAndEnableState())
|
||||
{
|
||||
return "";
|
||||
}
|
||||
return Identity.DeviceId;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Logs an event with the specified name and properties.
|
||||
/// </summary>
|
||||
/// <param name="name">The name of the event.</param>
|
||||
/// <param name="properties">Additional properties to associate with the event.</param>
|
||||
public void LogEvent(string name, string properties)
|
||||
{
|
||||
if (!CheckInitAndEnableState())
|
||||
{
|
||||
return;
|
||||
}
|
||||
// name 长度256非空,不符合的丢事件,打log
|
||||
if (!checkLength(name))
|
||||
{
|
||||
TapLog.Error(name + " Event name length should be less than or equal to 256 characters.");
|
||||
return;
|
||||
}
|
||||
Dictionary<string, object> prop = Json.Deserialize(properties) as Dictionary<string, object>;
|
||||
Tracker.TrackEvent(name, filterProperties(prop));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tracks device initialization with the specified properties.
|
||||
/// </summary>
|
||||
/// <param name="properties">Additional properties to associate with the device initialization.</param>
|
||||
public void DeviceInitialize(string properties)
|
||||
{
|
||||
if (!CheckInitAndEnableState())
|
||||
{
|
||||
return;
|
||||
}
|
||||
Dictionary<string, object> prop = Json.Deserialize(properties) as Dictionary<string, object>;
|
||||
Tracker.TrackDeviceProperties(Constants.PROPERTY_INITIALIZE_TYPE, filterProperties(prop));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tracks device update with the specified properties.
|
||||
/// </summary>
|
||||
/// <param name="properties">Additional properties to associate with the device update.</param>
|
||||
public void DeviceUpdate(string properties)
|
||||
{
|
||||
if (!CheckInitAndEnableState())
|
||||
{
|
||||
return;
|
||||
}
|
||||
Dictionary<string, object> prop = Json.Deserialize(properties) as Dictionary<string, object>;
|
||||
Tracker.TrackDeviceProperties(Constants.PROPERTY_UPDATE_TYPE, filterProperties(prop));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tracks device addition with the specified properties.
|
||||
/// </summary>
|
||||
/// <param name="properties">Additional properties to associate with the device addition.</param>
|
||||
public void DeviceAdd(string properties)
|
||||
{
|
||||
if (!CheckInitAndEnableState())
|
||||
{
|
||||
return;
|
||||
}
|
||||
Dictionary<string, object> prop = Json.Deserialize(properties) as Dictionary<string, object>;
|
||||
Tracker.TrackDeviceProperties(Constants.PROPERTY_ADD_TYPE, filterProperties(prop));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tracks user initialization with the specified properties.
|
||||
/// </summary>
|
||||
/// <param name="properties">Additional properties to associate with the user initialization.</param>
|
||||
public void UserInitialize(string properties)
|
||||
{
|
||||
if (!CheckInitAndEnableState())
|
||||
{
|
||||
return;
|
||||
}
|
||||
Dictionary<string, object> prop = Json.Deserialize(properties) as Dictionary<string, object>;
|
||||
Tracker.TrackUserProperties(Constants.PROPERTY_INITIALIZE_TYPE, filterProperties(prop));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tracks user update with the specified properties.
|
||||
/// </summary>
|
||||
/// <param name="properties">Additional properties to associate with the user update.</param>
|
||||
public void UserUpdate(string properties)
|
||||
{
|
||||
if (!CheckInitAndEnableState())
|
||||
{
|
||||
return;
|
||||
}
|
||||
Dictionary<string, object> prop = Json.Deserialize(properties) as Dictionary<string, object>;
|
||||
Tracker.TrackUserProperties(Constants.PROPERTY_UPDATE_TYPE, filterProperties(prop));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tracks user addition with the specified properties.
|
||||
/// </summary>
|
||||
/// <param name="properties">Additional properties to associate with the user addition.</param>
|
||||
public void UserAdd(string properties)
|
||||
{
|
||||
if (!CheckInitAndEnableState())
|
||||
{
|
||||
return;
|
||||
}
|
||||
Dictionary<string, object> prop = Json.Deserialize(properties) as Dictionary<string, object>;
|
||||
Tracker.TrackUserProperties(Constants.PROPERTY_ADD_TYPE, filterProperties(prop));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds a common property with the specified key and value.
|
||||
/// </summary>
|
||||
/// <param name="key">The key of the common property.</param>
|
||||
/// <param name="value">The value of the common property.</param>
|
||||
public void AddCommonProperty(string key, string value)
|
||||
{
|
||||
if (!CheckInitAndEnableState())
|
||||
{
|
||||
return;
|
||||
}
|
||||
if (!checkKeyValueLength(key, value))
|
||||
{
|
||||
return;
|
||||
}
|
||||
Tracker.AddCommonProperty(key, value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds common properties with the specified JSON string.
|
||||
/// </summary>
|
||||
/// <param name="properties">The JSON string containing the common properties.</param>
|
||||
public void AddCommon(string properties)
|
||||
{
|
||||
if (!CheckInitAndEnableState())
|
||||
{
|
||||
return;
|
||||
}
|
||||
Dictionary<string, object> prop = Json.Deserialize(properties) as Dictionary<string, object>;
|
||||
Tracker.AddCommon(filterProperties(prop));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Clears the common property with the specified key.
|
||||
/// </summary>
|
||||
/// <param name="key">The key of the common property to clear.</param>
|
||||
public void ClearCommonProperty(string key)
|
||||
{
|
||||
if (!CheckInitAndEnableState())
|
||||
{
|
||||
return;
|
||||
}
|
||||
Tracker.ClearCommonProperty(key);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Clears the common properties with the specified keys.
|
||||
/// </summary>
|
||||
/// <param name="keys">The keys of the common properties to clear.</param>
|
||||
public void ClearCommonProperties(string[] keys)
|
||||
{
|
||||
if (!CheckInitAndEnableState())
|
||||
{
|
||||
return;
|
||||
}
|
||||
Tracker.ClearCommonProperties(keys);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Clears all common properties.
|
||||
/// </summary>
|
||||
public void ClearAllCommonProperties()
|
||||
{
|
||||
if (!CheckInitAndEnableState())
|
||||
{
|
||||
return;
|
||||
}
|
||||
Tracker.ClearAllCommonProperties();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Logs a charge event with the specified details and properties.
|
||||
/// </summary>
|
||||
/// <param name="orderID">The ID of the order.</param>
|
||||
/// <param name="productName">The name of the product.</param>
|
||||
/// <param name="amount">The amount of the charge.</param>
|
||||
/// <param name="currencyType">The currency type of the charge.</param>
|
||||
/// <param name="paymentMethod">The payment method used for the charge.</param>
|
||||
/// <param name="properties">Additional properties to associate with the charge event.</param>
|
||||
public void LogChargeEvent(string orderID, string productName, long amount, string currencyType, string paymentMethod, string properties)
|
||||
{
|
||||
if (!CheckInitAndEnableState())
|
||||
{
|
||||
return;
|
||||
}
|
||||
if (amount <= 0 || amount > 100000000000)
|
||||
{
|
||||
TapLog.Error(amount + " is invalid, amount should be in range (0, 100000000000]");
|
||||
return;
|
||||
}
|
||||
Tracker.LogPurchasedEvent(orderID, productName, amount, currencyType, paymentMethod, properties);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Registers a callback function for retrieving dynamic properties.
|
||||
/// </summary>
|
||||
/// <param name="callback">The callback function that returns a JSON string containing the dynamic properties.</param>
|
||||
public void RegisterDynamicProperties(Func<string> callback)
|
||||
{
|
||||
if (!CheckInitAndEnableState())
|
||||
{
|
||||
return;
|
||||
}
|
||||
DynamicProperties dynamicProperties = new DynamicProperties(callback);
|
||||
Tracker.RegisterDynamicPropsDelegate(dynamicProperties);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// set custom oaid value
|
||||
/// </summary>
|
||||
/// <param name="value">oaid</param>
|
||||
public void SetOAID(string value)
|
||||
{
|
||||
TapLog.Log("SetOAID called in PC platform (empty implementation)");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Logs a device login event.
|
||||
/// </summary>
|
||||
public void LogDeviceLoginEvent()
|
||||
{
|
||||
TapLog.Log("LogDeviceLoginEvent called in PC platform (empty implementation)");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Represents the implementation of dynamic properties for the Tap event platform.
|
||||
/// </summary>
|
||||
public class DynamicProperties : Tracker.IDynamicProperties
|
||||
{
|
||||
readonly Func<string> callback;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="DynamicProperties"/> class with the specified callback function.
|
||||
/// </summary>
|
||||
/// <param name="callback">The callback function that returns a JSON string containing the dynamic properties.</param>
|
||||
public DynamicProperties(Func<string> callback)
|
||||
{
|
||||
this.callback = callback;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the dynamic properties.
|
||||
/// </summary>
|
||||
/// <returns>A dictionary containing the dynamic properties.</returns>
|
||||
public Dictionary<string, object> GetDynamicProperties()
|
||||
{
|
||||
var jsonString = callback();
|
||||
return Json.Deserialize(jsonString) as Dictionary<string, object>;
|
||||
}
|
||||
}
|
||||
|
||||
private bool checkLength(string value, int maxLength = 256)
|
||||
{
|
||||
if (value.Length <= 0 || value.Length > maxLength)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private bool IsValidUserID(string userID)
|
||||
{
|
||||
string pattern = @"^[a-zA-Z0-9_+/=.,:]{1,160}$";
|
||||
Regex regex = new Regex(pattern);
|
||||
return regex.IsMatch(userID);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 检查是否 Core模块初始化及 TapEvent 启用
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
private bool CheckInitAndEnableState()
|
||||
{
|
||||
if (!TapCoreStandalone.CheckInitState())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (eventOptions == null || !eventOptions.enableTapTapEvent)
|
||||
{
|
||||
string tip = "当前应用已关闭 TapTapEvent 开关,请开启后再调用相关接口";
|
||||
TapLog.Error(tip + " 开启方式:enableTapTapEvent = true");
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private Dictionary<string, object> filterProperties(Dictionary<string, object> properties)
|
||||
{
|
||||
Dictionary<string, object> filteredProperties = new Dictionary<string, object>();
|
||||
if (properties != null)
|
||||
{
|
||||
foreach (var property in properties)
|
||||
{
|
||||
if (!checkKeyValueLength(property.Key, property.Value))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
filteredProperties.Add(property.Key, property.Value);
|
||||
}
|
||||
}
|
||||
return filteredProperties;
|
||||
}
|
||||
|
||||
private bool checkKeyValueLength(string key, object value)
|
||||
{
|
||||
|
||||
if (key == null || key.Length <= 0 || key.Length > 256)
|
||||
{
|
||||
TapLog.Log(key + " Property key length should be more then 0 and less than or equal to 256 characters.");
|
||||
return false;
|
||||
}
|
||||
var maxLength = 4096;
|
||||
if (key.Equals("device_id") || key.Equals("user_id"))
|
||||
{
|
||||
maxLength = 256;
|
||||
}
|
||||
if (value is string stringValue)
|
||||
{
|
||||
if (stringValue.Length > maxLength)
|
||||
{
|
||||
TapLog.Log(key + " Property value length should be less than or equal to " + maxLength + " characters.");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user