using System; using System.Linq; using System.Runtime.InteropServices; using CSNetwork.GPDataType; namespace BrewMonster.Scripts.Task { [StructLayout(LayoutKind.Sequential, Pack = 1)] public struct task_sub_tags { // private const int MAX_SUB_TAGS = 32; // union // { // unsigned short sub_task; // unsigned char state; // }; // IMPORTANT: union public ushort sub_task; public byte state { get { return (byte)(sub_task & 0xFF); } set { sub_task = (ushort)((sub_task & 0xFF00) | value); } } public byte sz; [MarshalAs(UnmanagedType.ByValArray, SizeConst = TaskTemplConstants.MAX_SUB_TAGS)] public byte[] tags; public byte cur_index; // for temporary use, dont take into account public int get_size() { return sz + 3; } public bool valid_size(int _sz) { if (_sz < 3) return false; return get_size() == _sz; } }; [StructLayout(LayoutKind.Sequential, Pack = 1)] public struct task_notify_base { public byte reason; public ushort task; }; [StructLayout(LayoutKind.Sequential, Pack = 1)] public struct svr_monster_killed { public task_notify_base baseObj; public uint monster_id; public ushort monster_num; public int dps; public int dph; } [StructLayout(LayoutKind.Sequential, Pack = 1)] public struct svr_player_killed { public task_notify_base baseObj; public ushort index; public ushort player_num; } [StructLayout(LayoutKind.Sequential, Pack = 1)] public struct svr_task_err_code { public task_notify_base baseObj; public uint err_code; }; [StructLayout(LayoutKind.Sequential, Pack = 1)] public struct StorageTaskList { // unsigned short m_Storages[TASK_STORAGE_COUNT][TASK_STORAGE_LEN]; // NOTE: 2D array flattened [MarshalAs(UnmanagedType.ByValArray, SizeConst = TaskTemplConstants.TASK_STORAGE_COUNT * TaskTemplConstants.TASK_STORAGE_LEN)] public ushort[] m_Storages; // union { // unsigned short m_StoragesTaskSetCount[TASK_STORAGE_COUNT]; // unsigned short m_StoragesRefreshCount[TASK_STORAGE_COUNT]; // }; // NOTE: union [MarshalAs(UnmanagedType.ByValArray, SizeConst = TaskTemplConstants.TASK_STORAGE_COUNT)] public ushort[] m_StoragesTaskSetCount; // [MarshalAs(UnmanagedType.ByValArray, SizeConst = TaskTemplConstants.TASK_STORAGE_COUNT)] public ushort[] m_StoragesRefreshCount { get => m_StoragesTaskSetCount; set { m_StoragesTaskSetCount = value; } } [MarshalAs(UnmanagedType.ByValArray, SizeConst = TaskTemplConstants.TASK_STORAGE_COUNT)] public uint[] m_StoragesRefreshTime; [MarshalAs(UnmanagedType.ByValArray, SizeConst = TaskTemplConstants.TASK_STORAGE_COUNT)] public byte[] m_StoragesReceivePerDay; public void RemoveAll() { for (int i = 0; i < TaskTemplConstants.TASK_STORAGE_COUNT; i++) { for (int j = 0; j < TaskTemplConstants.TASK_STORAGE_LEN; j++) { m_Storages[i * TaskTemplConstants.TASK_STORAGE_LEN + j] = 0; } m_StoragesTaskSetCount[i] = 0; m_StoragesRefreshCount[i] = 0; m_StoragesRefreshTime[i] = 0; m_StoragesReceivePerDay[i] = 0; } } public void ReadByte(byte[] data) { int offset = 0; for (int i=0; i < TaskTemplConstants.TASK_STORAGE_COUNT; i++) { for (int j=0; j < TaskTemplConstants.TASK_STORAGE_LEN; j++) { m_Storages[i * TaskTemplConstants.TASK_STORAGE_LEN + j] = BitConverter.ToUInt16(data, (i * TaskTemplConstants.TASK_STORAGE_LEN + j) * 2); } } offset += TaskTemplConstants.TASK_STORAGE_COUNT * TaskTemplConstants.TASK_STORAGE_LEN * 2; // union for (int i=0; i < TaskTemplConstants.TASK_STORAGE_COUNT; i++) { m_StoragesTaskSetCount[i] = BitConverter.ToUInt16(data, offset + i * 2); // m_StoragesRefreshCount[i] = BitConverter.ToUInt16(data, offset + i * 2); } offset += TaskTemplConstants.TASK_STORAGE_COUNT * 2; for (int i=0; i < TaskTemplConstants.TASK_STORAGE_COUNT; i++) { m_StoragesRefreshTime[i] = BitConverter.ToUInt32(data, offset + i * 4); } offset += TaskTemplConstants.TASK_STORAGE_COUNT * 4; for (int i=0; i < TaskTemplConstants.TASK_STORAGE_COUNT; i++) { m_StoragesReceivePerDay[i] = data[offset + i]; } } } [ StructLayout(LayoutKind.Sequential, Pack = 1)] public struct svr_new_task { public task_notify_base baseObj; public uint cur_time; public uint cap_task; // In C++ use to store ActiveTaskEntry pointer's address -> In C#, we just store the task ID public task_sub_tags sub_tags; // public ActiveTaskEntry cap_task_entry; // public void set_data( uint _cur_time, uint _cap_task, task_sub_tags _sub_tags) { cur_time = _cur_time; cap_task = _cap_task; // memcpy(&sub_tags, &_sub_tags, _sub_tags.get_size()); sub_tags = _sub_tags; } public void get_data( ref uint _cur_time, ref uint _cap_task, ref task_sub_tags _sub_tags) { _cur_time = cur_time; _cap_task = cap_task; // memcpy(&_sub_tags, &sub_tags, sub_tags.get_size()); _sub_tags = sub_tags; } // inline size_t get_size() const { return sizeof(task_notify_base) + 8 + sub_tags.get_size(); } public int get_size() { return Marshal.SizeOf() + 8 + sub_tags.get_size(); } public bool valid_size(int sz) { int base_sz = Marshal.SizeOf() + 8; if (sz <= base_sz) return false; return sub_tags.valid_size(sz - base_sz); } } [ StructLayout(LayoutKind.Sequential, Pack = 1)] public struct svr_treasure_map { public task_notify_base baseObj; public int treasure_index; } public struct tm { public int tm_sec; /* seconds after the minute [0-60] */ public int tm_min; /* minutes after the hour [0-59] */ public int tm_hour; /* hours since midnight [0-23] */ public int tm_mday; /* day of the month [1-31] */ public int tm_mon; /* months since January [0-11] */ public int tm_year; /* years since 1900 */ public int tm_wday; /* days since Sunday [0-6] */ public int tm_yday; /* days since January 1 [0-365] */ public int tm_isdst; /* Daylight Savings Time flag */ public long tm_gmtoff; /* offset from UTC in seconds */ public byte tm_zone; /* timezone abbreviation */ }; [StructLayout(LayoutKind.Explicit)] public struct TaskGlobalData { [FieldOffset(0)] [MarshalAs(UnmanagedType.ByValArray, SizeConst = TaskInterfaceConstants.TASK_GLOBAL_DATA_SIZE)] public byte[] buf; [FieldOffset(0)] public ulong m_ulReceiverNum; [FieldOffset(8)] public ulong m_ulRcvUpdateTime; void AddRevNum() { m_ulReceiverNum++; } void CheckRcvUpdateTime(uint ulCurTime, int nFrequency) { // TODO: implement time-based receiver number reset logic // if (nFrequency == TaskCompletionMethod.enumTAFNormal || m_ulRcvUpdateTime == 0) // return; // // tm tmCur = *localtime((time_t*)&ulCurTime); // tm tmRcv = *localtime((time_t*)&m_ulRcvUpdateTime); // // if (nFrequency == enumTAFEachDay) // { // if (tmCur.tm_year != tmRcv.tm_year || tmCur.tm_yday != tmRcv.tm_yday) // m_ulReceiverNum = 0; // } // else if (nFrequency == enumTAFEachWeek) // { // if (!_is_same_week(&tmCur, &tmRcv, ulCurTime, m_ulRcvUpdateTime)) // m_ulReceiverNum = 0; // } // else if (nFrequency == enumTAFEachMonth) // { // if (tmCur.tm_year != tmRcv.tm_year || tmCur.tm_mon != tmRcv.tm_mon) // m_ulReceiverNum = 0; // } // else if (nFrequency == enumTAFEachYear) // { // if (tmCur.tm_year != tmRcv.tm_year) // m_ulReceiverNum = 0; // } } } [ StructLayout(LayoutKind.Sequential, Pack = 1)] public struct svr_task_complete { public task_notify_base baseObj; public uint cur_time; public task_sub_tags sub_tags; public void set_data( uint _cur_time, task_sub_tags _sub_tags ) { cur_time = _cur_time; // memcpy(&sub_tags, &_sub_tags, _sub_tags.get_size()); sub_tags = _sub_tags; } public void get_data( ref uint _cur_time, ref task_sub_tags _sub_tags) { _cur_time = cur_time; // memcpy(&_sub_tags, &sub_tags, sub_tags.get_size()); _sub_tags = sub_tags; } public int get_size() { return Marshal.SizeOf() + 4 + sub_tags.get_size(); } public bool valid_size(int sz) { int base_sz = Marshal.SizeOf() + 4; if (sz <= base_sz) return false; return sub_tags.valid_size(sz - base_sz); } } [StructLayout( LayoutKind.Sequential, Pack = 1)] public struct special_award { public uint id1; public uint id2; public uint special_mask; }; [StructLayout( LayoutKind.Sequential, Pack = 1)] public struct svr_task_special_award { public svr_task_complete baseObj; public special_award sa; }; [StructLayout(LayoutKind.Sequential, Pack = 1)] public struct FnshedTaskListHeader { public ushort m_uTaskCount; public byte m_Version; public byte m_Reserved; } [StructLayout(LayoutKind.Sequential, Pack = 1)] public struct FnshedTaskEntry { public ushort m_uTaskId; public byte m_Buf; // public byte m_Mask; // 1-bit mask: 0 = success, 1 = failure // public byte m_Reserved; // Remaining 7 bits reserved public byte m_FnshedCount; // Number of times the task has been completed public byte m_Mask { get { return (byte)(m_Buf & 0x1); } set { m_Buf = (byte)((m_Buf & 0xFE) | (value & 0x1)); } } public byte m_Reserved { get { return (byte)(m_Buf & 0x3); } set { m_Buf = (byte)((m_Buf & 0xFE) | (value & 0x3)); } } } [StructLayout(LayoutKind.Sequential, Pack = 1)] public struct FinishedTaskList { // --- Layout 1: Header + List --- public FnshedTaskListHeader m_FnshHeader { get { FnshedTaskListHeader header = new FnshedTaskListHeader(); header.m_uTaskCount = BitConverter.ToUInt16(m_Buf, 0); header.m_Version = m_Buf[2]; header.m_Reserved = m_Buf[3]; return header; } set { Array.Copy(BitConverter.GetBytes(value.m_uTaskCount), 0, m_Buf, 0, 2); m_Buf[2] = value.m_Version; m_Buf[3] = value.m_Reserved; } } // [MarshalAs(UnmanagedType.ByValArray, SizeConst = TaskInterfaceConstants.TASK_FINISHED_LIST_MAX_LEN)] public FnshedTaskEntry[] m_aTaskList { get { FnshedTaskEntry[] taskList = new FnshedTaskEntry[TaskInterfaceConstants.TASK_FINISHED_LIST_MAX_LEN]; for (int i=0; i < m_FnshHeader.m_uTaskCount; i++) { int size = Marshal.SizeOf(); int startIndex = i * size + 4; // 4 bytes for m_FnshHeader int endIndex = startIndex + size; taskList[i] = GPDataTypeHelper.FromBytes(m_Buf[startIndex..endIndex]); } return taskList; } } [MarshalAs(UnmanagedType.ByValArray, SizeConst = TaskInterfaceConstants.TASK_FINISHED_LIST_BUF_SIZE)] public byte[] m_Buf; public bool ReadFromBytes(byte[] data) { if (data.Length != TaskInterfaceConstants.TASK_FINISHED_LIST_BUF_SIZE) return false; m_Buf = new byte[TaskInterfaceConstants.TASK_FINISHED_LIST_BUF_SIZE]; Array.Copy(data, m_Buf, TaskInterfaceConstants.TASK_FINISHED_LIST_BUF_SIZE); return true; } public int GetTaskPos(uint ulID) { ushort uTaskCount = m_FnshHeader.m_uTaskCount; if (uTaskCount == 0) return -1; FnshedTaskEntry[] taskList = m_aTaskList; if (uTaskCount == 1) { if (ulID == taskList[0].m_uTaskId) return 0; else return -1; } uint ulStart = 0; uint ulEnd = (uint)(uTaskCount - 1); while (ulStart + 1 < ulEnd) { uint ulMid = (ulStart + ulEnd) >> 1; if (ulID == taskList[ulMid].m_uTaskId) return (int)ulMid; else if (ulID < taskList[ulMid].m_uTaskId) ulEnd = ulMid; else ulStart = ulMid; } if (taskList[ulStart].m_uTaskId == ulID) return (int)ulStart; if (taskList[ulEnd].m_uTaskId == ulID) return (int)ulEnd; return -1; } public void AddOneTask(uint ulID, bool bSuccess) { // TODO: Implement logic to add one task (for future use) //throw new NotImplementedException(); } public void RemoveTask(uint ulID) { // TODO: Implement logic to remove a task (for future use) //throw new NotImplementedException(); } // SearchTask returns: // -1 = task not found (never completed) // 0 = task successfully completed // 1 = task failed public int SearchTask(uint ulID) { int nPos = GetTaskPos(ulID); if (nPos < 0) return -1; FnshedTaskEntry[] taskList = m_aTaskList; return taskList[nPos].m_Mask; } public byte SearchTaskFinishCount(ulong ulID) { // TODO: Implement logic to search task finish count //throw new NotImplementedException(); return 0; } public void ResetFinishCount(ulong ulID) { // TODO: Implement logic to reset finish count //throw new NotImplementedException(); } public void AddForFinishCount(ulong ulID, bool bSuccess) { // TODO: Implement logic to add for finish count //throw new NotImplementedException(); } public void RemoveAll() { Array.Clear(m_aTaskList, 0, m_aTaskList.Length); Array.Clear(m_Buf, 0, m_Buf.Length); } public bool IsValid() { return m_FnshHeader.m_uTaskCount <= TaskInterfaceConstants.TASK_FINISHED_LIST_MAX_LEN; } } [ StructLayout(LayoutKind.Sequential, Pack = 1)] public struct TaskFinishCountEntry { public ushort TaskId; public uint FinishCount; public uint FinishTime; public uint Unused2; } [ StructLayout(LayoutKind.Sequential, Pack = 1)] public struct TaskFinishCountList { public ushort m_uCount; [MarshalAs(UnmanagedType.ByValArray, SizeConst = TaskInterfaceConstants.TASK_FINISH_COUNT_MAX_LEN)] public TaskFinishCountEntry[] m_aList; public TaskFinishCountList(byte[] data) { int offset = 0; m_uCount = BitConverter.ToUInt16(data, offset); offset += 2; m_aList = new TaskFinishCountEntry[TaskInterfaceConstants.TASK_FINISH_COUNT_MAX_LEN]; int entrySize = Marshal.SizeOf(); for (int i=0; i < m_uCount; i++) { m_aList[i] = GPDataTypeHelper.FromBytes(data[offset..]); offset += entrySize; } } public uint Search(uint ulID, uint ulTime) { // TODO: Implement logic to search return 0; } public void ResetAt(uint ulID) { // TODO: Implement logic to reset at } public void AddOrUpdate(uint ulID, uint ulFinishTime) { } public void RemoveAll() { m_uCount = 0; m_aList = Array.Empty(); } public bool IsValid() { return m_uCount <= TaskInterfaceConstants.TASK_FINISH_COUNT_MAX_LEN; } }; public struct FnshedTaskEntryOld { public ushort m_uTaskId; }; [ StructLayout(LayoutKind.Sequential, Pack = 1)] public struct FnshedTaskListOld { // union // { // struct // { // FnshedTaskListHeader m_FnshHeader; // FnshedTaskEntryOld m_aTaskList[TASK_FINISHED_LIST_MAX_LEN]; // }; // unsigned char m_Buf[TASK_FINISHED_LIST_BUF_SIZE_OLD]; // }; public FnshedTaskListHeader m_FnshHeader { get { var FnshHeader = new FnshedTaskListHeader { m_uTaskCount = BitConverter.ToUInt16(m_Buf, 0), m_Version = m_Buf[2], m_Reserved = m_Buf[3] }; return FnshHeader; } set { Array.Copy(BitConverter.GetBytes(value.m_uTaskCount), 0, m_Buf, 0, 2); m_Buf[2] = value.m_Version; m_Buf[3] = value.m_Reserved; } } // [MarshalAs(UnmanagedType.ByValArray, SizeConst = TaskInterfaceConstants.TASK_FINISHED_LIST_MAX_LEN)] public FnshedTaskEntryOld[] m_aTaskList { get { FnshedTaskEntryOld[] taskList = new FnshedTaskEntryOld[TaskInterfaceConstants.TASK_FINISHED_LIST_MAX_LEN]; for (int i=0; i < m_FnshHeader.m_uTaskCount; i++) { int size = Marshal.SizeOf(); int startIndex = i * size + 4; // 4 bytes for m_FnshHeader int endIndex = startIndex + size; taskList[i] = GPDataTypeHelper.FromBytes(m_Buf[startIndex..endIndex]); } return taskList; } set { for (int i=0; i < value.Length && i < TaskInterfaceConstants.TASK_FINISHED_LIST_MAX_LEN; i++) { int size = Marshal.SizeOf(); int startIndex = i * size + 4; // 4 bytes for m_FnshHeader byte[] entryBytes = GPDataTypeHelper.ToBytes(value[i]); Array.Copy(entryBytes, 0, m_Buf, startIndex, size); } } } [MarshalAs(UnmanagedType.ByValArray, SizeConst = TaskInterfaceConstants.TASK_FINISHED_LIST_BUF_SIZE_OLD)] public byte[] m_Buf; public FnshedTaskListOld(byte[] data) { m_Buf = new byte[TaskInterfaceConstants.TASK_FINISHED_LIST_BUF_SIZE]; Array.Copy(data, m_Buf, TaskInterfaceConstants.TASK_FINISHED_LIST_BUF_SIZE_OLD); } }; [ StructLayout(LayoutKind.Sequential, Pack = 1)] public struct svr_task_dyn_time_mark { public task_notify_base baseObj; public uint time_mark; public ushort version; } }