diff --git a/src/game/char.h b/src/game/char.h index 68d6be7..9a7b56c 100644 --- a/src/game/char.h +++ b/src/game/char.h @@ -1878,6 +1878,7 @@ class CHARACTER : public CEntity, public CFSM, public CHorseRider // by mhh LPITEM* GetCubeItem() { return m_pointsInstant.pCubeItems; } bool IsCubeOpen () const { return (m_pointsInstant.pCubeNpc?true:false); } + LPCHARACTER GetCubeNpc() const { return m_pointsInstant.pCubeNpc; } void SetCubeNpc(LPCHARACTER npc) { m_pointsInstant.pCubeNpc = npc; } bool CanDoCube() const; diff --git a/src/game/char_dragonsoul.cpp b/src/game/char_dragonsoul.cpp index 5b7bd99..cf64323 100644 --- a/src/game/char_dragonsoul.cpp +++ b/src/game/char_dragonsoul.cpp @@ -1,5 +1,6 @@ #include "stdafx.h" #include "char.h" +#include "utils.h" #include "item.h" #include "desc.h" #include "DragonSoul.h" @@ -150,5 +151,41 @@ bool CHARACTER::DragonSoul_RefineWindow_Close() bool CHARACTER::DragonSoul_RefineWindow_CanRefine() { - return NULL != m_pointsInstant.m_pDragonSoulRefineWindowOpener; -} \ No newline at end of file + if (NULL == m_pointsInstant.m_pDragonSoulRefineWindowOpener) + return false; + + LPENTITY pOpener = m_pointsInstant.m_pDragonSoulRefineWindowOpener; + if (!pOpener->IsType(ENTITY_CHARACTER)) + { + RecordAntiCheatViolation("DRAGON_SOUL_REFINE", 6, "state=invalid_opener", true); + m_pointsInstant.m_pDragonSoulRefineWindowOpener = NULL; + return false; + } + + LPCHARACTER npc = (LPCHARACTER)pOpener; + if (!npc->IsNPC() || npc == this || npc->GetMapIndex() != GetMapIndex()) + { + char szDetail[160]; + snprintf(szDetail, + sizeof(szDetail), + "npc=%u map=%ld npc_map=%ld", + npc->GetVID(), + static_cast(GetMapIndex()), + static_cast(npc->GetMapIndex())); + RecordAntiCheatViolation("DRAGON_SOUL_REFINE", 8, szDetail, true); + m_pointsInstant.m_pDragonSoulRefineWindowOpener = NULL; + return false; + } + + const int iDistance = DISTANCE_APPROX(GetX() - npc->GetX(), GetY() - npc->GetY()); + if (iDistance > 2000) + { + char szDetail[160]; + snprintf(szDetail, sizeof(szDetail), "npc=%u distance=%d max=%d", npc->GetVID(), iDistance, 2000); + RecordAntiCheatViolation("DRAGON_SOUL_REFINE", iDistance > 2800 ? 12 : 4, szDetail, iDistance > 2800); + m_pointsInstant.m_pDragonSoulRefineWindowOpener = NULL; + return false; + } + + return true; +} diff --git a/src/game/char_item.cpp b/src/game/char_item.cpp index dbc592c..236eacc 100644 --- a/src/game/char_item.cpp +++ b/src/game/char_item.cpp @@ -43,6 +43,57 @@ #include "buff_on_attributes.h" #include "belt_inventory_helper.h" +namespace +{ + constexpr int kRefineNpcMaxDistance = 2000; + + bool ValidateStoredRefineNpc(LPCHARACTER ch, DWORD dwRefineNpcVID, const char* action) + { + if (!ch) + return false; + + if (0 == dwRefineNpcVID) + { + char szDetail[128]; + snprintf(szDetail, sizeof(szDetail), "action=%s state=missing", action ? action : "-"); + ch->RecordAntiCheatViolation("REFINE_NPC", 8, szDetail, true); + return false; + } + + LPCHARACTER npc = CHARACTER_MANAGER::instance().Find(dwRefineNpcVID); + if (!npc || npc == ch || !npc->IsNPC() || npc->GetMapIndex() != ch->GetMapIndex()) + { + char szDetail[160]; + snprintf(szDetail, + sizeof(szDetail), + "action=%s npc=%u map=%ld npc_map=%ld", + action ? action : "-", + dwRefineNpcVID, + static_cast(ch->GetMapIndex()), + static_cast(npc ? npc->GetMapIndex() : 0)); + ch->RecordAntiCheatViolation("REFINE_NPC", 8, szDetail, true); + return false; + } + + const int iDistance = DISTANCE_APPROX(ch->GetX() - npc->GetX(), ch->GetY() - npc->GetY()); + if (iDistance > kRefineNpcMaxDistance) + { + char szDetail[160]; + snprintf(szDetail, + sizeof(szDetail), + "action=%s npc=%u distance=%d max=%d", + action ? action : "-", + dwRefineNpcVID, + iDistance, + kRefineNpcMaxDistance); + ch->RecordAntiCheatViolation("REFINE_NPC", iDistance > kRefineNpcMaxDistance + 800 ? 12 : 4, szDetail, iDistance > kRefineNpcMaxDistance + 800); + return false; + } + + return true; + } +} + const int ITEM_BROKEN_METIN_VNUM = 28960; // CHANGE_ITEM_ATTRIBUTES @@ -814,6 +865,12 @@ bool CHARACTER::DoRefine(LPITEM item, bool bMoneyOnly, int iType) ClearRefineMode(); return false; } + + if (!ValidateStoredRefineNpc(this, m_dwRefineNPCVID, bMoneyOnly ? "money_only" : "normal")) + { + ClearRefineMode(); + return false; + } //개량 시간제한 : upgrade_refine_scroll.quest 에서 개량후 5분이내에 일반 개량을 //진행할수 없음 @@ -993,6 +1050,12 @@ bool CHARACTER::DoRefineWithScroll(LPITEM item, int iType) return false; } + if (!ValidateStoredRefineNpc(this, m_dwRefineNPCVID, "scroll")) + { + ClearRefineMode(); + return false; + } + ClearRefineMode(); //개량 시간제한 : upgrade_refine_scroll.quest 에서 개량후 5분이내에 일반 개량을 diff --git a/src/game/cube.cpp b/src/game/cube.cpp index cc340b3..96f2d6b 100644 --- a/src/game/cube.cpp +++ b/src/game/cube.cpp @@ -170,6 +170,69 @@ static bool FN_check_valid_npc( WORD vnum ) return false; } +static void FN_close_cube_session(LPCHARACTER ch) +{ + if (!ch) + return; + + LPITEM* cube_item = ch->GetCubeItem(); + for (int i = 0; i < CUBE_MAX_NUM; ++i) + cube_item[i] = NULL; + + ch->SetCubeNpc(NULL); + ch->ChatPacket(CHAT_TYPE_COMMAND, "cube close"); +} + +static bool FN_validate_cube_npc(LPCHARACTER ch, const char* action) +{ + if (!ch || !ch->IsCubeOpen()) + return false; + + LPCHARACTER npc = ch->GetCubeNpc(); + if (NULL == npc) + { + char szDetail[128]; + snprintf(szDetail, sizeof(szDetail), "action=%s state=missing", action ? action : "-"); + ch->RecordAntiCheatViolation("CUBE_NPC", 6, szDetail, true); + FN_close_cube_session(ch); + return false; + } + + if (!npc->IsNPC() || !FN_check_valid_npc(npc->GetRaceNum()) || npc->GetMapIndex() != ch->GetMapIndex()) + { + char szDetail[160]; + snprintf(szDetail, + sizeof(szDetail), + "action=%s npc=%u map=%ld npc_map=%ld race=%u", + action ? action : "-", + npc->GetVID(), + static_cast(ch->GetMapIndex()), + static_cast(npc->GetMapIndex()), + npc->GetRaceNum()); + ch->RecordAntiCheatViolation("CUBE_NPC", 8, szDetail, true); + FN_close_cube_session(ch); + return false; + } + + const long distance = DISTANCE_APPROX(ch->GetX() - npc->GetX(), ch->GetY() - npc->GetY()); + if (distance >= CUBE_MAX_DISTANCE) + { + char szDetail[160]; + snprintf(szDetail, + sizeof(szDetail), + "action=%s npc=%u distance=%ld max=%d", + action ? action : "-", + npc->GetVID(), + distance, + CUBE_MAX_DISTANCE); + ch->RecordAntiCheatViolation("CUBE_NPC", distance >= CUBE_MAX_DISTANCE + 800 ? 12 : 4, szDetail, distance >= CUBE_MAX_DISTANCE + 800); + FN_close_cube_session(ch); + return false; + } + + return true; +} + // 큐브데이타가 올바르게 초기화 되었는지 체크한다. static bool FN_check_cube_data (CUBE_DATA *cube_data) { @@ -486,12 +549,10 @@ static bool FN_update_cube_status(LPCHARACTER ch) if (NULL == ch) return false; - if (!ch->IsCubeOpen()) + if (!FN_validate_cube_npc(ch, "status")) return false; - LPCHARACTER npc = ch->GetQuestNPC(); - if (NULL == npc) - return false; + LPCHARACTER npc = ch->GetCubeNpc(); CUBE_DATA* cube = FN_find_cube(ch->GetCubeItem(), npc->GetRaceNum()); @@ -525,11 +586,10 @@ bool Cube_make (LPCHARACTER ch) return false; } - npc = ch->GetQuestNPC(); - if (NULL == npc) - { + if (!FN_validate_cube_npc(ch, "make")) return false; - } + + npc = ch->GetCubeNpc(); items = ch->GetCubeItem(); cube_proto = FN_find_cube(items, npc->GetRaceNum()); @@ -613,6 +673,9 @@ void Cube_add_item (LPCHARACTER ch, int cube_index, int inven_index) RETURN_IF_CUBE_IS_NOT_OPENED(ch); + if (!FN_validate_cube_npc(ch, "add_item")) + return; + if (inven_index<0 || INVENTORY_MAX_NUM<=inven_index) return; if (cube_index<0 || CUBE_MAX_NUM<=cube_index) @@ -655,6 +718,9 @@ void Cube_delete_item (LPCHARACTER ch, int cube_index) RETURN_IF_CUBE_IS_NOT_OPENED(ch); + if (!FN_validate_cube_npc(ch, "delete_item")) + return; + if (cube_index<0 || CUBE_MAX_NUM<=cube_index) return; cube_item = ch->GetCubeItem(); @@ -876,10 +942,11 @@ void Cube_request_result_list(LPCHARACTER ch) { RETURN_IF_CUBE_IS_NOT_OPENED(ch); - LPCHARACTER npc = ch->GetQuestNPC(); - if (NULL == npc) + if (!FN_validate_cube_npc(ch, "result_list")) return; + LPCHARACTER npc = ch->GetCubeNpc(); + DWORD npcVNUM = npc->GetRaceNum(); size_t resultCount = 0; @@ -930,10 +997,11 @@ void Cube_request_material_info(LPCHARACTER ch, int requestStartIndex, int reque { RETURN_IF_CUBE_IS_NOT_OPENED(ch); - LPCHARACTER npc = ch->GetQuestNPC(); - if (NULL == npc) + if (!FN_validate_cube_npc(ch, "material_info")) return; + LPCHARACTER npc = ch->GetCubeNpc(); + DWORD npcVNUM = npc->GetRaceNum(); std::string materialInfoText = "";