mirror of
https://github.com/EQEmu/Server.git
synced 2025-12-13 18: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_ZoneSpawns),
|
||||||
N(OP_ZoneUnavail),
|
N(OP_ZoneUnavail),
|
||||||
N(OP_ResetAA),
|
N(OP_ResetAA),
|
||||||
|
N(OP_UnderWorld),
|
||||||
// mail and chat opcodes located in ../mail_oplist.h
|
// mail and chat opcodes located in ../mail_oplist.h
|
||||||
|
|||||||
@ -65,6 +65,7 @@
|
|||||||
#define AT_FindBits 46 // set FindBits, whatever those are!
|
#define AT_FindBits 46 // set FindBits, whatever those are!
|
||||||
#define AT_TextureType 48 // TextureType
|
#define AT_TextureType 48 // TextureType
|
||||||
#define AT_FacePick 49 // Turns off face pick window? maybe ...
|
#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_GuildShow 52 // this is what MQ2 call sit, not sure
|
||||||
#define AT_Offline 53 // Offline mode
|
#define AT_Offline 53 // Offline mode
|
||||||
|
|
||||||
|
|||||||
@ -5530,6 +5530,23 @@ struct SayLinkBodyFrame_Struct {
|
|||||||
/*056*/
|
/*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
|
// Restore structure packing to default
|
||||||
#pragma pack()
|
#pragma pack()
|
||||||
|
|
||||||
|
|||||||
@ -129,6 +129,7 @@ EQEmuLogSys *EQEmuLogSys::LoadLogSettingsDefaults()
|
|||||||
log_settings[Logs::HotReload].log_to_console = static_cast<uint8>(Logs::General);
|
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::Loot].log_to_gmsay = static_cast<uint8>(Logs::General);
|
||||||
log_settings[Logs::Scheduler].log_to_console = 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
|
* RFC 5424
|
||||||
|
|||||||
@ -122,6 +122,7 @@ namespace Logs {
|
|||||||
Expeditions,
|
Expeditions,
|
||||||
DynamicZones,
|
DynamicZones,
|
||||||
Scheduler,
|
Scheduler,
|
||||||
|
Cheat,
|
||||||
MaxCategoryID /* Don't Remove this */
|
MaxCategoryID /* Don't Remove this */
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -202,6 +203,7 @@ namespace Logs {
|
|||||||
"Expeditions",
|
"Expeditions",
|
||||||
"DynamicZones",
|
"DynamicZones",
|
||||||
"Scheduler",
|
"Scheduler",
|
||||||
|
"Cheat"
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -646,6 +646,16 @@
|
|||||||
OutF(LogSys, Logs::Detail, Logs::Scheduler, __FILE__, __func__, __LINE__, message, ##__VA_ARGS__);\
|
OutF(LogSys, Logs::Detail, Logs::Scheduler, __FILE__, __func__, __LINE__, message, ##__VA_ARGS__);\
|
||||||
} while (0)
|
} 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 {\
|
#define Log(debug_level, log_category, message, ...) do {\
|
||||||
if (LogSys.log_settings[log_category].is_category_enabled == 1)\
|
if (LogSys.log_settings[log_category].is_category_enabled == 1)\
|
||||||
LogSys.Out(debug_level, log_category, __FILE__, __func__, __LINE__, message, ##__VA_ARGS__);\
|
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_INT(DynamicZone, WorldProcessRate, 6000, "Timer interval (milliseconds) that systems check their dynamic zone states")
|
||||||
RULE_CATEGORY_END()
|
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_CATEGORY
|
||||||
#undef RULE_INT
|
#undef RULE_INT
|
||||||
#undef RULE_REAL
|
#undef RULE_REAL
|
||||||
|
|||||||
@ -14,6 +14,7 @@ SET(zone_sources
|
|||||||
bot_command.cpp
|
bot_command.cpp
|
||||||
bot_database.cpp
|
bot_database.cpp
|
||||||
botspellsai.cpp
|
botspellsai.cpp
|
||||||
|
cheat_manager.cpp
|
||||||
client.cpp
|
client.cpp
|
||||||
client_mods.cpp
|
client_mods.cpp
|
||||||
client_packet.cpp
|
client_packet.cpp
|
||||||
@ -157,7 +158,8 @@ SET(zone_sources
|
|||||||
zone_event_scheduler.cpp
|
zone_event_scheduler.cpp
|
||||||
zone_reload.cpp
|
zone_reload.cpp
|
||||||
zone_store.cpp
|
zone_store.cpp
|
||||||
zoning.cpp)
|
zoning.cpp
|
||||||
|
)
|
||||||
|
|
||||||
SET(zone_headers
|
SET(zone_headers
|
||||||
aa.h
|
aa.h
|
||||||
@ -171,6 +173,7 @@ SET(zone_headers
|
|||||||
bot_command.h
|
bot_command.h
|
||||||
bot_database.h
|
bot_database.h
|
||||||
bot_structs.h
|
bot_structs.h
|
||||||
|
cheat_manager.h
|
||||||
client.h
|
client.h
|
||||||
client_packet.h
|
client_packet.h
|
||||||
command.h
|
command.h
|
||||||
@ -274,7 +277,8 @@ SET(zone_headers
|
|||||||
zonedb.h
|
zonedb.h
|
||||||
zonedump.h
|
zonedump.h
|
||||||
zone_reload.h
|
zone_reload.h
|
||||||
zone_store.h)
|
zone_store.h
|
||||||
|
)
|
||||||
|
|
||||||
ADD_EXECUTABLE(zone ${zone_sources} ${zone_headers})
|
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 "mob_movement_manager.h"
|
||||||
#include "../common/content/world_content_service.h"
|
#include "../common/content/world_content_service.h"
|
||||||
#include "../common/expedition_lockout_timer.h"
|
#include "../common/expedition_lockout_timer.h"
|
||||||
|
#include "cheat_manager.h"
|
||||||
|
|
||||||
extern QueryServ* QServ;
|
extern QueryServ* QServ;
|
||||||
extern EntityList entity_list;
|
extern EntityList entity_list;
|
||||||
@ -177,7 +178,7 @@ Client::Client(EQStreamInterface* ieqs)
|
|||||||
|
|
||||||
for (int client_filter = 0; client_filter < _FilterCount; client_filter++)
|
for (int client_filter = 0; client_filter < _FilterCount; client_filter++)
|
||||||
ClientFilters[client_filter] = FilterShow;
|
ClientFilters[client_filter] = FilterShow;
|
||||||
|
cheat_manager.SetClient(this);
|
||||||
mMovementManager->AddClient(this);
|
mMovementManager->AddClient(this);
|
||||||
character_id = 0;
|
character_id = 0;
|
||||||
conn_state = NoPacketsReceived;
|
conn_state = NoPacketsReceived;
|
||||||
|
|||||||
@ -66,6 +66,7 @@ namespace EQ
|
|||||||
#include "zone_store.h"
|
#include "zone_store.h"
|
||||||
#include "task_manager.h"
|
#include "task_manager.h"
|
||||||
#include "task_client_state.h"
|
#include "task_client_state.h"
|
||||||
|
#include "cheat_manager.h"
|
||||||
|
|
||||||
#ifdef _WINDOWS
|
#ifdef _WINDOWS
|
||||||
// since windows defines these within windef.h (which windows.h include)
|
// since windows defines these within windef.h (which windows.h include)
|
||||||
@ -120,17 +121,6 @@ typedef enum {
|
|||||||
EvacToSafeCoords
|
EvacToSafeCoords
|
||||||
} ZoneMode;
|
} ZoneMode;
|
||||||
|
|
||||||
typedef enum {
|
|
||||||
MQWarp,
|
|
||||||
MQWarpShadowStep,
|
|
||||||
MQWarpKnockBack,
|
|
||||||
MQWarpLight,
|
|
||||||
MQZone,
|
|
||||||
MQZoneUnknownDest,
|
|
||||||
MQGate,
|
|
||||||
MQGhost
|
|
||||||
} CheatTypes;
|
|
||||||
|
|
||||||
enum {
|
enum {
|
||||||
HideCorpseNone = 0,
|
HideCorpseNone = 0,
|
||||||
HideCorpseAll = 1,
|
HideCorpseAll = 1,
|
||||||
@ -1598,6 +1588,7 @@ public:
|
|||||||
Raid *p_raid_instance;
|
Raid *p_raid_instance;
|
||||||
|
|
||||||
void ShowDevToolsMenu();
|
void ShowDevToolsMenu();
|
||||||
|
CheatManager cheat_manager;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
friend class Mob;
|
friend class Mob;
|
||||||
|
|||||||
@ -209,7 +209,7 @@ void MapOpcodes()
|
|||||||
ConnectedOpcodes[OP_FeignDeath] = &Client::Handle_OP_FeignDeath;
|
ConnectedOpcodes[OP_FeignDeath] = &Client::Handle_OP_FeignDeath;
|
||||||
ConnectedOpcodes[OP_FindPersonRequest] = &Client::Handle_OP_FindPersonRequest;
|
ConnectedOpcodes[OP_FindPersonRequest] = &Client::Handle_OP_FindPersonRequest;
|
||||||
ConnectedOpcodes[OP_Fishing] = &Client::Handle_OP_Fishing;
|
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_Forage] = &Client::Handle_OP_Forage;
|
||||||
ConnectedOpcodes[OP_FriendsWho] = &Client::Handle_OP_FriendsWho;
|
ConnectedOpcodes[OP_FriendsWho] = &Client::Handle_OP_FriendsWho;
|
||||||
ConnectedOpcodes[OP_GetGuildMOTD] = &Client::Handle_OP_GetGuildMOTD;
|
ConnectedOpcodes[OP_GetGuildMOTD] = &Client::Handle_OP_GetGuildMOTD;
|
||||||
@ -413,6 +413,7 @@ void MapOpcodes()
|
|||||||
ConnectedOpcodes[OP_YellForHelp] = &Client::Handle_OP_YellForHelp;
|
ConnectedOpcodes[OP_YellForHelp] = &Client::Handle_OP_YellForHelp;
|
||||||
ConnectedOpcodes[OP_ZoneChange] = &Client::Handle_OP_ZoneChange;
|
ConnectedOpcodes[OP_ZoneChange] = &Client::Handle_OP_ZoneChange;
|
||||||
ConnectedOpcodes[OP_ResetAA] = &Client::Handle_OP_ResetAA;
|
ConnectedOpcodes[OP_ResetAA] = &Client::Handle_OP_ResetAA;
|
||||||
|
ConnectedOpcodes[OP_UnderWorld] = &Client::Handle_OP_UnderWorld;
|
||||||
}
|
}
|
||||||
|
|
||||||
void ClearMappedOpcode(EmuOpcode op)
|
void ClearMappedOpcode(EmuOpcode op)
|
||||||
@ -2922,6 +2923,7 @@ void Client::Handle_OP_Assist(const EQApplicationPacket *app)
|
|||||||
Mob *new_target = assistee->GetTarget();
|
Mob *new_target = assistee->GetTarget();
|
||||||
if (new_target && (GetGM() ||
|
if (new_target && (GetGM() ||
|
||||||
Distance(m_Position, assistee->GetPosition()) <= TARGETING_RANGE)) {
|
Distance(m_Position, assistee->GetPosition()) <= TARGETING_RANGE)) {
|
||||||
|
cheat_manager.SetExemptStatus(Assist, true);
|
||||||
eid->entity_id = new_target->GetID();
|
eid->entity_id = new_target->GetID();
|
||||||
} else {
|
} else {
|
||||||
eid->entity_id = 0;
|
eid->entity_id = 0;
|
||||||
@ -4500,6 +4502,8 @@ void Client::Handle_OP_ClientUpdate(const EQApplicationPacket *app) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
cheat_manager.MovementCheck(glm::vec3(cx, cy, cz));
|
||||||
|
|
||||||
if (IsDraggingCorpse())
|
if (IsDraggingCorpse())
|
||||||
DragCorpses();
|
DragCorpses();
|
||||||
|
|
||||||
@ -8765,6 +8769,7 @@ void Client::Handle_OP_ItemVerifyRequest(const EQApplicationPacket *app)
|
|||||||
slot_id = request->slot;
|
slot_id = request->slot;
|
||||||
target_id = request->target;
|
target_id = request->target;
|
||||||
|
|
||||||
|
cheat_manager.ProcessItemVerifyRequest(request->slot, request->target);
|
||||||
|
|
||||||
EQApplicationPacket *outapp = nullptr;
|
EQApplicationPacket *outapp = nullptr;
|
||||||
outapp = new EQApplicationPacket(OP_ItemVerifyReply, sizeof(ItemVerifyReply_Struct));
|
outapp = new EQApplicationPacket(OP_ItemVerifyReply, sizeof(ItemVerifyReply_Struct));
|
||||||
@ -9614,6 +9619,7 @@ return;
|
|||||||
|
|
||||||
void Client::Handle_OP_MemorizeSpell(const EQApplicationPacket *app)
|
void Client::Handle_OP_MemorizeSpell(const EQApplicationPacket *app)
|
||||||
{
|
{
|
||||||
|
cheat_manager.CheckMemTimer();
|
||||||
OPMemorizeSpell(app);
|
OPMemorizeSpell(app);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -13465,6 +13471,8 @@ void Client::Handle_OP_SpawnAppearance(const EQApplicationPacket *app)
|
|||||||
}
|
}
|
||||||
SpawnAppearance_Struct* sa = (SpawnAppearance_Struct*)app->pBuffer;
|
SpawnAppearance_Struct* sa = (SpawnAppearance_Struct*)app->pBuffer;
|
||||||
|
|
||||||
|
cheat_manager.ProcessSpawnApperance(sa->spawn_id, sa->type, sa->parameter);
|
||||||
|
|
||||||
if (sa->spawn_id != GetID())
|
if (sa->spawn_id != GetID())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
@ -13917,6 +13925,11 @@ void Client::Handle_OP_TargetCommand(const EQApplicationPacket *app)
|
|||||||
GetTarget()->IsTargeted(1);
|
GetTarget()->IsTargeted(1);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
else if (cheat_manager.GetExemptStatus(Assist)) {
|
||||||
|
GetTarget()->IsTargeted(1);
|
||||||
|
cheat_manager.SetExemptStatus(Assist, false);
|
||||||
|
return;
|
||||||
|
}
|
||||||
else if (GetTarget()->IsClient())
|
else if (GetTarget()->IsClient())
|
||||||
{
|
{
|
||||||
//make sure this client is in our raid/group
|
//make sure this client is in our raid/group
|
||||||
@ -13932,6 +13945,15 @@ void Client::Handle_OP_TargetCommand(const EQApplicationPacket *app)
|
|||||||
SetTarget((Mob*)nullptr);
|
SetTarget((Mob*)nullptr);
|
||||||
return;
|
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()))
|
else if (IsXTarget(GetTarget()))
|
||||||
{
|
{
|
||||||
GetTarget()->IsTargeted(1);
|
GetTarget()->IsTargeted(1);
|
||||||
@ -15173,3 +15195,21 @@ void Client::Handle_OP_ResetAA(const EQApplicationPacket *app)
|
|||||||
}
|
}
|
||||||
return;
|
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_YellForHelp(const EQApplicationPacket *app);
|
||||||
void Handle_OP_ZoneChange(const EQApplicationPacket *app);
|
void Handle_OP_ZoneChange(const EQApplicationPacket *app);
|
||||||
void Handle_OP_ResetAA(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())
|
if (IsStunned() && stunned_timer.Check())
|
||||||
Mob::UnStun();
|
Mob::UnStun();
|
||||||
|
|
||||||
|
cheat_manager.ClientProcess();
|
||||||
|
|
||||||
if (bardsong_timer.Check() && bardsong != 0) {
|
if (bardsong_timer.Check() && bardsong != 0) {
|
||||||
//NOTE: this is kinda a heavy-handed check to make sure the mob still exists before
|
//NOTE: this is kinda a heavy-handed check to make sure the mob still exists before
|
||||||
//doing the next pulse on them...
|
//doing the next pulse on them...
|
||||||
|
|||||||
@ -120,6 +120,7 @@ const char *QuestEventSubroutines[_LargestEventID] = {
|
|||||||
"EVENT_USE_SKILL",
|
"EVENT_USE_SKILL",
|
||||||
"EVENT_COMBINE_VALIDATE",
|
"EVENT_COMBINE_VALIDATE",
|
||||||
"EVENT_BOT_COMMAND",
|
"EVENT_BOT_COMMAND",
|
||||||
|
"EVENT_WARP",
|
||||||
"EVENT_TEST_BUFF"
|
"EVENT_TEST_BUFF"
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -1636,6 +1637,13 @@ void PerlembParser::ExportEventVariables(
|
|||||||
ExportVar(package_name.c_str(), "langid", extradata);
|
ExportVar(package_name.c_str(), "langid", extradata);
|
||||||
break;
|
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: {
|
default: {
|
||||||
break;
|
break;
|
||||||
|
|||||||
@ -88,6 +88,7 @@ typedef enum {
|
|||||||
EVENT_USE_SKILL,
|
EVENT_USE_SKILL,
|
||||||
EVENT_COMBINE_VALIDATE,
|
EVENT_COMBINE_VALIDATE,
|
||||||
EVENT_BOT_COMMAND,
|
EVENT_BOT_COMMAND,
|
||||||
|
EVENT_WARP,
|
||||||
EVENT_TEST_BUFF,
|
EVENT_TEST_BUFF,
|
||||||
_LargestEventID
|
_LargestEventID
|
||||||
} QuestEventID;
|
} QuestEventID;
|
||||||
|
|||||||
@ -3225,6 +3225,7 @@ luabind::scope lua_register_events() {
|
|||||||
luabind::value("spawn_zone", static_cast<int>(EVENT_SPAWN_ZONE)),
|
luabind::value("spawn_zone", static_cast<int>(EVENT_SPAWN_ZONE)),
|
||||||
luabind::value("death_zone", static_cast<int>(EVENT_DEATH_ZONE)),
|
luabind::value("death_zone", static_cast<int>(EVENT_DEATH_ZONE)),
|
||||||
luabind::value("use_skill", static_cast<int>(EVENT_USE_SKILL)),
|
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))
|
luabind::value("test_buff", static_cast<int>(EVENT_TEST_BUFF))
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|||||||
@ -131,6 +131,7 @@ const char *LuaEvents[_LargestEventID] = {
|
|||||||
"event_use_skill",
|
"event_use_skill",
|
||||||
"event_combine_validate",
|
"event_combine_validate",
|
||||||
"event_bot_command",
|
"event_bot_command",
|
||||||
|
"event_warp",
|
||||||
"event_test_buff"
|
"event_test_buff"
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -217,6 +218,7 @@ LuaParser::LuaParser() {
|
|||||||
PlayerArgumentDispatch[EVENT_TEST_BUFF] = handle_test_buff;
|
PlayerArgumentDispatch[EVENT_TEST_BUFF] = handle_test_buff;
|
||||||
PlayerArgumentDispatch[EVENT_COMBINE_VALIDATE] = handle_player_combine_validate;
|
PlayerArgumentDispatch[EVENT_COMBINE_VALIDATE] = handle_player_combine_validate;
|
||||||
PlayerArgumentDispatch[EVENT_BOT_COMMAND] = handle_player_bot_command;
|
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] = handle_item_click;
|
||||||
ItemArgumentDispatch[EVENT_ITEM_CLICK_CAST] = 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");
|
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
|
//Item
|
||||||
void handle_item_click(QuestInterface *parse, lua_State* L, Client* client, EQ::ItemInstance* item, Mob *mob, std::string data, uint32 extra_data,
|
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) {
|
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);
|
std::vector<EQ::Any>* extra_pointers);
|
||||||
void handle_player_bot_command(QuestInterface *parse, lua_State* L, Client* client, std::string data, uint32 extra_data,
|
void handle_player_bot_command(QuestInterface *parse, lua_State* L, Client* client, std::string data, uint32 extra_data,
|
||||||
std::vector<EQ::Any> *extra_pointers);
|
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
|
//Item
|
||||||
void handle_item_click(QuestInterface *parse, lua_State* L, Client* client, EQ::ItemInstance* item, Mob *mob, std::string data, uint32 extra_data,
|
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())
|
if(IsClient())
|
||||||
{
|
{
|
||||||
|
CastToClient()->cheat_manager.SetExemptStatus(KnockBack, true);
|
||||||
auto outapp_push = new EQApplicationPacket(OP_ClientUpdate, sizeof(PlayerPositionUpdateServer_Struct));
|
auto outapp_push = new EQApplicationPacket(OP_ClientUpdate, sizeof(PlayerPositionUpdateServer_Struct));
|
||||||
PlayerPositionUpdateServer_Struct* spu = (PlayerPositionUpdateServer_Struct*)outapp_push->pBuffer;
|
PlayerPositionUpdateServer_Struct* spu = (PlayerPositionUpdateServer_Struct*)outapp_push->pBuffer;
|
||||||
|
|
||||||
|
|||||||
@ -2743,6 +2743,22 @@ void Mob::BardPulse(uint16 spell_id, Mob *caster) {
|
|||||||
|
|
||||||
action->effect_flag = 4;
|
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))
|
if(!IsEffectInSpell(spell_id, SE_BindAffinity))
|
||||||
{
|
{
|
||||||
CastToClient()->QueuePacket(packet);
|
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(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.x += action->force * g_Math.FastSin(action->hit_heading);
|
||||||
spelltar->m_Delta.y += action->force * g_Math.FastCos(action->hit_heading);
|
spelltar->m_Delta.y += action->force * g_Math.FastCos(action->hit_heading);
|
||||||
spelltar->m_Delta.z += action->hit_pitch;
|
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(!IsEffectInSpell(spell_id, SE_BindAffinity))
|
||||||
{
|
{
|
||||||
if(spelltar != this && spelltar->IsClient()) // send to target
|
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
|
// 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))
|
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("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());
|
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
|
//unable to find a zone point... is there anything else
|
||||||
//that can be a valid un-zolicited zone request?
|
//that can be a valid un-zolicited zone request?
|
||||||
|
|
||||||
//Todo cheat detection
|
|
||||||
Message(Chat::Red, "Invalid unsolicited zone request.");
|
Message(Chat::Red, "Invalid unsolicited zone request.");
|
||||||
LogError("Zoning [{}]: Invalid unsolicited zone request to zone id [{}]", GetName(), target_zone_id);
|
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);
|
SendZoneCancel(zc);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -134,7 +139,12 @@ void Client::Handle_OP_ZoneChange(const EQApplicationPacket *app) {
|
|||||||
//then we assume this is invalid.
|
//then we assume this is invalid.
|
||||||
if(!zone_point || zone_point->target_zone_id != target_zone_id) {
|
if(!zone_point || zone_point->target_zone_id != target_zone_id) {
|
||||||
LogError("Zoning [{}]: Invalid unsolicited zone request to zone id [{}]", GetName(), 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);
|
SendZoneCancel(zc);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -282,7 +292,12 @@ void Client::Handle_OP_ZoneChange(const EQApplicationPacket *app) {
|
|||||||
//for now, there are no other cases...
|
//for now, there are no other cases...
|
||||||
|
|
||||||
//could not find a valid reason for them to be zoning, stop it.
|
//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);
|
LogError("Zoning [{}]: Invalid unsolicited zone request to zone id [{}]. Not near a zone point", GetName(), target_zone_name);
|
||||||
SendZoneCancel(zc);
|
SendZoneCancel(zc);
|
||||||
return;
|
return;
|
||||||
@ -379,6 +394,7 @@ void Client::Handle_OP_ZoneChange(const EQApplicationPacket *app) {
|
|||||||
void Client::SendZoneCancel(ZoneChange_Struct *zc) {
|
void Client::SendZoneCancel(ZoneChange_Struct *zc) {
|
||||||
//effectively zone them right back to where they were
|
//effectively zone them right back to where they were
|
||||||
//unless we find a better way to stop the zoning process.
|
//unless we find a better way to stop the zoning process.
|
||||||
|
cheat_manager.SetExemptStatus(Port, true);
|
||||||
EQApplicationPacket *outapp = nullptr;
|
EQApplicationPacket *outapp = nullptr;
|
||||||
outapp = new EQApplicationPacket(OP_ZoneChange, sizeof(ZoneChange_Struct));
|
outapp = new EQApplicationPacket(OP_ZoneChange, sizeof(ZoneChange_Struct));
|
||||||
ZoneChange_Struct *zc2 = (ZoneChange_Struct*)outapp->pBuffer;
|
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)
|
void Client::SendZoneError(ZoneChange_Struct *zc, int8 err)
|
||||||
{
|
{
|
||||||
LogError("Zone [{}] is not available because target wasn't found or character insufficent level", zc->zoneID);
|
LogError("Zone [{}] is not available because target wasn't found or character insufficent level", zc->zoneID);
|
||||||
|
cheat_manager.SetExemptStatus(Port, true);
|
||||||
EQApplicationPacket *outapp = nullptr;
|
EQApplicationPacket *outapp = nullptr;
|
||||||
outapp = new EQApplicationPacket(OP_ZoneChange, sizeof(ZoneChange_Struct));
|
outapp = new EQApplicationPacket(OP_ZoneChange, sizeof(ZoneChange_Struct));
|
||||||
ZoneChange_Struct *zc2 = (ZoneChange_Struct*)outapp->pBuffer;
|
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);
|
pShortZoneName = ZoneName(zoneID);
|
||||||
content_db.GetZoneLongName(pShortZoneName, &pZoneName);
|
content_db.GetZoneLongName(pShortZoneName, &pZoneName);
|
||||||
|
|
||||||
|
cheat_manager.SetExemptStatus(Port, true);
|
||||||
|
|
||||||
if(!pZoneName) {
|
if(!pZoneName) {
|
||||||
Message(Chat::Red, "Invalid zone number specified");
|
Message(Chat::Red, "Invalid zone number specified");
|
||||||
safe_delete_array(pZoneName);
|
safe_delete_array(pZoneName);
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user