From d387565fb85ed5390df3e3729d94392c524e3b99 Mon Sep 17 00:00:00 2001 From: Le Duc Anh Date: Sat, 2 May 2026 21:22:17 +0700 Subject: [PATCH] Draw Transmit Way. Send Keep Alive package every 5 seconds --- Assets/PerfectWorld/Scene/Bootstrap.unity | 4 +- .../Network/CSNetwork/NetworkManager.cs | 13 +++ .../Network/CSNetwork/Protocols/keepalive.cs | 26 +++++ .../CSNetwork/Protocols/keepalive.cs.meta | 2 + .../PerfectWorld/Scripts/UI/Dialogs/DlgNPC.cs | 1 + .../Scripts/UI/WorldMap/DlgWorldMap.cs | 109 ++++++++++++++++-- Assets/Prefabs/UI/Map/DlgWorldMap.prefab | 77 +++++++++++++ 7 files changed, 220 insertions(+), 12 deletions(-) create mode 100644 Assets/PerfectWorld/Scripts/Network/CSNetwork/Protocols/keepalive.cs create mode 100644 Assets/PerfectWorld/Scripts/Network/CSNetwork/Protocols/keepalive.cs.meta diff --git a/Assets/PerfectWorld/Scene/Bootstrap.unity b/Assets/PerfectWorld/Scene/Bootstrap.unity index 450da71ea2..26c589fdce 100644 --- a/Assets/PerfectWorld/Scene/Bootstrap.unity +++ b/Assets/PerfectWorld/Scene/Bootstrap.unity @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:833a4e6a7e0e815ce2561d06a74cc92483952cd3b3812e09fc3c5b15d0029695 -size 303238 +oid sha256:329ce992197e8e90aa8ecc86ad5a2e9d0e4516ae41630b0e0fb4b68080d9ba75 +size 303602 diff --git a/Assets/PerfectWorld/Scripts/Network/CSNetwork/NetworkManager.cs b/Assets/PerfectWorld/Scripts/Network/CSNetwork/NetworkManager.cs index 4211b348da..879cd494e0 100644 --- a/Assets/PerfectWorld/Scripts/Network/CSNetwork/NetworkManager.cs +++ b/Assets/PerfectWorld/Scripts/Network/CSNetwork/NetworkManager.cs @@ -22,6 +22,7 @@ namespace CSNetwork private CancellationTokenSource? _cts; private Task? _receiveTask; private Task? _sendTask; + private Task? _keepAliveTask; private readonly ConcurrentQueue _sendQueue = new ConcurrentQueue(); private readonly object _streamLock = new object(); // Lock for stream access @@ -82,6 +83,7 @@ namespace CSNetwork _receiveTask = Task.Run(() => ProcessReceivedData(_cts.Token), _cts.Token); _sendTask = Task.Run(() => ProcessSendQueue(_cts.Token), _cts.Token); + _keepAliveTask = Task.Run(() => KeepAlive(_cts.Token), _cts.Token); } catch (Exception ex) { @@ -255,6 +257,17 @@ namespace CSNetwork _logger.Log(LogType.Info, "Send loop finished."); } + private async Task KeepAlive(CancellationToken token) + { + var keepAlivePack = new keepalive(); + keepAlivePack.code = (byte)Protocols.ProtocolType.PROTOCOL_KEEPALIVE; + while (!token.IsCancellationRequested) + { + await Task.Delay(5000, token); // send keep alive packet every 1000ms + Send(keepAlivePack); + } + } + private int _previousLength; // Internal task to read from network and process data private async Task ProcessReceivedData(CancellationToken token) diff --git a/Assets/PerfectWorld/Scripts/Network/CSNetwork/Protocols/keepalive.cs b/Assets/PerfectWorld/Scripts/Network/CSNetwork/Protocols/keepalive.cs new file mode 100644 index 0000000000..a3a691a977 --- /dev/null +++ b/Assets/PerfectWorld/Scripts/Network/CSNetwork/Protocols/keepalive.cs @@ -0,0 +1,26 @@ +using System; +using System.Collections.Generic; + +namespace CSNetwork.Protocols +{ + public class keepalive : Protocol + { + public byte code { get; set; } + public keepalive() : base(ProtocolType.PROTOCOL_KEEPALIVE) + { + + } + + public override Protocol Clone() => new keepalive { code = code }; + + public override void Marshal(OctetsStream os) + { + os.Write(code); + } + + public override void Unmarshal(OctetsStream os) + { + code = os.ReadByte(); + } + } +} \ No newline at end of file diff --git a/Assets/PerfectWorld/Scripts/Network/CSNetwork/Protocols/keepalive.cs.meta b/Assets/PerfectWorld/Scripts/Network/CSNetwork/Protocols/keepalive.cs.meta new file mode 100644 index 0000000000..a1c8bad1e2 --- /dev/null +++ b/Assets/PerfectWorld/Scripts/Network/CSNetwork/Protocols/keepalive.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: 872e7956373f84f2ea7610eadc366426 \ 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 4ecb7d13c1..014e6587ea 100644 --- a/Assets/PerfectWorld/Scripts/UI/Dialogs/DlgNPC.cs +++ b/Assets/PerfectWorld/Scripts/UI/Dialogs/DlgNPC.cs @@ -3469,6 +3469,7 @@ namespace BrewMonster.UI var pShow1 = m_pAUIManager.GetDialog("Win_WorldMap") as DlgWorldMap; pShow1.Show(true); pShow1.BuildTravelMap((uint)DATA_TYPE.DT_NPC_TRANSMIT_SERVICE, pData); + GetGameUIMan().EndNPCService(); //((CDlgWorldMap*)pShow1).BuildTravelMap(DT_NPC_TRANSMIT_SERVICE, pData); } // else if (idFunction == (int)SERVICE_TYPE.NPC_SKILL) diff --git a/Assets/PerfectWorld/Scripts/UI/WorldMap/DlgWorldMap.cs b/Assets/PerfectWorld/Scripts/UI/WorldMap/DlgWorldMap.cs index d1f0051a7e..1c8b6a3a9b 100644 --- a/Assets/PerfectWorld/Scripts/UI/WorldMap/DlgWorldMap.cs +++ b/Assets/PerfectWorld/Scripts/UI/WorldMap/DlgWorldMap.cs @@ -3,6 +3,8 @@ using BrewMonster.Network; using CSNetwork.Common; using CSNetwork.GPDataType; using ModelRenderer.Scripts.Common; +using ModelRenderer.Scripts.GameData; +using Unity.VisualScripting; using UnityEngine; using UnityEngine.UI; @@ -17,14 +19,13 @@ namespace BrewMonster.UI [SerializeField] private Button _closeButton; [Space(10)] - [Header("Transmit Points")] + [Header("Transmit Service")] [SerializeField] private RectTransform _transPointButtonParent; // this is the prefab for the trans point button [SerializeField] private WorldMapTransPoint _transPointButtonPrefab; + [SerializeField] private Image _trasmitWayImagePrefab; // this is the line to connect the trans point to the host player. - - - + private List _waypointImages = new(); // this is the list of waypoint images. private Dictionary _transPoints = new(); @@ -58,7 +59,7 @@ namespace BrewMonster.UI _closeButton.onClick.AddListener(OnCloseButtonClicked); } - +#region Override public override void Show(bool value) { base.Show(value); @@ -77,6 +78,12 @@ namespace BrewMonster.UI { transPoint.Value.gameObject.SetActive(false); } + + // clear the waypoint images + foreach(var waypointImage in _waypointImages) + { + waypointImage.gameObject.SetActive(false); + } } public override bool Render() @@ -84,12 +91,13 @@ namespace BrewMonster.UI UpdateHostPlayerPositionImage(); return base.Render(); } +#endregion - +#region Rendering Functions Vector3 _hostPlayerPosition; Quaternion _hostPlayerRotation; - private void UpdateHostPlayerPositionImage() + private void UpdateHostPlayerPositionImage() { if (_hostPlayerPositionImage == null) { @@ -129,6 +137,9 @@ namespace BrewMonster.UI int currentMapId = (int)GetDataPtr("WorldID"); foreach(var transPointPair in transPoints) { + // Only show the trans points for the current map. + if (transPointPair.Value.worldid != currentMapId) continue; + // check if the trans point is already created. If not, create it. if(_transPoints.TryGetValue(transPointPair.Key, out transPointObject)) { @@ -147,6 +158,11 @@ namespace BrewMonster.UI } } + private void ShowTransWays() + { + + } + private bool TryGetHostPlayerPosition(out Vector3 hostPlayerPosition, out Quaternion hostPlayerRotation) { hostPlayerPosition = Vector3.zero; @@ -173,6 +189,7 @@ namespace BrewMonster.UI // PAUIIMAGEPICTURE pImage = (PAUIIMAGEPICTURE)GetDlgItem("WorldMapTravel"); // prepare the necessary information + // This set the type of the map to show (DT_NPC_TRANSMIT_SERVICE or DT_TRANSMITSCROLL_ESSENCE) SetData(dwType); // CECMapDlgsMgr *pMgr = GetGameUIMan()->GetMapDlgsMgr(); @@ -185,11 +202,12 @@ namespace BrewMonster.UI var serviceData = (NPC_TRANSMIT_SERVICE)pData; var worldInstanceName = UnityGameSession.Instance.GetWorldInstanceName((int)serviceData.id); - // save the worldid + // save the worldid so the ShowTranPoints() function know which map to show. + //TODO: We have to get the worldid from the service data. + worldid = 161;//(int)serviceData.worldid; SetDataPtr(worldid, "WorldID"); - - // pMgr->LoadMapTexture(m_pA3DDevice, pImage, CECMapDlgsMgr::MAP_TYPE_TRANSMIT); ShowTranPoints(); + DrawWayPoints(GetGameUIMan().m_pCurNPCEssence, worldid, true); } else if(dwType == (uint)DATA_TYPE.DT_TRANSMITSCROLL_ESSENCE) { @@ -205,6 +223,75 @@ namespace BrewMonster.UI } } + // This function onriginally is in the CECMapDlgsMgr class. + // However I moved it here since we need to draw the waypoints on Unity Canvas. + /// Draw the waypoints from the current NPC Position to all the possible Transmit targets. + public void DrawWayPoints(NPC_ESSENCE? pNPC, int iWorldID, bool bDrawLines) + { + if (!pNPC.HasValue && bDrawLines) return; // only draw lines if the NPC is not null and bDrawLines is true. + DATA_TYPE dataType = DATA_TYPE.DT_INVALID; + + int idCur = (int)pNPC.Value.id_to_discover; + + var dataPtr = ElementDataManProvider.GetElementDataMan().get_data_ptr(pNPC.Value.id_transmit_service, ID_SPACE.ID_SPACE_ESSENCE, ref dataType); + + if (dataType == DATA_TYPE.DT_NPC_TRANSMIT_SERVICE) + { + var serviceData = (NPC_TRANSMIT_SERVICE)dataPtr; + + // start point is current location of the player or NPC + Vector3 startPoint = Vector3.zero; + bool hasHostPlayerPosition = TryGetHostPlayerPosition(out startPoint, out var startRotation); + CECGameUIMan pGameUI = CECUIManager.Instance.GetInGameUIMan(); + var transPoints = pGameUI.GetMapDlgsMgr().m_transPoints; + if (transPoints.TryGetValue(idCur, out var transPoint)) + { + startPoint = new Vector3(transPoint.vecPos.x, transPoint.vecPos.y, transPoint.vecPos.z); + } + + int targetNum = GetTransmitTargetsNum(serviceData); + TRANS_POINT traget; + Vector2 anchoredTargetPosition = Vector2.zero; + Image waypointImage = null; + for (int i = 0; i < targetNum; i++) + { + if (transPoints.TryGetValue(serviceData.targets[i].idTarget, out traget)) + { + // instantiate the waypoint image + if (i < _waypointImages.Count) + { + waypointImage = _waypointImages[i]; + } + else + { + waypointImage = Instantiate(_trasmitWayImagePrefab, _transPointButtonParent); + _waypointImages.Add(waypointImage); + } + + var rectTransform = waypointImage.rectTransform; + rectTransform.anchoredPosition = new Vector2(startPoint.x / _positionFactor, startPoint.z / _positionFactor); + anchoredTargetPosition = new Vector2(traget.vecPos.x / _positionFactor, traget.vecPos.z / _positionFactor); + Vector2 direction = anchoredTargetPosition - rectTransform.anchoredPosition; + float distance = direction.magnitude; + rectTransform.sizeDelta = new Vector2(distance, 2); + float angle = Mathf.Atan2(direction.y, direction.x) * Mathf.Rad2Deg; + rectTransform.localEulerAngles = new Vector3(0, 0, angle); + waypointImage.gameObject.SetActive(true); + } + } + } + } + + public static int GetTransmitTargetsNum(NPC_TRANSMIT_SERVICE pService) + { + int num_targets = 0; + num_targets = Mathf.Max(0, pService.num_targets); + num_targets = Mathf.Min(num_targets, pService.targets.Length); + return num_targets; + } +#endregion + +#region UI Event Handlers /// /// When user click on the map texture. /// We will calculate the world coordinates from the local cursor position. Then move the host player to the world coordinates. @@ -235,6 +322,7 @@ namespace BrewMonster.UI { CloseDialogue(); } +#endregion } @@ -295,6 +383,7 @@ namespace BrewMonster.UI } } } + } public class TransWay diff --git a/Assets/Prefabs/UI/Map/DlgWorldMap.prefab b/Assets/Prefabs/UI/Map/DlgWorldMap.prefab index a7d1b364ec..08a2cba55b 100644 --- a/Assets/Prefabs/UI/Map/DlgWorldMap.prefab +++ b/Assets/Prefabs/UI/Map/DlgWorldMap.prefab @@ -587,6 +587,7 @@ MonoBehaviour: _closeButton: {fileID: 8858186809287203567} _transPointButtonParent: {fileID: 7169122999130120872} _transPointButtonPrefab: {fileID: 2537562987844392478} + _trasmitWayImagePrefab: {fileID: 8731835385296660332} --- !u!1 &8308536083041954008 GameObject: m_ObjectHideFlags: 0 @@ -772,6 +773,7 @@ RectTransform: - {fileID: 1510574663178069641} - {fileID: 9142400375056319150} - {fileID: 8205908652006291613} + - {fileID: 4100535739032287444} m_Father: {fileID: 7323734624486819451} m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} m_AnchorMin: {x: 0.5, y: 0.5} @@ -832,3 +834,78 @@ MonoBehaviour: _hostPlayerPositionImage: {fileID: 5906545349664091413} dlgWorldMap: {fileID: 135853640611757204} _enableClickToMove: 1 +--- !u!1 &9069447884487553244 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 4100535739032287444} + - component: {fileID: 7327245972570987933} + - component: {fileID: 8731835385296660332} + m_Layer: 0 + m_Name: TransmitWayImage + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 0 +--- !u!224 &4100535739032287444 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 9069447884487553244} + 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: 7169122999130120872} + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0.5, y: 0.518} + m_AnchorMax: {x: 0.5, y: 0.518} + m_AnchoredPosition: {x: -2.5000153, y: -0.000015258789} + m_SizeDelta: {x: 5, y: 5} + m_Pivot: {x: 0, y: 0.5} +--- !u!222 &7327245972570987933 +CanvasRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 9069447884487553244} + m_CullTransparentMesh: 1 +--- !u!114 &8731835385296660332 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 9069447884487553244} + 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: 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