mirror of
https://github.com/EQEmu/Server.git
synced 2025-12-11 16:51:29 +00:00
[Cheat Detection] Anti-Cheat reimplementation (#1434)
* [Cheat Detection] Anti-Cheat reimplementation * minor patch fixes * ceiling to server side runspeed Warp(LT) was picking up a bunch of expected 6.2 but it was reported back as 6.5, this should help reduce the amount of false positives we get * use ceil instead of std::ceilf for linux * boat false positive fix * stopping the double detection * fixes and cleanup * auto merge tricked me... * dummy divide by 0 checks this should prevent anyone from setting Zone:MQWarpDetectionDistanceFactor to 0 and causing a crash. * Formatting * encapsulation to its own class and clean up * more detections * typo * OP_UnderWorld implmentation * Update client_packet.h * Syntax changes, formatting, cleanup * preventing crashes due to invalid packet size * typos and clearer logic * seperated the catagory for cheats * Updated MQGhost for more detail Co-authored-by: Akkadius <akkadius1@gmail.com>
This commit is contained in:
parent
26299354b6
commit
7b069dcf20
@ -567,4 +567,5 @@ N(OP_ZoneServerReady),
|
||||
N(OP_ZoneSpawns),
|
||||
N(OP_ZoneUnavail),
|
||||
N(OP_ResetAA),
|
||||
N(OP_UnderWorld),
|
||||
// mail and chat opcodes located in ../mail_oplist.h
|
||||
|
||||
@ -65,6 +65,7 @@
|
||||
#define AT_FindBits 46 // set FindBits, whatever those are!
|
||||
#define AT_TextureType 48 // TextureType
|
||||
#define AT_FacePick 49 // Turns off face pick window? maybe ...
|
||||
#define AT_AntiCheat 51 // sent by the client randomly telling the server how long since last action has occured
|
||||
#define AT_GuildShow 52 // this is what MQ2 call sit, not sure
|
||||
#define AT_Offline 53 // Offline mode
|
||||
|
||||
|
||||
@ -5530,6 +5530,23 @@ struct SayLinkBodyFrame_Struct {
|
||||
/*056*/
|
||||
};
|
||||
|
||||
struct UpdateMovementEntry {
|
||||
/* 00 */ float Y;
|
||||
/* 04 */ float X;
|
||||
/* 08 */ float Z;
|
||||
/* 12 */ uint8 type;
|
||||
/* 13 */ unsigned int timestamp;
|
||||
/* 17 */
|
||||
};
|
||||
|
||||
struct UnderWorld {
|
||||
/* 00 */ int spawn_id;
|
||||
/* 04 */ float y;
|
||||
/* 08 */ float x;
|
||||
/* 12 */ float z;
|
||||
/* 16 */
|
||||
};
|
||||
|
||||
// Restore structure packing to default
|
||||
#pragma pack()
|
||||
|
||||
|
||||
@ -129,6 +129,7 @@ EQEmuLogSys *EQEmuLogSys::LoadLogSettingsDefaults()
|
||||
log_settings[Logs::HotReload].log_to_console = static_cast<uint8>(Logs::General);
|
||||
log_settings[Logs::Loot].log_to_gmsay = static_cast<uint8>(Logs::General);
|
||||
log_settings[Logs::Scheduler].log_to_console = static_cast<uint8>(Logs::General);
|
||||
log_settings[Logs::Cheat].log_to_console = static_cast<uint8>(Logs::General);
|
||||
|
||||
/**
|
||||
* RFC 5424
|
||||
|
||||
@ -122,6 +122,7 @@ namespace Logs {
|
||||
Expeditions,
|
||||
DynamicZones,
|
||||
Scheduler,
|
||||
Cheat,
|
||||
MaxCategoryID /* Don't Remove this */
|
||||
};
|
||||
|
||||
@ -202,6 +203,7 @@ namespace Logs {
|
||||
"Expeditions",
|
||||
"DynamicZones",
|
||||
"Scheduler",
|
||||
"Cheat"
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@ -646,6 +646,16 @@
|
||||
OutF(LogSys, Logs::Detail, Logs::Scheduler, __FILE__, __func__, __LINE__, message, ##__VA_ARGS__);\
|
||||
} while (0)
|
||||
|
||||
#define LogCheat(message, ...) do {\
|
||||
if (LogSys.log_settings[Logs::Cheat].is_category_enabled == 1)\
|
||||
OutF(LogSys, Logs::General, Logs::Cheat, __FILE__, __func__, __LINE__, message, ##__VA_ARGS__);\
|
||||
} while (0)
|
||||
|
||||
#define LogCheatDetail(message, ...) do {\
|
||||
if (LogSys.log_settings[Logs::Cheat].is_category_enabled == 1)\
|
||||
OutF(LogSys, Logs::Detail, Logs::Cheat, __FILE__, __func__, __LINE__, message, ##__VA_ARGS__);\
|
||||
} while (0)
|
||||
|
||||
#define Log(debug_level, log_category, message, ...) do {\
|
||||
if (LogSys.log_settings[log_category].is_category_enabled == 1)\
|
||||
LogSys.Out(debug_level, log_category, __FILE__, __func__, __LINE__, message, ##__VA_ARGS__);\
|
||||
|
||||
@ -762,6 +762,21 @@ RULE_BOOL(DynamicZone, EnableInDynamicZoneStatus, false, "Enables the 'In Dynami
|
||||
RULE_INT(DynamicZone, WorldProcessRate, 6000, "Timer interval (milliseconds) that systems check their dynamic zone states")
|
||||
RULE_CATEGORY_END()
|
||||
|
||||
RULE_CATEGORY(Cheat)
|
||||
RULE_REAL(Cheat, MQWarpDetectionDistanceFactor, 9.0, "clients move at 4.4 about if in a straight line but with movement and to acct for lag we raise it a bit")
|
||||
RULE_INT(Cheat, MQWarpExemptStatus, -1, "Required status level to exempt the MQWarpDetector. Set to -1 to disable this feature.")
|
||||
RULE_INT(Cheat, MQZoneExemptStatus, -1, "Required status level to exempt the MQZoneDetector. Set to -1 to disable this feature.")
|
||||
RULE_INT(Cheat, MQGateExemptStatus, -1, "Required status level to exempt the MQGateDetector. Set to -1 to disable this feature.")
|
||||
RULE_INT(Cheat, MQGhostExemptStatus, -1, "Required status level to exempt the MQGhostDetector. Set to -1 to disable this feature.")
|
||||
RULE_INT(Cheat, MQFastMemExemptStatus, -1, "Required status level to exempt the MQFastMemDetector. Set to -1 to disable this feature.")
|
||||
RULE_BOOL(Cheat, EnableMQWarpDetector, true, "Enable the MQWarp Detector. Set to False to disable this feature.")
|
||||
RULE_BOOL(Cheat, EnableMQZoneDetector, true, "Enable the MQZone Detector. Set to False to disable this feature.")
|
||||
RULE_BOOL(Cheat, EnableMQGateDetector, true, "Enable the MQGate Detector. Set to False to disable this feature.")
|
||||
RULE_BOOL(Cheat, EnableMQGhostDetector, true, "Enable the MQGhost Detector. Set to False to disable this feature.")
|
||||
RULE_BOOL(Cheat, EnableMQFastMemDetector, true, "Enable the MQFastMem Detector. Set to False to disable this feature.")
|
||||
RULE_BOOL(Cheat, MarkMQWarpLT, false, "Mark clients makeing smaller warps")
|
||||
RULE_CATEGORY_END()
|
||||
|
||||
#undef RULE_CATEGORY
|
||||
#undef RULE_INT
|
||||
#undef RULE_REAL
|
||||
|
||||
@ -14,6 +14,7 @@ SET(zone_sources
|
||||
bot_command.cpp
|
||||
bot_database.cpp
|
||||
botspellsai.cpp
|
||||
cheat_manager.cpp
|
||||
client.cpp
|
||||
client_mods.cpp
|
||||
client_packet.cpp
|
||||
@ -157,7 +158,8 @@ SET(zone_sources
|
||||
zone_event_scheduler.cpp
|
||||
zone_reload.cpp
|
||||
zone_store.cpp
|
||||
zoning.cpp)
|
||||
zoning.cpp
|
||||
)
|
||||
|
||||
SET(zone_headers
|
||||
aa.h
|
||||
@ -171,6 +173,7 @@ SET(zone_headers
|
||||
bot_command.h
|
||||
bot_database.h
|
||||
bot_structs.h
|
||||
cheat_manager.h
|
||||
client.h
|
||||
client_packet.h
|
||||
command.h
|
||||
@ -274,7 +277,8 @@ SET(zone_headers
|
||||
zonedb.h
|
||||
zonedump.h
|
||||
zone_reload.h
|
||||
zone_store.h)
|
||||
zone_store.h
|
||||
)
|
||||
|
||||
ADD_EXECUTABLE(zone ${zone_sources} ${zone_headers})
|
||||
|
||||
|
||||
386
zone/cheat_manager.cpp
Normal file
386
zone/cheat_manager.cpp
Normal file
@ -0,0 +1,386 @@
|
||||
#include "cheat_manager.h"
|
||||
#include "client.h"
|
||||
#include "quest_parser_collection.h"
|
||||
|
||||
void CheatManager::SetClient(Client *cli)
|
||||
{
|
||||
m_target = cli;
|
||||
}
|
||||
|
||||
void CheatManager::SetExemptStatus(ExemptionType type, bool v)
|
||||
{
|
||||
if (v == true) {
|
||||
MovementCheck();
|
||||
}
|
||||
m_exemption[type] = v;
|
||||
}
|
||||
|
||||
bool CheatManager::GetExemptStatus(ExemptionType type)
|
||||
{
|
||||
return m_exemption[type];
|
||||
}
|
||||
|
||||
void CheatManager::CheatDetected(CheatTypes type, glm::vec3 position1, glm::vec3 position2)
|
||||
{
|
||||
switch (type) {
|
||||
case MQWarp:
|
||||
if (m_time_since_last_warp_detection.GetRemainingTime() == 0 && RuleB(Cheat, EnableMQWarpDetector) &&
|
||||
((m_target->Admin() < RuleI(Cheat, MQWarpExemptStatus) || (RuleI(Cheat, MQWarpExemptStatus)) == -1))) {
|
||||
std::string message = fmt::format(
|
||||
"/MQWarp (large warp detection) with location from x [{:.2f}] y [{:.2f}] z [{:.2f}] to x [{:.2f}] y [{:.2f}] z [{:.2f}] Distance [{:.2f}]",
|
||||
position1.x,
|
||||
position1.y,
|
||||
position1.z,
|
||||
position2.x,
|
||||
position2.y,
|
||||
position2.z,
|
||||
Distance(position1, position2)
|
||||
);
|
||||
database.SetMQDetectionFlag(
|
||||
m_target->AccountName(),
|
||||
m_target->GetName(),
|
||||
message.c_str(),
|
||||
zone->GetShortName()
|
||||
);
|
||||
LogCheat(message);
|
||||
std::string export_string = fmt::format("{} {} {}", position1.x, position1.y, position1.z);
|
||||
parse->EventPlayer(EVENT_WARP, m_target, export_string, 0);
|
||||
}
|
||||
break;
|
||||
case MQWarpAbsolute:
|
||||
if (RuleB(Cheat, EnableMQWarpDetector) &&
|
||||
((m_target->Admin() < RuleI(Cheat, MQWarpExemptStatus) || (RuleI(Cheat, MQWarpExemptStatus)) == -1))) {
|
||||
std::string message = fmt::format(
|
||||
"/MQWarp (Absolute) with location from x [{:.2f}] y [{:.2f}] z [{:.2f}] to x [{:.2f}] y [{:.2f}] z [{:.2f}] Distance [{:.2f}]",
|
||||
position1.x,
|
||||
position1.y,
|
||||
position1.z,
|
||||
position2.x,
|
||||
position2.y,
|
||||
position2.z,
|
||||
Distance(position1, position2)
|
||||
);
|
||||
database.SetMQDetectionFlag(
|
||||
m_target->AccountName(),
|
||||
m_target->GetName(),
|
||||
message.c_str(),
|
||||
zone->GetShortName()
|
||||
);
|
||||
LogCheat(message);
|
||||
std::string export_string = fmt::format("{} {} {}", position1.x, position1.y, position1.z);
|
||||
parse->EventPlayer(EVENT_WARP, m_target, export_string, 0);
|
||||
m_time_since_last_warp_detection.Start(2500);
|
||||
}
|
||||
break;
|
||||
case MQWarpShadowStep:
|
||||
if (RuleB(Cheat, EnableMQWarpDetector) &&
|
||||
((m_target->Admin() < RuleI(Cheat, MQWarpExemptStatus) || (RuleI(Cheat, MQWarpExemptStatus)) == -1))) {
|
||||
std::string message = fmt::format(
|
||||
"/MQWarp(ShadowStep) with location from x [{:.2f}] y [{:.2f}] z [{:.2f}] the target was shadow step exempt but we still found this suspicious.",
|
||||
position1.x,
|
||||
position1.y,
|
||||
position1.z
|
||||
);
|
||||
database.SetMQDetectionFlag(
|
||||
m_target->AccountName(),
|
||||
m_target->GetName(),
|
||||
message.c_str(),
|
||||
zone->GetShortName()
|
||||
);
|
||||
LogCheat(message);
|
||||
}
|
||||
break;
|
||||
case MQWarpKnockBack:
|
||||
if (RuleB(Cheat, EnableMQWarpDetector) &&
|
||||
((m_target->Admin() < RuleI(Cheat, MQWarpExemptStatus) || (RuleI(Cheat, MQWarpExemptStatus)) == -1))) {
|
||||
std::string message = fmt::format(
|
||||
"/MQWarp(Knockback) with location from x [{:.2f}] y [{:.2f}] z [{:.2f}] the target was Knock Back exempt but we still found this suspicious.",
|
||||
position1.x,
|
||||
position1.y,
|
||||
position1.z
|
||||
);
|
||||
database.SetMQDetectionFlag(
|
||||
m_target->AccountName(),
|
||||
m_target->GetName(),
|
||||
message.c_str(),
|
||||
zone->GetShortName()
|
||||
);
|
||||
LogCheat(message);
|
||||
}
|
||||
break;
|
||||
|
||||
case MQWarpLight:
|
||||
if (RuleB(Cheat, EnableMQWarpDetector) &&
|
||||
((m_target->Admin() < RuleI(Cheat, MQWarpExemptStatus) || (RuleI(Cheat, MQWarpExemptStatus)) == -1))) {
|
||||
if (RuleB(Cheat, MarkMQWarpLT)) {
|
||||
std::string message = fmt::format(
|
||||
"/MQWarp(Knockback) with location from x [{:.2f}] y [{:.2f}] z [{:.2f}] running fast but not fast enough to get killed, possibly: small warp, speed hack, excessive lag, marked as suspicious.",
|
||||
position1.x,
|
||||
position1.y,
|
||||
position1.z
|
||||
);
|
||||
database.SetMQDetectionFlag(
|
||||
m_target->AccountName(),
|
||||
m_target->GetName(),
|
||||
message.c_str(),
|
||||
zone->GetShortName()
|
||||
);
|
||||
LogCheat(message);
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case MQZone:
|
||||
if (RuleB(Cheat, EnableMQZoneDetector) &&
|
||||
((m_target->Admin() < RuleI(Cheat, MQZoneExemptStatus) || (RuleI(Cheat, MQZoneExemptStatus)) == -1))) {
|
||||
std::string message = fmt::format(
|
||||
"/MQZone used at x [{:.2f}] y [{:.2f}] z [{:.2f}]",
|
||||
position1.x,
|
||||
position1.y,
|
||||
position1.z
|
||||
);
|
||||
database.SetMQDetectionFlag(
|
||||
m_target->AccountName(),
|
||||
m_target->GetName(),
|
||||
message.c_str(),
|
||||
zone->GetShortName()
|
||||
);
|
||||
LogCheat(message);
|
||||
}
|
||||
break;
|
||||
case MQZoneUnknownDest:
|
||||
if (RuleB(Cheat, EnableMQZoneDetector) &&
|
||||
((m_target->Admin() < RuleI(Cheat, MQZoneExemptStatus) || (RuleI(Cheat, MQZoneExemptStatus)) == -1))) {
|
||||
std::string message = fmt::format(
|
||||
"/MQZone used at x [{:.2f}] y [{:.2f}] z [{:.2f}] with Unknown Destination",
|
||||
position1.x,
|
||||
position1.y,
|
||||
position1.z
|
||||
);
|
||||
database.SetMQDetectionFlag(
|
||||
m_target->AccountName(),
|
||||
m_target->GetName(),
|
||||
message.c_str(),
|
||||
zone->GetShortName());
|
||||
LogCheat(message);
|
||||
}
|
||||
break;
|
||||
case MQGate:
|
||||
if (RuleB(Cheat, EnableMQGateDetector) &&
|
||||
((m_target->Admin() < RuleI(Cheat, MQGateExemptStatus) || (RuleI(Cheat, MQGateExemptStatus)) == -1))) {
|
||||
std::string message = fmt::format(
|
||||
"/MQGate used at x [{:.2f}] y [{:.2f}] z [{:.2f}]",
|
||||
position1.x,
|
||||
position1.y,
|
||||
position1.z
|
||||
);
|
||||
database.SetMQDetectionFlag(
|
||||
m_target->AccountName(),
|
||||
m_target->GetName(),
|
||||
message.c_str(),
|
||||
zone->GetShortName()
|
||||
);
|
||||
LogCheat(message);
|
||||
}
|
||||
break;
|
||||
case MQGhost:
|
||||
// this isn't just for ghost, its also for if a person isn't sending their MovementHistory packet also.
|
||||
if (RuleB(Cheat, EnableMQGhostDetector) &&
|
||||
((m_target->Admin() < RuleI(Cheat, MQGhostExemptStatus) || (RuleI(Cheat, MQGhostExemptStatus)) == -1))) {
|
||||
database.SetMQDetectionFlag(
|
||||
m_target->AccountName(),
|
||||
m_target->GetName(),
|
||||
"Packet blocking detected.",
|
||||
zone->GetShortName());
|
||||
LogCheat("{} was caught not sending the proper packets as regularly as they were suppose to.");
|
||||
}
|
||||
break;
|
||||
case MQFastMem:
|
||||
if (RuleB(Cheat, EnableMQFastMemDetector) &&
|
||||
((m_target->Admin() < RuleI(Cheat, MQFastMemExemptStatus) ||
|
||||
(RuleI(Cheat, MQFastMemExemptStatus)) == -1))) {
|
||||
std::string message = fmt::format(
|
||||
"/MQFastMem used at x [{:.2f}] y [{:.2f}] z [{:.2f}]",
|
||||
position1.x,
|
||||
position1.y,
|
||||
position1.z
|
||||
);
|
||||
database.SetMQDetectionFlag(
|
||||
m_target->AccountName(),
|
||||
m_target->GetName(),
|
||||
message.c_str(),
|
||||
zone->GetShortName()
|
||||
);
|
||||
LogCheat(message);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
std::string message = fmt::format(
|
||||
"Unhandled HackerDetection flag with location from x [{:.2f}] y [{:.2f}] z [{:.2f}]",
|
||||
position1.x,
|
||||
position1.y,
|
||||
position1.z
|
||||
);
|
||||
database.SetMQDetectionFlag(
|
||||
m_target->AccountName(),
|
||||
m_target->GetName(),
|
||||
message.c_str(),
|
||||
zone->GetShortName()
|
||||
);
|
||||
LogCheat(message);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void CheatManager::MovementCheck(glm::vec3 updated_position)
|
||||
{
|
||||
if (m_time_since_last_movement_history.GetRemainingTime() == 0) {
|
||||
CheatDetected(MQGhost, updated_position);
|
||||
}
|
||||
|
||||
float dist = DistanceNoZ(m_target->GetPosition(), updated_position);
|
||||
uint32 cur_time = Timer::GetCurrentTime();
|
||||
if (dist == 0) {
|
||||
if (m_distance_since_last_position_check > 0.0f) {
|
||||
MovementCheck(0);
|
||||
}
|
||||
else {
|
||||
m_time_since_last_position_check = cur_time;
|
||||
m_cheat_detect_moved = false;
|
||||
}
|
||||
}
|
||||
else {
|
||||
m_distance_since_last_position_check += dist;
|
||||
m_cheat_detect_moved = true;
|
||||
if (m_time_since_last_position_check == 0) {
|
||||
m_time_since_last_position_check = cur_time;
|
||||
}
|
||||
else {
|
||||
MovementCheck(2500);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void CheatManager::MovementCheck(uint32 time_between_checks)
|
||||
{
|
||||
uint32 cur_time = Timer::GetCurrentTime();
|
||||
if ((cur_time - m_time_since_last_position_check) > time_between_checks) {
|
||||
float estimated_speed =
|
||||
(m_distance_since_last_position_check * 100) / (float) (cur_time - m_time_since_last_position_check);
|
||||
float run_speed = m_target->GetRunspeed() /
|
||||
std::min(RuleR(Cheat, MQWarpDetectionDistanceFactor), 1.0f); // MQWarpDetection shouldn't go below 1.0f so we can't end up dividing by 0.
|
||||
if (estimated_speed > run_speed) {
|
||||
bool using_gm_speed = m_target->GetGMSpeed();
|
||||
bool is_immobile = m_target->GetRunspeed() == 0; // this covers stuns, roots, mez, and pseudorooted.
|
||||
if (!using_gm_speed && !is_immobile) {
|
||||
if (GetExemptStatus(ShadowStep)) {
|
||||
if (m_distance_since_last_position_check > 800) {
|
||||
CheatDetected(
|
||||
MQWarpShadowStep,
|
||||
glm::vec3(
|
||||
m_target->GetX(),
|
||||
m_target->GetY(),
|
||||
m_target->GetZ()
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
else if (GetExemptStatus(KnockBack)) {
|
||||
if (estimated_speed > 30.0f) {
|
||||
CheatDetected(MQWarpKnockBack, glm::vec3(m_target->GetX(), m_target->GetY(), m_target->GetZ()));
|
||||
}
|
||||
}
|
||||
else if (!GetExemptStatus(Port)) {
|
||||
if (estimated_speed > (run_speed * 1.5)) {
|
||||
CheatDetected(MQWarp, glm::vec3(m_target->GetX(), m_target->GetY(), m_target->GetZ()));
|
||||
m_time_since_last_position_check = cur_time;
|
||||
m_distance_since_last_position_check = 0.0f;
|
||||
}
|
||||
else {
|
||||
CheatDetected(MQWarpLight, glm::vec3(m_target->GetX(), m_target->GetY(), m_target->GetZ()));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (time_between_checks != 1000) {
|
||||
SetExemptStatus(ShadowStep, false);
|
||||
SetExemptStatus(KnockBack, false);
|
||||
SetExemptStatus(Port, false);
|
||||
}
|
||||
m_time_since_last_position_check = cur_time;
|
||||
m_distance_since_last_position_check = 0.0f;
|
||||
}
|
||||
}
|
||||
|
||||
void CheatManager::CheckMemTimer()
|
||||
{
|
||||
if (m_target == nullptr) {
|
||||
return;
|
||||
}
|
||||
if (m_time_since_last_memorization - Timer::GetCurrentTime() <= 1) {
|
||||
glm::vec3 pos = m_target->GetPosition();
|
||||
CheatDetected(MQFastMem, pos);
|
||||
}
|
||||
m_time_since_last_memorization = Timer::GetCurrentTime();
|
||||
}
|
||||
|
||||
void CheatManager::ProcessMovementHistory(const EQApplicationPacket *app)
|
||||
{
|
||||
// if they haven't sent sent the packet within this time... they are probably spoofing...
|
||||
// linux users reported that they don't send this packet at all but i can't prove they don't so i'm not sure if thats a fake or not.
|
||||
m_time_since_last_movement_history.Start(70000);
|
||||
if (GetExemptStatus(Port)) {
|
||||
return;
|
||||
}
|
||||
auto *m_MovementHistory = (UpdateMovementEntry *) app->pBuffer;
|
||||
if (app->size < sizeof(UpdateMovementEntry))
|
||||
{
|
||||
LogDebug("Size mismatch in OP_MovementHistoryList, expected {}, got [{}]", sizeof(UpdateMovementEntry), app->size);
|
||||
DumpPacket(app);
|
||||
return;
|
||||
}
|
||||
|
||||
for (int index = 0; index < (app->size) / sizeof(UpdateMovementEntry); index++) {
|
||||
glm::vec3 to = glm::vec3(m_MovementHistory[index].X, m_MovementHistory[index].Y, m_MovementHistory[index].Z);
|
||||
switch (m_MovementHistory[index].type) {
|
||||
case UpdateMovementType::ZoneLine:
|
||||
SetExemptStatus(Port, true);
|
||||
break;
|
||||
case UpdateMovementType::TeleportA:
|
||||
if (index != 0) {
|
||||
glm::vec3 from = glm::vec3(
|
||||
m_MovementHistory[index - 1].X,
|
||||
m_MovementHistory[index - 1].Y,
|
||||
m_MovementHistory[index - 1].Z
|
||||
);
|
||||
CheatDetected(MQWarpAbsolute, from, to);
|
||||
}
|
||||
SetExemptStatus(Port, false);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void CheatManager::ProcessSpawnApperance(uint16 spawn_id, uint16 type, uint32 parameter)
|
||||
{
|
||||
if (type == AT_Anim && parameter == ANIM_SIT) {
|
||||
m_time_since_last_memorization = Timer::GetCurrentTime();
|
||||
}
|
||||
else if (spawn_id == 0 && type == AT_AntiCheat) {
|
||||
m_time_since_last_action = parameter;
|
||||
}
|
||||
}
|
||||
|
||||
void CheatManager::ProcessItemVerifyRequest(int32 slot_id, uint32 target_id)
|
||||
{
|
||||
if (slot_id == -1 && m_warp_counter != target_id) {
|
||||
m_warp_counter = target_id;
|
||||
}
|
||||
}
|
||||
|
||||
void CheatManager::ClientProcess()
|
||||
{
|
||||
if (!m_cheat_detect_moved) {
|
||||
m_time_since_last_position_check = Timer::GetCurrentTime();
|
||||
}
|
||||
}
|
||||
88
zone/cheat_manager.h
Normal file
88
zone/cheat_manager.h
Normal file
@ -0,0 +1,88 @@
|
||||
#ifndef ANTICHEAT_H
|
||||
#define ANTICHEAT_H
|
||||
class CheatManager;
|
||||
class Client;
|
||||
|
||||
#include "../common/timer.h"
|
||||
#include "../common/rulesys.h"
|
||||
#include <glm/ext/vector_float3.hpp>
|
||||
#include "../common/eq_packet_structs.h"
|
||||
#include "../common/eq_packet.h"
|
||||
|
||||
typedef enum {
|
||||
Collision = 1,
|
||||
TeleportB,
|
||||
TeleportA,
|
||||
ZoneLine,
|
||||
Unknown0x5,
|
||||
Unknown0x6,
|
||||
SpellA, // Titanium - UF
|
||||
Unknown0x8,
|
||||
SpellB // Used in RoF+
|
||||
} UpdateMovementType;
|
||||
|
||||
typedef enum {
|
||||
ShadowStep,
|
||||
KnockBack,
|
||||
Port,
|
||||
Assist,
|
||||
Sense,
|
||||
MAX_EXEMPTIONS
|
||||
} ExemptionType;
|
||||
|
||||
typedef enum {
|
||||
MQWarp,
|
||||
MQWarpShadowStep,
|
||||
MQWarpKnockBack,
|
||||
MQWarpLight,
|
||||
MQZone,
|
||||
MQZoneUnknownDest,
|
||||
MQGate,
|
||||
MQGhost,
|
||||
MQFastMem,
|
||||
MQWarpAbsolute
|
||||
} CheatTypes;
|
||||
|
||||
class CheatManager {
|
||||
public:
|
||||
CheatManager()
|
||||
{
|
||||
SetExemptStatus(ShadowStep, false);
|
||||
SetExemptStatus(KnockBack, false);
|
||||
SetExemptStatus(Port, false);
|
||||
SetExemptStatus(Assist, false);
|
||||
SetExemptStatus(Sense, false);
|
||||
m_distance_since_last_position_check = 0.0f;
|
||||
m_cheat_detect_moved = false;
|
||||
m_target = nullptr;
|
||||
m_time_since_last_memorization = 0;
|
||||
m_time_since_last_position_check = 0;
|
||||
m_time_since_last_warp_detection.Start();
|
||||
m_time_since_last_movement_history.Start(70000);
|
||||
m_warp_counter = 0;
|
||||
}
|
||||
void SetClient(Client *cli);
|
||||
void SetExemptStatus(ExemptionType type, bool v);
|
||||
bool GetExemptStatus(ExemptionType type);
|
||||
void CheatDetected(CheatTypes type, glm::vec3 position1, glm::vec3 position2 = glm::vec3(0, 0, 0));
|
||||
void MovementCheck(glm::vec3 updated_position);
|
||||
void MovementCheck(uint32 time_between_checks = 1000);
|
||||
void CheckMemTimer();
|
||||
void ProcessMovementHistory(const EQApplicationPacket *app);
|
||||
void ProcessSpawnApperance(uint16 spawn_id, uint16 type, uint32 parameter);
|
||||
void ProcessItemVerifyRequest(int32 slot_id, uint32 target_id);
|
||||
void ClientProcess();
|
||||
private:
|
||||
bool m_exemption[ExemptionType::MAX_EXEMPTIONS]{};
|
||||
float m_distance_since_last_position_check;
|
||||
bool m_cheat_detect_moved;
|
||||
|
||||
Client *m_target;
|
||||
uint32 m_time_since_last_position_check;
|
||||
uint32 m_time_since_last_memorization;
|
||||
uint32 m_time_since_last_action{};
|
||||
Timer m_time_since_last_warp_detection;
|
||||
Timer m_time_since_last_movement_history;
|
||||
uint32 m_warp_counter;
|
||||
};
|
||||
#endif ANTICHEAT_H
|
||||
@ -61,6 +61,7 @@ extern volatile bool RunLoops;
|
||||
#include "mob_movement_manager.h"
|
||||
#include "../common/content/world_content_service.h"
|
||||
#include "../common/expedition_lockout_timer.h"
|
||||
#include "cheat_manager.h"
|
||||
|
||||
extern QueryServ* QServ;
|
||||
extern EntityList entity_list;
|
||||
@ -177,7 +178,7 @@ Client::Client(EQStreamInterface* ieqs)
|
||||
|
||||
for (int client_filter = 0; client_filter < _FilterCount; client_filter++)
|
||||
ClientFilters[client_filter] = FilterShow;
|
||||
|
||||
cheat_manager.SetClient(this);
|
||||
mMovementManager->AddClient(this);
|
||||
character_id = 0;
|
||||
conn_state = NoPacketsReceived;
|
||||
@ -10258,7 +10259,7 @@ void Client::ApplyWeaponsStance()
|
||||
- From spells, just remove the Primary buff that contains the WeaponStance effect in it.
|
||||
- For items with worn effect, unequip the item.
|
||||
- For AA abilities, a hotkey is used to Enable and Disable the effect. See. Client::TogglePassiveAlternativeAdvancement in aa.cpp for extensive details.
|
||||
|
||||
|
||||
Rank
|
||||
- Most important for AA, but if you have more than one of WeaponStance effect for a given type, the spell trigger buff will apply whatever has the highest
|
||||
'rank' value from the spells table. AA's on live for this effect naturally do this. Be awere of this if making custom spells/worn effects/AA.
|
||||
@ -10270,7 +10271,7 @@ void Client::ApplyWeaponsStance()
|
||||
if (!IsWeaponStanceEnabled()) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
bool enabled = false;
|
||||
bool item_bonus_exists = false;
|
||||
bool aa_bonus_exists = false;
|
||||
@ -10326,7 +10327,7 @@ void Client::ApplyWeaponsStance()
|
||||
|
||||
if (itembonuses.WeaponStance[WEAPON_STANCE_TYPE_2H] || itembonuses.WeaponStance[WEAPON_STANCE_TYPE_SHIELD] ||
|
||||
itembonuses.WeaponStance[WEAPON_STANCE_TYPE_DUAL_WIELD]) {
|
||||
|
||||
|
||||
enabled = true;
|
||||
item_bonus_exists = true;
|
||||
|
||||
|
||||
@ -66,6 +66,7 @@ namespace EQ
|
||||
#include "zone_store.h"
|
||||
#include "task_manager.h"
|
||||
#include "task_client_state.h"
|
||||
#include "cheat_manager.h"
|
||||
|
||||
#ifdef _WINDOWS
|
||||
// since windows defines these within windef.h (which windows.h include)
|
||||
@ -120,17 +121,6 @@ typedef enum {
|
||||
EvacToSafeCoords
|
||||
} ZoneMode;
|
||||
|
||||
typedef enum {
|
||||
MQWarp,
|
||||
MQWarpShadowStep,
|
||||
MQWarpKnockBack,
|
||||
MQWarpLight,
|
||||
MQZone,
|
||||
MQZoneUnknownDest,
|
||||
MQGate,
|
||||
MQGhost
|
||||
} CheatTypes;
|
||||
|
||||
enum {
|
||||
HideCorpseNone = 0,
|
||||
HideCorpseAll = 1,
|
||||
@ -604,7 +594,7 @@ public:
|
||||
inline double GetEXPModifier(uint32 zone_id) const { return database.GetEXPModifier(CharacterID(), zone_id); };
|
||||
inline void SetAAEXPModifier(uint32 zone_id, double aa_modifier) { database.SetAAEXPModifier(CharacterID(), zone_id, aa_modifier); };
|
||||
inline void SetEXPModifier(uint32 zone_id, double exp_modifier) { database.SetEXPModifier(CharacterID(), zone_id, exp_modifier); };
|
||||
|
||||
|
||||
bool UpdateLDoNPoints(uint32 theme_id, int points);
|
||||
void SetPVPPoints(uint32 Points) { m_pp.PVPCurrentPoints = Points; }
|
||||
uint32 GetPVPPoints() { return m_pp.PVPCurrentPoints; }
|
||||
@ -1598,6 +1588,7 @@ public:
|
||||
Raid *p_raid_instance;
|
||||
|
||||
void ShowDevToolsMenu();
|
||||
CheatManager cheat_manager;
|
||||
|
||||
protected:
|
||||
friend class Mob;
|
||||
|
||||
@ -209,7 +209,7 @@ void MapOpcodes()
|
||||
ConnectedOpcodes[OP_FeignDeath] = &Client::Handle_OP_FeignDeath;
|
||||
ConnectedOpcodes[OP_FindPersonRequest] = &Client::Handle_OP_FindPersonRequest;
|
||||
ConnectedOpcodes[OP_Fishing] = &Client::Handle_OP_Fishing;
|
||||
ConnectedOpcodes[OP_FloatListThing] = &Client::Handle_OP_Ignore;
|
||||
ConnectedOpcodes[OP_FloatListThing] = &Client::Handle_OP_MovementHistoryList;
|
||||
ConnectedOpcodes[OP_Forage] = &Client::Handle_OP_Forage;
|
||||
ConnectedOpcodes[OP_FriendsWho] = &Client::Handle_OP_FriendsWho;
|
||||
ConnectedOpcodes[OP_GetGuildMOTD] = &Client::Handle_OP_GetGuildMOTD;
|
||||
@ -413,6 +413,7 @@ void MapOpcodes()
|
||||
ConnectedOpcodes[OP_YellForHelp] = &Client::Handle_OP_YellForHelp;
|
||||
ConnectedOpcodes[OP_ZoneChange] = &Client::Handle_OP_ZoneChange;
|
||||
ConnectedOpcodes[OP_ResetAA] = &Client::Handle_OP_ResetAA;
|
||||
ConnectedOpcodes[OP_UnderWorld] = &Client::Handle_OP_UnderWorld;
|
||||
}
|
||||
|
||||
void ClearMappedOpcode(EmuOpcode op)
|
||||
@ -2922,6 +2923,7 @@ void Client::Handle_OP_Assist(const EQApplicationPacket *app)
|
||||
Mob *new_target = assistee->GetTarget();
|
||||
if (new_target && (GetGM() ||
|
||||
Distance(m_Position, assistee->GetPosition()) <= TARGETING_RANGE)) {
|
||||
cheat_manager.SetExemptStatus(Assist, true);
|
||||
eid->entity_id = new_target->GetID();
|
||||
} else {
|
||||
eid->entity_id = 0;
|
||||
@ -4488,7 +4490,7 @@ void Client::Handle_OP_ClientUpdate(const EQApplicationPacket *app) {
|
||||
double cosine = std::cos(thetar);
|
||||
double sine = std::sin(thetar);
|
||||
|
||||
double normalizedx, normalizedy;
|
||||
double normalizedx, normalizedy;
|
||||
normalizedx = cx * cosine - -cy * sine;
|
||||
normalizedy = -cx * sine + cy * cosine;
|
||||
|
||||
@ -4500,6 +4502,8 @@ void Client::Handle_OP_ClientUpdate(const EQApplicationPacket *app) {
|
||||
}
|
||||
}
|
||||
|
||||
cheat_manager.MovementCheck(glm::vec3(cx, cy, cz));
|
||||
|
||||
if (IsDraggingCorpse())
|
||||
DragCorpses();
|
||||
|
||||
@ -8765,6 +8769,7 @@ void Client::Handle_OP_ItemVerifyRequest(const EQApplicationPacket *app)
|
||||
slot_id = request->slot;
|
||||
target_id = request->target;
|
||||
|
||||
cheat_manager.ProcessItemVerifyRequest(request->slot, request->target);
|
||||
|
||||
EQApplicationPacket *outapp = nullptr;
|
||||
outapp = new EQApplicationPacket(OP_ItemVerifyReply, sizeof(ItemVerifyReply_Struct));
|
||||
@ -9614,6 +9619,7 @@ return;
|
||||
|
||||
void Client::Handle_OP_MemorizeSpell(const EQApplicationPacket *app)
|
||||
{
|
||||
cheat_manager.CheckMemTimer();
|
||||
OPMemorizeSpell(app);
|
||||
return;
|
||||
}
|
||||
@ -13465,6 +13471,8 @@ void Client::Handle_OP_SpawnAppearance(const EQApplicationPacket *app)
|
||||
}
|
||||
SpawnAppearance_Struct* sa = (SpawnAppearance_Struct*)app->pBuffer;
|
||||
|
||||
cheat_manager.ProcessSpawnApperance(sa->spawn_id, sa->type, sa->parameter);
|
||||
|
||||
if (sa->spawn_id != GetID())
|
||||
return;
|
||||
|
||||
@ -13917,6 +13925,11 @@ void Client::Handle_OP_TargetCommand(const EQApplicationPacket *app)
|
||||
GetTarget()->IsTargeted(1);
|
||||
return;
|
||||
}
|
||||
else if (cheat_manager.GetExemptStatus(Assist)) {
|
||||
GetTarget()->IsTargeted(1);
|
||||
cheat_manager.SetExemptStatus(Assist, false);
|
||||
return;
|
||||
}
|
||||
else if (GetTarget()->IsClient())
|
||||
{
|
||||
//make sure this client is in our raid/group
|
||||
@ -13932,6 +13945,15 @@ void Client::Handle_OP_TargetCommand(const EQApplicationPacket *app)
|
||||
SetTarget((Mob*)nullptr);
|
||||
return;
|
||||
}
|
||||
else if (cheat_manager.GetExemptStatus(Port)) {
|
||||
GetTarget()->IsTargeted(1);
|
||||
return;
|
||||
}
|
||||
else if (cheat_manager.GetExemptStatus(Sense)) {
|
||||
GetTarget()->IsTargeted(1);
|
||||
cheat_manager.SetExemptStatus(Sense, false);
|
||||
return;
|
||||
}
|
||||
else if (IsXTarget(GetTarget()))
|
||||
{
|
||||
GetTarget()->IsTargeted(1);
|
||||
@ -15173,3 +15195,21 @@ void Client::Handle_OP_ResetAA(const EQApplicationPacket *app)
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
void Client::Handle_OP_MovementHistoryList(const EQApplicationPacket* app) {
|
||||
cheat_manager.ProcessMovementHistory(app);
|
||||
}
|
||||
|
||||
void Client::Handle_OP_UnderWorld(const EQApplicationPacket* app) {
|
||||
UnderWorld* m_UnderWorld = (UnderWorld*)app->pBuffer;
|
||||
if (app->size != sizeof(UnderWorld))
|
||||
{
|
||||
LogDebug("Size mismatch in OP_UnderWorld, expected {}, got [{}]", sizeof(UnderWorld), app->size);
|
||||
DumpPacket(app);
|
||||
return;
|
||||
}
|
||||
auto dist = Distance(glm::vec3(m_UnderWorld->x, m_UnderWorld->y, zone->newzone_data.underworld), glm::vec3(m_UnderWorld->x, m_UnderWorld->y, m_UnderWorld->z));
|
||||
cheat_manager.MovementCheck(glm::vec3(m_UnderWorld->x, m_UnderWorld->y, m_UnderWorld->z));
|
||||
if (m_UnderWorld->spawn_id == GetID() && dist <= 5.0f && zone->newzone_data.underworld_teleport_index != 0)
|
||||
cheat_manager.SetExemptStatus(Port, true);
|
||||
}
|
||||
|
||||
@ -313,3 +313,5 @@
|
||||
void Handle_OP_YellForHelp(const EQApplicationPacket *app);
|
||||
void Handle_OP_ZoneChange(const EQApplicationPacket *app);
|
||||
void Handle_OP_ResetAA(const EQApplicationPacket *app);
|
||||
void Handle_OP_MovementHistoryList(const EQApplicationPacket* app);
|
||||
void Handle_OP_UnderWorld(const EQApplicationPacket* app);
|
||||
|
||||
@ -203,6 +203,8 @@ bool Client::Process() {
|
||||
if (IsStunned() && stunned_timer.Check())
|
||||
Mob::UnStun();
|
||||
|
||||
cheat_manager.ClientProcess();
|
||||
|
||||
if (bardsong_timer.Check() && bardsong != 0) {
|
||||
//NOTE: this is kinda a heavy-handed check to make sure the mob still exists before
|
||||
//doing the next pulse on them...
|
||||
|
||||
@ -120,6 +120,7 @@ const char *QuestEventSubroutines[_LargestEventID] = {
|
||||
"EVENT_USE_SKILL",
|
||||
"EVENT_COMBINE_VALIDATE",
|
||||
"EVENT_BOT_COMMAND",
|
||||
"EVENT_WARP",
|
||||
"EVENT_TEST_BUFF"
|
||||
};
|
||||
|
||||
@ -1636,6 +1637,13 @@ void PerlembParser::ExportEventVariables(
|
||||
ExportVar(package_name.c_str(), "langid", extradata);
|
||||
break;
|
||||
}
|
||||
case EVENT_WARP: {
|
||||
Seperator sep(data);
|
||||
ExportVar(package_name.c_str(), "from_x", sep.arg[0]);
|
||||
ExportVar(package_name.c_str(), "from_y", sep.arg[1]);
|
||||
ExportVar(package_name.c_str(), "from_z", sep.arg[2]);
|
||||
break;
|
||||
}
|
||||
|
||||
default: {
|
||||
break;
|
||||
|
||||
@ -88,6 +88,7 @@ typedef enum {
|
||||
EVENT_USE_SKILL,
|
||||
EVENT_COMBINE_VALIDATE,
|
||||
EVENT_BOT_COMMAND,
|
||||
EVENT_WARP,
|
||||
EVENT_TEST_BUFF,
|
||||
_LargestEventID
|
||||
} QuestEventID;
|
||||
|
||||
@ -3225,6 +3225,7 @@ luabind::scope lua_register_events() {
|
||||
luabind::value("spawn_zone", static_cast<int>(EVENT_SPAWN_ZONE)),
|
||||
luabind::value("death_zone", static_cast<int>(EVENT_DEATH_ZONE)),
|
||||
luabind::value("use_skill", static_cast<int>(EVENT_USE_SKILL)),
|
||||
luabind::value("warp", static_cast<int>(EVENT_WARP)),
|
||||
luabind::value("test_buff", static_cast<int>(EVENT_TEST_BUFF))
|
||||
];
|
||||
}
|
||||
|
||||
@ -131,6 +131,7 @@ const char *LuaEvents[_LargestEventID] = {
|
||||
"event_use_skill",
|
||||
"event_combine_validate",
|
||||
"event_bot_command",
|
||||
"event_warp",
|
||||
"event_test_buff"
|
||||
};
|
||||
|
||||
@ -217,6 +218,7 @@ LuaParser::LuaParser() {
|
||||
PlayerArgumentDispatch[EVENT_TEST_BUFF] = handle_test_buff;
|
||||
PlayerArgumentDispatch[EVENT_COMBINE_VALIDATE] = handle_player_combine_validate;
|
||||
PlayerArgumentDispatch[EVENT_BOT_COMMAND] = handle_player_bot_command;
|
||||
PlayerArgumentDispatch[EVENT_WARP] = handle_player_warp;
|
||||
|
||||
ItemArgumentDispatch[EVENT_ITEM_CLICK] = handle_item_click;
|
||||
ItemArgumentDispatch[EVENT_ITEM_CLICK_CAST] = handle_item_click;
|
||||
|
||||
@ -561,6 +561,18 @@ void handle_player_bot_command(QuestInterface* parse, lua_State* L, Client* clie
|
||||
lua_setfield(L, -2, "args");
|
||||
}
|
||||
|
||||
void handle_player_warp(QuestInterface* parse, lua_State* L, Client* client, std::string data, uint32 extra_data, std::vector<EQ::Any>* extra_pointers) {
|
||||
Seperator sep(data.c_str());
|
||||
lua_pushnumber(L, std::stof(sep.arg[0]));
|
||||
lua_setfield(L, -2, "from_x");
|
||||
|
||||
lua_pushnumber(L, std::stof(sep.arg[1]));
|
||||
lua_setfield(L, -2, "from_y");
|
||||
|
||||
lua_pushnumber(L, std::stof(sep.arg[2]));
|
||||
lua_setfield(L, -2, "from_z");
|
||||
}
|
||||
|
||||
//Item
|
||||
void handle_item_click(QuestInterface *parse, lua_State* L, Client* client, EQ::ItemInstance* item, Mob *mob, std::string data, uint32 extra_data,
|
||||
std::vector<EQ::Any> *extra_pointers) {
|
||||
|
||||
@ -103,6 +103,8 @@ void handle_player_combine_validate(QuestInterface* parse, lua_State* L, Client*
|
||||
std::vector<EQ::Any>* extra_pointers);
|
||||
void handle_player_bot_command(QuestInterface *parse, lua_State* L, Client* client, std::string data, uint32 extra_data,
|
||||
std::vector<EQ::Any> *extra_pointers);
|
||||
void handle_player_warp(QuestInterface* parse, lua_State* L, Client* client, std::string data, uint32 extra_data,
|
||||
std::vector<EQ::Any>* extra_pointers);
|
||||
|
||||
//Item
|
||||
void handle_item_click(QuestInterface *parse, lua_State* L, Client* client, EQ::ItemInstance* item, Mob *mob, std::string data, uint32 extra_data,
|
||||
|
||||
@ -4657,6 +4657,7 @@ void Mob::DoKnockback(Mob *caster, uint32 pushback, uint32 pushup)
|
||||
{
|
||||
if(IsClient())
|
||||
{
|
||||
CastToClient()->cheat_manager.SetExemptStatus(KnockBack, true);
|
||||
auto outapp_push = new EQApplicationPacket(OP_ClientUpdate, sizeof(PlayerPositionUpdateServer_Struct));
|
||||
PlayerPositionUpdateServer_Struct* spu = (PlayerPositionUpdateServer_Struct*)outapp_push->pBuffer;
|
||||
|
||||
|
||||
@ -2743,6 +2743,22 @@ void Mob::BardPulse(uint16 spell_id, Mob *caster) {
|
||||
|
||||
action->effect_flag = 4;
|
||||
|
||||
if (spells[spell_id].pushback != 0.0f || spells[spell_id].pushup != 0.0f)
|
||||
{
|
||||
if (IsClient())
|
||||
{
|
||||
if (!IsBuffSpell(spell_id))
|
||||
{
|
||||
CastToClient()->cheat_manager.SetExemptStatus(KnockBack, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (IsClient() && IsEffectInSpell(spell_id, SE_ShadowStep))
|
||||
{
|
||||
CastToClient()->cheat_manager.SetExemptStatus(ShadowStep, true);
|
||||
}
|
||||
|
||||
if(!IsEffectInSpell(spell_id, SE_BindAffinity))
|
||||
{
|
||||
CastToClient()->QueuePacket(packet);
|
||||
@ -4028,7 +4044,14 @@ bool Mob::SpellOnTarget(uint16 spell_id, Mob *spelltar, bool reflect, bool use_r
|
||||
|
||||
if(spells[spell_id].pushback != 0.0f || spells[spell_id].pushup != 0.0f)
|
||||
{
|
||||
if (RuleB(Spells, NPCSpellPush) && !spelltar->IsRooted() && spelltar->ForcedMovement == 0) {
|
||||
if (spelltar->IsClient())
|
||||
{
|
||||
if (!IsBuffSpell(spell_id))
|
||||
{
|
||||
spelltar->CastToClient()->cheat_manager.SetExemptStatus(KnockBack, true);
|
||||
}
|
||||
}
|
||||
else if (RuleB(Spells, NPCSpellPush) && !spelltar->IsRooted() && spelltar->ForcedMovement == 0) {
|
||||
spelltar->m_Delta.x += action->force * g_Math.FastSin(action->hit_heading);
|
||||
spelltar->m_Delta.y += action->force * g_Math.FastCos(action->hit_heading);
|
||||
spelltar->m_Delta.z += action->hit_pitch;
|
||||
@ -4036,6 +4059,11 @@ bool Mob::SpellOnTarget(uint16 spell_id, Mob *spelltar, bool reflect, bool use_r
|
||||
}
|
||||
}
|
||||
|
||||
if (spelltar->IsClient() && IsEffectInSpell(spell_id, SE_ShadowStep))
|
||||
{
|
||||
spelltar->CastToClient()->cheat_manager.SetExemptStatus(ShadowStep, true);
|
||||
}
|
||||
|
||||
if(!IsEffectInSpell(spell_id, SE_BindAffinity))
|
||||
{
|
||||
if(spelltar != this && spelltar->IsClient()) // send to target
|
||||
|
||||
@ -1911,7 +1911,11 @@ ZonePoint* Zone::GetClosestZonePoint(const glm::vec3& location, uint32 to, Clien
|
||||
// this shouldn't open up any exploits since those situations are detected later on
|
||||
if ((zone->HasWaterMap() && !zone->watermap->InZoneLine(glm::vec3(client->GetPosition()))) || (!zone->HasWaterMap() && closest_dist > 400.0f && closest_dist < max_distance2))
|
||||
{
|
||||
//TODO cheat detection
|
||||
if (client) {
|
||||
if (!client->cheat_manager.GetExemptStatus(Port)) {
|
||||
client->cheat_manager.CheatDetected(MQZoneUnknownDest, location);
|
||||
}
|
||||
}
|
||||
LogInfo("WARNING: Closest zone point for zone id [{}] is [{}], you might need to update your zone_points table if you dont arrive at the right spot", to, closest_dist);
|
||||
LogInfo("<Real Zone Points>. [{}]", to_string(location).c_str());
|
||||
}
|
||||
|
||||
@ -99,9 +99,14 @@ void Client::Handle_OP_ZoneChange(const EQApplicationPacket *app) {
|
||||
//unable to find a zone point... is there anything else
|
||||
//that can be a valid un-zolicited zone request?
|
||||
|
||||
//Todo cheat detection
|
||||
Message(Chat::Red, "Invalid unsolicited zone request.");
|
||||
LogError("Zoning [{}]: Invalid unsolicited zone request to zone id [{}]", GetName(), target_zone_id);
|
||||
if (GetBindZoneID() == target_zone_id) {
|
||||
cheat_manager.CheatDetected(MQGate, glm::vec3(zc->x, zc->y, zc->z));
|
||||
}
|
||||
else {
|
||||
cheat_manager.CheatDetected(MQZone, glm::vec3(zc->x, zc->y, zc->z));
|
||||
}
|
||||
SendZoneCancel(zc);
|
||||
return;
|
||||
}
|
||||
@ -134,7 +139,12 @@ void Client::Handle_OP_ZoneChange(const EQApplicationPacket *app) {
|
||||
//then we assume this is invalid.
|
||||
if(!zone_point || zone_point->target_zone_id != target_zone_id) {
|
||||
LogError("Zoning [{}]: Invalid unsolicited zone request to zone id [{}]", GetName(), target_zone_id);
|
||||
//todo cheat detection
|
||||
if (GetBindZoneID() == target_zone_id) {
|
||||
cheat_manager.CheatDetected(MQGate, glm::vec3(zc->x, zc->y, zc->z));
|
||||
}
|
||||
else {
|
||||
cheat_manager.CheatDetected(MQZone, glm::vec3(zc->x, zc->y, zc->z));
|
||||
}
|
||||
SendZoneCancel(zc);
|
||||
return;
|
||||
}
|
||||
@ -282,7 +292,12 @@ void Client::Handle_OP_ZoneChange(const EQApplicationPacket *app) {
|
||||
//for now, there are no other cases...
|
||||
|
||||
//could not find a valid reason for them to be zoning, stop it.
|
||||
//todo cheat detection
|
||||
if (GetBindZoneID() == target_zone_id) {
|
||||
cheat_manager.CheatDetected(MQGate, glm::vec3(zc->x, zc->y, zc->z));
|
||||
}
|
||||
else {
|
||||
cheat_manager.CheatDetected(MQZone, glm::vec3(zc->x, zc->y, zc->z));
|
||||
}
|
||||
LogError("Zoning [{}]: Invalid unsolicited zone request to zone id [{}]. Not near a zone point", GetName(), target_zone_name);
|
||||
SendZoneCancel(zc);
|
||||
return;
|
||||
@ -379,6 +394,7 @@ void Client::Handle_OP_ZoneChange(const EQApplicationPacket *app) {
|
||||
void Client::SendZoneCancel(ZoneChange_Struct *zc) {
|
||||
//effectively zone them right back to where they were
|
||||
//unless we find a better way to stop the zoning process.
|
||||
cheat_manager.SetExemptStatus(Port, true);
|
||||
EQApplicationPacket *outapp = nullptr;
|
||||
outapp = new EQApplicationPacket(OP_ZoneChange, sizeof(ZoneChange_Struct));
|
||||
ZoneChange_Struct *zc2 = (ZoneChange_Struct*)outapp->pBuffer;
|
||||
@ -397,7 +413,7 @@ void Client::SendZoneCancel(ZoneChange_Struct *zc) {
|
||||
void Client::SendZoneError(ZoneChange_Struct *zc, int8 err)
|
||||
{
|
||||
LogError("Zone [{}] is not available because target wasn't found or character insufficent level", zc->zoneID);
|
||||
|
||||
cheat_manager.SetExemptStatus(Port, true);
|
||||
EQApplicationPacket *outapp = nullptr;
|
||||
outapp = new EQApplicationPacket(OP_ZoneChange, sizeof(ZoneChange_Struct));
|
||||
ZoneChange_Struct *zc2 = (ZoneChange_Struct*)outapp->pBuffer;
|
||||
@ -667,6 +683,8 @@ void Client::ZonePC(uint32 zoneID, uint32 instance_id, float x, float y, float z
|
||||
pShortZoneName = ZoneName(zoneID);
|
||||
content_db.GetZoneLongName(pShortZoneName, &pZoneName);
|
||||
|
||||
cheat_manager.SetExemptStatus(Port, true);
|
||||
|
||||
if(!pZoneName) {
|
||||
Message(Chat::Red, "Invalid zone number specified");
|
||||
safe_delete_array(pZoneName);
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user