Implement deserialize logic for data of new, complete, giveup task
This commit is contained in:
@@ -1,5 +1,6 @@
|
||||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
using UnityEngine;
|
||||
using BrewMonster.Network;
|
||||
using BrewMonster.Scripts.Task;
|
||||
using CSNetwork.GPDataType;
|
||||
@@ -394,6 +395,28 @@ namespace BrewMonster.Scripts.Task
|
||||
}
|
||||
}
|
||||
// bool CanShowInExclusiveUI (TaskInterface* pTask, unsigned long ulCurTime) const;
|
||||
#if !TASK_TEMPL_EDITOR
|
||||
// RecursiveCheckPunchMonster(const ATaskTempl* pTempl)
|
||||
// 递归检查“沙包怪”并弹提示 // English: Recursively check "punch bag" monster and show prompt
|
||||
// NOTE: The original C++ checks monster essence switches (MCS_SUMMONER_ATTACK_ONLY + MCS_RECORD_DPS_RANK)
|
||||
// and calls ShowPunchBagMessage(). The Unity port doesn't currently expose elementdataman/monster essence,
|
||||
// so this is a safe no-op placeholder that preserves call sites and recursion shape.
|
||||
private static void RecursiveCheckPunchMonster(ATaskTempl pTempl)
|
||||
{
|
||||
if (pTempl == null) return;
|
||||
|
||||
// TODO: Port elementdataman lookup + MONSTER_ESSENCE combined_switch checks and call:
|
||||
// pTask.ShowPunchBagMessage(false,false,monsterId,0,0);
|
||||
|
||||
var child = pTempl.m_pFirstChild;
|
||||
while (child != null)
|
||||
{
|
||||
RecursiveCheckPunchMonster(child);
|
||||
child = child.m_pNextSibling;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
public void OnServerNotify(
|
||||
TaskInterface pTask,
|
||||
ActiveTaskEntry pEntry,
|
||||
@@ -424,7 +447,7 @@ namespace BrewMonster.Scripts.Task
|
||||
int iIndex = pKilledPlayer.index;
|
||||
if (iIndex < TaskInterfaceConstants.MAX_MONSTER_WANTED)
|
||||
{
|
||||
pEntry.m_wMonsterNum[iIndex] = pKilledPlayer.player_num;
|
||||
pEntry.SetMonsterNum(iIndex, pKilledPlayer.player_num);
|
||||
}
|
||||
}
|
||||
break;
|
||||
@@ -447,7 +470,7 @@ namespace BrewMonster.Scripts.Task
|
||||
|
||||
if (mw.m_ulMonsterTemplId == pKilled.monster_id)
|
||||
{
|
||||
pEntry.m_wMonsterNum[i] = pKilled.monster_num;
|
||||
pEntry.SetMonsterNum((int)i, pKilled.monster_num);
|
||||
|
||||
if (pKilled.dps > 0 && pKilled.dph > 0)
|
||||
{
|
||||
@@ -459,12 +482,60 @@ namespace BrewMonster.Scripts.Task
|
||||
|
||||
break;
|
||||
case TaskTemplConstants.TASK_SVR_NOTIFY_NEW:
|
||||
var svr_new_task = GPDataTypeHelper.FromBytes<svr_new_task>(pBuf);
|
||||
if (svr_new_task.valid_size((int)sz))
|
||||
// Follow C++ logic: valid_size checks the buffer size before deserializing
|
||||
// In C++, pNotify is a pointer to the structure, so valid_size can read sub_tags.sz directly
|
||||
// In C#, we need to read sub_tags.sz from the buffer first, then validate
|
||||
|
||||
// Calculate base size: task_notify_base (3) + cur_time (4) + cap_task (4) = 11
|
||||
int baseSzNew = Marshal.SizeOf<task_notify_base>() + 8;
|
||||
if (sz <= (uint)baseSzNew)
|
||||
{
|
||||
BMLogger.LogError($" [TASK_SVR_NOTIFY_NEW] the size of byte not meet !!!");
|
||||
Debug.Log($" [ATaskTempl] TASK_SVR_NOTIFY_NEW: buffer too small, need more than {baseSzNew} bytes, got {sz}");
|
||||
break;
|
||||
}
|
||||
|
||||
// Read sub_tags.sz from buffer to validate size (matching C++ valid_size logic)
|
||||
// Offset to sub_tags: baseSzNew = 11
|
||||
// sub_tags layout: sub_task/state (2 bytes) + sz (1 byte) = 3 bytes minimum
|
||||
if ((int)sz < baseSzNew + 3)
|
||||
{
|
||||
Debug.Log($" [ATaskTempl] TASK_SVR_NOTIFY_NEW: buffer too small to read sub_tags.sz");
|
||||
break;
|
||||
}
|
||||
|
||||
byte subTagsSzNew = pBuf[baseSzNew + 2]; // Read sz field from sub_tags (after sub_task/state which is 2 bytes)
|
||||
|
||||
// Manually implement valid_size check (matching C++ logic)
|
||||
// C++: return sub_tags.valid_size(sz - base_sz);
|
||||
// sub_tags.valid_size checks: get_size() == _sz, where get_size() = sz + 3
|
||||
int subTagsSize = subTagsSzNew + 3; // get_size() = sz + 3
|
||||
if (subTagsSize != (int)sz - baseSzNew)
|
||||
{
|
||||
Debug.Log($" [TASK_SVR_NOTIFY_NEW] the size of byte not meet !!! expected sub_tags size {subTagsSize}, got {sz - baseSzNew}");
|
||||
break;
|
||||
}
|
||||
|
||||
// Now safe to deserialize - buffer size matches expected size
|
||||
// Marshal.SizeOf returns size with full MAX_SUB_TAGS array, but actual data is smaller
|
||||
int fixedSizeNew = Marshal.SizeOf<svr_new_task>();
|
||||
svr_new_task svr_new_task;
|
||||
|
||||
if (sz < (uint)fixedSizeNew)
|
||||
{
|
||||
// Buffer is smaller than fixed structure size, create padded buffer for safe deserialization
|
||||
byte[] paddedBufNew = new byte[fixedSizeNew];
|
||||
Array.Copy(pBuf, paddedBufNew, (int)sz);
|
||||
// Zero-fill the rest (tags array will be zero-filled, which is safe)
|
||||
|
||||
svr_new_task = GPDataTypeHelper.FromBytes<svr_new_task>(paddedBufNew);
|
||||
// Restore the original sub_tags.sz value since padding might have corrupted it
|
||||
svr_new_task.sub_tags.sz = subTagsSzNew;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Buffer is large enough for fixed structure size
|
||||
svr_new_task = GPDataTypeHelper.FromBytes<svr_new_task>(pBuf);
|
||||
}
|
||||
pLst = pTask.GetActiveTaskList();
|
||||
svr_new_task.get_data(
|
||||
ref ulTime,
|
||||
@@ -472,7 +543,9 @@ namespace BrewMonster.Scripts.Task
|
||||
ref sub_tags
|
||||
);
|
||||
|
||||
GetTaskTemplMan().RemoveActiveStorageTask(pStorage, m_FixedData.m_ID);
|
||||
// NOTE: Disabled for now due to an ambiguous call error being reported by the Unity compiler in this project setup.
|
||||
// TODO: Re-enable once the underlying duplicate/assembly ambiguity is resolved.
|
||||
// GetTaskTemplMan().RemoveActiveStorageTask(pStorage, m_FixedData.m_ID);
|
||||
|
||||
if (sub_tags.sub_task > 0)
|
||||
{
|
||||
@@ -494,7 +567,7 @@ namespace BrewMonster.Scripts.Task
|
||||
pSub,
|
||||
ref sub_tags,
|
||||
new TaskGlobalData(),
|
||||
0);
|
||||
0xFF);
|
||||
|
||||
if (m_FixedData.m_lAvailFrequency != (int)TaskAwardFreq.enumTAFNormal &&
|
||||
!m_FixedData.m_bAccountTaskLimit && !m_FixedData.m_bRoleTaskLimit)
|
||||
@@ -526,8 +599,60 @@ namespace BrewMonster.Scripts.Task
|
||||
|
||||
break;
|
||||
case TaskTemplConstants.TASK_SVR_NOTIFY_COMPLETE:
|
||||
var svr_task_complete = GPDataTypeHelper.FromBytes<svr_task_complete>(pBuf);
|
||||
if (svr_task_complete.valid_size((int)sz)) break;
|
||||
// Follow C++ logic: valid_size checks the buffer size before deserializing
|
||||
// In C++, pNotify is a pointer to the structure, so valid_size can read sub_tags.sz directly
|
||||
// In C#, we need to read sub_tags.sz from the buffer first, then validate
|
||||
|
||||
// Calculate base size: task_notify_base (3) + cur_time (4) = 7
|
||||
int baseSz = Marshal.SizeOf<task_notify_base>() + 4;
|
||||
if (sz <= (uint)baseSz)
|
||||
{
|
||||
Debug.Log($" [ATaskTempl] TASK_SVR_NOTIFY_COMPLETE: buffer too small, need more than {baseSz} bytes, got {sz}");
|
||||
break;
|
||||
}
|
||||
|
||||
// Read sub_tags.sz from buffer to validate size (matching C++ valid_size logic)
|
||||
// Offset to sub_tags: baseSz = 7
|
||||
// sub_tags layout: sub_task/state (2 bytes) + sz (1 byte) = 3 bytes minimum
|
||||
if ((int)sz < baseSz + 3)
|
||||
{
|
||||
Debug.Log($" [ATaskTempl] TASK_SVR_NOTIFY_COMPLETE: buffer too small to read sub_tags.sz");
|
||||
break;
|
||||
}
|
||||
|
||||
byte subTagsSz = pBuf[baseSz + 2]; // Read sz field from sub_tags (after sub_task/state which is 2 bytes)
|
||||
|
||||
// Manually implement valid_size check (matching C++ logic)
|
||||
// C++: return sub_tags.valid_size(sz - base_sz);
|
||||
// sub_tags.valid_size checks: get_size() == _sz, where get_size() = sz + 3
|
||||
int subTagsSizeComplete = subTagsSz + 3; // get_size() = sz + 3
|
||||
if (subTagsSizeComplete != (int)sz - baseSz)
|
||||
{
|
||||
Debug.Log($" [TASK_SVR_NOTIFY_COMPLETE] the size of byte not meet !!! expected sub_tags size {subTagsSizeComplete}, got {sz - baseSz}");
|
||||
break;
|
||||
}
|
||||
|
||||
// Now safe to deserialize - buffer size matches expected size
|
||||
// Marshal.SizeOf returns size with full MAX_SUB_TAGS array, but actual data is smaller
|
||||
int fixedSize = Marshal.SizeOf<svr_task_complete>();
|
||||
svr_task_complete svr_task_complete;
|
||||
|
||||
if (sz < (uint)fixedSize)
|
||||
{
|
||||
// Buffer is smaller than fixed structure size, create padded buffer for safe deserialization
|
||||
byte[] paddedBuf = new byte[fixedSize];
|
||||
Array.Copy(pBuf, paddedBuf, (int)sz);
|
||||
// Zero-fill the rest (tags array will be zero-filled, which is safe)
|
||||
|
||||
svr_task_complete = GPDataTypeHelper.FromBytes<svr_task_complete>(paddedBuf);
|
||||
// Restore the original sub_tags.sz value since padding might have corrupted it
|
||||
svr_task_complete.sub_tags.sz = subTagsSz;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Buffer is large enough for fixed structure size
|
||||
svr_task_complete = GPDataTypeHelper.FromBytes<svr_task_complete>(pBuf);
|
||||
}
|
||||
svr_task_complete.get_data(
|
||||
ref ulTime,
|
||||
ref sub_tags
|
||||
@@ -537,7 +662,7 @@ namespace BrewMonster.Scripts.Task
|
||||
|
||||
if (!pEntry.IsSuccess())
|
||||
{
|
||||
#if TASK_TEMPL_EDITOR
|
||||
#if !TASK_TEMPL_EDITOR
|
||||
RecursiveCheckPunchMonster(this);
|
||||
#endif
|
||||
}
|
||||
@@ -573,7 +698,8 @@ namespace BrewMonster.Scripts.Task
|
||||
break;
|
||||
case TaskTemplConstants.TASK_SVR_NOTIFY_GIVE_UP:
|
||||
pLst = pTask.GetActiveTaskList();
|
||||
pLst.ClearTask(pTask, pEntry, false);
|
||||
// Use simplified removal to keep list consistent in the managed port
|
||||
pLst.RemoveEntry(pEntry);
|
||||
|
||||
// TODO: Log task give up
|
||||
// if (m_bDisplayInTitleTaskUI) TaskInterface::UpdateTitleUI(m_ID);
|
||||
@@ -805,9 +931,9 @@ namespace BrewMonster.Scripts.Task
|
||||
// ����ﵽ����
|
||||
bool bReachLimit = false;
|
||||
if (m_bHidden)
|
||||
bReachLimit = pList.m_uTopHideTaskCount >= TASK_HIDDEN_COUNT;
|
||||
bReachLimit = pList.m_uTopHideTaskCount >= TASK_.TASK_HIDDEN_COUNT;
|
||||
else if (m_bDisplayInTitleTaskUI)
|
||||
bReachLimit = bReachLimit || pList.m_uTitleTaskCount >= TASK_TITLE_TASK_COUNT;
|
||||
bReachLimit = bReachLimit || pList.m_uTitleTaskCount >= TASK_.TASK_TITLE_TASK_COUNT;
|
||||
else
|
||||
bReachLimit = bReachLimit || pList.m_uTopShowTaskCount >= pList.GetMaxSimultaneousCount();
|
||||
|
||||
@@ -1591,8 +1717,54 @@ namespace BrewMonster.Scripts.Task
|
||||
TaskGlobalData pGlobal,
|
||||
byte uParentIndex)
|
||||
{
|
||||
// TODO: implement full logic when ActiveTaskList/ActiveTaskEntry and TaskInterface APIs are available
|
||||
return null;
|
||||
// 最小实现:保证接任务/放弃任务会真实改变 ActiveTaskList,从而驱动任务面板刷新
|
||||
// English: Minimal implementation: make NEW/GIVEUP actually mutate ActiveTaskList so UI can refresh.
|
||||
if (pTask == null || pList == null) return null;
|
||||
|
||||
// Already has task
|
||||
var existed = pList.GetEntry(m_FixedData.m_ID);
|
||||
if (existed != null && existed.m_ID != 0) return existed;
|
||||
|
||||
if (pList.m_uTaskCount >= TaskInterfaceConstants.TASK_ACTIVE_LIST_MAX_LEN) return null;
|
||||
|
||||
int insertIndex = pList.m_uTaskCount;
|
||||
ActiveTaskEntry entry = pEntry ?? new ActiveTaskEntry();
|
||||
|
||||
// Fill fixed data (top-level by default)
|
||||
entry.m_ID = (ushort)m_FixedData.m_ID;
|
||||
entry.m_ulTemplAddr = m_FixedData.m_ID; // non-zero marker; template is resolved by ID in GetTempl()
|
||||
entry.m_ulTaskTime = ulCurTime;
|
||||
|
||||
entry.m_ParentIndex = (char)0xff;
|
||||
entry.m_PrevSblIndex = (char)0xff;
|
||||
entry.m_NextSblIndex = (char)0xff;
|
||||
entry.m_ChildIndex = (char)0xff;
|
||||
|
||||
entry.m_uState = (char)0;
|
||||
entry.SetSuccess(); // default success, may be cleared by later checks
|
||||
|
||||
if (ulCaptainTask != 0)
|
||||
{
|
||||
entry.m_uCapTaskId = (ushort)ulCaptainTask;
|
||||
entry.m_ulCapTemplAddr = ulCaptainTask; // marker only
|
||||
}
|
||||
else
|
||||
{
|
||||
entry.m_uCapTaskId = 0;
|
||||
entry.m_ulCapTemplAddr = 0;
|
||||
}
|
||||
|
||||
// Clear union buffer
|
||||
if (entry.m_BufData != null)
|
||||
Array.Clear(entry.m_BufData, 0, entry.m_BufData.Length);
|
||||
|
||||
// Insert and update list count
|
||||
pList.m_TaskEntries[insertIndex] = entry;
|
||||
pList.m_uTaskCount = (byte)(insertIndex + 1);
|
||||
|
||||
// Recount for UI and budget checks
|
||||
pList.RecountTaskCounters();
|
||||
return entry;
|
||||
|
||||
/*ActiveTaskEntry* aEntries = pList->m_TaskEntries;
|
||||
if (!pEntry) pEntry = aEntries + pList->m_uTaskCount;
|
||||
@@ -1814,6 +1986,29 @@ namespace BrewMonster.Scripts.Task
|
||||
int nChoice,
|
||||
ref task_sub_tags pSubTag)
|
||||
{
|
||||
// 最小实现(客户端):
|
||||
// - 记录到 FinishedTaskList(用于前置任务/可重复接判断)
|
||||
// - 从 ActiveTaskList 移除该任务条目,从而驱动任务面板刷新
|
||||
// English minimal client port:
|
||||
// - Record into FinishedTaskList (prerequisites/redo checks)
|
||||
// - Remove entry from ActiveTaskList to refresh UI lists
|
||||
if (pTask == null || pList == null || pEntry == null) return;
|
||||
|
||||
bool success = pEntry.IsSuccess() && !pEntry.IsGiveUp();
|
||||
|
||||
// Only record on top-level templates (same as C++: !m_pParent && m_bNeedRecord)
|
||||
if (m_pParent == null && m_FixedData.m_bNeedRecord)
|
||||
{
|
||||
if (pTask is CECTaskInterface cec)
|
||||
{
|
||||
cec.RecordFinishedTask(m_FixedData.m_ID, success);
|
||||
}
|
||||
}
|
||||
|
||||
// Remove from active list (C++ RecursiveAward clears children + removes current entry)
|
||||
pList.RemoveEntry(pEntry);
|
||||
return;
|
||||
|
||||
// TODO : implement full logic when ActiveTaskList/ActiveTaskEntry and TaskInterface APIs are available
|
||||
/*{
|
||||
char log[1024];
|
||||
|
||||
Reference in New Issue
Block a user