diff --git a/Assets/PerfectWorld/Prefab/UI/MessageBox.prefab b/Assets/PerfectWorld/Prefab/UI/MessageBox.prefab index c68a796b79..4c624559eb 100644 --- a/Assets/PerfectWorld/Prefab/UI/MessageBox.prefab +++ b/Assets/PerfectWorld/Prefab/UI/MessageBox.prefab @@ -1,5 +1,126 @@ %YAML 1.1 %TAG !u! tag:unity3d.com,2011: +--- !u!1 &99051929539797809 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 4915293664251870536} + - component: {fileID: 7416811990837147698} + - component: {fileID: 6809282891346298764} + - component: {fileID: 7010901635634620631} + m_Layer: 5 + m_Name: ButtonNo + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &4915293664251870536 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 99051929539797809} + 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_ConstrainProportionsScale: 0 + m_Children: + - {fileID: 2385775756193422540} + m_Father: {fileID: 2782318616460798463} + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0, y: 0} + m_AnchorMax: {x: 0, y: 0} + m_AnchoredPosition: {x: 0, y: 0} + m_SizeDelta: {x: 205.9258, y: 56.2433} + m_Pivot: {x: 0.5, y: 0.5} +--- !u!222 &7416811990837147698 +CanvasRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 99051929539797809} + m_CullTransparentMesh: 1 +--- !u!114 &6809282891346298764 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 99051929539797809} + 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.14578143, g: 0.7924528, b: 0.1864423, a: 1} + m_RaycastTarget: 1 + m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0} + 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!114 &7010901635634620631 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 99051929539797809} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 4e29b1a8efbd4b44bb3f3716e73f07ff, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Navigation: + m_Mode: 3 + m_WrapAround: 0 + 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: 6809282891346298764} + m_OnClick: + m_PersistentCalls: + m_Calls: [] --- !u!1 &1448650841350251410 GameObject: m_ObjectHideFlags: 0 @@ -168,10 +289,10 @@ RectTransform: m_Children: [] m_Father: {fileID: 7906706137011413807} 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: -81} - m_SizeDelta: {x: 200, y: 50} + 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 &5808684862484431307 CanvasRenderer: @@ -201,7 +322,7 @@ MonoBehaviour: m_OnCullStateChanged: m_PersistentCalls: m_Calls: [] - m_text: OK + m_text: "\u0110\u1ED3ng \xDD(Y)" m_isRightToLeft: 0 m_fontAsset: {fileID: 11400000, guid: 8f586378b4e144a9851e7b34d9b748ee, type: 2} m_sharedMaterial: {fileID: 2180264, guid: 8f586378b4e144a9851e7b34d9b748ee, type: 2} @@ -228,10 +349,10 @@ MonoBehaviour: m_faceColor: serializedVersion: 2 rgba: 4294967295 - m_fontSize: 36 + m_fontSize: 44.25 m_fontSizeBase: 36 m_fontWeight: 400 - m_enableAutoSizing: 0 + m_enableAutoSizing: 1 m_fontSizeMin: 18 m_fontSizeMax: 72 m_fontStyle: 0 @@ -272,6 +393,70 @@ MonoBehaviour: m_hasFontAssetChanged: 0 m_baseMaterial: {fileID: 0} m_maskOffset: {x: 0, y: 0, z: 0, w: 0} +--- !u!1 &4140524140714306011 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 2782318616460798463} + - component: {fileID: 6567280976245013390} + m_Layer: 5 + m_Name: Buttons + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &2782318616460798463 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 4140524140714306011} + 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_ConstrainProportionsScale: 0 + m_Children: + - {fileID: 4915293664251870536} + - {fileID: 7906706137011413807} + m_Father: {fileID: 8578995796031649400} + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0, y: 0} + m_AnchorMax: {x: 1, y: 0} + m_AnchoredPosition: {x: 0, y: 41.609} + m_SizeDelta: {x: 0, y: 83.217} + m_Pivot: {x: 0.5, y: 0.5} +--- !u!114 &6567280976245013390 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 4140524140714306011} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 30649d3a9faa99c48a7b1166b86bf2a0, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Padding: + m_Left: 0 + m_Right: 0 + m_Top: 0 + m_Bottom: 0 + m_ChildAlignment: 4 + m_Spacing: 11.43 + m_ChildForceExpandWidth: 0 + m_ChildForceExpandHeight: 1 + m_ChildControlWidth: 0 + m_ChildControlHeight: 0 + m_ChildScaleWidth: 0 + m_ChildScaleHeight: 0 + m_ReverseArrangement: 0 --- !u!1 &4839074738306786208 GameObject: m_ObjectHideFlags: 0 @@ -285,7 +470,7 @@ GameObject: - component: {fileID: 8250962023850685786} - component: {fileID: 7766051278568089760} m_Layer: 5 - m_Name: ButtonExit + m_Name: ButtonOk m_TagString: Untagged m_Icon: {fileID: 0} m_NavMeshLayer: 0 @@ -298,18 +483,18 @@ RectTransform: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 4839074738306786208} - m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + 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_ConstrainProportionsScale: 0 m_Children: - {fileID: 7677644201189461152} - m_Father: {fileID: 8578995796031649400} + m_Father: {fileID: 2782318616460798463} 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_AnchorMin: {x: 0, y: 0} + m_AnchorMax: {x: 0, y: 0} m_AnchoredPosition: {x: 0, y: 0} - m_SizeDelta: {x: 160, y: 30} + m_SizeDelta: {x: 205.9258, y: 56.2433} m_Pivot: {x: 0.5, y: 0.5} --- !u!222 &8280971203118505009 CanvasRenderer: @@ -332,7 +517,7 @@ MonoBehaviour: m_Name: m_EditorClassIdentifier: m_Material: {fileID: 0} - m_Color: {r: 1, g: 1, b: 1, a: 1} + m_Color: {r: 0.14578143, g: 0.7924528, b: 0.1864423, a: 1} m_RaycastTarget: 1 m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0} m_Maskable: 1 @@ -425,10 +610,10 @@ RectTransform: m_Children: [] m_Father: {fileID: 8578995796031649400} 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: 3} - m_SizeDelta: {x: 200, y: 50} + m_AnchorMin: {x: 0, y: 0.5} + m_AnchorMax: {x: 1, y: 0.5} + m_AnchoredPosition: {x: 0, y: 0} + m_SizeDelta: {x: 0, y: 50} m_Pivot: {x: 0.5, y: 0.5} --- !u!222 &655909173274991632 CanvasRenderer: @@ -487,10 +672,10 @@ MonoBehaviour: m_faceColor: serializedVersion: 2 rgba: 4294967295 - m_fontSize: 36 + m_fontSize: 44.75 m_fontSizeBase: 36 m_fontWeight: 400 - m_enableAutoSizing: 0 + m_enableAutoSizing: 1 m_fontSizeMin: 18 m_fontSizeMax: 72 m_fontStyle: 0 @@ -562,15 +747,15 @@ RectTransform: m_LocalScale: {x: 1, y: 1, z: 1} m_ConstrainProportionsScale: 0 m_Children: - - {fileID: 7906706137011413807} - {fileID: 1931146730219979515} - {fileID: 6428994832978992641} + - {fileID: 2782318616460798463} m_Father: {fileID: 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: 30.6318} - m_SizeDelta: {x: 434.6896, y: 248.9211} + m_SizeDelta: {x: 1067.9689, y: 248.9211} m_Pivot: {x: 0.5, y: 0.5} --- !u!222 &2243330050876855902 CanvasRenderer: @@ -625,3 +810,140 @@ MonoBehaviour: titleText: {fileID: 5031655611580643013} messageText: {fileID: 7448521238108099750} okButton: {fileID: 7766051278568089760} + _noButton: {fileID: 7010901635634620631} +--- !u!1 &5664175764923475105 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 2385775756193422540} + - component: {fileID: 8838082653881325633} + - component: {fileID: 438315464709753381} + m_Layer: 5 + m_Name: Text (TMP) + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &2385775756193422540 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 5664175764923475105} + 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_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 4915293664251870536} + 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 &8838082653881325633 +CanvasRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 5664175764923475105} + m_CullTransparentMesh: 1 +--- !u!114 &438315464709753381 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 5664175764923475105} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: f4688fdb7df04437aeb418b961361dc5, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Material: {fileID: 0} + m_Color: {r: 1, g: 1, b: 1, a: 1} + m_RaycastTarget: 1 + m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0} + m_Maskable: 1 + m_OnCullStateChanged: + m_PersistentCalls: + m_Calls: [] + m_text: "H\u1EE7y(N)" + m_isRightToLeft: 0 + m_fontAsset: {fileID: 11400000, guid: 8f586378b4e144a9851e7b34d9b748ee, type: 2} + m_sharedMaterial: {fileID: 2180264, guid: 8f586378b4e144a9851e7b34d9b748ee, type: 2} + m_fontSharedMaterials: [] + m_fontMaterial: {fileID: 0} + m_fontMaterials: [] + m_fontColor32: + serializedVersion: 2 + rgba: 4278190080 + m_fontColor: {r: 0, g: 0, b: 0, a: 1} + m_enableVertexGradient: 0 + m_colorMode: 3 + m_fontColorGradient: + topLeft: {r: 1, g: 1, b: 1, a: 1} + topRight: {r: 1, g: 1, b: 1, a: 1} + bottomLeft: {r: 1, g: 1, b: 1, a: 1} + bottomRight: {r: 1, g: 1, b: 1, a: 1} + m_fontColorGradientPreset: {fileID: 0} + m_spriteAsset: {fileID: 0} + m_tintAllSprites: 0 + m_StyleSheet: {fileID: 0} + m_TextStyleHashCode: -1183493901 + m_overrideHtmlColors: 0 + m_faceColor: + serializedVersion: 2 + rgba: 4294967295 + m_fontSize: 50.3 + m_fontSizeBase: 36 + m_fontWeight: 400 + m_enableAutoSizing: 1 + m_fontSizeMin: 18 + m_fontSizeMax: 72 + m_fontStyle: 0 + m_HorizontalAlignment: 2 + m_VerticalAlignment: 512 + m_textAlignment: 65535 + m_characterSpacing: 0 + m_wordSpacing: 0 + m_lineSpacing: 0 + m_lineSpacingMax: 0 + m_paragraphSpacing: 0 + m_charWidthMaxAdj: 0 + m_TextWrappingMode: 0 + m_wordWrappingRatios: 0.4 + m_overflowMode: 0 + m_linkedTextComponent: {fileID: 0} + parentLinkedComponent: {fileID: 0} + m_enableKerning: 0 + m_ActiveFontFeatures: 6e72656b + m_enableExtraPadding: 0 + checkPaddingRequired: 0 + m_isRichText: 1 + m_EmojiFallbackSupport: 1 + m_parseCtrlCharacters: 1 + m_isOrthographic: 1 + m_isCullingEnabled: 0 + m_horizontalMapping: 0 + m_verticalMapping: 0 + m_uvLineOffset: 0 + m_geometrySortingOrder: 0 + m_IsTextObjectScaleStatic: 0 + m_VertexBufferAutoSizeReduction: 0 + m_useMaxVisibleDescender: 1 + m_pageToDisplay: 1 + m_margin: {x: 0, y: 0, z: 0, w: 0} + m_isUsingLegacyAnimationComponent: 0 + m_isVolumetricText: 0 + m_hasFontAssetChanged: 0 + m_baseMaterial: {fileID: 0} + m_maskOffset: {x: 0, y: 0, z: 0, w: 0} diff --git a/Assets/PerfectWorld/Prefab/UIManager.prefab b/Assets/PerfectWorld/Prefab/UIManager.prefab index d54d64101c..ab2d2b4c7e 100644 --- a/Assets/PerfectWorld/Prefab/UIManager.prefab +++ b/Assets/PerfectWorld/Prefab/UIManager.prefab @@ -999,6 +999,7 @@ RectTransform: - {fileID: 3483809415181351540} - {fileID: 7451658084820611230} - {fileID: 807958754388851913} + - {fileID: 7749074831901819156} m_Father: {fileID: 2780428059708698453} m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} m_AnchorMin: {x: 0, y: 0} @@ -2282,6 +2283,151 @@ MonoBehaviour: - {fileID: 2554621538146193444} - {fileID: 5079395126799912635} - {fileID: 2024142370080989935} +--- !u!1 &5525441299837637062 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 7749074831901819156} + - component: {fileID: 7710645045682326551} + - component: {fileID: 4846176155215941588} + - component: {fileID: 2039217357137269515} + - component: {fileID: 354882936729747584} + m_Layer: 0 + m_Name: BackToSelect_btn + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &7749074831901819156 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 5525441299837637062} + 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_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 3233441867675090637} + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0, y: 1} + m_AnchorMax: {x: 0, y: 1} + m_AnchoredPosition: {x: 49, y: -229.8} + m_SizeDelta: {x: 85, y: 92} + m_Pivot: {x: 0.5, y: 0.5} +--- !u!222 &7710645045682326551 +CanvasRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 5525441299837637062} + m_CullTransparentMesh: 1 +--- !u!114 &4846176155215941588 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 5525441299837637062} + 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_RaycastPadding: {x: 0, y: 0, z: 0, w: 0} + m_Maskable: 1 + m_OnCullStateChanged: + m_PersistentCalls: + m_Calls: [] + m_Sprite: {fileID: 21300000, guid: 052d4b5b3b147d04cbd00f737f5c7c53, 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 &2039217357137269515 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 5525441299837637062} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 4e29b1a8efbd4b44bb3f3716e73f07ff, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Navigation: + m_Mode: 3 + m_WrapAround: 0 + 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: 4846176155215941588} + m_OnClick: + m_PersistentCalls: + m_Calls: + - m_Target: {fileID: 354882936729747584} + m_TargetAssemblyTypeName: BrewMonster.UI.BtnBackToSelectRole, Assembly-CSharp + m_MethodName: OnClick + m_Mode: 1 + m_Arguments: + m_ObjectArgument: {fileID: 0} + m_ObjectArgumentAssemblyTypeName: UnityEngine.Object, UnityEngine + m_IntArgument: 0 + m_FloatArgument: 0 + m_StringArgument: + m_BoolArgument: 0 + m_CallState: 2 +--- !u!114 &354882936729747584 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 5525441299837637062} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: f9aed6caec10d2a44a39e3b9c458c5c5, type: 3} + m_Name: + m_EditorClassIdentifier: --- !u!1 &5539920506348156658 GameObject: m_ObjectHideFlags: 0 diff --git a/Assets/PerfectWorld/Resources/UI/DialogScriptTableObject.asset b/Assets/PerfectWorld/Resources/UI/DialogScriptTableObject.asset index f1405a4f75..393710000b 100644 --- a/Assets/PerfectWorld/Resources/UI/DialogScriptTableObject.asset +++ b/Assets/PerfectWorld/Resources/UI/DialogScriptTableObject.asset @@ -39,5 +39,5 @@ MonoBehaviour: prefab: {fileID: 6310702841431484757, guid: 6620f766cee7c8f4cb00dd457ac77675, type: 3} - id: Win_ArrangeTeam prefab: {fileID: 2452003196178065293, guid: 5a3e828efd5c542469d1f17565205ded, type: 3} - - id: CDlgInfoTooltip - prefab: {fileID: 6830833846243993097, guid: 97dd1de3aba08a04980849e40d5c1ea4, type: 3} + - id: DlgMessageBox + prefab: {fileID: 5492547392745930423, guid: 54cccb2c6a758a24183474cd385ccb2c, type: 3} diff --git a/Assets/PerfectWorld/Scripts/Common/Singleton.cs b/Assets/PerfectWorld/Scripts/Common/Singleton.cs index 5b16cdb2e1..460a57c271 100644 --- a/Assets/PerfectWorld/Scripts/Common/Singleton.cs +++ b/Assets/PerfectWorld/Scripts/Common/Singleton.cs @@ -16,6 +16,13 @@ namespace BrewMonster { if (_instance.IsValueCreated) throw new InvalidOperationException($"Singleton<{typeof(T).Name}> already created!"); + + Initialize(); + } + + protected virtual void Initialize() + { + } } } diff --git a/Assets/PerfectWorld/Scripts/Managers/EC_IvtrItem/EC_IvtrAutoMp.cs b/Assets/PerfectWorld/Scripts/Managers/EC_IvtrItem/EC_IvtrAutoMp.cs index ed77e2bee8..e51e89d92e 100644 --- a/Assets/PerfectWorld/Scripts/Managers/EC_IvtrItem/EC_IvtrAutoMp.cs +++ b/Assets/PerfectWorld/Scripts/Managers/EC_IvtrItem/EC_IvtrAutoMp.cs @@ -165,8 +165,9 @@ namespace PerfectWorld.Scripts.Managers } // Get item cool time - public int GetCoolTime(ref int piMax) + public override int GetCoolTime(out int piMax) { + piMax = 0; CECHostPlayer pHost = EC_Game.GetGameRun().GetHostPlayer(); return pHost != null ? pHost.GetCoolTime((int)CoolTimeIndex.GP_CT_AUTOMP, out piMax) : 0; } diff --git a/Assets/PerfectWorld/Scripts/Managers/EC_IvtrItem/EC_IvtrGeneralCard.cs b/Assets/PerfectWorld/Scripts/Managers/EC_IvtrItem/EC_IvtrGeneralCard.cs index bbc1cd4fee..53e3c432cd 100644 --- a/Assets/PerfectWorld/Scripts/Managers/EC_IvtrItem/EC_IvtrGeneralCard.cs +++ b/Assets/PerfectWorld/Scripts/Managers/EC_IvtrItem/EC_IvtrGeneralCard.cs @@ -3,6 +3,8 @@ namespace PerfectWorld.Scripts.Managers { public class EC_IvtrGeneralCard : EC_IvtrItem { + private IVTR_ESSENCE_GENERALCARD m_Essence; + /// /// Not create logic yet (add summary later) /// @@ -15,6 +17,11 @@ namespace PerfectWorld.Scripts.Managers public EC_IvtrGeneralCard(EC_IvtrGeneralCard other) : base(other) { } + + public IVTR_ESSENCE_GENERALCARD GetEssence() + { + return m_Essence; + } } } diff --git a/Assets/PerfectWorld/Scripts/Managers/EC_IvtrItem/EC_IvtrTaskDice.cs b/Assets/PerfectWorld/Scripts/Managers/EC_IvtrItem/EC_IvtrTaskDice.cs index 934066e194..77db9842cb 100644 --- a/Assets/PerfectWorld/Scripts/Managers/EC_IvtrItem/EC_IvtrTaskDice.cs +++ b/Assets/PerfectWorld/Scripts/Managers/EC_IvtrItem/EC_IvtrTaskDice.cs @@ -80,6 +80,11 @@ namespace PerfectWorld.Scripts.Managers { return m_pDBEssence.FileMatter; } + + public TASKDICE_ESSENCE GetDBEssence() + { + return m_pDBEssence; + } } } diff --git a/Assets/PerfectWorld/Scripts/Managers/EC_IvtrItem/EC_IvtrType.cs b/Assets/PerfectWorld/Scripts/Managers/EC_IvtrItem/EC_IvtrType.cs index df359a2ed2..3ecaf3f726 100644 --- a/Assets/PerfectWorld/Scripts/Managers/EC_IvtrItem/EC_IvtrType.cs +++ b/Assets/PerfectWorld/Scripts/Managers/EC_IvtrItem/EC_IvtrType.cs @@ -348,7 +348,7 @@ namespace BrewMonster.Scripts.Managers public struct IVTR_ESSENCE_GENERALCARD { // TODO : implement data later - // int type; + public int type; // int rank; // int require_level; // int require_leadership; diff --git a/Assets/PerfectWorld/Scripts/Network/CSNetwork/C2SCommand/C2SCommand.cs b/Assets/PerfectWorld/Scripts/Network/CSNetwork/C2SCommand/C2SCommand.cs index 106516a128..abe6fe50b2 100644 --- a/Assets/PerfectWorld/Scripts/Network/CSNetwork/C2SCommand/C2SCommand.cs +++ b/Assets/PerfectWorld/Scripts/Network/CSNetwork/C2SCommand/C2SCommand.cs @@ -1600,6 +1600,12 @@ namespace CSNetwork.S2CCommand public int strength; public int agility; } + [StructLayout(LayoutKind.Sequential, Pack = 1)] + public struct SevClearEmbeddedChipContent + { + public int iEquipIdx; + public int tidEquip; + } } // Player and NPC state \ No newline at end of file diff --git a/Assets/PerfectWorld/Scripts/Network/CSNetwork/C2SCommand/C2SCommandFactory.cs b/Assets/PerfectWorld/Scripts/Network/CSNetwork/C2SCommand/C2SCommandFactory.cs index 7ef1385a41..6a0fecedbe 100644 --- a/Assets/PerfectWorld/Scripts/Network/CSNetwork/C2SCommand/C2SCommandFactory.cs +++ b/Assets/PerfectWorld/Scripts/Network/CSNetwork/C2SCommand/C2SCommandFactory.cs @@ -269,6 +269,15 @@ namespace CSNetwork.C2SCommand }; return SerializeCommand(CommandID.STOP_MOVE, cmd); } + + public static Octets CreatePlayerLogoutCmd(int outType) + { + var cmd = new CMD_PlayerLogout + { + outType = outType + }; + return SerializeCommand(CommandID.LOGOUT, cmd); + } public static Octets CreatePlayerCastSkill(int idSkill, byte byPVPMask, int iNumTarget, int[] aTargets) { var cmd = new CMD_CastSkill @@ -993,5 +1002,19 @@ namespace CSNetwork.C2SCommand }; return SerializeCommand(CommandID.SET_STATUS_POINT, pCmd); } + public static Octets CreateNPCSevClearEmbeddedChipCmd(int iEquipIdx, int tidEquip) + { + var cmd = new cmd_sevnpc_serve + { + service_type = NPC_service_type.GP_NPCSEV_CLEAR_TESSERA, + len = (uint)Marshal.SizeOf() + }; + SevClearEmbeddedChipContent content = new SevClearEmbeddedChipContent() + { + iEquipIdx = iEquipIdx, + tidEquip = tidEquip, + }; + return SerializeCommand(CommandID.SEVNPC_SERVE, cmd, content); + } } } diff --git a/Assets/PerfectWorld/Scripts/Network/CSNetwork/GameSession.cs b/Assets/PerfectWorld/Scripts/Network/CSNetwork/GameSession.cs index b3fda706b1..19e2261b35 100644 --- a/Assets/PerfectWorld/Scripts/Network/CSNetwork/GameSession.cs +++ b/Assets/PerfectWorld/Scripts/Network/CSNetwork/GameSession.cs @@ -47,6 +47,12 @@ namespace CSNetwork public static SynchronizationContext Context; private CECC2SCmdCache m_CmdCache; // C2S command cache + /// Raised when server sends PROTOCOL_PLAYERLOGOUT(69). + public event Action PlayerLogoutReceived; + + /// Raised when the underlying network disconnects. + public event Action Disconnected; + private static void PostToUnityContext(Action action) { if (action == null) return; @@ -564,13 +570,16 @@ namespace CSNetwork OnPrtcPlayerBaseInfoRe(protocol); break; case ProtocolType.PROTOCOL_GETUICONFIG_RE: OnPrtcGetConfigRe(protocol); break; + case ProtocolType.PROTOCOL_PLAYERLOGOUT: + HandlePlayerLogout((playerlogout)protocol); + break; case ProtocolType.PROTOCOL_AUTOTEAMSETGOAL_RE: - { - // CECAutoTeam pAutoTeam = CECGameRun.Instance.GetHostPlayer().GetAutoTeam(); - // if( pAutoTeam !=null) - // pAutoTeam.OnPrtcAutoTeamSetGoalRe((AutoTeamSetGoal_Re)protocol); - } + { + // CECAutoTeam pAutoTeam = CECGameRun.Instance.GetHostPlayer().GetAutoTeam(); + // if( pAutoTeam !=null) + // pAutoTeam.OnPrtcAutoTeamSetGoalRe((AutoTeamSetGoal_Re)protocol); + } break; default: @@ -579,6 +588,13 @@ namespace CSNetwork } } + private void HandlePlayerLogout(playerlogout protocol) + { + // Original client receives this before EVENT_DISCONNECT. + // We just publish it to allow higher-level flow (UnityGameSession/UI) to react. + PostToUnityContext(() => PlayerLogoutReceived?.Invoke(protocol)); + } + private void HandleServerDataSend(gamedatasend protocol) { int lenghtHeader = Marshal.SizeOf(); @@ -811,7 +827,7 @@ namespace CSNetwork #if UNITY_EDITOR BMLogger.LogError($"### GameDataSend: ERROR_MESSAGE: {errRaw}"); #endif - cmd_error_msg pCmd = GPDataTypeHelper.FromBytes(pDataBuf); + cmd_error_msg pCmd = GPDataTypeHelper.FromBytes(pDataBuf); #if UNITY_EDITOR BMLogger.LogError($"### GameDataSend: ERROR_MESSAGE parsed iMessage={pCmd.iMessage}"); #endif @@ -829,50 +845,50 @@ namespace CSNetwork // g_pGame.GetGameRun().AddChatMessage(szMsg, GP_CHAT_MISC); } - if (pCmd.iMessage == 2) - { - // Attack target is too far - EC_ManMessage.PostMessage(EC_MsgDef.MSG_HST_TARGETISFAR, MANAGER_INDEX.MAN_PLAYER, 0, pDataBuf, pCmdHeader); - } - else if (pCmd.iMessage == 20) - { - // Failed to cast skill - //pGameRun.PostMessage(MSG_PM_CASTSKILL, MAN_PLAYER, 0, (DWORD)pDataBuf, pCmdHeader.cmd); - } - else if (pCmd.iMessage == 133 || pCmd.iMessage == 134) - { - // deal failed - //pGameRun.PostMessage(MSG_HST_BUY_SELL_FAIL, MAN_PLAYER, 0, (DWORD)pDataBuf, pCmdHeader.cmd); - } - else if (pCmd.iMessage == 158) - { - // µ±Ç°»ãÂʲ»¶Ô£¬ÖØÐÂÈ¡»ãÂÊ - //c2s_CmdGetCashMoneyRate(); - } - else if (pCmd.iMessage == 108 /*&& pGameRun.GetHostPlayer().IsInKingService()*/) - { - /* CECGameUIMan* pGameUI = pGameRun.GetUIManager().GetInGameUIMan(); - if (pGameUI) - pGameUI.EndNPCService();*/ - } - else if - (pCmd.iMessage == 108 /*&& pGameRun.GetHostPlayer().GetOfflineShopCtrl().GetNPCSevFlag() != COfflineShopCtrl::NPCSEV_NULL*/ - ) - { - /* CECGameUIMan* pGameUI = pGameRun.GetUIManager().GetInGameUIMan(); - if (pGameUI) - pGameUI.EndNPCService();*/ - } - else if (pCmd.iMessage == 175) - { - //c2s_CmdQueryParallelWorld(); - } - else if (pCmd.iMessage == 6) - { - //AP_ActionEvent(AP_EVENT_CANNOTPICKUP); - } + if (pCmd.iMessage == 2) + { + // Attack target is too far + EC_ManMessage.PostMessage(EC_MsgDef.MSG_HST_TARGETISFAR, MANAGER_INDEX.MAN_PLAYER, 0, pDataBuf, pCmdHeader); + } + else if (pCmd.iMessage == 20) + { + // Failed to cast skill + //pGameRun.PostMessage(MSG_PM_CASTSKILL, MAN_PLAYER, 0, (DWORD)pDataBuf, pCmdHeader.cmd); + } + else if (pCmd.iMessage == 133 || pCmd.iMessage == 134) + { + // deal failed + //pGameRun.PostMessage(MSG_HST_BUY_SELL_FAIL, MAN_PLAYER, 0, (DWORD)pDataBuf, pCmdHeader.cmd); + } + else if (pCmd.iMessage == 158) + { + // µ±Ç°»ãÂʲ»¶Ô£¬ÖØÐÂÈ¡»ãÂÊ + //c2s_CmdGetCashMoneyRate(); + } + else if (pCmd.iMessage == 108 /*&& pGameRun.GetHostPlayer().IsInKingService()*/) + { + /* CECGameUIMan* pGameUI = pGameRun.GetUIManager().GetInGameUIMan(); + if (pGameUI) + pGameUI.EndNPCService();*/ + } + else if + (pCmd.iMessage == 108 /*&& pGameRun.GetHostPlayer().GetOfflineShopCtrl().GetNPCSevFlag() != COfflineShopCtrl::NPCSEV_NULL*/ + ) + { + /* CECGameUIMan* pGameUI = pGameRun.GetUIManager().GetInGameUIMan(); + if (pGameUI) + pGameUI.EndNPCService();*/ + } + else if (pCmd.iMessage == 175) + { + //c2s_CmdQueryParallelWorld(); + } + else if (pCmd.iMessage == 6) + { + //AP_ActionEvent(AP_EVENT_CANNOTPICKUP); + } - break; + break; } case CommandID.SELECT_TARGET: case CommandID.UNSELECT: @@ -941,7 +957,7 @@ namespace CSNetwork case CommandID.OBJECT_CAST_INSTANT_SKILL: case CommandID.OBJECT_CAST_POS_SKILL: { - cmd_object_cast_skill pCmd2 = GPDataTypeHelper.FromBytes(pDataBuf, true); + cmd_object_cast_skill pCmd2 = GPDataTypeHelper.FromBytes(pDataBuf,true); if (ISPLAYERID(pCmd2.caster)) EC_ManMessage.PostMessage(EC_MsgDef.MSG_PM_CASTSKILL, MANAGER_INDEX.MAN_PLAYER, -1, pDataBuf, pCmdHeader); else if (ISNPCID(pCmd2.caster)) @@ -1141,6 +1157,7 @@ namespace CSNetwork FailRoleListInProgress("Disconnected"); // Clear command cache m_CmdCache.RemoveAllCmds(); + PostToUnityContext(() => Disconnected?.Invoke()); } // --- Protocol Handling Logic --- @@ -1200,7 +1217,7 @@ namespace CSNetwork var callback = _loginCallback; _loginCallback = null; - callback?.Invoke(true); + PostToUnityContext(() => callback?.Invoke(true)); } private void RequestRoleListInternal(int lastHandle = -1) @@ -1396,6 +1413,18 @@ namespace CSNetwork gamedatasend.Data = C2SCommandFactory.CreateNakeCmd(C2SCommand.CommandID.CONTINUE_ACTION); SendProtocol(gamedatasend); } + + /// + /// Client logout request. Mirrors original client behavior: + /// - outType=1: back to select role + /// - outType=0: logout account + /// + public void SendPlayerLogout(int outType, Action complete = null) + { + var g = new gamedatasend(); + g.Data = C2SCommandFactory.CreatePlayerLogoutCmd(outType); + SendProtocol(g, complete); + } public void c2s_SendCmdStopMove(in Vector3 vDest, float fSpeed, int iMoveMode, byte byDir, ushort wStamp, int iTime) { @@ -1479,24 +1508,24 @@ namespace CSNetwork // Get referral name for adding friend or other display //TODO: a Hung lam phan select role info di - /* RoleInfo info = EC_Game.GetGameRun().GetSelectedRoleInfo(); - if (info.referrer_role > 0) - GetPlayerBriefInfo(1, info.referrer_role, 2);*/ + /* RoleInfo info = EC_Game.GetGameRun().GetSelectedRoleInfo(); + if (info.referrer_role > 0) + GetPlayerBriefInfo(1, info.referrer_role, 2);*/ } CECHostPlayer pHost = EC_Game.GetGameRun().GetHostPlayer(); pHost.OnAllInitDataReady(); - /* if (pHost.IsGM()) - { - CDlgCountryMap pDlgCountryMap = (CDlgCountryMap)pGameUI.GetDialog("Win_CountryMap"); - pDlgCountryMap.GetConfig(); - } + /* if (pHost.IsGM()) + { + CDlgCountryMap pDlgCountryMap = (CDlgCountryMap)pGameUI.GetDialog("Win_CountryMap"); + pDlgCountryMap.GetConfig(); + } - g_pGame.GetConfigs().ApplyOptimizeSetting(); + g_pGame.GetConfigs().ApplyOptimizeSetting(); - if (g_pGame.GetConfigs().IsMiniClient()) - CECMCDownload::GetInstance().SendGetDownloadOK();*/ + if (g_pGame.GetConfigs().IsMiniClient()) + CECMCDownload::GetInstance().SendGetDownloadOK();*/ } } private void OnPrtcPlayerBaseInfoRe(Protocol pProtocol) @@ -1703,7 +1732,8 @@ namespace CSNetwork public void c2s_SendCmdTaskNotify(byte[] pData, uint dwDataSize) { gamedatasend gamedatasend = new gamedatasend(); - gamedatasend.Data = C2SCommandFactory.CreateTaskNotifyCmd(pData, dwDataSize); + gamedatasend.Data = C2SCommandFactory.CreateTaskNotifyCmd( pData, dwDataSize); + BMLogger.Log($"[MH Task] c2s_SendCmdTaskNotify Command ID : {pData[0]} Size: {dwDataSize}"); SendProtocol(gamedatasend); } @@ -1713,11 +1743,11 @@ namespace CSNetwork gamedatasend.Data = C2SCommandFactory.CreateNakeCmd(C2SCommand.CommandID.STAND_UP); SendProtocol(gamedatasend); } - + public void c2s_SendCmdAutoTeamSetGoal(int type, int goal_id, int op) { gamedatasend gamedatasend = new gamedatasend(); - gamedatasend.Data = C2SCommandFactory.CreateAutoTeamSetGoalCommand(type, goal_id, op); + gamedatasend.Data = C2SCommandFactory.CreateAutoTeamSetGoalCommand(type,goal_id, op); SendProtocol(gamedatasend); } @@ -1773,14 +1803,14 @@ namespace CSNetwork public void c2s_CmdGoto(float x, float y, float z) { - c2s_SendCmdGoto(x, y, z); + c2s_SendCmdGoto(x, y, z); } - + // Send C2S::GOTO command data void c2s_SendCmdGoto(float x, float y, float z) { gamedatasend gamedatasend = new gamedatasend(); - gamedatasend.Data = C2SCommandFactory.CreateGoToCommed(x, y, z); + gamedatasend.Data = C2SCommandFactory.CreateGoToCommed( x, y, z); SendProtocol(gamedatasend); } @@ -1858,5 +1888,11 @@ namespace CSNetwork gamedatasend.Data = C2SCommandFactory.CreateSetStatusPtCmd(vitality, energy, strength, agility); SendProtocol(gamedatasend); } + public void c2s_CmdNPCSevClearEmbeddedChip(int iEquipIdx, int tidEquip) + { + gamedatasend gamedatasend = new gamedatasend(); + gamedatasend.Data = C2SCommandFactory.CreateNPCSevClearEmbeddedChipCmd(iEquipIdx, tidEquip); + SendProtocol(gamedatasend); + } } } \ No newline at end of file diff --git a/Assets/PerfectWorld/Scripts/Network/UnityGameSession.cs b/Assets/PerfectWorld/Scripts/Network/UnityGameSession.cs index 0162ca2a57..be910eaca7 100644 --- a/Assets/PerfectWorld/Scripts/Network/UnityGameSession.cs +++ b/Assets/PerfectWorld/Scripts/Network/UnityGameSession.cs @@ -14,8 +14,10 @@ using System.Text; using System.Threading; using System.Threading.Tasks; using BrewMonster.Scripts.Task; +using BrewMonster.UI; using UnityEngine; using UnityEngine.SceneManagement; +using BrewMonster.Scripts; namespace BrewMonster.Network { @@ -24,6 +26,8 @@ namespace BrewMonster.Network // 2. Login public class UnityGameSession : MonoSingleton { + private const string WorldSceneName = "a61"; + private const string LoginSceneName = "LoginScene"; private GameSession _gameSession; private bool _isInitialized = false; @@ -82,6 +86,28 @@ namespace BrewMonster.Network Instance._ip = ip; Instance._port = port; } + + /// + /// Origin-like: In-game can only return to Select Role (LOGOUT(1)). + /// This will load LoginScene and auto-login (using saved creds) to show the Select Role UI. + /// + public static void ReturnToSelectRole() + { + if (Instance == null) return; + // Origin-like: in-game only returns to Select Role (auto-login using saved creds). + // Keep world scene loaded (a61) but cleaned. + _ = Instance.LogoutAndReturnAsync(outType: 1, entryTarget: LogoutFlowState.LoginEntryTarget.SelectRole, clearSavedCreds: false); + } + + /// + /// Origin-like: Account logout from Select Role (LOGOUT(0)). + /// This will clear saved creds and load LoginScene showing username/password UI. + /// + public static void LogoutAccount() + { + if (Instance == null) return; + _ = Instance.LogoutAndReturnAsync(outType: 0, entryTarget: LogoutFlowState.LoginEntryTarget.LoginUI, clearSavedCreds: true); + } public static void c2s_CmdCastSkill(int idSkill, byte byPVPMask, int iNumTarget, int[] aTargets) { Instance._gameSession.CmdCache.SendCmdCastSkill(idSkill, byPVPMask, iNumTarget, aTargets); @@ -146,6 +172,169 @@ namespace BrewMonster.Network DontDestroyOnLoad(gameObject); } + + private async Task LogoutAndReturnAsync(int outType, LogoutFlowState.LoginEntryTarget entryTarget, bool clearSavedCreds) + { + // Tell LoginScene what to show next. + LogoutFlowState.NextLoginEntry = entryTarget; + + if (clearSavedCreds) + { + PlayerPrefs.DeleteKey("username"); + PlayerPrefs.DeleteKey("password"); + PlayerPrefs.Save(); + } + + try + { + if (_gameSession != null && _gameSession.IsConnected) + { + // Send LOGOUT(outType) like the original client. + _gameSession.SendPlayerLogout(outType); + + // Wait briefly for server-driven disconnect. + await WaitForDisconnectAsync(timeoutMs: 4000); + } + } + catch (Exception ex) + { + BMLogger.LogError($"LogoutAndReturnAsync exception: {ex.Message}"); + } + finally + { + // Fallback: if server didn't close, close locally. + if (_gameSession != null && _gameSession.IsConnected) + { + _gameSession.Disconnect(); + } + } + + // Return to LoginScene. + // IMPORTANT: for outType=1 we must keep the world scene (a61) loaded; only "clean" runtime objects. + await Task.Yield(); + // Requirement: even on account logout, keep a61 loaded (do not delete the world scene), + // just clean runtime objects and show LoginScene UI. + CleanRuntimeObjectsKeepWorld(); + await EnsureSceneLoadedAdditiveAsync(WorldSceneName); + await EnsureLoginSceneAdditiveAndActivateAsync(keepSceneName: WorldSceneName); + + // When LoginScene is already loaded additively, Start() won't re-run. + // Force the login UI to refresh to the correct entry state immediately. + ApplyLoginEntryToUI(entryTarget); + + // now we have to logout from Tech3C SDK + Tech3CSDKWrapper.Instance.Logout(); + } + + private static void ApplyLoginEntryToUI(LogoutFlowState.LoginEntryTarget entryTarget) + { + try + { + // Find even if inactive (Resources.FindObjectsOfTypeAll returns inactive objects too). + var all = Resources.FindObjectsOfTypeAll(); + for (int i = 0; i < all.Length; i++) + { + var ui = all[i]; + if (ui == null) continue; + if (!ui.gameObject.scene.IsValid() || ui.gameObject.scene.name != LoginSceneName) continue; + // Avoid hard dependency on method existence (merges may edit LoginScreenUI). + ui.SendMessage("ApplyLoginEntry", entryTarget, SendMessageOptions.DontRequireReceiver); + return; + } + } + catch (Exception ex) + { + BMLogger.LogError($"ApplyLoginEntryToUI error: {ex.Message}"); + } + } + + private static void CleanRuntimeObjectsKeepWorld() + { + // Spawned runtime objects (player/monsters/vfx) are parented under ObjectSpawner. + // Destroying them "cleans" the world without unloading the scene (a61 remains loaded). + try + { + var spawner = BrewMonster.ObjectSpawner.Instance; + if (spawner == null) return; + + var root = spawner.transform; + for (int i = root.childCount - 1; i >= 0; i--) + { + var child = root.GetChild(i); + if (child != null) + UnityEngine.Object.Destroy(child.gameObject); + } + } + catch (Exception ex) + { + BMLogger.LogError($"CleanRuntimeObjectsKeepWorld error: {ex.Message}"); + } + } + + private static async Task EnsureSceneLoadedAdditiveAsync(string sceneName) + { + if (string.IsNullOrEmpty(sceneName)) return; + var s = SceneManager.GetSceneByName(sceneName); + if (s.IsValid() && s.isLoaded) return; + + var op = SceneManager.LoadSceneAsync(sceneName, LoadSceneMode.Additive); + while (op != null && !op.isDone) + await Task.Yield(); + } + + private static async Task EnsureLoginSceneAdditiveAndActivateAsync(string keepSceneName) + { + // Load LoginScene additively if needed (do not unload keepSceneName, e.g., a61). + var loginScene = SceneManager.GetSceneByName(LoginSceneName); + if (!loginScene.IsValid() || !loginScene.isLoaded) + { + var op = SceneManager.LoadSceneAsync(LoginSceneName, LoadSceneMode.Additive); + while (op != null && !op.isDone) + await Task.Yield(); + loginScene = SceneManager.GetSceneByName(LoginSceneName); + } + + if (loginScene.IsValid() && loginScene.isLoaded) + { + SceneManager.SetActiveScene(loginScene); + } + + // Optionally unload other non-world scenes (keep a61 + LoginScene). + // This prevents extra scenes like NPCRender staying around. + for (int i = 0; i < SceneManager.sceneCount; i++) + { + var s = SceneManager.GetSceneAt(i); + if (!s.IsValid() || !s.isLoaded) continue; + if (s.name == LoginSceneName) continue; + if (!string.IsNullOrEmpty(keepSceneName) && s.name == keepSceneName) continue; + + // Only unload if it's not the active scene (we already switched active to LoginScene above). + if (SceneManager.GetActiveScene() == s) continue; + _ = SceneManager.UnloadSceneAsync(s); + } + } + + private Task WaitForDisconnectAsync(int timeoutMs) + { + if (_gameSession == null || !_gameSession.IsConnected) + return Task.CompletedTask; + + var tcs = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously); + void OnDisc() => tcs.TrySetResult(true); + _gameSession.Disconnected += OnDisc; + + return Task.Run(async () => + { + try + { + await Task.WhenAny(tcs.Task, Task.Delay(timeoutMs)); + } + finally + { + _gameSession.Disconnected -= OnDisc; + } + }); + } public RoleInfo GetRoleInfo() { return _gameSession.GetRoleInfo(); @@ -499,5 +688,38 @@ namespace BrewMonster.Network { Instance._gameSession.c2s_SendCmdGetItemInfo(byPackage, bySlot); } + public static void c2s_CmdNPCSevClearEmbeddedChip(int iEquipIdx, int tidEquip) + { + Instance._gameSession.c2s_CmdNPCSevClearEmbeddedChip(iEquipIdx, tidEquip); + } + } + + /// + /// Small cross-scene state to tell LoginScene what to show after a logout flow. + /// This mirrors the original client: in-game can only return to Select Role; Account Logout happens from Select Role. + /// + public static class LogoutFlowState + { + public enum LoginEntryTarget + { + LoginUI = 0, + SelectRole = 1, + } + + private static LoginEntryTarget _nextEntry = LoginEntryTarget.LoginUI; + + public static LoginEntryTarget NextLoginEntry + { + get => _nextEntry; + set => _nextEntry = value; + } + + /// Consume and reset to default (LoginUI). + public static LoginEntryTarget ConsumeNextLoginEntry() + { + var v = _nextEntry; + _nextEntry = LoginEntryTarget.LoginUI; + return v; + } } } \ No newline at end of file diff --git a/Assets/PerfectWorld/Scripts/Tech3CSDK.meta b/Assets/PerfectWorld/Scripts/Tech3CSDK.meta new file mode 100644 index 0000000000..06775562c2 --- /dev/null +++ b/Assets/PerfectWorld/Scripts/Tech3CSDK.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 9cfabbaaddd233a4a9d4f07a3577d3a2 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/PerfectWorld/Scripts/Tech3CSDK/Tech3CSDKWrapper.cs b/Assets/PerfectWorld/Scripts/Tech3CSDK/Tech3CSDKWrapper.cs new file mode 100644 index 0000000000..348554316c --- /dev/null +++ b/Assets/PerfectWorld/Scripts/Tech3CSDK/Tech3CSDKWrapper.cs @@ -0,0 +1,208 @@ +using Tech3C; +using BrewMonster; +using System; +using UnityEngine; + +namespace BrewMonster.Scripts +{ + public enum LOGIN_ERROR_CODE + { + SUCCESS = 0, + CANCELLED = -1 + } + + public class Tech3CSDKWrapper : Singleton + { + public string clientId = "tghh"; + public string clientSecret = "1nBnWnUJadlqzlcd7x7uibXZwW9Bxx9h"; + + private AuthCallback authCallback; + private LogoutCallback logoutCallback; + + /// + /// After Tech3C SDK returns result, this callback will be called.
+ /// (errorCode, userId, password) + ///
+ public Action onLoginCallback; + public Action onLogOutCallback; + + public bool isInitialized = false; + public bool isProcessing = false; // true if the SDK is processing a request (login or logout) + + #region private functions + protected override void Initialize() + { + base.Initialize(); + + SetupCallbacks(); + + // Auto-initialize SDK + if (!string.IsNullOrEmpty(clientId) && !string.IsNullOrEmpty(clientSecret)) + { + BMLogger.Log($"Initializing Tech3C SDK...\n Client ID: {clientId}"); + Tech3CSDK.Instance.Initialize(clientId, clientSecret); + + if (Tech3CSDK.Instance.IsInitialized) + { + BMLogger.Log("[SDK] SDK Initialized Successfully!"); + } + else + { + BMLogger.Log("[SDK] Failed to initialize SDK"); + } + } + else + { + BMLogger.Log("[SDK] Client ID and/or Client Secret not set. Please configure in Inspector."); + } + + BMLogger.Log("[SDK] Tech3C SDK Started"); + + isInitialized = true; + } + + private void SetupCallbacks() + { + // Auth callback + authCallback = new AuthCallback(); + authCallback.OnAuthSuccessEvent += OnAuthSuccessEvent; + authCallback.OnAuthCancelledEvent += OnAuthCancelledEvent; + authCallback.OnAuthErrorEvent += OnAuthErrorEvent; + + // Logout callback + logoutCallback = new LogoutCallback(); + logoutCallback.OnLogoutSuccessEvent += OnLogoutSuccessEvent; + logoutCallback.OnLogoutErrorEvent += OnLogoutErrorEvent; + } + #endregion + + #region SDK Callbacks + private void OnAuthSuccessEvent(string userId, string password, string accessToken, string refreshToken, LoginType loginType, long expiryTime) + { + isProcessing = false; + BMLogger.Log($"[SDK] Auth Success!\n User ID: {userId}\n Password: {password}\n Login Type: {loginType}\n Token: {accessToken?.Substring(0, Mathf.Min(20, accessToken?.Length ?? 0))}..."); + onLoginCallback?.Invoke((byte)LOGIN_ERROR_CODE.SUCCESS, userId, password); + } + + private void OnAuthCancelledEvent() + { + isProcessing = false; + BMLogger.Log("[SDK] Auth Cancelled by user"); + onLoginCallback?.Invoke((int)LOGIN_ERROR_CODE.CANCELLED, "", ""); + } + + private void OnAuthErrorEvent(int errorCode, string errorMessage) + { + isProcessing = false; + BMLogger.Log($"[SDK] Auth Error [{errorCode}]: {errorMessage}"); + onLoginCallback?.Invoke(errorCode, errorMessage, string.Empty); + } + + private void OnLogoutSuccessEvent() + { + isProcessing = false; + BMLogger.Log("[SDK] Logout Successful"); + onLogOutCallback?.Invoke((int)LOGIN_ERROR_CODE.SUCCESS, ""); + } + + private void OnLogoutErrorEvent(int errorCode, string errorMessage) + { + isProcessing = false; + BMLogger.Log($"[SDK] Logout Error [{errorCode}]: {errorMessage}"); + onLogOutCallback?.Invoke(errorCode, errorMessage); + } + #endregion + + + #region public functions + + public void SetLoginCallback(Action callback) + { + if (callback != null) + { + BMLogger.LogWarning("[SDK] Login callback is already set, it will be overridden"); + } + onLoginCallback = callback; + } + + public void RemoveLoginCallback() + { + onLoginCallback = null; + } + + public void SetLogoutCallback(Action callback) + { + if (callback != null) + { + BMLogger.LogWarning("[SDK] Logout callback is already set, it will be overridden"); + } + onLogOutCallback = callback; + } + + public void RemoveLogoutCallback() + { + onLogOutCallback = null; + } + + /// + /// Show Tech3C SDK login screen. + /// Callback will be called after login success or failed. + /// + public bool Login() + { + if (!isInitialized) + { + BMLogger.LogError("[SDK] [Login] SDK is not initialized, please call Initialize() first"); + return false; + } + + if (isProcessing) + { + BMLogger.LogError("[SDK] [Login] SDK is already processing a request, please wait for the current request to complete"); + return false; + } + + if (onLoginCallback == null) + { + BMLogger.LogError("[SDK] [Login] Login callback is not set, please set it using SetLoginCallback"); + return false; + } + + isProcessing = true; + + Tech3CSDK.Instance.ShowAuth(authCallback); + return true; + } + + public bool Logout() + { + if (!isInitialized) + { + BMLogger.LogError("[SDK] [Logout] SDK is not initialized, please call Initialize() first"); + return false; + } + + if (isProcessing) + { + BMLogger.LogError("[SDK] [Logout] SDK is already processing a request, please wait for the current request to complete"); + return false; + } + + + if (onLogOutCallback == null) + { + BMLogger.LogError("[SDK] [Logout] Logout callback is not set, please set it using SetLogoutCallback"); + return false; + } + + //TODO: Check again after 3C resolve the callback issue. + // isProcessing = true; + + Tech3CSDK.Instance.Logout(logoutCallback); + return true; + } + + #endregion + + } +} \ No newline at end of file diff --git a/Assets/PerfectWorld/Scripts/Tech3CSDK/Tech3CSDKWrapper.cs.meta b/Assets/PerfectWorld/Scripts/Tech3CSDK/Tech3CSDKWrapper.cs.meta new file mode 100644 index 0000000000..07834551d0 --- /dev/null +++ b/Assets/PerfectWorld/Scripts/Tech3CSDK/Tech3CSDKWrapper.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: 61795692226b05849aec56bf168c905a \ No newline at end of file diff --git a/Assets/PerfectWorld/Scripts/UI/Action/CDlgSkillSubAction.cs b/Assets/PerfectWorld/Scripts/UI/Action/CDlgSkillSubAction.cs index 7553bf9e76..1d518858fa 100644 --- a/Assets/PerfectWorld/Scripts/UI/Action/CDlgSkillSubAction.cs +++ b/Assets/PerfectWorld/Scripts/UI/Action/CDlgSkillSubAction.cs @@ -4,8 +4,6 @@ using System; using System.Collections.Generic; using TMPro; using UnityEngine; -using UnityEngine.UI; -using static UnityEditor.AddressableAssets.Build.Layout.BuildLayout; namespace BrewMonster { diff --git a/Assets/PerfectWorld/Scripts/UI/CDlgMessageBox.cs b/Assets/PerfectWorld/Scripts/UI/CDlgMessageBox.cs index 488f82b625..b75c1bb055 100644 --- a/Assets/PerfectWorld/Scripts/UI/CDlgMessageBox.cs +++ b/Assets/PerfectWorld/Scripts/UI/CDlgMessageBox.cs @@ -1,28 +1,80 @@ -using BrewMonster.UI; +using System; +using BrewMonster.UI; using UnityEngine; using static CECUIManager; namespace BrewMonster { + public enum MessageBoxType + { + YesButton, + NoButton, + BothYesNoButton, + } + public struct MessageBoxData + { + public string Title; + public string Message; + public AUIDialog Dlg; + public MessageBoxType MessageBoxType; + public Action OnClickedYes; + public Action OnClickedNo; + } public class CDlgMessageBox : AUIDialog { [SerializeField] private TMPro.TextMeshProUGUI titleText; [SerializeField] private TMPro.TextMeshProUGUI messageText; [SerializeField] private UnityEngine.UI.Button okButton; - private void Awake() + [SerializeField] private UnityEngine.UI.Button _noButton; + + private MessageBoxData _messageData; + + public override void OnEnable() { - okButton.onClick.RemoveAllListeners(); + base.OnEnable(); okButton.onClick.AddListener(OnOkClicked); + _noButton.onClick.AddListener(OnNoClicked); } + + public override void OnDisable() + { + okButton.onClick.RemoveListener(OnOkClicked); + _noButton.onClick.RemoveListener(OnNoClicked); + } + private void OnOkClicked() { - EventBus.Publish(new MessageBoxEvent(1,this)); + EventBus.Publish(new MessageBoxEvent(1,_messageData.Dlg)); + _messageData.OnClickedYes?.Invoke(); Show(false); } - public void ShowMessageBox(string title, string message) + private void OnNoClicked() { - SetName(title); - messageText.text = message; + EventBus.Publish(new MessageBoxEvent(0,_messageData.Dlg)); + _messageData.OnClickedNo?.Invoke(); + Show(false); + } + public void ShowMessageBox(MessageBoxData messageBoxData) + { + _messageData = messageBoxData; + SetName(messageBoxData.Title); + messageText.text = messageBoxData.Message; + + okButton.gameObject.SetActive(false); + _noButton.gameObject.SetActive(false); + switch (_messageData.MessageBoxType) + { + case MessageBoxType.YesButton: + okButton.gameObject.SetActive(true); + break; + case MessageBoxType.NoButton: + _noButton.gameObject.SetActive(true); + break; + case MessageBoxType.BothYesNoButton: + okButton.gameObject.SetActive(true); + _noButton.gameObject.SetActive(true); + break; + } Show(true); } } diff --git a/Assets/PerfectWorld/Scripts/UI/Dialogs/DlgInstall.cs b/Assets/PerfectWorld/Scripts/UI/Dialogs/DlgInstall.cs index b42dd8c65f..d76b053eb1 100644 --- a/Assets/PerfectWorld/Scripts/UI/Dialogs/DlgInstall.cs +++ b/Assets/PerfectWorld/Scripts/UI/Dialogs/DlgInstall.cs @@ -33,6 +33,7 @@ namespace BrewMonster [SerializeField] private Button m_BtnCancel; [SerializeField] private Sprite khung_item; + [SerializeField] private Transform itemInventoryRoot; private EC_IvtrItem m_SelectedEquip; private EC_IvtrItem m_SelectedMaterial; @@ -50,12 +51,28 @@ namespace BrewMonster RegisterClick(m_SlotSecondlParent, OnClickMaterialSlot); } + public override void Update() + { +#if UNITY_EDITOR || UNITY_STANDALONE + if (Input.GetMouseButtonDown(0)) + { + CheckHidePanel(Input.mousePosition); + } +#else + if (Input.touchCount > 0 && Input.GetTouch(0).phase == TouchPhase.Began) + { + CheckHidePanel(Input.GetTouch(0).position); + } +#endif + } + public override void OnEnable() { base.OnEnable(); //todo need to set from other class // SetName("Win_Enchase"); m_BtnMerge.onClick.AddListener(OnClickedMerge); + m_BtnCancel.onClick.AddListener(OnClickedCancel); m_install_price = -1; } @@ -63,11 +80,7 @@ namespace BrewMonster { base.OnDisable(); m_BtnMerge.onClick.RemoveListener(OnClickedMerge); - } - - public void OpenInstall(uint npcId) - { - + m_BtnCancel.onClick.RemoveListener(OnClickedCancel); } public void CloseInstall() @@ -217,6 +230,12 @@ namespace BrewMonster else detailedItem.GetDetailDataFromLocal(); + if (m_FirstInvSlot >= 0) + { + var previosBtn = FindInventoryButtonBySlot(m_FirstInvSlot); + SetInventorySlotGray(previosBtn, false); + } + m_SelectedEquip?.Freeze(false); m_SelectedEquip = detailedItem; m_FirstInvSlot = slotIndex; @@ -227,8 +246,6 @@ namespace BrewMonster SetInventorySlotGray(btn, true); detailedItem.Freeze(true); - - Debug.Log($"[Install] Equipment: {detailedItem.m_tid} from slot {slotIndex}"); } private void OnDropMaterial(PointerEventData eventData) @@ -252,6 +269,12 @@ namespace BrewMonster else detailedItem.GetDetailDataFromLocal(); + if (m_SecondInvSlot >= 0) + { + var previosBtn = FindInventoryButtonBySlot(m_SecondInvSlot); + SetInventorySlotGray(previosBtn, false); + } + m_SelectedMaterial?.Freeze(false); m_SelectedMaterial = detailedItem; m_SelectedMaterial?.Freeze(true); @@ -297,8 +320,6 @@ namespace BrewMonster // a_sprintf(szText, _AL("%d"), nAmount); // m_pTxtGold->SetText(szText); // } - - Debug.Log($"[Install] Material: {detailedItem.m_tid} from slot {slotIndex}"); } private void SetInventorySlotGray(Button btn, bool gray) @@ -343,23 +364,19 @@ namespace BrewMonster private void ClearEquipSlot() { m_SelectedEquip?.Freeze(false); - m_SelectedMaterial?.Freeze(false); m_SelectedEquip = null; m_FirstInvSlot = -1; m_TxtFirstName.text = "___"; ClearSlotIcon(m_SlotFirstParent); - - Debug.Log("[Install] Equipment slot cleared"); } private void ClearMaterialSlot() { + m_SelectedMaterial?.Freeze(false); m_SelectedMaterial = null; m_SecondInvSlot = -1; m_TxtSecondName.text = "___"; ClearSlotIcon(m_SlotSecondlParent); - - Debug.Log("[Install] Material slot cleared"); } private void ClearSlotIcon(Transform slot) @@ -385,8 +402,8 @@ namespace BrewMonster if( nMoney > pHost.GetMoneyAmount() ) { message = GetGameUIMan().GetStringFromTable(226); - Debug.LogError(message); - // GetGameUIMan()->MessageBox("", GetGameUIMan().GetStringFromTable(226), MB_OK, + CECUIManager.Instance.ShowMessageBox("", message); + // GetGameUIMan().GetDialog("") // A3DCOLORRGBA(255, 255, 255, 160), &pMsgBox); // pMsgBox->SetLife(3); return; @@ -396,7 +413,7 @@ namespace BrewMonster if( !pIvtrA.IsEquipment() ) { message = GetGameUIMan().GetStringFromTable(223); - Debug.LogError(message); + CECUIManager.Instance.ShowMessageBox("", message); // GetGameUIMan().MessageBox("", GetGameUIMan().GetStringFromTable(223), MB_OK, // A3DCOLORRGBA(255, 255, 255, 160), &pMsgBox); // pMsgBox.SetLife(3); @@ -410,7 +427,7 @@ namespace BrewMonster if( pEquipA.GetEmptyHoleNum() <= 0 ) { message = GetGameUIMan().GetStringFromTable(224); - Debug.LogError(message); + CECUIManager.Instance.ShowMessageBox("", message); // GetGameUIMan().MessageBox("", GetGameUIMan()->GetStringFromTable(224), MB_OK, // A3DCOLORRGBA(255, 255, 255, 160), &pMsgBox); // pMsgBox.SetLife(3); @@ -421,7 +438,7 @@ namespace BrewMonster if(pIvtrB == null || !pIvtrB.IsEmbeddable() ) { message = GetGameUIMan().GetStringFromTable(225); - Debug.LogError(message); + CECUIManager.Instance.ShowMessageBox("", message); // GetGameUIMan().MessageBox("", GetGameUIMan().GetStringFromTable(225), MB_OK, // A3DCOLORRGBA(255, 255, 255, 160), &pMsgBox); // pMsgBox.SetLife(3); @@ -449,7 +466,7 @@ namespace BrewMonster if( nStoneLevel > nEquipLevel ) { message = GetGameUIMan().GetStringFromTable(300); - Debug.LogError(message); + CECUIManager.Instance.ShowMessageBox("", message); // GetGameUIMan().MessageBox("", GetGameUIMan().GetStringFromTable(300), MB_OK, // A3DCOLORRGBA(255, 255, 255, 160), &pMsgBox); // pMsgBox.SetLife(3); @@ -464,7 +481,7 @@ namespace BrewMonster pHost.GetPack(InventoryConst.IVTRTYPE_PACK).UnfreezeAllItems(); message = GetGameUIMan().GetStringFromTable(228); - Debug.LogError(message); + CECUIManager.Instance.ShowMessageBox("", message); // GetGameUIMan().MessageBox("", GetGameUIMan().GetStringFromTable(228), // MB_OK, A3DCOLORRGBA(255, 255, 255, 160), &pMsgBox); // pMsgBox.SetLife(3); @@ -484,5 +501,41 @@ namespace BrewMonster // MB_OKCANCEL, A3DCOLORRGBA(255, 255, 255, 160)); // } } + private void OnClickedCancel() + { + Show(false); + } + + public void ResetInstallUI() + { + RestoreInventoryColors(); + + m_SelectedEquip = null; + m_SelectedMaterial = null; + + m_FirstInvSlot = -1; + m_SecondInvSlot = -1; + + m_TxtFirstName.text = "___"; + m_TxtSecondName.text = "___"; + m_TxtMoney.text = "0"; + m_install_price = -1; + + ClearSlotIcon(m_SlotFirstParent); + ClearSlotIcon(m_SlotSecondlParent); + + gameObject.SetActive(false); + } + + private void CheckHidePanel(Vector2 screenPos) + { + if (!RectTransformUtility.RectangleContainsScreenPoint( + itemInventoryRoot as RectTransform, screenPos, + Camera.main)) + { + if(itemInventoryRoot!=null) + itemInventoryRoot.gameObject.SetActive(false); + } + } } } \ No newline at end of file diff --git a/Assets/PerfectWorld/Scripts/UI/Dialogs/DlgNPC.cs b/Assets/PerfectWorld/Scripts/UI/Dialogs/DlgNPC.cs index a546e83502..ab8369e45a 100644 --- a/Assets/PerfectWorld/Scripts/UI/Dialogs/DlgNPC.cs +++ b/Assets/PerfectWorld/Scripts/UI/Dialogs/DlgNPC.cs @@ -3406,27 +3406,9 @@ namespace BrewMonster.UI if (pCurNPCEssence.HasValue) { uint npcID = pCurNPCEssence.Value.id; - DlgInstall dlgInstall = FindFirstObjectByType(); - if (dlgInstall == null) - { - CECGameUIMan gameUIMan = GetGameUIMan(); - DialogScriptTableObject dialogResource = gameUIMan.GetDialogResource(); - Canvas canvas = gameUIMan.GetCanvas(); - - if(dialogResource != null && canvas != null) - { - GameObject ob = dialogResource.GetPrefabDialog("Win_Enchase"); - if (ob != null) - { - dlgInstall = GameObject.Instantiate(ob, canvas.transform).GetComponent(); - } - } - } - - if(dlgInstall != null) - { - dlgInstall.OpenInstall(npcID); - } + + var dlgInstall =GetGameUIMan().GetDialog("Win_Enchase"); + dlgInstall.Show(true); } //pShow1 = m_pAUIManager.GetDialog("Win_Enchase"); //pShow2 = m_pAUIManager.GetDialog("Win_Inventory"); diff --git a/Assets/PerfectWorld/Scripts/UI/Inventory/CECSCItem.cs b/Assets/PerfectWorld/Scripts/UI/Inventory/CECSCItem.cs index 8ab2bba4c8..8101130b87 100644 --- a/Assets/PerfectWorld/Scripts/UI/Inventory/CECSCItem.cs +++ b/Assets/PerfectWorld/Scripts/UI/Inventory/CECSCItem.cs @@ -1,15 +1,5 @@ using BrewMonster.Network; -using BrewMonster.Scripts.Skills; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; -using Unity.VisualScripting; -using static BrewMonster.SkillArrayWrapper; -using UnityEngine; using BrewMonster.Scripts.Managers; -using UnityEditorInternal.Profiling.Memory.Experimental; using BrewMonster.Scripts; namespace BrewMonster diff --git a/Assets/PerfectWorld/Scripts/UI/Login/BtnBackToSelectRole.cs b/Assets/PerfectWorld/Scripts/UI/Login/BtnBackToSelectRole.cs new file mode 100644 index 0000000000..59d081a721 --- /dev/null +++ b/Assets/PerfectWorld/Scripts/UI/Login/BtnBackToSelectRole.cs @@ -0,0 +1,17 @@ +using UnityEngine; +using BrewMonster.Network; + +namespace BrewMonster.UI +{ + /// + /// Attach to the in-game button: sends LOGOUT(1) and returns to Select Role. + /// + public class BtnBackToSelectRole : MonoBehaviour + { + public void OnClick() + { + UnityGameSession.ReturnToSelectRole(); + } + } +} + diff --git a/Assets/PerfectWorld/Scripts/UI/Login/BtnBackToSelectRole.cs.meta b/Assets/PerfectWorld/Scripts/UI/Login/BtnBackToSelectRole.cs.meta new file mode 100644 index 0000000000..b500247166 --- /dev/null +++ b/Assets/PerfectWorld/Scripts/UI/Login/BtnBackToSelectRole.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: f9aed6caec10d2a44a39e3b9c458c5c5 \ No newline at end of file diff --git a/Assets/PerfectWorld/Scripts/UI/Login/BtnLogoutAccount.cs b/Assets/PerfectWorld/Scripts/UI/Login/BtnLogoutAccount.cs new file mode 100644 index 0000000000..53dcf51254 --- /dev/null +++ b/Assets/PerfectWorld/Scripts/UI/Login/BtnLogoutAccount.cs @@ -0,0 +1,17 @@ +using UnityEngine; +using BrewMonster.Network; + +namespace BrewMonster.UI +{ + /// + /// Attach to the Select Role screen button: sends LOGOUT(0) and returns to Login UI. + /// + public class BtnLogoutAccount : MonoBehaviour + { + public void OnClick() + { + UnityGameSession.LogoutAccount(); + } + } +} + diff --git a/Assets/PerfectWorld/Scripts/UI/Login/BtnLogoutAccount.cs.meta b/Assets/PerfectWorld/Scripts/UI/Login/BtnLogoutAccount.cs.meta new file mode 100644 index 0000000000..fec91d3246 --- /dev/null +++ b/Assets/PerfectWorld/Scripts/UI/Login/BtnLogoutAccount.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: 725d0d91266148944894d6c831bf2650 \ No newline at end of file diff --git a/Assets/PerfectWorld/Scripts/UI/Login/LoginScreenUI.cs b/Assets/PerfectWorld/Scripts/UI/Login/LoginScreenUI.cs index 559b51cac9..ee6024729e 100644 --- a/Assets/PerfectWorld/Scripts/UI/Login/LoginScreenUI.cs +++ b/Assets/PerfectWorld/Scripts/UI/Login/LoginScreenUI.cs @@ -3,6 +3,7 @@ using System.Collections.Generic; using System.Threading; using System.Threading.Tasks; using BrewMonster.Network; +using BrewMonster.Scripts; using CSNetwork.Protocols; using CSNetwork.Protocols.RPCData; using TMPro; @@ -29,19 +30,51 @@ namespace BrewMonster.UI private List _roleInfos; private List _currentRoles; private RoleInfo _pendingCreatedRole; + private bool _loginInProgress; bool isDoneWorldRender = false; bool isDoneNPCRender = false; private SynchronizationContext context; public AudioClip loginBGM; + + void Awake() + { + // Ensure wrapper created early (Tech3C SDK). + _ = Tech3CSDKWrapper.Instance; + } + + void OnEnable() + { + Tech3CSDKWrapper.Instance.SetLoginCallback(OnLoginCallback); + Tech3CSDKWrapper.Instance.SetLogoutCallback(OnLogoutCallback); + } + + private void OnDisable() + { + Tech3CSDKWrapper.Instance.RemoveLoginCallback(); + Tech3CSDKWrapper.Instance.RemoveLogoutCallback(); + } + void Start() { AudioManager.Instance.PlayBGM(loginBGM, 1.5f); _loginButton.onClick.AddListener(OnLoginButtonClicked); context = SynchronizationContext.Current; + // Requirement: Login UI should also have a61 loaded. + var world = SceneManager.GetSceneByName("a61"); + if (!world.IsValid() || !world.isLoaded) + { + SceneManager.LoadSceneAsync("a61", LoadSceneMode.Additive); + } + _usernameInputField.text = PlayerPrefs.GetString("username", ""); _passwordInputField.text = PlayerPrefs.GetString("password", ""); - + + // Default: login UI first, select-role hidden until login succeeds. + if (_selectCharacterScreen != null) + _selectCharacterScreen.gameObject.SetActive(false); + + ApplyLoginEntry(LogoutFlowState.ConsumeNextLoginEntry()); } // Update is called once per frame @@ -69,18 +102,86 @@ namespace BrewMonster.UI #endif } + public async void OnLoginButtonClicked() { + if (_loginInProgress) + { + BMLogger.LogWarning("[LoginScreenUI] Login already in progress (ignored click)."); + return; + } + + _loginInProgress = true; + if (_loginButton != null) _loginButton.interactable = false; + + // If username or password is empty, use Tech3C SDK login UI. + if (string.IsNullOrEmpty(_usernameInputField.text) || string.IsNullOrEmpty(_passwordInputField.text)) + { + // Use Tech3C SDK login UI. + bool started = Tech3CSDKWrapper.Instance.Login(); + if (!started) + { + // Fallback: manual username/password login (useful in dev if SDK not configured). + BMLogger.LogWarning("[LoginScreenUI] Tech3CSDKWrapper.Login() failed, fallback to manual login."); + await BeginGameLoginAsync(_usernameInputField.text, _passwordInputField.text); + } + } + else + { + // otherwise use manual username/password login. + BMLogger.LogError("[LoginScreenUI] Username/password empty."); + await BeginGameLoginAsync(_usernameInputField.text, _passwordInputField.text); + } + + } + + private async Task BeginGameLoginAsync(string username, string password) + { + if (string.IsNullOrEmpty(username) || string.IsNullOrEmpty(password)) + { + BMLogger.LogError("[LoginScreenUI] Username/password empty."); + _loginInProgress = false; + if (_loginButton != null) _loginButton.interactable = true; + return; + } + BMLogger.Log("OnLoginButtonClicked"); - string username = _usernameInputField.text; - string password = _passwordInputField.text; - // UnityGameSession.SetConnectionInfo("103.182.22.52", 29000); UnityGameSession.SetConnectionInfo("103.51.120.195", 29000); PlayerPrefs.SetString("username", username); PlayerPrefs.SetString("password", password); PlayerPrefs.Save(); + + BMLogger.Log($"[LoginScreenUI] Connecting+login start user='{username}'"); await UnityGameSession.Login(username, password, OnLoginComplete); - _selectCharacterScreen.gameObject.SetActive(true); + } + + /// + /// Apply how LoginScene should look after a logout flow. + /// Call this when LoginScene is already loaded (additive) and you need to switch UI without reloading the scene. + /// + public void ApplyLoginEntry(BrewMonster.Network.LogoutFlowState.LoginEntryTarget entry) + { + _loginInProgress = false; + if (_loginButton != null) _loginButton.interactable = true; + + // Always refresh fields from PlayerPrefs (LogoutAccount clears them). + if (_usernameInputField != null) _usernameInputField.text = PlayerPrefs.GetString("username", ""); + if (_passwordInputField != null) _passwordInputField.text = PlayerPrefs.GetString("password", ""); + + if (_selectCharacterScreen != null) + _selectCharacterScreen.gameObject.SetActive(false); + + if (entry == BrewMonster.Network.LogoutFlowState.LoginEntryTarget.SelectRole) + { + // Auto-login to reach Select Role like the original client, without showing Tech3C auth UI again. + if (!string.IsNullOrEmpty(_usernameInputField.text) && !string.IsNullOrEmpty(_passwordInputField.text)) + { + BMLogger.Log("[LoginScreenUI] Auto-login triggered (return-to-select-role)."); + _loginInProgress = true; + if (_loginButton != null) _loginButton.interactable = false; + _ = BeginGameLoginAsync(_usernameInputField.text, _passwordInputField.text); + } + } } /// @@ -89,12 +190,19 @@ namespace BrewMonster.UI /// private void OnLoginComplete(bool result) { + BMLogger.Log($"[LoginScreenUI] OnLoginComplete result={result}"); if (!result) { BMLogger.LogError("Login failed"); + if (_selectCharacterScreen != null) + _selectCharacterScreen.gameObject.SetActive(false); + _loginInProgress = false; + if (_loginButton != null) _loginButton.interactable = true; return; } + if (_selectCharacterScreen != null) + _selectCharacterScreen.gameObject.SetActive(true); UnityGameSession.GetRoleListAsync(OnGetRoleListComplete); } @@ -108,6 +216,8 @@ namespace BrewMonster.UI { BMLogger.LogError("OnGetRoleListComplete: roleInfos is null"); // Keep whatever is currently shown; don't overwrite UI state with null. + _loginInProgress = false; + if (_loginButton != null) _loginButton.interactable = true; return; } @@ -142,6 +252,9 @@ namespace BrewMonster.UI BMLogger.Log($"OnGetRoleListComplete: roles={roleInfos.Count}"); _roleInfos = roleInfos; _currentRoles = roleInfos; + + // Login flow finished; keep login button disabled (origin-like) once you're at select role. + _loginInProgress = false; } private void OnClickSelectCharacter(RoleInfo roleInfo) @@ -278,5 +391,38 @@ namespace BrewMonster.UI } } #endif + + private async void OnLoginCallback(int errorCode, string userId, string password) + { + if (errorCode == 0) + { + BMLogger.Log($"Login success -- userId: {userId} - {password}"); + // UnityGameSession.SetConnectionInfo("103.182.22.52", 29000); + UnityGameSession.SetConnectionInfo("103.51.120.195", 29000); + PlayerPrefs.SetString("username", userId); + PlayerPrefs.SetString("password", password); + PlayerPrefs.Save(); + await BeginGameLoginAsync(userId, password); + } + else + { + // if it failed, the userId will be the error message + BMLogger.LogError($"Login failed -- errorCode: {errorCode}: {userId}"); + _loginInProgress = false; + if (_loginButton != null) _loginButton.interactable = true; + } + } + + private void OnLogoutCallback(int errorCode, string errorMessage) + { + if (errorCode == 0) + { + BMLogger.Log("Logout success"); + } + else + { + BMLogger.LogError($"Logout failed -- errorCode: {errorCode}: {errorMessage}"); + } + } } } \ No newline at end of file diff --git a/Assets/Plugins/Android.meta b/Assets/Plugins/Android.meta new file mode 100644 index 0000000000..ba484d202e --- /dev/null +++ b/Assets/Plugins/Android.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: f45408c4c89289d498475d0e1e1e5b7e +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Plugins/Android/AndroidManifest.xml b/Assets/Plugins/Android/AndroidManifest.xml new file mode 100644 index 0000000000..783069970e --- /dev/null +++ b/Assets/Plugins/Android/AndroidManifest.xml @@ -0,0 +1,26 @@ + + + + + + + + + + + + + + + + + + + + + + diff --git a/Assets/Plugins/Android/AndroidManifest.xml.meta b/Assets/Plugins/Android/AndroidManifest.xml.meta new file mode 100644 index 0000000000..bfa9b92c0a --- /dev/null +++ b/Assets/Plugins/Android/AndroidManifest.xml.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: 11f5d7426047d6048a47d330b02103d9 +TextScriptImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Plugins/Android/baseProjectTemplate.gradle.DISABLED b/Assets/Plugins/Android/baseProjectTemplate.gradle.DISABLED new file mode 100644 index 0000000000..fd43460978 --- /dev/null +++ b/Assets/Plugins/Android/baseProjectTemplate.gradle.DISABLED @@ -0,0 +1,13 @@ +plugins { + // If you are changing the Android Gradle Plugin version, make sure it is compatible with the Gradle version preinstalled with Unity + // See which Gradle version is preinstalled with Unity here https://docs.unity3d.com/Manual/android-gradle-overview.html + // See official Gradle and Android Gradle Plugin compatibility table here https://developer.android.com/studio/releases/gradle-plugin#updating-gradle + // To specify a custom Gradle version in Unity, go do "Preferences > External Tools", uncheck "Gradle Installed with Unity (recommended)" and specify a path to a custom Gradle version + id 'com.android.application' version '7.4.2' apply false + id 'com.android.library' version '7.4.2' apply false + **BUILD_SCRIPT_DEPS** +} + +task clean(type: Delete) { + delete rootProject.buildDir +} diff --git a/Assets/Plugins/Android/baseProjectTemplate.gradle.DISABLED.meta b/Assets/Plugins/Android/baseProjectTemplate.gradle.DISABLED.meta new file mode 100644 index 0000000000..f58f05f816 --- /dev/null +++ b/Assets/Plugins/Android/baseProjectTemplate.gradle.DISABLED.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: 7736ab544ec5cab4a89af84ac45b5a75 +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Plugins/Android/mainTemplate.gradle b/Assets/Plugins/Android/mainTemplate.gradle new file mode 100644 index 0000000000..323c9d1038 --- /dev/null +++ b/Assets/Plugins/Android/mainTemplate.gradle @@ -0,0 +1,51 @@ +apply plugin: 'com.android.library' +apply from: '../shared/keepUnitySymbols.gradle' +**APPLY_PLUGINS** + +dependencies { + implementation fileTree(dir: 'libs', include: ['*.jar']) + implementation "androidx.appcompat:appcompat:1.6.1" + implementation "com.google.android.material:material:1.11.0" +**DEPS**} + +android { + ndkVersion "**NDKVERSION**" + namespace "com.unity3d.player" + ndkPath "**NDKPATH**" + compileSdk **APIVERSION** + buildToolsVersion '**BUILDTOOLS**' + + compileOptions { + sourceCompatibility JavaVersion.VERSION_11 + targetCompatibility JavaVersion.VERSION_11 + } + + buildFeatures { + viewBinding true + } + + defaultConfig { +**DEFAULT_CONFIG_SETUP** + minSdk **MINSDK** + targetSdk **TARGETSDK** + ndk { + debugSymbolLevel **DEBUGSYMBOLLEVEL** + abiFilters **ABIFILTERS** + } + versionCode **VERSIONCODE** + versionName '**VERSIONNAME**' + consumerProguardFiles 'proguard-unity.txt'**USER_PROGUARD** + } + + lint { + abortOnError false + } + + androidResources { + noCompress = **BUILTIN_NOCOMPRESS** + unityStreamingAssets.tokenize(', ') + ignoreAssetsPattern = "!.svn:!.git:!.ds_store:!*.scc:!CVS:!thumbs.db:!picasa.ini:!*~" + }**PACKAGING** +} +**IL_CPP_BUILD_SETUP** +**SOURCE_BUILD_SETUP** +**EXTERNAL_SOURCES** diff --git a/Assets/Plugins/Android/mainTemplate.gradle.backup b/Assets/Plugins/Android/mainTemplate.gradle.backup new file mode 100644 index 0000000000..c5203ee0d7 --- /dev/null +++ b/Assets/Plugins/Android/mainTemplate.gradle.backup @@ -0,0 +1,47 @@ +apply plugin: 'com.android.library' +**APPLY_PLUGINS** + +dependencies { + implementation fileTree(dir: 'libs', include: ['*.jar']) + implementation "androidx.appcompat:appcompat:1.6.1" + implementation "com.google.android.material:material:1.11.0" +**DEPS**} + +android { + namespace "com.unity3d.player" + ndkPath "**NDKPATH**" + compileSdkVersion **APIVERSION** + buildToolsVersion '**BUILDTOOLS**' + + compileOptions { + sourceCompatibility JavaVersion.VERSION_11 + targetCompatibility JavaVersion.VERSION_11 + } + + buildFeatures { + viewBinding true + } + + defaultConfig { + minSdkVersion **MINSDKVERSION** + targetSdkVersion **TARGETSDKVERSION** + ndk { + abiFilters **ABIFILTERS** + } + versionCode **VERSIONCODE** + versionName '**VERSIONNAME**' + consumerProguardFiles 'proguard-unity.txt'**USER_PROGUARD** + } + + lintOptions { + abortOnError false + } + + aaptOptions { + noCompress = **BUILTIN_NOCOMPRESS** + unityStreamingAssets.tokenize(', ') + ignoreAssetsPattern = "!.svn:!.git:!.ds_store:!*.scc:!CVS:!thumbs.db:!picasa.ini:!*~" + }**PACKAGING_OPTIONS** +} +**IL_CPP_BUILD_SETUP** +**SOURCE_BUILD_SETUP** +**EXTERNAL_SOURCES** diff --git a/Assets/Plugins/Android/mainTemplate.gradle.backup.meta b/Assets/Plugins/Android/mainTemplate.gradle.backup.meta new file mode 100644 index 0000000000..db9977c019 --- /dev/null +++ b/Assets/Plugins/Android/mainTemplate.gradle.backup.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: f3bedb9070cd041a2bb8a27d9751aada +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Plugins/Android/mainTemplate.gradle.meta b/Assets/Plugins/Android/mainTemplate.gradle.meta new file mode 100644 index 0000000000..ff480e90c1 --- /dev/null +++ b/Assets/Plugins/Android/mainTemplate.gradle.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: 950fdc575e5b3b74ea1d3f1c60322d4f +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Prefabs/UI/DlgInstall.prefab b/Assets/Prefabs/UI/DlgInstall.prefab index bed021b314..04a23593d8 100644 --- a/Assets/Prefabs/UI/DlgInstall.prefab +++ b/Assets/Prefabs/UI/DlgInstall.prefab @@ -1660,7 +1660,7 @@ MonoBehaviour: m_OnCullStateChanged: m_PersistentCalls: m_Calls: [] - m_text: 10000000000000 + m_text: 0 m_isRightToLeft: 0 m_fontAsset: {fileID: 11400000, guid: 369c2e14814cc9a4b8e3ad4e37769134, type: 2} m_sharedMaterial: {fileID: 9092487103257209053, guid: 369c2e14814cc9a4b8e3ad4e37769134, type: 2} @@ -10577,6 +10577,7 @@ MonoBehaviour: m_BtnMerge: {fileID: 8208092408021918524} m_BtnCancel: {fileID: 4503836757578509720} khung_item: {fileID: 21300000, guid: a5366f3bce011c046902e39b6bd3a077, type: 3} + itemInventoryRoot: {fileID: 7750009739432212686} --- !u!1 &5641506892578507279 GameObject: m_ObjectHideFlags: 0 @@ -14989,7 +14990,7 @@ MonoBehaviour: m_Calls: - m_Target: {fileID: 2206910701173095729} m_TargetAssemblyTypeName: BrewMonster.DlgInstall, Assembly-CSharp - m_MethodName: CloseInstall + m_MethodName: ResetInstallUI m_Mode: 1 m_Arguments: m_ObjectArgument: {fileID: 0} diff --git a/Assets/Prefabs/UI/SelectCharacterUI.prefab b/Assets/Prefabs/UI/SelectCharacterUI.prefab index dc077c82ca..6309ad986a 100644 --- a/Assets/Prefabs/UI/SelectCharacterUI.prefab +++ b/Assets/Prefabs/UI/SelectCharacterUI.prefab @@ -142,7 +142,7 @@ RectTransform: m_AnchorMin: {x: 0, y: 0} m_AnchorMax: {x: 1, y: 1} m_AnchoredPosition: {x: 0.0009808608, y: -0.00012207031} - m_SizeDelta: {x: 0, y: 0} + m_SizeDelta: {x: -474.1602, y: -682.9546} m_Pivot: {x: 0, y: 1} --- !u!114 &8239567059120679520 MonoBehaviour: @@ -504,7 +504,7 @@ GameObject: m_Icon: {fileID: 0} m_NavMeshLayer: 0 m_StaticEditorFlags: 0 - m_IsActive: 1 + m_IsActive: 0 --- !u!224 &5968911800527563993 RectTransform: m_ObjectHideFlags: 0 @@ -580,7 +580,7 @@ MonoBehaviour: characterItemPrefab: {fileID: 5263746738484752443, guid: 726ee9eade6587245ac1b55d2335e9b9, type: 3} addCharacterItemPrefab: {fileID: 5263746738484752443, guid: a0b31ed0940ec9942b243a0b8cec8ad3, type: 3} parentItems: {fileID: 2643174602035272289} - createCharacterButton: {fileID: 0} + createCharacterButton: {fileID: 2685968509838782728} createCharacterScreen: {fileID: 0} --- !u!1 &7510180475820570348 GameObject: @@ -594,6 +594,7 @@ GameObject: - component: {fileID: 8869589995078313987} - component: {fileID: 1038588709929429120} - component: {fileID: 1166159884785039946} + - component: {fileID: 1481527591792018053} m_Layer: 5 m_Name: back_btn m_TagString: Untagged @@ -701,7 +702,31 @@ MonoBehaviour: m_TargetGraphic: {fileID: 1038588709929429120} m_OnClick: m_PersistentCalls: - m_Calls: [] + m_Calls: + - m_Target: {fileID: 1481527591792018053} + m_TargetAssemblyTypeName: BrewMonster.UI.BtnLogoutAccount, Assembly-CSharp + m_MethodName: OnClick + m_Mode: 1 + m_Arguments: + m_ObjectArgument: {fileID: 0} + m_ObjectArgumentAssemblyTypeName: UnityEngine.Object, UnityEngine + m_IntArgument: 0 + m_FloatArgument: 0 + m_StringArgument: + m_BoolArgument: 0 + m_CallState: 2 +--- !u!114 &1481527591792018053 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 7510180475820570348} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 725d0d91266148944894d6c831bf2650, type: 3} + m_Name: + m_EditorClassIdentifier: --- !u!1 &7885375190050319286 GameObject: m_ObjectHideFlags: 0 diff --git a/Assets/Scripts/CECHostPlayer.cs b/Assets/Scripts/CECHostPlayer.cs index 0921735cee..14f00ce172 100644 --- a/Assets/Scripts/CECHostPlayer.cs +++ b/Assets/Scripts/CECHostPlayer.cs @@ -802,19 +802,26 @@ namespace BrewMonster if (pCmd.cooldown_index == (int)CoolTimeIndex.GP_CT_CAST_ELF_SKILL) { int i; - // other goblin skills should be set public cool down, 1 second - /* for (i = 0; i < m_aGoblinSkills.GetSize(); i++) - { - if (m_aGoblinSkills[i] && m_aGoblinSkills[i]->GetCoolingCnt() == 0) - m_aGoblinSkills[i]->StartCooling(GetCoolTime(GP_CT_CAST_ELF_SKILL), GetCoolTime(GP_CT_CAST_ELF_SKILL)); - }*/ + for (i = 0; i < m_aGoblinSkills.Count; i++) + { + if (m_aGoblinSkills[i] != null && m_aGoblinSkills[i].GetCoolingCnt() == 0) + { + int fakeRef = 0; + int coolTime = GetCoolTime((int)CoolTimeIndex.GP_CT_CAST_ELF_SKILL, out fakeRef); + m_aGoblinSkills[i].StartCooling(coolTime, coolTime); + } + } - /* for (i = 0; i < m_aPsSkills.Count; i++) - { - CECSkill pSkill = GetPassiveSkillByIndex(i); - if (pSkill && (pSkill->GetCommonCoolDown() & (1 << (pCmd->cooldown_index - GP_CT_SKILLCOMMONCOOLDOWN0)))) - pSkill->StartCooling(GetCoolTime(pCmd->cooldown_index), GetCoolTime(pCmd->cooldown_index)); - }*/ + for (i = 0; i < m_aPsSkills.Count; i++) + { + CECSkill pSkill = GetPassiveSkillByIndex(i); + if (pSkill != null && (pSkill.GetCommonCoolDown() & (1 << (pCmd.cooldown_index - (int)CoolTimeIndex.GP_CT_SKILLCOMMONCOOLDOWN0))) != 0) + { + int fakeRef = 0; + int coolTime = GetCoolTime(pCmd.cooldown_time, out fakeRef); + pSkill.StartCooling(coolTime, coolTime); + } + } } if (pCmd.cooldown_index >= (int)CoolTimeIndex.GP_CT_SKILLCOMMONCOOLDOWN0 && pCmd.cooldown_index <= (int)CoolTimeIndex.GP_CT_SKILLCOMMONCOOLDOWN4) @@ -826,7 +833,11 @@ namespace BrewMonster CECSkill pSkill = GetPositiveSkillByIndex(i); int fakeRef = 0; if (pSkill != null && (pSkill.GetCommonCoolDown() & mask) != 0) - pSkill.StartCooling(GetCoolTime(pCmd.cooldown_index, out fakeRef), GetCoolTime(pCmd.cooldown_index, out fakeRef)); + { + int coolTime = GetCoolTime(pCmd.cooldown_index, out fakeRef); + pSkill.StartCooling(coolTime, coolTime); + //pSkill.StartCooling(GetCoolTime(pCmd.cooldown_index, out fakeRef), GetCoolTime(pCmd.cooldown_index, out fakeRef)); + } } /*const std::map&inherentSkillMap = CECComboSkillState::Instance().GetInherentSkillMap(); std::map < unsigned int, CECSkill*>::const_iterator it; @@ -849,7 +860,9 @@ namespace BrewMonster } ct.iCurTime = pCmd.cooldown_time; ct.iMaxTime = pCmd.cooldown_time; - Math.Clamp(ct.iCurTime, 0, ct.iMaxTime); + ct.iCurTime = Math.Clamp(ct.iCurTime, 0, ct.iMaxTime); + m_skillCoolTime[idSkill] = ct; + //Math.Clamp(ct.iCurTime, 0, ct.iMaxTime); CECSkill pSkill = GetNormalSkill(idSkill); if (pSkill != null) @@ -864,6 +877,18 @@ namespace BrewMonster { BMLogger.LogError("HoangDev: pSkill " + pSkill); } + else + { + pSkill = CECComboSkillState.Instance.GetInherentSkillByID((uint)idSkill); + if (pSkill != null) + { + pSkill.StartCooling(pCmd.cooldown_time, pCmd.cooldown_time); + } + else if (GetEquipSkillByID(idSkill) == null) + { + Debug.LogWarning($"OnMsgHstSetCoolTime: Skill {idSkill} not found in nomal/equip skills"); + } + } } else { @@ -874,6 +899,11 @@ namespace BrewMonster UpdateEquipSkillCoolDown(pCmd.cooldown_index); } + private CECSkill GetPassiveSkillByIndex(int n) + { + return m_aPsSkills[n]; + } + private void OnMsgHstCoolTimeData(ECMSG Msg) { cmd_cooltime_data pCmd = default; @@ -7493,16 +7523,97 @@ namespace BrewMonster CECGameRun pGameRun = EC_Game.GetGameRun(); //CECGameSession pSession = g_pGame.GetGameSession(); + if (pItem.GetClassID() == (int)EC_IvtrItem.InventoryClassId.ICID_FIREWORK) + { + if (GetProfession() == (int)PROFESSION.PROF_GHOST && IsInvisible()) + { + if (showMsg) + pGameRun.AddFixedMessage((int)FixedMsg.FIXMSG_CANNOT_USE_WHEN_INVISIBLE); + return false; + } + } + + if (pItem.GetClassID() == (int)EC_IvtrItem.InventoryClassId.ICID_INCSKILLABILITY) + { + EC_IvtrIncSkillAbility pIncSkill = pItem as EC_IvtrIncSkillAbility; + //if (pIncSkill != null) + //{ + // var pDBEssence = pIncSkill.GetDBEssence(); + // CECSkill pSkill = GetNormalSkill(pDBEssence.id_skill); + // if (pSkill != null) + // { + // if (pSkill.GetSkillLevel() != pDBEssence.level_required) + // { + // if (showMsg) + // pGameRun.AddFixedMessage((int)FixedMsg.FIXMSG_PRODUCE_LEVEL_INVALID); + // return false; + // } + // if (GetSkillAbilityPercent(pDBEssence.id_skill) >= 100) + // { + // if (showMsg) + // pGameRun.AddFixedMessage((int)FixedMsg.FIXMSG_PRODUCE_ABILITY_FULL); + // return false; + // } + // } + //} + } + if (pItem.GetClassID() == (int)EC_IvtrItem.InventoryClassId.ICID_TRANSMITSCROLL) { CECGameUIMan pGameUI = pGameRun.GetUIManager().GetInGameUIMan(); if (pGameUI != null && !IsFighting()) { // TODO: Implement travel map dialog + //CDlgWorldMap* pMap = (CDlgWorldMap*)pGameUI->GetDialog("Win_WorldMapTravel"); + //pMap->BuildTravelMap(DT_TRANSMITSCROLL_ESSENCE, (void*)iSlot); + //pMap->Show(true); } return true; } + if (pItem.GetClassID() == (int)EC_IvtrItem.InventoryClassId.ICID_SHOPTOKEN) + { + CECGameUIMan pGameUI = pGameRun.GetUIManager().GetInGameUIMan(); + if (pGameUI != null && !IsFighting()) + { + //CDlgTokenShop* pDlg = dynamic_cast(pGameUI->GetDialog("Win_TokenShop")); + //if (pDlg) + //{ + // pDlg->InitTokenShopItem(pItem->GetTemplateID()); + // pDlg->Show(!pDlg->IsShow()); + //} + } + return true; + } + + if (pItem.GetClassID() == (int)EC_IvtrItem.InventoryClassId.ICID_UNIVERSAL_TOKEN) + { + // TODO: Implement universal token when available + EC_IvtrUniversalToken pUniversalToken = pItem as EC_IvtrUniversalToken; + //if (pUniversalToken != null && pUniversalToken.HasAnyUsage()) + //{ + // CECUseUniversalTokenCommandManager.Instance.Use(pUniversalToken, pUniversalToken.UsageIndexAt(0)); + // return true; + //} + } + + if (pItem.GetClassID() == (int)EC_IvtrItem.InventoryClassId.ICID_TASKDICE) + { + EC_IvtrTaskDice pTaskDice = pItem as EC_IvtrTaskDice; + if (pTaskDice != null) + { + if (pTaskDice != null) + { + if (IsFlying() && pTaskDice.GetDBEssence().no_use_in_combat == 1) + { + if(showMsg) + pGameRun.AddFixedMessage((int)FixedMsg.FIXMSG_CANNOT_USE_IN_BATTLE); + return false; + } + } + } + } + if (pItem.GetClassID() == (int)EC_IvtrItem.InventoryClassId.ICID_TARGETITEM) { EC_IvtrTargetItem pTargetItem = pItem as EC_IvtrTargetItem; @@ -7521,7 +7632,7 @@ namespace BrewMonster return false; } - if (pTargetItem.GetDBEssence().use_in_sanctuary_only != 0 && !IsInSanctuary()) + if (essence.use_in_sanctuary_only != 0 && !IsInSanctuary()) { if (showMsg) pGameRun.AddFixedMessage((int)FixedMsg.FIXMSG_USE_IN_SANCTUARY_ONLY); @@ -7662,6 +7773,24 @@ namespace BrewMonster } } } + else if(pSkill.GetType() == (int)CECSkill.SkillType.TYPE_BLESS) + { + // TODO: Implement pet blessing when petsystem is available + //CECSCPet pPet = EC_Game.GetGameRun().GetWorld().GetPetByID(m_idSelTarget); + //if (pPet == null || pPet.GetMasterID() == GetCharacterID()) + //{ + // CECPetData pPetData = m_pPetCorral.GetActivePet(); + // if (pPetData == null || + // pPetData.GetClass() != GP_PET_TYPE.GP_PET_CLASS_COMBAT && + // pPetData.GetClass() != GP_PET_TYPE.GP_PET_CLASS_SUMMON && + // pPetData.GetClass() != GP_PET_TYPE.GP_PET_CLASS_EVOLUTION) + // return false; + + // idCastTarget = m_pPetCorral.GetActivePetNPCID(); + //} + //if(iTargetType != 0 && idCastTarget == 0) + // return false; + } if (iTargetType != 0) { @@ -7684,9 +7813,8 @@ namespace BrewMonster if (!pSkill.IsInstant() && pSkill.GetType() != (int)CECSkill.SkillType.TYPE_FLASHMOVE) { - // TODO: Implement NaturallyStopMoving - //if (!NaturallyStopMoving()) - // return false; + if (!NaturallyStopMoving()) + return false; } else if (pSkill.GetType() == (int)CECSkill.SkillType.TYPE_FLASHMOVE) { @@ -7749,6 +7877,16 @@ namespace BrewMonster return false; } + if (pItem.GetClassID() == (int)EC_IvtrItem.InventoryClassId.ICID_DYNSKILLEQUIP) + { + int iSameIDPos = m_pEquipPack.FindItem(pItem.GetTemplateID()); + if (iSameIDPos >= 0) + { + iDst = iSameIDPos; + } + + } + if (pItem.GetClassID() == (int)EC_IvtrItem.InventoryClassId.ICID_ARROW || pItem.GetClassID() == (int)EC_IvtrItem.InventoryClassId.ICID_DYNSKILLEQUIP) { @@ -7757,6 +7895,7 @@ namespace BrewMonster UnityGameSession.RequestEquipItemAsync((byte)iSlot, (byte)iDst, null); else { + // TODO: Implement c2s_CmdMoveItemToEquip when available //UnityGameSession.c2s_CmdMoveItemToEquip((byte)iSlot, (byte)iDst); } } @@ -7765,6 +7904,11 @@ namespace BrewMonster if (pItem.GetClassID() == (int)EC_IvtrItem.InventoryClassId.ICID_GENERALCARD) { //TODO: Add general card equip request + EC_IvtrGeneralCard pCard = pItem as EC_IvtrGeneralCard; + if (pCard != null) + { + iDst = InventoryConst.EQUIPIVTR_GENERALCARD1 + pCard.GetEssence().type; + } } UnityGameSession.RequestEquipItemAsync((byte)iSlot, (byte)iDst, null); } @@ -7826,19 +7970,21 @@ namespace BrewMonster float fDist = 0, fTargetRag = 0; CECObject pObject = null; - //if (CalcDist(m_idSelTarget, ref fDist, ref pObject)) - //{ - // return false; - //} + if (CalcDist(m_idSelTarget, out fDist, out pObject)) + { + return false; + } if (GPDataTypeHelper.ISNPCID(m_idSelTarget)) { + pObject = EC_ManMessageMono.Instance.CECNPCMan.GetNPC(m_idSelTarget); CECNPC pNPC = pObject as CECNPC; if (pNPC != null) fTargetRag = pNPC.GetTouchRadius(); } else if (GPDataTypeHelper.ISPLAYERID(m_idSelTarget)) { + pObject = EC_ManMessageMono.Instance.GetECManPlayer.GetElsePlayer(m_idSelTarget); EC_ElsePlayer pPlayer = pObject as EC_ElsePlayer; if (pPlayer != null) fTargetRag = pPlayer.GetTouchRadius(); @@ -7869,6 +8015,7 @@ namespace BrewMonster if (showMsg) { CECGameUIMan pGameUI = pGameRun.GetUIManager().GetInGameUIMan(); + //pGameUI.MessageBox("", pGameUI.GetStringFromTable(828), MB_OK, new Color32(1, 1, 1, 0.6)); } return false; } @@ -7891,14 +8038,12 @@ namespace BrewMonster EC_Inventory pPack = GetPack(pCmd.byPackage); if (pPack == null) { - Debug.LogError("[OnMsgHstUseItem] Pack not found"); return; } EC_IvtrItem pItem = pPack.GetItem(pCmd.bySlot, false); if (pItem == null || pItem.GetTemplateID() != pCmd.item_id) { - Debug.LogError($"[OnMsgHstUseItem] Item mismatch at slot {pCmd.bySlot}"); return; } @@ -7911,12 +8056,12 @@ namespace BrewMonster { if (pPack.GetItem(pCmd.bySlot, false) == null) { - Debug.Log($"[OnMsgHstUseItem] Item {pCmd.item_id} removed from slot {pCmd.bySlot}"); + //Debug.Log($"[OnMsgHstUseItem] Item {pCmd.item_id} removed from slot {pCmd.bySlot}"); } } else { - Debug.LogError($"[OnMsgHstUseItem] Failed to remove item {pCmd.item_id} from slot {pCmd.bySlot}"); + //Debug.LogError($"[OnMsgHstUseItem] Failed to remove item {pCmd.item_id} from slot {pCmd.bySlot}"); } var ui = GameObject.FindFirstObjectByType(); @@ -7927,7 +8072,7 @@ namespace BrewMonster } else { - Debug.LogError("[OnMsgHstUseItem] EC_InventoryUI not found, UI may not update"); + //Debug.LogError("[OnMsgHstUseItem] EC_InventoryUI not found, UI may not update"); } } @@ -7944,58 +8089,64 @@ namespace BrewMonster } } - /// - /// Calculate distance to an object and optionally retrieve the object reference - /// 计算到对象的距离,并可选地获取对象引用 - /// - /// Target object ID / 目标对象ID - /// Output distance / 输出距离 - /// Output object reference (optional) / 输出对象引用(可选) - /// True if calculation succeeded / 计算成功返回true - //public bool CalcDist(int idObject, ref float pfDist, out CECObject ppObject) - //{ - // ppObject = null; + // + // Calculate distance to an object and optionally retrieve the object reference + // 计算到对象的距离,并可选地获取对象引用 + // + // Target object ID / 目标对象ID + // Output distance / 输出距离 + // Output object reference (optional) / 输出对象引用(可选) + // True if calculation succeeded / 计算成功返回true + public bool CalcDist(int idObject, out float pfDist, out CECObject ppObject) + { + pfDist = 0.0f; + ppObject = null; - // if (idObject == 0 || idObject == m_PlayerInfo.cid) - // return false; + if (idObject == 0 || idObject == m_PlayerInfo.cid) + return false; - // CECObject pObject = CECGameRun.Instance.GetWorld()?.GetObject(idObject, 1); - // if (pObject == null) - // return false; + CECWorld pWorld = CECGameRun.Instance.GetWorld(); + if(pWorld == null) + return false; - // ppObject = pObject; - // float fDist = 0.0f; + CECObject pObject = pWorld.GetObject(idObject, 1); + if(ppObject == null) + return false; - // if (ISNPCID(idObject)) - // { - // CECNPC pNPC = (CECNPC)pObject; - // fDist = pNPC.CalcDist(GetPos(), true); - // } - // else if (ISPLAYERID(idObject)) - // { - // Debug.Assert(pObject.GetClassID() == CECObject.OCID_ELSEPLAYER); - // EC_ElsePlayer pPlayer = (EC_ElsePlayer)pObject; - // fDist = pPlayer.CalcDist(GetPos(), true); - // } - // else if (ISMATTERID(idObject)) - // { - // Debug.Assert(pObject.GetClassID() == CECObject.OCID_MATTER); - // CECMatter pMatter = (CECMatter)pObject; - // fDist = (pMatter.GetPos() - GetPos()).magnitude; - // } - // else - // return false; + ppObject = pObject; + float fDist = 0.0f; - // pfDist = fDist; - // return true; - //} + if (GPDataTypeHelper.ISNPCID(idObject)) + { + CECNPC pNPC = pObject as CECNPC; + if (pNPC == null) + return false; + fDist = pNPC.CalcDist(GetPos(), true); + } + else if (GPDataTypeHelper.ISMATTERID(idObject)) + { + Debug.Assert(pObject.GetClassID() == Class_ID.OCID_MATTER); + CECMatter pMatter = pObject as CECMatter; + if (pMatter == null) + return false; + A3DVECTOR3 vDelta = pMatter.GetPos() - GetPos(); + fDist = A3d_Magnitude(vDelta); + } + else + { + return false; + } - //// Helper method overload without object output - //public bool CalcDist(int idObject, ref float pfDist) - //{ - // CECObject pObject; - // return CalcDist(idObject, ref pfDist, out pObject); - //} + pfDist = fDist; + return true; + } + + // Helper method overload without object output + public bool CalcDist(int idObject, out float pfDist) + { + CECObject pObject; + return CalcDist(idObject, out pfDist, out pObject); + } //// ID checking helper methods //private bool ISNPCID(int id) => ((id & 0x80000000) != 0) && ((id & 0x40000000) == 0); diff --git a/Assets/Scripts/CECUIManager.cs b/Assets/Scripts/CECUIManager.cs index 131d4d63a6..d511547f9a 100644 --- a/Assets/Scripts/CECUIManager.cs +++ b/Assets/Scripts/CECUIManager.cs @@ -4,6 +4,7 @@ using BrewMonster.UI; using CSNetwork; using System; using System.Collections.Generic; +using BrewMonster.Scripts.Managers; using UnityEngine; using UnityEngine.UI; using UnityEngine.UIElements; @@ -109,7 +110,12 @@ public class CECUIManager : MonoSingleton var msgBox = GetInGameUIMan().GetDialog("DlgMessageBox") as CDlgMessageBox; if (msgBox != null) { - msgBox.ShowMessageBox(title, message); + msgBox.ShowMessageBox(new MessageBoxData() + { + Title = title, + Message = message, + MessageBoxType = MessageBoxType.YesButton + }); return msgBox; } else @@ -230,6 +236,25 @@ public class CECUIManager : MonoSingleton } } + else if(string.CompareOrdinal("Game_Disenchase",pDlg.GetName())==0 && DialogBoxCommandIDs.IDOK == iRetVal) + { + // PAUIDIALOG pMsgBox; + var clearEmbeddedChipDlg = GetInGameUIMan().GetDialog("Game_Disenchase"); + // EC_IvtrItem pIvtr = (EC_IvtrItem)m_pDlgUninstall->m_pItema->GetDataPtr("ptr_CECIvtrItem"); + // + // pSession.c2s_CmdNPCSevClearEmbeddedChip( + // (WORD)m_pDlgUninstall->m_pItema->GetData(), pIvtr->GetTemplateID()); + // + // m_pDlgUninstall->Show(false); + // pHost->EndNPCService(); + // m_pCurNPCEssence = NULL; + // m_pDlgInventory->Show(false); + // pHost->GetPack(IVTRTYPE_PACK)->UnfreezeAllItems(); + // + // MessageBox("", GetStringFromTable(228), MB_OK, + // A3DCOLORRGBA(255, 255, 255, 160), &pMsgBox); + // pMsgBox->SetLife(3); + } } private bool OnNewMessageBox(int iRetVal) diff --git a/Assets/Tech3C.meta b/Assets/Tech3C.meta new file mode 100644 index 0000000000..b197cdb0af --- /dev/null +++ b/Assets/Tech3C.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 86378052e9798924e8ee14f4fa780f0f +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Tech3C/Plugins.meta b/Assets/Tech3C/Plugins.meta new file mode 100644 index 0000000000..9c87ae31f2 --- /dev/null +++ b/Assets/Tech3C/Plugins.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 4c935d070fff35741adebc5424c4e02b +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Tech3C/Plugins/Android.meta b/Assets/Tech3C/Plugins/Android.meta new file mode 100644 index 0000000000..f279ada1af --- /dev/null +++ b/Assets/Tech3C/Plugins/Android.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 2111256da5c2b2a469d98ce0f103cdb7 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Tech3C/Plugins/Android/Tech3CUnityBridge.java b/Assets/Tech3C/Plugins/Android/Tech3CUnityBridge.java new file mode 100644 index 0000000000..6dd99c4ee8 --- /dev/null +++ b/Assets/Tech3C/Plugins/Android/Tech3CUnityBridge.java @@ -0,0 +1,184 @@ +package vn.tech3c.sdk.unity; + +import android.util.Log; + +import vn.tech3c.sdk.auth.callback.OnAuthCallback; +import vn.tech3c.sdk.auth.controller.Tech3CIdController; +import vn.tech3c.sdk.auth.entities.enums.LoginType; +import vn.tech3c.sdk.auth.exceptions.Tech3CIdException; + +import com.unity3d.player.UnityPlayer; + +/** + * Unity Bridge for Tech3C SDK + * This class receives callbacks from Tech3C SDK and sends them to Unity via UnitySendMessage + */ +public class Tech3CUnityBridge implements OnAuthCallback { + private static final String TAG = "Tech3CUnityBridge"; + private static final String UNITY_GAME_OBJECT = "Tech3CSDKBridge"; + + private static Tech3CUnityBridge instance; + + private OnUnityAuthCallback unityAuthCallback; + private OnUnityLogoutCallback unityLogoutCallback; + private OnUnityUserInfoCallback unityUserInfoCallback; + + // Singleton + public static synchronized Tech3CUnityBridge getInstance() { + if (instance == null) { + instance = new Tech3CUnityBridge(); + } + return instance; + } + + // Interfaces for Unity callbacks + public interface OnUnityAuthCallback { + void onAuthSuccess(String userId, String password, String accessToken, String refreshToken, LoginType loginType, long expiryTime); + void onAuthCancelled(); + void onAuthError(int errorCode, String errorMessage); + } + + public interface OnUnityLogoutCallback { + void onLogoutSuccess(); + void onLogoutError(int errorCode, String errorMessage); + } + + public interface OnUnityUserInfoCallback { + void onUserInfoReceived(String userId, String username, String email, String phone); + void onUserInfoCancelled(); + void onUserInfoError(int errorCode, String errorMessage); + } + + // Set callbacks + public void setUnityAuthCallback(OnUnityAuthCallback callback) { + this.unityAuthCallback = callback; + } + + public void setUnityLogoutCallback(OnUnityLogoutCallback callback) { + this.unityLogoutCallback = callback; + } + + public void setUnityUserInfoCallback(OnUnityUserInfoCallback callback) { + this.unityUserInfoCallback = callback; + } + + // Send message to Unity + private void sendToUnity(String methodName, String message) { + try { + UnityPlayer.UnitySendMessage(UNITY_GAME_OBJECT, methodName, message); + Log.d(TAG, "Sent to Unity: " + methodName + " - " + message); + } catch (Exception e) { + Log.e(TAG, "Error sending to Unity: " + e.getMessage()); + } + } + + // ========== OnAuthCallback Implementation ========== + + @Override + public void onLoginSuccess(String userId, String password, String accessToken, String refreshToken, LoginType loginType, long expiryTime) { + Log.d(TAG, "onLoginSuccess: " + userId); + + // Send to Unity + String message = userId + "|" + password + "|" + accessToken + "|" + refreshToken + "|" + loginType.name() + "|" + expiryTime; + sendToUnity("OnAuthSuccess", message); + + // Also call Unity callback if set + if (unityAuthCallback != null) { + unityAuthCallback.onAuthSuccess(userId, password, accessToken, refreshToken, loginType, expiryTime); + } + } + + @Override + public void onRegisterSuccess(String accessToken, String refreshToken, String userId, long expiryTime) { + Log.d(TAG, "onRegisterSuccess: " + userId); + + // Treat register success as auth success (password is empty for register) + String message = userId + "|" + "" + "|" + accessToken + "|" + refreshToken + "|REGISTER|" + expiryTime; + sendToUnity("OnAuthSuccess", message); + + if (unityAuthCallback != null) { + unityAuthCallback.onAuthSuccess(userId, "", accessToken, refreshToken, LoginType.ACCOUNT, expiryTime); + } + } + + @Override + public void onAuthCancelled() { + Log.d(TAG, "onAuthCancelled"); + sendToUnity("OnAuthCancelled", ""); + + if (unityAuthCallback != null) { + unityAuthCallback.onAuthCancelled(); + } + } + + @Override + public void onAuthScreenOpened() { + Log.d(TAG, "onAuthScreenOpened"); + // Optional: Send to Unity if needed + } + + @Override + public void onError(Tech3CIdException exception) { + Log.d(TAG, "onError: " + exception.getMessage()); + + String message = exception.getErrorCode() + "|" + exception.getMessage(); + sendToUnity("OnAuthError", message); + + if (unityAuthCallback != null) { + unityAuthCallback.onAuthError(exception.getErrorCode(), exception.getMessage()); + } + } + + // ========== Additional Callback Methods ========== + + public void onLogoutSuccess() { + Log.d(TAG, "onLogoutSuccess"); + sendToUnity("OnLogoutSuccess", ""); + + if (unityLogoutCallback != null) { + unityLogoutCallback.onLogoutSuccess(); + } + } + + public void onLogoutError(int errorCode, String errorMessage) { + Log.d(TAG, "onLogoutError: " + errorMessage); + + String message = errorCode + "|" + errorMessage; + sendToUnity("OnLogoutError", message); + + if (unityLogoutCallback != null) { + unityLogoutCallback.onLogoutError(errorCode, errorMessage); + } + } + + public void onUserInfoReceived(String userId, String username, String email, String phone) { + Log.d(TAG, "onUserInfoReceived: " + userId); + + String message = userId + "|" + username + "|" + email + "|" + phone; + sendToUnity("OnUserInfoReceived", message); + + if (unityUserInfoCallback != null) { + unityUserInfoCallback.onUserInfoReceived(userId, username, email, phone); + } + } + + public void onUserInfoCancelled() { + Log.d(TAG, "onUserInfoCancelled"); + sendToUnity("OnUserInfoCancelled", ""); + + if (unityUserInfoCallback != null) { + unityUserInfoCallback.onUserInfoCancelled(); + } + } + + public void onUserInfoError(int errorCode, String errorMessage) { + Log.d(TAG, "onUserInfoError: " + errorMessage); + + String message = errorCode + "|" + errorMessage; + sendToUnity("OnUserInfoError", message); + + if (unityUserInfoCallback != null) { + unityUserInfoCallback.onUserInfoError(errorCode, errorMessage); + } + } +} diff --git a/Assets/Tech3C/Plugins/Android/Tech3CUnityBridge.java.meta b/Assets/Tech3C/Plugins/Android/Tech3CUnityBridge.java.meta new file mode 100644 index 0000000000..6f929e7398 --- /dev/null +++ b/Assets/Tech3C/Plugins/Android/Tech3CUnityBridge.java.meta @@ -0,0 +1,32 @@ +fileFormatVersion: 2 +guid: e32ed25f4a2155a4f8106c56c82bb7fc +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Android: Android + second: + enabled: 1 + settings: {} + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Tech3C/Plugins/Android/auth-1.2.1.aar b/Assets/Tech3C/Plugins/Android/auth-1.2.1.aar new file mode 100644 index 0000000000..b972ff9123 Binary files /dev/null and b/Assets/Tech3C/Plugins/Android/auth-1.2.1.aar differ diff --git a/Assets/Tech3C/Plugins/Android/auth-1.2.1.aar.meta b/Assets/Tech3C/Plugins/Android/auth-1.2.1.aar.meta new file mode 100644 index 0000000000..26ca9658d7 --- /dev/null +++ b/Assets/Tech3C/Plugins/Android/auth-1.2.1.aar.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: 3385cda9ff7dcd74ebc47f50d2e8196c \ No newline at end of file diff --git a/Assets/Tech3C/Plugins/Android/gradle.properties b/Assets/Tech3C/Plugins/Android/gradle.properties new file mode 100644 index 0000000000..646c51b977 --- /dev/null +++ b/Assets/Tech3C/Plugins/Android/gradle.properties @@ -0,0 +1,2 @@ +android.useAndroidX=true +android.enableJetifier=true diff --git a/Assets/Tech3C/Plugins/Android/gradle.properties.meta b/Assets/Tech3C/Plugins/Android/gradle.properties.meta new file mode 100644 index 0000000000..235ab6ba4b --- /dev/null +++ b/Assets/Tech3C/Plugins/Android/gradle.properties.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: 5b51400b9b30374459987374d5fb2a0a +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Tech3C/Plugins/Android/mainTemplate.gradle b/Assets/Tech3C/Plugins/Android/mainTemplate.gradle new file mode 100644 index 0000000000..d5267dce20 --- /dev/null +++ b/Assets/Tech3C/Plugins/Android/mainTemplate.gradle @@ -0,0 +1,43 @@ +apply plugin: 'com.android.library' +**APPLY_PLUGINS** + +dependencies { + implementation fileTree(dir: 'libs', include: ['*.jar']) + implementation "androidx.appcompat:appcompat:1.6.1" + implementation "com.google.android.material:material:1.11.0" +**DEPS**} + +android { + namespace "com.unity3d.player" + ndkPath "**NDKPATH**" + compileSdkVersion **APIVERSION** + buildToolsVersion '**BUILDTOOLS**' + + compileOptions { + sourceCompatibility JavaVersion.VERSION_11 + targetCompatibility JavaVersion.VERSION_11 + } + + defaultConfig { + minSdkVersion **MINSDKVERSION** + targetSdkVersion **TARGETSDKVERSION** + ndk { + abiFilters **ABIFILTERS** + } + versionCode **VERSIONCODE** + versionName '**VERSIONNAME**' + consumerProguardFiles 'proguard-unity.txt'**USER_PROGUARD** + } + + lintOptions { + abortOnError false + } + + aaptOptions { + noCompress = **BUILTIN_NOCOMPRESS** + unityStreamingAssets.tokenize(', ') + ignoreAssetsPattern = "!.svn:!.git:!.ds_store:!*.scc:!CVS:!thumbs.db:!picasa.ini:!*~" + }**PACKAGING_OPTIONS** +} +**IL_CPP_BUILD_SETUP** +**SOURCE_BUILD_SETUP** +**EXTERNAL_SOURCES** diff --git a/Assets/Tech3C/Plugins/Android/mainTemplate.gradle.meta b/Assets/Tech3C/Plugins/Android/mainTemplate.gradle.meta new file mode 100644 index 0000000000..8ce6936aca --- /dev/null +++ b/Assets/Tech3C/Plugins/Android/mainTemplate.gradle.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: 68ef4c077c6bfee47ad1aa16645a896d +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Tech3C/Plugins/Android/proguard-user.txt b/Assets/Tech3C/Plugins/Android/proguard-user.txt new file mode 100644 index 0000000000..6b42f31f02 --- /dev/null +++ b/Assets/Tech3C/Plugins/Android/proguard-user.txt @@ -0,0 +1,30 @@ +#### Proguard class tech3c +-keep public class vn.tech3c.sdk.auth.entities.* { + public *; +} +-keep public class vn.tech3c.sdk.auth.entities.enums.* { + public *; +} +-keep public class vn.tech3c.sdk.auth.entities.response.* { + public *; +} +-keep public class vn.tech3c.sdk.auth.entities.request.* { + public *; +} + +-keep public interface vn.tech3c.sdk.auth.** { *; } + +-keep public class vn.tech3c.sdk.auth.controller.Tech3CIdController { public *;} +-keep public class vn.tech3c.sdk.auth.exceptions.Tech3CIdException { public *;} + +-keep public class vn.tech3c.sdk.auth.Tech3CIdAuthentication { + public *; +} + +-keep public class vn.tech3c.sdk.auth.config.Tech3CIdConfig { + public *; +} + +-keep public class vn.tech3c.sdk.example.Tech3CUnityBridge { public *;} +-keep public class vn.tech3c.sdk.example.Tech3CUnityBridge$** { *; } + diff --git a/Assets/Tech3C/Plugins/Android/proguard-user.txt.meta b/Assets/Tech3C/Plugins/Android/proguard-user.txt.meta new file mode 100644 index 0000000000..6b05d1673c --- /dev/null +++ b/Assets/Tech3C/Plugins/Android/proguard-user.txt.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: 0a1d44356c1df074d8cd78bee53cbafa +TextScriptImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Tech3C/Runtime.meta b/Assets/Tech3C/Runtime.meta new file mode 100644 index 0000000000..8c1c38fee8 --- /dev/null +++ b/Assets/Tech3C/Runtime.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: c5469f93c69421146854ce74deba7931 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Tech3C/Runtime/Tech3CAndroidBridge.cs b/Assets/Tech3C/Runtime/Tech3CAndroidBridge.cs new file mode 100644 index 0000000000..c781c13e3c --- /dev/null +++ b/Assets/Tech3C/Runtime/Tech3CAndroidBridge.cs @@ -0,0 +1,452 @@ +using UnityEngine; +using System; + +namespace Tech3C +{ + /// + /// Bridge class for Android platform to communicate with Tech3C native SDK + /// + public class Tech3CAndroidBridge : ITech3CNativeBridge + { + private const string CLASS_NAME = "vn.tech3c.sdk.auth.controller.Tech3CIdController"; + private const string BRIDGE_CLASS = "vn.tech3c.sdk.unity.Tech3CUnityBridge"; + + private AndroidJavaObject controller; + private AndroidJavaObject unityBridge; + + /// + /// Initialize the Tech3C SDK + /// + public void Initialize(string clientId, string clientSecret, Tech3CConfig config) + { + try + { + Debug.Log("[Tech3C] Bridge GameObject initialized"); + + // Initialize Unity Bridge for callbacks first + using (AndroidJavaClass bridgeClass = new AndroidJavaClass(BRIDGE_CLASS)) + { + unityBridge = bridgeClass.CallStatic("getInstance"); + } + + // Initialize controller with method chaining to set callback + if (controller == null) + { + using (AndroidJavaClass unityPlayer = new AndroidJavaClass("com.unity3d.player.UnityPlayer")) + using (AndroidJavaObject activity = unityPlayer.GetStatic("currentActivity")) + using (AndroidJavaClass controllerClass = new AndroidJavaClass(CLASS_NAME)) + { + // Chain methods like MainActivity: initialize() -> setDebug() -> setOnAuthCallback() + controller = controllerClass.CallStatic( + "initialize", + activity, + clientId, + clientSecret + ) + // .Call("setDebug", true) + // .Call("setDisableExitLogin", false) + // .Call("setEnableMaintenanceCheck", true) + .Call("setOnAuthCallback", unityBridge); + + Debug.Log("[Tech3C] Unity Bridge registered with native SDK"); + } + } + + if (controller != null) + { + // Apply configuration + ApplyConfig(config); + Debug.Log("[Tech3C] Initialized successfully"); + } + else + { + Debug.LogError("[Tech3C] Failed to initialize controller"); + } + } + catch (Exception e) + { + Debug.LogError($"[Tech3C] Initialize error: {e.Message}"); + } + } + + /// + /// Apply configuration settings to the SDK + /// + private void ApplyConfig(Tech3CConfig config) + { + if (controller == null || config == null) return; + + try + { + // Set debug mode (returns Tech3CIdController for chaining) + controller = controller.Call("setDebug", config.debugMode); + + // Set language - pass Java Language enum + using (AndroidJavaClass languageEnumClass = new AndroidJavaClass("vn.tech3c.sdk.auth.entities.enums.Language")) + { + string javaEnumValue = config.language == Language.Vietnamese ? "VIETNAMESE" : "ENGLISH"; + AndroidJavaObject javaLanguage = languageEnumClass.GetStatic(javaEnumValue); + controller = controller.Call("setLanguageDisplay", javaLanguage); + } + + // Set UI mode - pass Java UiMode enum + using (AndroidJavaClass uiModeEnumClass = new AndroidJavaClass("vn.tech3c.sdk.auth.entities.enums.UiMode")) + { + string javaEnumValue = config.uiMode == UiMode.Fullscreen ? "FULLSCREEN" : "DIALOG"; + AndroidJavaObject javaUiMode = uiModeEnumClass.GetStatic(javaEnumValue); + controller = controller.Call("setUiMode", javaUiMode); + } + + // Set orientation - pass Java OrientationMode enum + using (AndroidJavaClass orientationEnumClass = new AndroidJavaClass("vn.tech3c.sdk.auth.entities.enums.OrientationMode")) + { + string javaEnumValue = config.screenOrientation.ToString().ToUpper(); + AndroidJavaObject javaOrientation = orientationEnumClass.GetStatic(javaEnumValue); + controller = controller.Call("setOrientation", javaOrientation); + } + + // Set guest login + controller = controller.Call("setEnableGuestLogin", config.enableGuestLogin); + + // Set OTP requirement + controller = controller.Call("setRequireOtp", config.requireOtp); + + // Set disable exit login + controller = controller.Call("setDisableExitLogin", config.disableExitLogin); + + // Set maintenance check + controller = controller.Call("setEnableMaintenanceCheck", config.enableMaintenanceCheck); + + if (!string.IsNullOrEmpty(config.maintenanceCheckUrl)) + { + controller = controller.Call("setMaintenanceCheckUrl", config.maintenanceCheckUrl); + } + + if (!string.IsNullOrEmpty(config.serverIp)) + { + controller = controller.Call("setIpMaintenanceCheck", config.serverIp); + } + + Debug.Log("[Tech3C] Configuration applied successfully"); + } + catch (Exception e) + { + Debug.LogError($"[Tech3C] ApplyConfig error: {e.Message}"); + } + } + + /// + /// Show authentication screen + /// + public void ShowAuth(IAuthCallback callback) + { + try + { + if (controller == null) + { + Debug.LogError("[Tech3C] Controller not initialized"); + callback?.OnAuthError(-1, "SDK not initialized"); + return; + } + + // Register callback with bridge + Tech3CSDKBridge.SetAuthCallback(callback); + + using (AndroidJavaClass unityPlayer = new AndroidJavaClass("com.unity3d.player.UnityPlayer")) + using (AndroidJavaObject activity = unityPlayer.GetStatic("currentActivity")) + { + controller.Call("showAuth", activity); + Debug.Log("[Tech3C] Auth screen shown"); + } + } + catch (Exception e) + { + Debug.LogError($"[Tech3C] ShowAuth error: {e.Message}"); + callback?.OnAuthError(-1, e.Message); + } + } + + /// + /// Logout current user + /// + public void Logout(ILogoutCallback callback) + { + try + { + if (controller == null) + { + Debug.LogError("[Tech3C] Controller not initialized"); + callback?.OnLogoutError(-1, "SDK not initialized"); + return; + } + + // Register callback with bridge + Tech3CSDKBridge.SetLogoutCallback(callback); + + controller.Call("logout"); + Debug.Log("[Tech3C] Logout initiated"); + } + catch (Exception e) + { + Debug.LogError($"[Tech3C] Logout error: {e.Message}"); + callback?.OnLogoutError(-1, e.Message); + } + } + + /// + /// Get user information + /// + public void GetUserInfo(IUserInfoCallback callback) + { + try + { + if (controller == null) + { + Debug.LogError("[Tech3C] Controller not initialized"); + callback?.OnUserInfoError(-1, "SDK not initialized"); + return; + } + + // Register callback with bridge + Tech3CSDKBridge.SetUserInfoCallback(callback); + + controller.Call("getUserInfo"); + Debug.Log("[Tech3C] GetUserInfo called"); + } + catch (Exception e) + { + Debug.LogError($"[Tech3C] GetUserInfo error: {e.Message}"); + callback?.OnUserInfoError(-1, e.Message); + } + } + + /// + /// Check if user is logged in + /// + public bool IsLoggedIn() + { + try + { + if (controller == null) return false; + + return controller.Call("isLogin"); + } + catch (Exception e) + { + Debug.LogError($"[Tech3C] IsLoggedIn error: {e.Message}"); + return false; + } + } + + /// + /// Get access token + /// + public string GetAccessToken() + { + try + { + if (controller == null) return null; + + return controller.Call("getAccessToken"); + } + catch (Exception e) + { + Debug.LogError($"[Tech3C] GetAccessToken error: {e.Message}"); + return null; + } + } + + /// + /// Get refresh token + /// + public string GetRefreshToken() + { + try + { + if (controller == null) return null; + + return controller.Call("getRefreshToken"); + } + catch (Exception e) + { + Debug.LogError($"[Tech3C] GetRefreshToken error: {e.Message}"); + return null; + } + } + + /// + /// Get user ID + /// + public string GetUserId() + { + try + { + if (controller == null) return null; + + return controller.Call("getUserId"); + } + catch (Exception e) + { + Debug.LogError($"[Tech3C] GetUserId error: {e.Message}"); + return null; + } + } + + /// + /// Get device ID + /// + public string GetDeviceId() + { + try + { + if (controller == null) return null; + + return controller.Call("getDeviceId"); + } + catch (Exception e) + { + Debug.LogError($"[Tech3C] GetDeviceId error: {e.Message}"); + return null; + } + } + + /// + /// Get login time + /// + public long GetLoginTime() + { + try + { + if (controller == null) return 0; + + return controller.Call("getLoginTime"); + } + catch (Exception e) + { + Debug.LogError($"[Tech3C] GetLoginTime error: {e.Message}"); + return 0; + } + } + + /// + /// Get token expiry time + /// + public long GetTokenExpiry() + { + try + { + if (controller == null) return 0; + + return controller.Call("getTokenExpiry"); + } + catch (Exception e) + { + Debug.LogError($"[Tech3C] GetTokenExpiry error: {e.Message}"); + return 0; + } + } + + /// + /// Check if token is expired + /// + public bool IsTokenExpired() + { + try + { + if (controller == null) return false; + + return controller.Call("isTokenExpired"); + } + catch (Exception e) + { + Debug.LogError($"[Tech3C] IsTokenExpired error: {e.Message}"); + return false; + } + } + + /// + /// Set language + /// + public void SetLanguage(Language language) + { + try + { + if (controller == null) return; + + // Set language - pass Java Language enum + using (AndroidJavaClass languageEnumClass = new AndroidJavaClass("vn.tech3c.sdk.auth.entities.enums.Language")) + { + string javaEnumValue = language == Language.Vietnamese ? "VIETNAMESE" : "ENGLISH"; + AndroidJavaObject javaLanguage = languageEnumClass.GetStatic(javaEnumValue); + controller = controller.Call("setLanguageDisplay", javaLanguage); + } + Debug.Log($"[Tech3C] Language set to {language}"); + } + catch (Exception e) + { + Debug.LogError($"[Tech3C] SetLanguage error: {e.Message}"); + } + } + + /// + /// Set debug mode + /// + public void SetDebug(bool debug) + { + try + { + if (controller == null) return; + + controller = controller.Call("setDebug", debug); + Debug.Log($"[Tech3C] Debug mode set to {debug}"); + } + catch (Exception e) + { + Debug.LogError($"[Tech3C] SetDebug error: {e.Message}"); + } + } + + /// + /// Cleanup resources + /// + public void Cleanup() + { + try + { + if (controller != null) + { + controller.Call("cleanup"); + controller.Dispose(); + controller = null; + Debug.Log("[Tech3C] Cleanup completed"); + } + } + catch (Exception e) + { + Debug.LogError($"[Tech3C] Cleanup error: {e.Message}"); + } + } + } + + /// + /// Interface for native bridge implementations + /// + public interface ITech3CNativeBridge + { + void Initialize(string clientId, string clientSecret, Tech3CConfig config); + void ShowAuth(IAuthCallback callback); + void Logout(ILogoutCallback callback); + void GetUserInfo(IUserInfoCallback callback); + bool IsLoggedIn(); + string GetAccessToken(); + string GetRefreshToken(); + string GetUserId(); + string GetDeviceId(); + long GetLoginTime(); + long GetTokenExpiry(); + bool IsTokenExpired(); + void SetLanguage(Language language); + void SetDebug(bool debug); + void Cleanup(); + } +} diff --git a/Assets/Tech3C/Runtime/Tech3CAndroidBridge.cs.meta b/Assets/Tech3C/Runtime/Tech3CAndroidBridge.cs.meta new file mode 100644 index 0000000000..937d79ca0b --- /dev/null +++ b/Assets/Tech3C/Runtime/Tech3CAndroidBridge.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 50744a202f3e9e24a80e10aecf427dac +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Tech3C/Runtime/Tech3CCallbacks.cs b/Assets/Tech3C/Runtime/Tech3CCallbacks.cs new file mode 100644 index 0000000000..7f40b826a3 --- /dev/null +++ b/Assets/Tech3C/Runtime/Tech3CCallbacks.cs @@ -0,0 +1,150 @@ +using System; + +namespace Tech3C +{ + /// + /// Callback for authentication operations (login, register, etc.) + /// + public interface IAuthCallback + { + /// + /// Called when authentication is successful + /// + /// The authenticated user ID + /// The user password + /// The access token + /// The refresh token + /// The type of login used + /// Token expiry timestamp + void OnAuthSuccess(string userId, string password, string accessToken, string refreshToken, LoginType loginType, long expiryTime); + + /// + /// Called when authentication is cancelled by the user + /// + void OnAuthCancelled(); + + /// + /// Called when an error occurs during authentication + /// + /// The error code + /// The error message + void OnAuthError(int errorCode, string errorMessage); + } + + /// + /// Callback for logout operations + /// + public interface ILogoutCallback + { + /// + /// Called when logout is successful + /// + void OnLogoutSuccess(); + + /// + /// Called when an error occurs during logout + /// + /// The error code + /// The error message + void OnLogoutError(int errorCode, string errorMessage); + } + + /// + /// Callback for user info operations + /// + public interface IUserInfoCallback + { + /// + /// Called when user info is successfully retrieved + /// + /// The user ID + /// The username + /// The email address + /// The phone number + void OnUserInfoReceived(string userId, string username, string email, string phone); + + /// + /// Called when user info retrieval is cancelled + /// + void OnUserInfoCancelled(); + + /// + /// Called when an error occurs while retrieving user info + /// + /// The error code + /// The error message + void OnUserInfoError(int errorCode, string errorMessage); + } + + /// + /// Default implementation of IAuthCallback using Unity events + /// + [Serializable] + public class AuthCallback : IAuthCallback + { + public event Action OnAuthSuccessEvent; + public event Action OnAuthCancelledEvent; + public event Action OnAuthErrorEvent; + + public void OnAuthSuccess(string userId, string password, string accessToken, string refreshToken, LoginType loginType, long expiryTime) + { + OnAuthSuccessEvent?.Invoke(userId, password, accessToken, refreshToken, loginType, expiryTime); + } + + public void OnAuthCancelled() + { + OnAuthCancelledEvent?.Invoke(); + } + + public void OnAuthError(int errorCode, string errorMessage) + { + OnAuthErrorEvent?.Invoke(errorCode, errorMessage); + } + } + + /// + /// Default implementation of ILogoutCallback using Unity events + /// + [Serializable] + public class LogoutCallback : ILogoutCallback + { + public event Action OnLogoutSuccessEvent; + public event Action OnLogoutErrorEvent; + + public void OnLogoutSuccess() + { + OnLogoutSuccessEvent?.Invoke(); + } + + public void OnLogoutError(int errorCode, string errorMessage) + { + OnLogoutErrorEvent?.Invoke(errorCode, errorMessage); + } + } + + /// + /// Default implementation of IUserInfoCallback using Unity events + /// + [Serializable] + public class UserInfoCallback : IUserInfoCallback + { + public event Action OnUserInfoReceivedEvent; + public event Action OnUserInfoCancelledEvent; + public event Action OnUserInfoErrorEvent; + + public void OnUserInfoReceived(string userId, string username, string email, string phone) + { + OnUserInfoReceivedEvent?.Invoke(userId, username, email, phone); + } + + public void OnUserInfoCancelled() + { + OnUserInfoCancelledEvent?.Invoke(); + } + + public void OnUserInfoError(int errorCode, string errorMessage) + { + OnUserInfoErrorEvent?.Invoke(errorCode, errorMessage); + } + } +} diff --git a/Assets/Tech3C/Runtime/Tech3CCallbacks.cs.meta b/Assets/Tech3C/Runtime/Tech3CCallbacks.cs.meta new file mode 100644 index 0000000000..b7e5fd3738 --- /dev/null +++ b/Assets/Tech3C/Runtime/Tech3CCallbacks.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: ed5aa8a6da34849438530838098c8ca0 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Tech3C/Runtime/Tech3CConfig.cs b/Assets/Tech3C/Runtime/Tech3CConfig.cs new file mode 100644 index 0000000000..6d31bb34b2 --- /dev/null +++ b/Assets/Tech3C/Runtime/Tech3CConfig.cs @@ -0,0 +1,114 @@ +using UnityEngine; + +namespace Tech3C +{ + /// + /// Configuration class for Tech3C SDK + /// + [CreateAssetMenu(fileName = "Tech3CConfig", menuName = "Tech3C SDK/Configuration", order = 1)] + public class Tech3CConfig : ScriptableObject + { + [Header("Client Credentials")] + [Tooltip("Client ID for Tech3C authentication")] + public string clientId; + + [Tooltip("Client Secret for Tech3C authentication")] + public string clientSecret; + + [Header("General Settings")] + [Tooltip("Enable debug mode for detailed logging")] + public bool debugMode = false; + + [Tooltip("Display language for the SDK")] + public Language language = Language.English; + + [Tooltip("Environment for the SDK")] + public Environment environment = Environment.Production; + + [Header("UI Settings")] + [Tooltip("UI mode for authentication (Fullscreen or Dialog)")] + public UiMode uiMode = UiMode.Fullscreen; + + [Tooltip("Dialog size when UI mode is Dialog")] + public DialogSize dialogSize = DialogSize.Medium; + + [Tooltip("Screen orientation for authentication")] + public OrientationMode screenOrientation = OrientationMode.Auto; + + [Tooltip("Disable exit button on login screen")] + public bool disableExitLogin = false; + + [Header("Authentication Settings")] + [Tooltip("Enable guest login")] + public bool enableGuestLogin = false; + + [Tooltip("Require OTP for authentication")] + public bool requireOtp = false; + + [Tooltip("Enable Google login")] + public bool enableGoogleLogin = false; + + [Tooltip("Enable Facebook login")] + public bool enableFacebookLogin = false; + + [Tooltip("Enable Apple login")] + public bool enableAppleLogin = false; + + [Header("Maintenance Settings")] + [Tooltip("Enable maintenance check")] + public bool enableMaintenanceCheck = false; + + [Tooltip("Maintenance check URL")] + public string maintenanceCheckUrl; + + [Tooltip("Server IP for maintenance check")] + public string serverIp; + + [Header("Network Settings")] + [Tooltip("Timeout in seconds for network requests")] + public int timeoutSeconds = 30; + + /// + /// Creates a default Tech3CConfig asset + /// + public static Tech3CConfig CreateDefault() + { + var config = CreateInstance(); + config.clientId = ""; + config.clientSecret = ""; + config.debugMode = false; + config.language = Language.English; + config.environment = Environment.Production; + config.uiMode = UiMode.Fullscreen; + config.dialogSize = DialogSize.Medium; + config.screenOrientation = OrientationMode.Auto; + config.disableExitLogin = false; + config.enableGuestLogin = false; + config.requireOtp = false; + config.enableGoogleLogin = false; + config.enableFacebookLogin = false; + config.enableAppleLogin = false; + config.enableMaintenanceCheck = false; + config.maintenanceCheckUrl = ""; + config.serverIp = ""; + config.timeoutSeconds = 30; + return config; + } + + /// + /// Validates the configuration + /// + public bool IsValid() + { + return !string.IsNullOrEmpty(clientId) && !string.IsNullOrEmpty(clientSecret); + } + + /// + /// Gets the language code string + /// + public string GetLanguageCode() + { + return language == Language.Vietnamese ? "vi" : "en"; + } + } +} diff --git a/Assets/Tech3C/Runtime/Tech3CConfig.cs.meta b/Assets/Tech3C/Runtime/Tech3CConfig.cs.meta new file mode 100644 index 0000000000..39fd2c7efd --- /dev/null +++ b/Assets/Tech3C/Runtime/Tech3CConfig.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 95a442ac7dacf8b4484a10035308bc31 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Tech3C/Runtime/Tech3CEnums.cs b/Assets/Tech3C/Runtime/Tech3CEnums.cs new file mode 100644 index 0000000000..9107769385 --- /dev/null +++ b/Assets/Tech3C/Runtime/Tech3CEnums.cs @@ -0,0 +1,64 @@ +using System; + +namespace Tech3C +{ + /// + /// Language options for the SDK + /// + public enum Language + { + Vietnamese, + English + } + + /// + /// UI mode for authentication dialog + /// + public enum UiMode + { + Fullscreen, + Dialog + } + + /// + /// Screen orientation for authentication + /// + public enum OrientationMode + { + Auto, + Portrait, + Landscape + } + + /// + /// Login type for authentication + /// Matches the native SDK's LoginType enum + /// + public enum LoginType + { + GUEST, + ACCOUNT, + SOCIAL, + REGISTER // For register success handling + } + + /// + /// Environment for the SDK + /// + public enum Environment + { + Production, + Staging, + Development + } + + /// + /// Dialog size for UI mode + /// + public enum DialogSize + { + Small, + Medium, + Large + } +} diff --git a/Assets/Tech3C/Runtime/Tech3CEnums.cs.meta b/Assets/Tech3C/Runtime/Tech3CEnums.cs.meta new file mode 100644 index 0000000000..0b58d42a40 --- /dev/null +++ b/Assets/Tech3C/Runtime/Tech3CEnums.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: c35f3ea36736dfe439eb6f4b8a314dde +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Tech3C/Runtime/Tech3CSDK.cs b/Assets/Tech3C/Runtime/Tech3CSDK.cs new file mode 100644 index 0000000000..dd9111009a --- /dev/null +++ b/Assets/Tech3C/Runtime/Tech3CSDK.cs @@ -0,0 +1,473 @@ +using UnityEngine; +using System; + +namespace Tech3C +{ + /// + /// Main API wrapper for Tech3C SDK + /// This is the main entry point for using the SDK in Unity + /// + public class Tech3CSDK : MonoBehaviour + { + private static Tech3CSDK instance; + private ITech3CNativeBridge nativeBridge; + private Tech3CConfig config; + private bool isInitialized = false; + + #region Singleton + + /// + /// Get the singleton instance of Tech3CSDK + /// + public static Tech3CSDK Instance + { + get + { + if (instance == null) + { + GameObject go = new GameObject("Tech3CSDK"); + instance = go.AddComponent(); + DontDestroyOnLoad(go); + } + return instance; + } + } + + private void Awake() + { + if (instance == null) + { + instance = this; + DontDestroyOnLoad(gameObject); + } + else if (instance != this) + { + Destroy(gameObject); + } + } + + #endregion + + #region Initialization + + /// + /// Initialize the Tech3C SDK with configuration + /// + /// The configuration object + public void Initialize(Tech3CConfig config) + { + if (config == null) + { + Debug.LogError("[Tech3C] Config cannot be null"); + return; + } + + if (!config.IsValid()) + { + Debug.LogError("[Tech3C] Invalid config: Client ID and Client Secret are required"); + return; + } + + this.config = config; + + // Ensure Tech3CSDKBridge is created to receive callbacks from native code + var bridge = Tech3CSDKBridge.Instance; + Debug.Log("[Tech3C] Tech3CSDKBridge ensured for callbacks"); + + // Create platform-specific bridge + CreateNativeBridge(); + + if (nativeBridge != null) + { + nativeBridge.Initialize(config.clientId, config.clientSecret, config); + isInitialized = true; + Debug.Log("[Tech3C] Initialization completed successfully"); + } + else + { + Debug.LogError("[Tech3C] Failed to create native bridge"); + } + } + + /// + /// Initialize the Tech3C SDK with client credentials + /// + /// The client ID + /// The client secret + public void Initialize(string clientId, string clientSecret) + { + if (string.IsNullOrEmpty(clientId) || string.IsNullOrEmpty(clientSecret)) + { + Debug.LogError("[Tech3C] Client ID and Client Secret are required"); + return; + } + + config = Tech3CConfig.CreateDefault(); + config.clientId = clientId; + config.clientSecret = clientSecret; + + Initialize(config); + } + + private void CreateNativeBridge() + { + #if UNITY_ANDROID && !UNITY_EDITOR + nativeBridge = new Tech3CAndroidBridge(); + #elif UNITY_IOS && !UNITY_EDITOR + nativeBridge = new Tech3CiOSBridge(); + #else + #if UNITY_ANDROID + nativeBridge = new Tech3CAndroidBridge(); + #elif UNITY_IOS + nativeBridge = new Tech3CiOSBridge(); + #else + Debug.LogWarning("[Tech3C] Running in Editor or unsupported platform. Using mock implementation."); + nativeBridge = new MockBridge(); + #endif + #endif + } + + #endregion + + #region Authentication + + /// + /// Show authentication screen (login/register/forgot password) + /// + /// The callback for authentication result + public void ShowAuth(IAuthCallback callback) + { + if (!isInitialized) + { + Debug.LogError("[Tech3C] SDK not initialized. Call Initialize() first."); + callback?.OnAuthError(-1, "SDK not initialized"); + return; + } + + nativeBridge?.ShowAuth(callback); + } + + /// + /// Show authentication screen without callback + /// + public void ShowAuth() + { + ShowAuth(null); + } + + #endregion + + #region User Management + + /// + /// Logout current user + /// + /// The callback for logout result + public void Logout(ILogoutCallback callback) + { + if (!isInitialized) + { + Debug.LogError("[Tech3C] SDK not initialized. Call Initialize() first."); + callback?.OnLogoutError(-1, "SDK not initialized"); + return; + } + + nativeBridge?.Logout(callback); + } + + /// + /// Logout current user without callback + /// + public void Logout() + { + Logout(null); + } + + /// + /// Get user information + /// + /// The callback for user info result + public void GetUserInfo(IUserInfoCallback callback) + { + if (!isInitialized) + { + Debug.LogError("[Tech3C] SDK not initialized. Call Initialize() first."); + callback?.OnUserInfoError(-1, "SDK not initialized"); + return; + } + + nativeBridge?.GetUserInfo(callback); + } + + /// + /// Get user information without callback + /// + public void GetUserInfo() + { + GetUserInfo(null); + } + + #endregion + + #region State Checkers + + /// + /// Check if user is logged in + /// + public bool IsLoggedIn() + { + if (!isInitialized) + { + Debug.LogWarning("[Tech3C] SDK not initialized"); + return false; + } + + return nativeBridge?.IsLoggedIn() ?? false; + } + + /// + /// Check if access token is expired + /// + public bool IsTokenExpired() + { + if (!isInitialized) + { + Debug.LogWarning("[Tech3C] SDK not initialized"); + return true; + } + + return nativeBridge?.IsTokenExpired() ?? true; + } + + #endregion + + #region Token Management + + /// + /// Get access token + /// + public string GetAccessToken() + { + if (!isInitialized) + { + Debug.LogWarning("[Tech3C] SDK not initialized"); + return null; + } + + return nativeBridge?.GetAccessToken(); + } + + /// + /// Get refresh token + /// + public string GetRefreshToken() + { + if (!isInitialized) + { + Debug.LogWarning("[Tech3C] SDK not initialized"); + return null; + } + + return nativeBridge?.GetRefreshToken(); + } + + #endregion + + #region User Information + + /// + /// Get user ID + /// + public string GetUserId() + { + if (!isInitialized) + { + Debug.LogWarning("[Tech3C] SDK not initialized"); + return null; + } + + return nativeBridge?.GetUserId(); + } + + /// + /// Get device ID + /// + public string GetDeviceId() + { + if (!isInitialized) + { + Debug.LogWarning("[Tech3C] SDK not initialized"); + return null; + } + + return nativeBridge?.GetDeviceId(); + } + + /// + /// Get login timestamp + /// + public long GetLoginTime() + { + if (!isInitialized) + { + Debug.LogWarning("[Tech3C] SDK not initialized"); + return 0; + } + + return nativeBridge?.GetLoginTime() ?? 0; + } + + /// + /// Get token expiry timestamp + /// + public long GetTokenExpiry() + { + if (!isInitialized) + { + Debug.LogWarning("[Tech3C] SDK not initialized"); + return 0; + } + + return nativeBridge?.GetTokenExpiry() ?? 0; + } + + #endregion + + #region Configuration + + /// + /// Set display language + /// + /// The language to set + public void SetLanguage(Language language) + { + if (!isInitialized) + { + Debug.LogWarning("[Tech3C] SDK not initialized"); + return; + } + + nativeBridge?.SetLanguage(language); + } + + /// + /// Set debug mode + /// + /// Enable or disable debug mode + public void SetDebug(bool debug) + { + if (!isInitialized) + { + Debug.LogWarning("[Tech3C] SDK not initialized"); + return; + } + + nativeBridge?.SetDebug(debug); + } + + #endregion + + #region Cleanup + + /// + /// Cleanup SDK resources + /// + public void Cleanup() + { + nativeBridge?.Cleanup(); + nativeBridge = null; + config = null; + isInitialized = false; + Debug.Log("[Tech3C] Cleanup completed"); + } + + private void OnDestroy() + { + Cleanup(); + } + + #endregion + + #region Properties + + /// + /// Get the current configuration + /// + public Tech3CConfig Config => config; + + /// + /// Check if SDK is initialized + /// + public bool IsInitialized => isInitialized; + + #endregion + } + + /// + /// Mock bridge for testing in editor or unsupported platforms + /// + internal class MockBridge : ITech3CNativeBridge + { + private bool isLoggedIn = false; + private string accessToken = "mock_access_token"; + private string refreshToken = "mock_refresh_token"; + private string userId = "mock_user_id"; + + public void Initialize(string clientId, string clientSecret, Tech3CConfig config) + { + Debug.Log($"[Tech3C Mock] Initialized with Client ID: {clientId}"); + } + + public void ShowAuth(IAuthCallback callback) + { + Debug.Log("[Mock3C Mock] ShowAuth called"); + isLoggedIn = true; + string password = "mock_password"; + callback?.OnAuthSuccess(userId, password, accessToken, refreshToken, LoginType.ACCOUNT, DateTime.Now.Ticks + 3600000); + } + + public void Logout(ILogoutCallback callback) + { + Debug.Log("[Tech3C Mock] Logout called"); + isLoggedIn = false; + callback?.OnLogoutSuccess(); + } + + public void GetUserInfo(IUserInfoCallback callback) + { + Debug.Log("[Tech3C Mock] GetUserInfo called"); + callback?.OnUserInfoReceived(userId, "mock_username", "mock@example.com", "1234567890"); + } + + public bool IsLoggedIn() => isLoggedIn; + + public string GetAccessToken() => isLoggedIn ? accessToken : null; + + public string GetRefreshToken() => isLoggedIn ? refreshToken : null; + + public string GetUserId() => isLoggedIn ? userId : null; + + public string GetDeviceId() => "mock_device_id"; + + public long GetLoginTime() => isLoggedIn ? DateTime.Now.Ticks : 0; + + public long GetTokenExpiry() => isLoggedIn ? DateTime.Now.Ticks + 3600000 : 0; + + public bool IsTokenExpired() => false; + + public void SetLanguage(Language language) + { + Debug.Log($"[Tech3C Mock] Language set to {language}"); + } + + public void SetDebug(bool debug) + { + Debug.Log($"[Tech3C Mock] Debug mode set to {debug}"); + } + + public void Cleanup() + { + Debug.Log("[Tech3C Mock] Cleanup completed"); + } + } +} diff --git a/Assets/Tech3C/Runtime/Tech3CSDK.cs.meta b/Assets/Tech3C/Runtime/Tech3CSDK.cs.meta new file mode 100644 index 0000000000..72d6fb5d15 --- /dev/null +++ b/Assets/Tech3C/Runtime/Tech3CSDK.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 3afb6189c673cea45a34e6e75c1f5ccb +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Tech3C/Runtime/Tech3CSDKBridge.cs b/Assets/Tech3C/Runtime/Tech3CSDKBridge.cs new file mode 100644 index 0000000000..4c0b34086e --- /dev/null +++ b/Assets/Tech3C/Runtime/Tech3CSDKBridge.cs @@ -0,0 +1,332 @@ +using UnityEngine; +using System; + +namespace Tech3C +{ + /// + /// Bridge component to receive callbacks from native Android/iOS SDK via Unity messaging + /// This GameObject receives UnitySendMessage calls from native code + /// + public class Tech3CSDKBridge : MonoBehaviour + { + private static Tech3CSDKBridge instance; + + // Callback references + private static IAuthCallback authCallback; + private static ILogoutCallback logoutCallback; + private static IUserInfoCallback userInfoCallback; + + #region Singleton + + public static Tech3CSDKBridge Instance + { + get + { + if (instance == null) + { + GameObject go = new GameObject("Tech3CSDKBridge"); + instance = go.AddComponent(); + DontDestroyOnLoad(go); + } + return instance; + } + } + + private void Awake() + { + if (instance == null) + { + instance = this; + DontDestroyOnLoad(gameObject); + Debug.Log("[Tech3C Bridge] Bridge initialized"); + } + else if (instance != this) + { + Destroy(gameObject); + } + } + + #endregion + + #region Set Callbacks + + /// + /// Set the auth callback to be invoked when auth events occur + /// + public static void SetAuthCallback(IAuthCallback callback) + { + authCallback = callback; + Debug.Log("[Tech3C Bridge] Auth callback set"); + } + + /// + /// Set the logout callback to be invoked when logout events occur + /// + public static void SetLogoutCallback(ILogoutCallback callback) + { + logoutCallback = callback; + Debug.Log("[Tech3C Bridge] Logout callback set"); + } + + /// + /// Set the user info callback to be invoked when user info events occur + /// + public static void SetUserInfoCallback(IUserInfoCallback callback) + { + userInfoCallback = callback; + Debug.Log("[Tech3C Bridge] User info callback set"); + } + + #endregion + + #region Auth Callbacks + + /// + /// Called from native code when authentication is successful + /// Message format: "userId|password|accessToken|refreshToken|loginType|expiryTime" + /// + public void OnAuthSuccess(string message) + { + Debug.Log($"[Tech3C Bridge] OnAuthSuccess received: {message}"); + + if (authCallback == null) + { + Debug.LogWarning("[Tech3C Bridge] No auth callback registered"); + return; + } + + try + { + string[] parts = message.Split('|'); + if (parts.Length >= 6) + { + string userId = parts[0]; + string password = parts[1]; + string accessToken = parts[2]; + string refreshToken = parts[3]; + LoginType loginType = (LoginType)Enum.Parse(typeof(LoginType), parts[4]); + long expiryTime = long.Parse(parts[5]); + + Debug.Log($"[Tech3C Bridge] Parsed auth success: userId={userId}, loginType={loginType}"); + authCallback.OnAuthSuccess(userId, password, accessToken, refreshToken, loginType, expiryTime); + } + else + { + Debug.LogError($"[Tech3C Bridge] Invalid message format: {message}"); + } + } + catch (Exception e) + { + Debug.LogError($"[Tech3C Bridge] Error parsing auth success message: {e.Message}"); + } + } + + /// + /// Called from native code when authentication is cancelled + /// + public void OnAuthCancelled(string message) + { + Debug.Log("[Tech3C Bridge] OnAuthCancelled received"); + + if (authCallback == null) + { + Debug.LogWarning("[Tech3C Bridge] No auth callback registered"); + return; + } + + authCallback.OnAuthCancelled(); + } + + /// + /// Called from native code when an authentication error occurs + /// Message format: "errorCode|errorMessage" + /// + public void OnAuthError(string message) + { + Debug.Log($"[Tech3C Bridge] OnAuthError received: {message}"); + + if (authCallback == null) + { + Debug.LogWarning("[Tech3C Bridge] No auth callback registered"); + return; + } + + try + { + string[] parts = message.Split('|'); + int errorCode = -1; + string errorMessage = "Unknown error"; + + if (parts.Length >= 1) + { + int.TryParse(parts[0], out errorCode); + } + if (parts.Length >= 2) + { + errorMessage = parts[1]; + } + + Debug.Log($"[Tech3C Bridge] Parsed auth error: code={errorCode}, message={errorMessage}"); + authCallback.OnAuthError(errorCode, errorMessage); + } + catch (Exception e) + { + Debug.LogError($"[Tech3C Bridge] Error parsing auth error message: {e.Message}"); + } + } + + #endregion + + #region Logout Callbacks + + /// + /// Called from native code when logout is successful + /// + public void OnLogoutSuccess(string message) + { + Debug.Log("[Tech3C Bridge] OnLogoutSuccess received"); + + if (logoutCallback == null) + { + Debug.LogWarning("[Tech3C Bridge] No logout callback registered"); + return; + } + + logoutCallback.OnLogoutSuccess(); + } + + /// + /// Called from native code when a logout error occurs + /// Message format: "errorCode|errorMessage" + /// + public void OnLogoutError(string message) + { + Debug.Log($"[Tech3C Bridge] OnLogoutError received: {message}"); + + if (logoutCallback == null) + { + Debug.LogWarning("[Tech3C Bridge] No logout callback registered"); + return; + } + + try + { + string[] parts = message.Split('|'); + int errorCode = -1; + string errorMessage = "Unknown error"; + + if (parts.Length >= 1) + { + int.TryParse(parts[0], out errorCode); + } + if (parts.Length >= 2) + { + errorMessage = parts[1]; + } + + logoutCallback.OnLogoutError(errorCode, errorMessage); + } + catch (Exception e) + { + Debug.LogError($"[Tech3C Bridge] Error parsing logout error message: {e.Message}"); + } + } + + #endregion + + #region User Info Callbacks + + /// + /// Called from native code when user info is received + /// Message format: "userId|username|email|phone" + /// + public void OnUserInfoReceived(string message) + { + Debug.Log($"[Tech3C Bridge] OnUserInfoReceived received: {message}"); + + if (userInfoCallback == null) + { + Debug.LogWarning("[Tech3C Bridge] No user info callback registered"); + return; + } + + try + { + string[] parts = message.Split('|'); + if (parts.Length >= 4) + { + string userId = parts[0]; + string username = parts[1]; + string email = parts[2]; + string phone = parts[3]; + + Debug.Log($"[Tech3C Bridge] Parsed user info: userId={userId}, username={username}"); + userInfoCallback.OnUserInfoReceived(userId, username, email, phone); + } + else + { + Debug.LogError($"[Tech3C Bridge] Invalid message format: {message}"); + } + } + catch (Exception e) + { + Debug.LogError($"[Tech3C Bridge] Error parsing user info message: {e.Message}"); + } + } + + /// + /// Called from native code when user info retrieval is cancelled + /// + public void OnUserInfoCancelled(string message) + { + Debug.Log("[Tech3C Bridge] OnUserInfoCancelled received"); + + if (userInfoCallback == null) + { + Debug.LogWarning("[Tech3C Bridge] No user info callback registered"); + return; + } + + userInfoCallback.OnUserInfoCancelled(); + } + + /// + /// Called from native code when a user info error occurs + /// Message format: "errorCode|errorMessage" + /// + public void OnUserInfoError(string message) + { + Debug.Log($"[Tech3C Bridge] OnUserInfoError received: {message}"); + + if (userInfoCallback == null) + { + Debug.LogWarning("[Tech3C Bridge] No user info callback registered"); + return; + } + + try + { + string[] parts = message.Split('|'); + int errorCode = -1; + string errorMessage = "Unknown error"; + + if (parts.Length >= 1) + { + int.TryParse(parts[0], out errorCode); + } + if (parts.Length >= 2) + { + errorMessage = parts[1]; + } + + userInfoCallback.OnUserInfoError(errorCode, errorMessage); + } + catch (Exception e) + { + Debug.LogError($"[Tech3C Bridge] Error parsing user info error message: {e.Message}"); + } + } + + #endregion + } +} diff --git a/Assets/Tech3C/Runtime/Tech3CSDKBridge.cs.meta b/Assets/Tech3C/Runtime/Tech3CSDKBridge.cs.meta new file mode 100644 index 0000000000..353dcb291b --- /dev/null +++ b/Assets/Tech3C/Runtime/Tech3CSDKBridge.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 46cf26ff28457b04890e322b5b161a67 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Tech3C/Runtime/Tech3CiOSBridge.cs b/Assets/Tech3C/Runtime/Tech3CiOSBridge.cs new file mode 100644 index 0000000000..2aa3a12b95 --- /dev/null +++ b/Assets/Tech3C/Runtime/Tech3CiOSBridge.cs @@ -0,0 +1,147 @@ +using UnityEngine; +using System; + +namespace Tech3C +{ + /// + /// Bridge class for iOS platform to communicate with Tech3C native SDK + /// Note: iOS implementation requires native framework. This is a placeholder. + /// + public class Tech3CiOSBridge : ITech3CNativeBridge + { + private bool initialized = false; + + /// + /// Initialize the Tech3C SDK + /// + public void Initialize(string clientId, string clientSecret, Tech3CConfig config) + { + Debug.LogWarning("[Tech3C] iOS platform is not yet supported. Please provide the native iOS framework."); + initialized = true; + } + + /// + /// Show authentication screen + /// + public void ShowAuth(IAuthCallback callback) + { + Debug.LogWarning("[Tech3C] ShowAuth is not implemented for iOS platform"); + callback?.OnAuthError(-1, "iOS platform not supported"); + } + + /// + /// Logout current user + /// + public void Logout(ILogoutCallback callback) + { + Debug.LogWarning("[Tech3C] Logout is not implemented for iOS platform"); + callback?.OnLogoutError(-1, "iOS platform not supported"); + } + + /// + /// Get user information + /// + public void GetUserInfo(IUserInfoCallback callback) + { + Debug.LogWarning("[Tech3C] GetUserInfo is not implemented for iOS platform"); + callback?.OnUserInfoError(-1, "iOS platform not supported"); + } + + /// + /// Check if user is logged in + /// + public bool IsLoggedIn() + { + Debug.LogWarning("[Tech3C] IsLoggedIn is not implemented for iOS platform"); + return false; + } + + /// + /// Get access token + /// + public string GetAccessToken() + { + Debug.LogWarning("[Tech3C] GetAccessToken is not implemented for iOS platform"); + return null; + } + + /// + /// Get refresh token + /// + public string GetRefreshToken() + { + Debug.LogWarning("[Tech3C] GetRefreshToken is not implemented for iOS platform"); + return null; + } + + /// + /// Get user ID + /// + public string GetUserId() + { + Debug.LogWarning("[Tech3C] GetUserId is not implemented for iOS platform"); + return null; + } + + /// + /// Get device ID + /// + public string GetDeviceId() + { + Debug.LogWarning("[Tech3C] GetDeviceId is not implemented for iOS platform"); + return null; + } + + /// + /// Get login time + /// + public long GetLoginTime() + { + Debug.LogWarning("[Tech3C] GetLoginTime is not implemented for iOS platform"); + return 0; + } + + /// + /// Get token expiry time + /// + public long GetTokenExpiry() + { + Debug.LogWarning("[Tech3C] GetTokenExpiry is not implemented for iOS platform"); + return 0; + } + + /// + /// Check if token is expired + /// + public bool IsTokenExpired() + { + Debug.LogWarning("[Tech3C] IsTokenExpired is not implemented for iOS platform"); + return false; + } + + /// + /// Set language + /// + public void SetLanguage(Language language) + { + Debug.LogWarning("[Tech3C] SetLanguage is not implemented for iOS platform"); + } + + /// + /// Set debug mode + /// + public void SetDebug(bool debug) + { + Debug.LogWarning("[Tech3C] SetDebug is not implemented for iOS platform"); + } + + /// + /// Cleanup resources + /// + public void Cleanup() + { + Debug.Log("[Tech3C] iOS bridge cleanup completed"); + initialized = false; + } + } +} diff --git a/Assets/Tech3C/Runtime/Tech3CiOSBridge.cs.meta b/Assets/Tech3C/Runtime/Tech3CiOSBridge.cs.meta new file mode 100644 index 0000000000..327d3972f3 --- /dev/null +++ b/Assets/Tech3C/Runtime/Tech3CiOSBridge.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 7199bd7af3b75294c84f9d6451df86d4 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Tech3C/Samples.meta b/Assets/Tech3C/Samples.meta new file mode 100644 index 0000000000..a7dd7ed56a --- /dev/null +++ b/Assets/Tech3C/Samples.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: a947977eda82ae34b85413ea17ef265a +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Tech3C/Samples/Tech3CSimpleDemo.cs b/Assets/Tech3C/Samples/Tech3CSimpleDemo.cs new file mode 100644 index 0000000000..0c609186bb --- /dev/null +++ b/Assets/Tech3C/Samples/Tech3CSimpleDemo.cs @@ -0,0 +1,473 @@ +using UnityEngine; +using UnityEngine.UI; +using UnityEngine.EventSystems; +using System; + +namespace Tech3C +{ + /// + /// Simple demo that creates UI programmatically + /// Just attach this script to any GameObject in your scene + /// + public class Tech3CSimpleDemo : MonoBehaviour + { + [Header("Configuration")] + public string clientId = "your_client_id"; + public string clientSecret = "your_client_secret"; + + private Canvas canvas; + private Text logText; + private AuthCallback authCallback; + private LogoutCallback logoutCallback; + + private GameObject dialogPanel; + private Text dialogText; + private Button dialogCloseButton; + + private void Start() + { + EnsureEventSystem(); + CreateUI(); + SetupCallbacks(); + + // Auto-initialize SDK + if (!string.IsNullOrEmpty(clientId) && !string.IsNullOrEmpty(clientSecret)) + { + Log($"Initializing Tech3C SDK...\n Client ID: {clientId}"); + Tech3CSDK.Instance.Initialize(clientId, clientSecret); + + if (Tech3CSDK.Instance.IsInitialized) + { + Log("✓ SDK Initialized Successfully!"); + } + else + { + Log("✗ Failed to initialize SDK"); + } + } + else + { + Log("⚠ Client ID and/or Client Secret not set. Please configure in Inspector."); + } + + Debug.Log("[Tech3C Demo] Tech3C SDK Simple Demo Started"); + } + + private void EnsureEventSystem() + { + if (EventSystem.current == null) + { + GameObject eventSystemGO = new GameObject("EventSystem"); + eventSystemGO.AddComponent(); + eventSystemGO.AddComponent(); + Debug.Log("[Tech3C Demo] EventSystem created"); + } + } + + private void CreateUI() + { + // Create Canvas + GameObject canvasGO = new GameObject("Tech3CDemoCanvas"); + canvasGO.transform.SetParent(transform); + canvas = canvasGO.AddComponent(); + canvas.renderMode = RenderMode.ScreenSpaceOverlay; + + // Configure CanvasScaler for responsive 16:9 ratio + CanvasScaler canvasScaler = canvasGO.AddComponent(); + canvasScaler.uiScaleMode = CanvasScaler.ScaleMode.ScaleWithScreenSize; + canvasScaler.referenceResolution = new Vector2(1920, 1080); // 16:9 ratio + canvasScaler.screenMatchMode = CanvasScaler.ScreenMatchMode.MatchWidthOrHeight; + canvasScaler.matchWidthOrHeight = 0.5f; // Balance between width and height + + canvasGO.AddComponent(); + + // Create Scroll Panel + GameObject scrollPanel = new GameObject("ScrollPanel"); + scrollPanel.transform.SetParent(canvasGO.transform, false); + RectTransform scrollPanelRect = scrollPanel.AddComponent(); + scrollPanelRect.anchorMin = Vector2.zero; + scrollPanelRect.anchorMax = Vector2.one; + scrollPanelRect.sizeDelta = Vector2.zero; + Image scrollPanelImage = scrollPanel.AddComponent(); + scrollPanelImage.color = new Color(0.2f, 0.2f, 0.2f, 1f); + + // Create ScrollRect + ScrollRect scrollRect = scrollPanel.AddComponent(); + scrollRect.horizontal = false; + scrollRect.vertical = true; + scrollRect.scrollSensitivity = 50f; + + // Create Content Panel + GameObject panel = new GameObject("Content"); + panel.transform.SetParent(scrollPanel.transform, false); + RectTransform panelRect = panel.AddComponent(); + panelRect.anchorMin = new Vector2(0, 1); + panelRect.anchorMax = new Vector2(1, 1); + panelRect.pivot = new Vector2(0.5f, 1f); + panelRect.sizeDelta = new Vector2(0, 0); + scrollRect.content = panelRect; + + // Create Vertical Layout Group + VerticalLayoutGroup layoutGroup = panel.AddComponent(); + layoutGroup.childControlHeight = false; + layoutGroup.childControlWidth = true; + layoutGroup.childForceExpandWidth = true; + layoutGroup.childForceExpandHeight = false; + layoutGroup.spacing = 15; + layoutGroup.padding = new RectOffset(40, 40, 40, 40); + ContentSizeFitter contentSizeFitter = panel.AddComponent(); + contentSizeFitter.verticalFit = ContentSizeFitter.FitMode.PreferredSize; + + // Create Title + CreateTextResponsive(panel.transform, "Tech3C SDK Demo", 28, TextAnchor.MiddleCenter, 60); + + // Create Separator + CreateTextResponsive(panel.transform, "--- Authentication ---", 18, TextAnchor.MiddleCenter, 30); + + // Create Show Auth Button + Button showAuthButton = CreateButtonResponsive(panel.transform, "Show Auth Screen", 70); + showAuthButton.onClick.AddListener(OnShowAuthClick); + + // Create Logout Button + Button logoutButton = CreateButtonResponsive(panel.transform, "Logout", 70); + logoutButton.onClick.AddListener(OnLogoutClick); + + // Create Log Text + CreateTextResponsive(panel.transform, "Logs:", 16, TextAnchor.MiddleLeft, 35); + logText = CreateTextResponsive(panel.transform, "Ready...", 16, TextAnchor.MiddleLeft, 400); + logText.color = Color.green; + + // Reposition panel + panelRect.anchoredPosition = Vector2.zero; + } + + private void SetupCallbacks() + { + // Auth callback + authCallback = new AuthCallback(); + authCallback.OnAuthSuccessEvent += (userId, password, accessToken, refreshToken, loginType, expiryTime) => + { + Debug.Log($"[Tech3C Demo] === AUTH SUCCESS CALLBACK FIRED ==="); + Debug.Log($"[Tech3C Demo] UserId: {userId}"); + Debug.Log($"[Tech3C Demo] Password: {password?.Substring(0, Math.Min(3, password?.Length ?? 0))}..."); + Debug.Log($"[Tech3C Demo] LoginType: {loginType}"); + Debug.Log($"[Tech3C Demo] AccessToken: {accessToken?.Substring(0, Math.Min(20, accessToken?.Length ?? 0))}..."); + Debug.Log($"[Tech3C Demo] RefreshToken: {refreshToken?.Substring(0, Math.Min(20, refreshToken?.Length ?? 0))}..."); + Debug.Log($"[Tech3C Demo] ExpiryTime: {expiryTime}"); + + Log($"✓ Auth Success!\n User ID: {userId}\n Password: {password}\n Login Type: {loginType}\n Token: {accessToken?.Substring(0, Math.Min(20, accessToken?.Length ?? 0))}..."); + + ShowAuthSuccessDialog(userId, password, loginType.ToString()); + }; + authCallback.OnAuthCancelledEvent += () => + { + Debug.Log($"[Tech3C Demo] === AUTH CANCELLED CALLBACK FIRED ==="); + Log("✗ Auth Cancelled by user"); + }; + authCallback.OnAuthErrorEvent += (errorCode, errorMessage) => + { + Debug.Log($"[Tech3C Demo] === AUTH ERROR CALLBACK FIRED ==="); + Debug.Log($"[Tech3C Demo] ErrorCode: {errorCode}"); + Debug.Log($"[Tech3C Demo] ErrorMessage: {errorMessage}"); + Log($"✗ Auth Error [{errorCode}]: {errorMessage}"); + }; + + // Logout callback + logoutCallback = new LogoutCallback(); + logoutCallback.OnLogoutSuccessEvent += () => + { + Debug.Log($"[Tech3C Demo] === LOGOUT SUCCESS CALLBACK FIRED ==="); + Log("✓ Logout Successful"); + }; + logoutCallback.OnLogoutErrorEvent += (errorCode, errorMessage) => + { + Debug.Log($"[Tech3C Demo] === LOGOUT ERROR CALLBACK FIRED ==="); + Debug.Log($"[Tech3C Demo] ErrorCode: {errorCode}"); + Debug.Log($"[Tech3C Demo] ErrorMessage: {errorMessage}"); + Log($"✗ Logout Error [{errorCode}]: {errorMessage}"); + }; + } + + #region Button Handlers + + private void OnInitializeClick(string newClientId, string newClientSecret) + { + if (string.IsNullOrEmpty(newClientId) || string.IsNullOrEmpty(newClientSecret)) + { + Log("✗ Client ID and Client Secret are required!"); + return; + } + + clientId = newClientId; + clientSecret = newClientSecret; + + Log($"Initializing Tech3C SDK...\n Client ID: {clientId}"); + Tech3CSDK.Instance.Initialize(clientId, clientSecret); + + if (Tech3CSDK.Instance.IsInitialized) + { + Log("✓ SDK Initialized Successfully!"); + } + else + { + Log("✗ Failed to initialize SDK"); + } + } + + private void OnShowAuthClick() + { + if (!Tech3CSDK.Instance.IsInitialized) + { + Log("✗ SDK not initialized. Click Initialize first!"); + return; + } + + Log("Opening Auth Screen..."); + Tech3CSDK.Instance.ShowAuth(authCallback); + } + + private void OnLogoutClick() + { + if (!Tech3CSDK.Instance.IsInitialized) + { + Log("✗ SDK not initialized."); + return; + } + + Log("Logging out..."); + Tech3CSDK.Instance.Logout(logoutCallback); + } + + #endregion + + #region UI Helper Methods + + private Button CreateButton(Transform parent, string text, float width, float height) + { + GameObject buttonGO = new GameObject(text.Replace(" ", "_")); + buttonGO.transform.SetParent(parent, false); + + RectTransform rect = buttonGO.AddComponent(); + rect.sizeDelta = new Vector2(width, height); + + Image image = buttonGO.AddComponent(); + image.color = new Color(0.3f, 0.5f, 0.8f, 1f); + + Button button = buttonGO.AddComponent