Compare commits

...

16 Commits

Author SHA1 Message Date
Chris Miles fcb0a47280 [Release] 22.4.3 (#2976) 2023-02-21 10:42:43 -06:00
Chris Miles 1e50f19f7e [Pathing] Improvements to z-clipping, z-recovery and z-calculations (#2975)
* zclip adjustments

* Remove debug
2023-02-21 10:24:25 -06:00
Chris Miles 33bb5aa8e5 [Database] Address deadlock in player events (#2974)
* DB mutex testing

* Mutex tweaks, native string escaping
2023-02-20 22:32:29 -06:00
Aeadoin 6a668f8aa5 [Crash] Fix crash with EVENT_UNEQUIP_ITEM_BOT (#2973) 2023-02-20 17:31:15 -05:00
Alex King df499b22ab [Bug Fix] Fix MIR LDoN Theme Items on LDoN Merchants (#2971)
# Notes
- These items weren't showing as MIR due to this condition being typo'd in https://github.com/EQEmu/Server/pull/1611/files.
2023-02-19 21:51:37 -05:00
Alex King 7bc00cb466 [Bug Fix] Fix OOCMute not functioning (#2970)
# Notes
- #oocmute was not functioning as the packet wasn't being handled by the server.
2023-02-19 20:39:24 -05:00
Paul Coene 51f6108aab [Pets] Client Pet summoned by NPC should not change guard location. (#2967)
* [Pets] Client Pet summoned by NPC should not change guard location.

* Update mob.cpp

---------

Co-authored-by: Alex King <89047260+Kinglykrab@users.noreply.github.com>
2023-02-19 16:56:34 -05:00
Aeadoin c13f9f80d9 [Bots] Change HasBotItem(item_id) to return slot_id instead of bool. (#2966) 2023-02-19 16:13:28 -05:00
JJ 443abf9199 [SQL] Add date to optional Drakkin Guktan Faction Update (#2965) 2023-02-19 16:11:07 -05:00
Alex King 1556e05b2f [Quest API] Add client->SignalClient() overload to Perl (#2963)
# Notes
- Fixes an issue with Guild Lobby quests not properly using `client` as first parameter in `mob->SignalClient(client, signal_id)` method.
2023-02-19 15:20:28 -05:00
Alex King 1d645aa5f6 [Quest API] Fix Perl SetSimpleRoamBox Overloads (#2961)
# Notes
- These overloads were non-functional as they didn't have a method to actually fall back to.
2023-02-19 11:17:35 -05:00
Chris Miles 9f42da5bad [Crash] Fix world crash in player event processing (#2960)
* [Crash] Fix world crash in player event processing

* Add rule BatchPlayerEventProcessChunkSize
2023-02-18 16:06:16 -06:00
Aeadoin 4a8222f243 [Bots] Fix output of ^spells while ^Enforcespellsettings is enabled (#2959) 2023-02-18 16:06:06 -06:00
Chris Miles db4c515853 [Player Events] Create new event ITEM_CREATION (#2944) 2023-02-18 16:05:23 -06:00
Chris Miles 462656a201 [Reload API] Add world handlers for certain opcodes (#2958) 2023-02-18 15:58:29 -06:00
Aeadoin ddd98be383 [Bot] Change SaveTimers to Replace instead of Insert. (#2951)
* [Bot] Change SaveTimers to Replace instead of Insert.

* [Bot] Change SaveTimers to Replace instead of Insert.

* fix formatting
2023-02-18 16:13:36 -05:00
28 changed files with 242 additions and 70 deletions
+47
View File
@@ -1,3 +1,50 @@
## [22.4.3] - 02/21/2023
### Bots
* Change HasBotItem(item_id) to return slot_id instead of bool. ([#2966](https://github.com/EQEmu/Server/pull/2966)) ([Aeadoin](https://github.com/Aeadoin)) 2023-02-19
* Change SaveTimers to Replace instead of Insert. ([#2951](https://github.com/EQEmu/Server/pull/2951)) ([Aeadoin](https://github.com/Aeadoin)) 2023-02-18
* Fix output of ^spells while ^Enforcespellsettings is enabled ([#2959](https://github.com/EQEmu/Server/pull/2959)) ([Aeadoin](https://github.com/Aeadoin)) 2023-02-18
### Crash
* Fix crash with EVENT_UNEQUIP_ITEM_BOT ([#2973](https://github.com/EQEmu/Server/pull/2973)) ([Aeadoin](https://github.com/Aeadoin)) 2023-02-20
* Fix world crash in player event processing ([#2960](https://github.com/EQEmu/Server/pull/2960)) ([Akkadius](https://github.com/Akkadius)) 2023-02-18
### Database
* Address deadlock in player events ([#2974](https://github.com/EQEmu/Server/pull/2974)) ([Akkadius](https://github.com/Akkadius)) 2023-02-21
### Fixes
* Fix MIR LDoN Theme Items on LDoN Merchants ([#2971](https://github.com/EQEmu/Server/pull/2971)) ([Kinglykrab](https://github.com/Kinglykrab)) 2023-02-20
* Fix OOCMute not functioning ([#2970](https://github.com/EQEmu/Server/pull/2970)) ([Kinglykrab](https://github.com/Kinglykrab)) 2023-02-20
### Pathing
* Improvements to z-clipping, z-recovery and z-calculations ([#2975](https://github.com/EQEmu/Server/pull/2975)) ([Akkadius](https://github.com/Akkadius)) 2023-02-21
### Pets
* Client Pet summoned by NPC should not change guard location. ([#2967](https://github.com/EQEmu/Server/pull/2967)) ([noudess](https://github.com/noudess)) 2023-02-19
### Player Events
* Create new event ITEM_CREATION ([#2944](https://github.com/EQEmu/Server/pull/2944)) ([Akkadius](https://github.com/Akkadius)) 2023-02-18
### Quest API
* Add client->SignalClient() overload to Perl ([#2963](https://github.com/EQEmu/Server/pull/2963)) ([Kinglykrab](https://github.com/Kinglykrab)) 2023-02-19
* Fix Perl SetSimpleRoamBox Overloads ([#2961](https://github.com/EQEmu/Server/pull/2961)) ([Kinglykrab](https://github.com/Kinglykrab)) 2023-02-19
### Reload API
* Add world handlers for certain opcodes ([#2958](https://github.com/EQEmu/Server/pull/2958)) ([Akkadius](https://github.com/Akkadius)) 2023-02-18
### SQL
* Add date to optional Drakkin Guktan Faction Update ([#2965](https://github.com/EQEmu/Server/pull/2965)) ([joligario](https://github.com/joligario)) 2023-02-19
## [22.4.2] - 02/18/2023
### Content
+15 -7
View File
@@ -70,17 +70,20 @@ DBcore::~DBcore()
// Sends the MySQL server a keepalive
void DBcore::ping()
{
if (!MDatabase.trylock()) {
if (!m_query_lock.try_lock()) {
// well, if's it's locked, someone's using it. If someone's using it, it doesnt need a keepalive
return;
}
mysql_ping(&mysql);
MDatabase.unlock();
m_query_lock.unlock();
}
MySQLRequestResult DBcore::QueryDatabase(std::string query, bool retryOnFailureOnce)
{
return QueryDatabase(query.c_str(), query.length(), retryOnFailureOnce);
m_query_lock.lock();
auto r = QueryDatabase(query.c_str(), query.length(), retryOnFailureOnce);
m_query_lock.unlock();
return r;
}
bool DBcore::DoesTableExist(std::string table_name)
@@ -95,15 +98,11 @@ MySQLRequestResult DBcore::QueryDatabase(const char *query, uint32 querylen, boo
BenchTimer timer;
timer.reset();
LockMutex lock(&MDatabase);
// Reconnect if we are not connected before hand.
if (pStatus != Connected) {
Open();
}
// request query. != 0 indicates some kind of error.
if (mysql_real_query(&mysql, query, querylen) != 0) {
unsigned int errorNumber = mysql_errno(&mysql);
@@ -299,3 +298,12 @@ void DBcore::SetOriginHost(const std::string &origin_host)
{
DBcore::origin_host = origin_host;
}
std::string DBcore::Escape(const std::string& s)
{
const std::size_t s_len = s.length();
std::vector<char> temp((s_len * 2) + 1, '\0');
mysql_real_escape_string(&mysql, temp.data(), s.c_str(), s_len);
return temp.data();
}
+4
View File
@@ -12,6 +12,7 @@
#include <mysql.h>
#include <string.h>
#include <mutex>
class DBcore {
public:
@@ -27,6 +28,7 @@ public:
void TransactionBegin();
void TransactionCommit();
void TransactionRollback();
std::string Escape(const std::string& s);
uint32 DoEscapeString(char *tobuf, const char *frombuf, uint32 fromlen);
void ping();
MYSQL *getMySQL() { return &mysql; }
@@ -57,6 +59,8 @@ private:
Mutex MDatabase;
eStatus pStatus;
std::mutex m_query_lock{};
std::string origin_host;
char *pHost;
+7 -1
View File
@@ -113,7 +113,9 @@ bool PlayerEventLogs::IsEventEnabled(PlayerEvent::EventType event)
// this processes any current player events on the queue
void PlayerEventLogs::ProcessBatchQueue()
{
m_batch_queue_lock.lock();
if (m_record_batch_queue.empty()) {
m_batch_queue_lock.unlock();
return;
}
@@ -128,7 +130,6 @@ void PlayerEventLogs::ProcessBatchQueue()
);
// empty
m_batch_queue_lock.lock();
m_record_batch_queue = {};
m_batch_queue_lock.unlock();
}
@@ -139,6 +140,10 @@ void PlayerEventLogs::AddToQueue(const PlayerEventLogsRepository::PlayerEventLog
m_batch_queue_lock.lock();
m_record_batch_queue.emplace_back(log);
m_batch_queue_lock.unlock();
if (m_record_batch_queue.size() >= RuleI(Logging, BatchPlayerEventProcessChunkSize)) {
ProcessBatchQueue();
}
}
// fills common event data in the SendEvent function
@@ -699,6 +704,7 @@ void PlayerEventLogs::SetSettingsDefaults()
m_settings[PlayerEvent::KILLED_NPC].event_enabled = 0;
m_settings[PlayerEvent::KILLED_NAMED_NPC].event_enabled = 1;
m_settings[PlayerEvent::KILLED_RAID_NPC].event_enabled = 1;
m_settings[PlayerEvent::ITEM_CREATION].event_enabled = 1;
for (int i = PlayerEvent::GM_COMMAND; i != PlayerEvent::MAX; i++) {
m_settings[i].retention_days = RETENTION_DAYS_DEFAULT;
+37 -1
View File
@@ -55,6 +55,7 @@ namespace PlayerEvent {
KILLED_NPC,
KILLED_NAMED_NPC,
KILLED_RAID_NPC,
ITEM_CREATION,
MAX // dont remove
};
@@ -110,7 +111,8 @@ namespace PlayerEvent {
"Possible Hack",
"Killed NPC",
"Killed Named NPC",
"Killed Raid NPC"
"Killed Raid NPC",
"Item Creation"
};
// Generic struct used by all events
@@ -184,6 +186,40 @@ namespace PlayerEvent {
}
};
// used in Trade event
struct ItemCreationEvent {
int64 item_id;
std::string item_name;
uint16 to_slot;
int16 charges;
uint32 aug1;
uint32 aug2;
uint32 aug3;
uint32 aug4;
uint32 aug5;
uint32 aug6;
bool attuned;
// cereal
template<class Archive>
void serialize(Archive &ar)
{
ar(
CEREAL_NVP(item_id),
CEREAL_NVP(item_name),
CEREAL_NVP(to_slot),
CEREAL_NVP(charges),
CEREAL_NVP(aug1),
CEREAL_NVP(aug2),
CEREAL_NVP(aug3),
CEREAL_NVP(aug4),
CEREAL_NVP(aug5),
CEREAL_NVP(aug6),
CEREAL_NVP(attuned)
);
}
};
// used in Trade event
struct TradeItem {
int64 item_id;
@@ -240,8 +240,8 @@ public:
v.push_back(columns[7] + " = " + std::to_string(e.z));
v.push_back(columns[8] + " = " + std::to_string(e.heading));
v.push_back(columns[9] + " = " + std::to_string(e.event_type_id));
v.push_back(columns[10] + " = '" + Strings::Escape(e.event_type_name) + "'");
v.push_back(columns[11] + " = '" + Strings::Escape(e.event_data) + "'");
v.push_back(columns[10] + " = '" + db.Escape(e.event_type_name) + "'");
v.push_back(columns[11] + " = '" + db.Escape(e.event_data) + "'");
v.push_back(columns[12] + " = FROM_UNIXTIME(" + (e.created_at > 0 ? std::to_string(e.created_at) : "null") + ")");
auto results = db.QueryDatabase(
@@ -274,8 +274,8 @@ public:
v.push_back(std::to_string(e.z));
v.push_back(std::to_string(e.heading));
v.push_back(std::to_string(e.event_type_id));
v.push_back("'" + Strings::Escape(e.event_type_name) + "'");
v.push_back("'" + Strings::Escape(e.event_data) + "'");
v.push_back("'" + db.Escape(e.event_type_name) + "'");
v.push_back("'" + db.Escape(e.event_data) + "'");
v.push_back("FROM_UNIXTIME(" + (e.created_at > 0 ? std::to_string(e.created_at) : "null") + ")");
auto results = db.QueryDatabase(
@@ -316,8 +316,8 @@ public:
v.push_back(std::to_string(e.z));
v.push_back(std::to_string(e.heading));
v.push_back(std::to_string(e.event_type_id));
v.push_back("'" + Strings::Escape(e.event_type_name) + "'");
v.push_back("'" + Strings::Escape(e.event_data) + "'");
v.push_back("'" + db.Escape(e.event_type_name) + "'");
v.push_back("'" + db.Escape(e.event_data) + "'");
v.push_back("FROM_UNIXTIME(" + (e.created_at > 0 ? std::to_string(e.created_at) : "null") + ")");
insert_chunks.push_back("(" + Strings::Implode(",", v) + ")");
+1
View File
@@ -783,6 +783,7 @@ RULE_BOOL(Logging, PrintFileFunctionAndLine, false, "Ex: [World Server] [net.cpp
RULE_BOOL(Logging, WorldGMSayLogging, true, "Relay worldserver logging to zone processes via GM say output")
RULE_BOOL(Logging, PlayerEventsQSProcess, false, "Have query server process player events instead of world. Useful when wanting to use a dedicated server and database for processing player events on separate disk")
RULE_INT(Logging, BatchPlayerEventProcessIntervalSeconds, 5, "This is the interval in which player events are processed in world or qs")
RULE_INT(Logging, BatchPlayerEventProcessChunkSize, 10000, "This is the cap of events that can be inserted into the queue before a force flush. This is to keep from hitting MySQL max_allowed_packet and killing the connection")
RULE_CATEGORY_END()
RULE_CATEGORY(HotReload)
+1 -1
View File
@@ -25,7 +25,7 @@
// Build variables
// these get injected during the build pipeline
#define CURRENT_VERSION "22.4.2-dev" // always append -dev to the current version for custom-builds
#define CURRENT_VERSION "22.4.3-dev" // always append -dev to the current version for custom-builds
#define LOGIN_VERSION "0.8.0"
#define COMPILE_DATE __DATE__
#define COMPILE_TIME __TIME__
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "eqemu-server",
"version": "22.4.2",
"version": "22.4.3",
"repository": {
"type": "git",
"url": "https://github.com/EQEmu/Server.git"
+7
View File
@@ -224,6 +224,13 @@ void EQEmuApiWorldDataService::reload(Json::Value &r, const std::vector<std::str
else {
pack = new ServerPacket(c.opcode, 0);
message(r, fmt::format("Reloading [{}] globally", c.desc));
if (c.opcode == ServerOP_ReloadLogs) {
LogSys.LoadLogDatabaseSettings();
}
else if (c.opcode == ServerOP_ReloadRules) {
RuleManager::Instance()->LoadRules(&database, RuleManager::Instance()->GetActiveRuleset(), true);
}
}
found_command = true;
+1
View File
@@ -1332,6 +1332,7 @@ void ZoneServer::HandleMessage(uint16 opcode, const EQ::Net::Packet &p) {
case ServerOP_ItemStatus:
case ServerOP_KickPlayer:
case ServerOP_KillPlayer:
case ServerOP_OOCMute:
case ServerOP_OOZGroupMessage:
case ServerOP_Petition:
case ServerOP_RaidGroupSay:
+14 -19
View File
@@ -4478,23 +4478,19 @@ uint32 Bot::CountBotItem(uint32 item_id) {
return item_count;
}
bool Bot::HasBotItem(uint32 item_id) {
bool has_item = false;
EQ::ItemInstance *inst = nullptr;
int16 Bot::HasBotItem(uint32 item_id) {
EQ::ItemInstance const *inst = nullptr;
for (uint16 slot_id = EQ::invslot::EQUIPMENT_BEGIN; slot_id <= EQ::invslot::EQUIPMENT_END; ++slot_id) {
inst = GetBotItem(slot_id);
if (!inst || !inst->GetItem()) {
continue;
}
if (inst->GetID() == item_id) {
has_item = true;
break;
return slot_id;
}
}
return has_item;
return INVALID_INDEX;
}
void Bot::RemoveBotItem(uint32 item_id) {
@@ -4616,17 +4612,17 @@ void Bot::PerformTradeWithClient(int16 begin_slot_id, int16 end_slot_id, Client*
struct ClientTrade {
ItemInstance* trade_item_instance;
int16 from_client_slot;
int16 to_bot_slot;
int16 to_bot_slot = invslot::SLOT_INVALID;
ClientTrade(ItemInstance* item, int16 from) : trade_item_instance(item), from_client_slot(from), to_bot_slot(invslot::SLOT_INVALID) { }
ClientTrade(ItemInstance* item, int16 from) : trade_item_instance(item), from_client_slot(from) { }
};
struct ClientReturn {
const ItemInstance* return_item_instance;
ItemInstance* return_item_instance;
int16 from_bot_slot;
int16 to_client_slot;
int16 to_client_slot = invslot::SLOT_INVALID;
ClientReturn(const ItemInstance* item, int16 from) : return_item_instance(item), from_bot_slot(from), to_client_slot(invslot::SLOT_INVALID) { }
ClientReturn(ItemInstance* item, int16 from) : return_item_instance(item), from_bot_slot(from) { }
};
static const int16 bot_equip_order[invslot::EQUIPMENT_COUNT] = {
@@ -4906,7 +4902,7 @@ void Bot::PerformTradeWithClient(int16 begin_slot_id, int16 end_slot_id, Client*
if (trade_instance->GetItem()->IsType2HWeapon()) {
if (!melee_secondary) {
melee_2h_weapon = true;
auto equipped_secondary_weapon = m_inv[invslot::slotSecondary];
auto equipped_secondary_weapon = GetBotItem(invslot::slotSecondary);
if (equipped_secondary_weapon) {
client_return.push_back(ClientReturn(equipped_secondary_weapon, invslot::slotSecondary));
}
@@ -4922,7 +4918,7 @@ void Bot::PerformTradeWithClient(int16 begin_slot_id, int16 end_slot_id, Client*
!trade_instance->IsWeapon()
) {
melee_secondary = true;
auto equipped_primary_weapon = m_inv[invslot::slotPrimary];
auto equipped_primary_weapon = GetBotItem(invslot::slotPrimary);
if (equipped_primary_weapon && equipped_primary_weapon->GetItem()->IsType2HWeapon()) {
client_return.push_back(ClientReturn(equipped_primary_weapon, invslot::slotPrimary));
}
@@ -4937,7 +4933,7 @@ void Bot::PerformTradeWithClient(int16 begin_slot_id, int16 end_slot_id, Client*
trade_iterator.to_bot_slot = index;
if (m_inv[index]) {
client_return.push_back(ClientReturn(m_inv[index], index));
client_return.push_back(ClientReturn(GetBotItem(index), index));
}
break;
@@ -5071,9 +5067,8 @@ void Bot::PerformTradeWithClient(int16 begin_slot_id, int16 end_slot_id, Client*
client->DeleteItemInInventory(return_iterator.from_bot_slot);
} else { // successful trade returns
auto return_instance = m_inv.PopItem(return_iterator.from_bot_slot);
//if (*return_instance != *return_iterator.return_item_instance) {
// // TODO: add logging
//}
if (!database.botdb.DeleteItemBySlot(GetBotID(), return_iterator.from_bot_slot)) {
OwnerMessage(
@@ -9490,7 +9485,7 @@ void Bot::ListBotSpells(uint8 min_level)
auto spell_count = 0;
auto spell_number = 1;
for (const auto& s : (AIBot_spells.size() > AIBot_spells_enforced.size()) ? AIBot_spells : AIBot_spells_enforced) {
for (const auto& s : (GetBotEnforceSpellSetting()) ? AIBot_spells_enforced : AIBot_spells) {
auto b = bot_spell_settings.find(s.spellid);
if (b == bot_spell_settings.end() && s.minlevel >= min_level) {
bot_owner->Message(
+1 -1
View File
@@ -720,7 +720,7 @@ public:
uint32 CountBotItem(uint32 item_id);
std::map<uint16, uint32> GetBotItemSlots();
uint32 GetBotItemBySlot(uint16 slot_id);
bool HasBotItem(uint32 item_id);
int16 HasBotItem(uint32 item_id);
void RemoveBotItem(uint32 item_id);
uint32 GetTotalPlayTime();
+1 -1
View File
@@ -9320,7 +9320,7 @@ void bot_subcommand_inventory_remove(Client *c, const Seperator *sep)
return;
}
const auto* inst = my_bot->GetBotItem(slot_id);
auto* inst = my_bot->GetBotItem(slot_id);
if (!inst) {
std::string slot_message = "is";
switch (slot_id) {
+4 -1
View File
@@ -962,7 +962,10 @@ bool BotDatabase::SaveTimers(Bot* bot_inst)
if (bot_timers[timer_index] <= Timer::GetCurrentTime())
continue;
query = StringFormat("INSERT INTO `bot_timers` (`bot_id`, `timer_id`, `timer_value`) VALUES ('%u', '%u', '%u')", bot_inst->GetBotID(), (timer_index + 1), bot_timers[timer_index]);
query = fmt::format(
"REPLACE INTO `bot_timers` (`bot_id`, `timer_id`, `timer_value`) VALUES ('{}', '{}', '{}')",
bot_inst->GetBotID(), (timer_index + 1), bot_timers[timer_index]
);
auto results = database.QueryDatabase(query);
if (!results.Success()) {
DeleteTimers(bot_inst->GetBotID());
+1 -1
View File
@@ -2144,7 +2144,7 @@ void Client::Handle_OP_AdventureMerchantRequest(const EQApplicationPacket *app)
theme = LDoNThemes::RUJ;
} else if (item->LDoNTheme & LDoNThemeBits::MMCBit) {
theme = LDoNThemes::MMC;
} else if (item->LDoNTheme & LDoNThemeBits::RUJBit) {
} else if (item->LDoNTheme & LDoNThemeBits::MIRBit) {
theme = LDoNThemes::MIR;
} else if (item->LDoNTheme & LDoNThemeBits::GUKBit) {
theme = LDoNThemes::GUK;
+1 -1
View File
@@ -116,7 +116,7 @@ void command_gearup(Client *c, const Seperator *sep)
if (t->IsClient()) {
has_item = t->CastToClient()->GetInv().HasItem(item_id, 1, invWhereWorn) != INVALID_INDEX;
} else if (t->IsBot()) {
has_item = t->CastToBot()->HasBotItem(item_id);
has_item = t->CastToBot()->HasBotItem(item_id) != INVALID_INDEX;
}
bool can_wear_item = false;
+19 -2
View File
@@ -794,6 +794,23 @@ bool Client::SummonItem(uint32 item_id, int16 charges, uint32 aug1, uint32 aug2,
}
}
if (player_event_logs.IsEventEnabled(PlayerEvent::ITEM_CREATION)) {
auto e = PlayerEvent::ItemCreationEvent{};
e.item_id = item->ID;
e.item_name = item->Name;
e.to_slot = to_slot;
e.charges = charges;
e.aug1 = aug1;
e.aug2 = aug2;
e.aug3 = aug3;
e.aug4 = aug4;
e.aug5 = aug5;
e.aug6 = aug6;
e.attuned = attuned;
RecordPlayerEventLog(PlayerEvent::ITEM_CREATION, e);
}
// put item into inventory
if (to_slot == EQ::invslot::slotCursor) {
PushItemOnCursor(*inst);
@@ -848,13 +865,13 @@ void Client::DropItem(int16 slot_id, bool recurse)
}
}
}
std::string message = fmt::format(
"Tried to drop an item on the ground that was no-drop! item_name [{}] item_id ({})",
invalid_drop->GetItem()->Name,
invalid_drop->GetItem()->ID
);
invalid_drop = nullptr;
RecordPlayerEventLog(PlayerEvent::POSSIBLE_HACK, PlayerEvent::PossibleHackEvent{.message = message});
GetInv().DeleteItem(slot_id);
+3 -3
View File
@@ -65,8 +65,8 @@ Lua_Mob Lua_Bot::GetOwner() {
return Lua_Mob(self->GetOwner());
}
bool Lua_Bot::HasBotItem(uint32 item_id) {
Lua_Safe_Call_Bool();
int16 Lua_Bot::HasBotItem(uint32 item_id) {
Lua_Safe_Call_Int();
return self->HasBotItem(item_id);
}
@@ -507,7 +507,7 @@ luabind::scope lua_register_bot() {
.def("GetRawItemAC", (int(Lua_Bot::*)(void))&Lua_Bot::GetRawItemAC)
.def("GetSpellDamage", (int(Lua_Bot::*)(void))&Lua_Bot::GetSpellDamage)
.def("HasAugmentEquippedByID", (bool(Lua_Bot::*)(uint32))&Lua_Bot::HasAugmentEquippedByID)
.def("HasBotItem", (bool(Lua_Bot::*)(uint32))&Lua_Bot::HasBotItem)
.def("HasBotItem", (int16(Lua_Bot::*)(uint32))&Lua_Bot::HasBotItem)
.def("HasBotSpellEntry", (bool(Lua_Bot::*)(uint16)) & Lua_Bot::HasBotSpellEntry)
.def("HasItemEquippedByID", (bool(Lua_Bot::*)(uint32))&Lua_Bot::HasItemEquippedByID)
.def("IsGrouped", (bool(Lua_Bot::*)(void))&Lua_Bot::IsGrouped)
+1 -1
View File
@@ -43,7 +43,7 @@ public:
uint32 GetBotItemIDBySlot(uint16 slot_id);
int GetExpansionBitmask();
Lua_Mob GetOwner();
bool HasBotItem(uint32 item_id);
int16 HasBotItem(uint32 item_id);
void OwnerMessage(std::string message);
bool ReloadBotDataBuckets();
bool ReloadBotOwnerDataBuckets();
+29 -6
View File
@@ -103,6 +103,7 @@ Mob::Mob(
attack_dw_timer(2000),
ranged_timer(2000),
hp_regen_per_second_timer(1000),
m_z_clip_check_timer(1000),
tic_timer(6000),
mana_timer(2000),
spellend_timer(0),
@@ -2374,14 +2375,14 @@ void Mob::ShowBuffList(Client* client) {
}
}
void Mob::GMMove(float x, float y, float z, float heading) {
void Mob::GMMove(float x, float y, float z, float heading, bool save_guard_spot) {
m_Position.x = x;
m_Position.y = y;
m_Position.z = z;
SetHeading(heading);
mMovementManager->SendCommandToClients(this, 0.0, 0.0, 0.0, 0.0, 0, ClientRangeAny);
if (IsNPC()) {
if (IsNPC() && save_guard_spot) {
CastToNPC()->SaveGuardSpot(glm::vec4(x, y, z, heading));
}
}
@@ -3714,10 +3715,32 @@ bool Mob::HateSummon() {
// probably should be like half melee range, but we can't get melee range nicely because reasons :)
new_pos = target->TryMoveAlong(new_pos, 5.0f, angle);
if (target->IsClient())
target->CastToClient()->MovePC(zone->GetZoneID(), zone->GetInstanceID(), new_pos.x, new_pos.y, new_pos.z, new_pos.w, 0, SummonPC);
else
target->GMMove(new_pos.x, new_pos.y, new_pos.z, new_pos.w);
if (target->IsClient()) {
target->CastToClient()->MovePC(
zone->GetZoneID(),
zone->GetInstanceID(),
new_pos.x,
new_pos.y,
new_pos.z,
new_pos.w,
0,
SummonPC
);
} else {
bool target_is_client_pet = (
target->IsPet() &&
target->IsPetOwnerClient()
);
bool set_new_guard_spot = !(IsNPC() && target_is_client_pet);
target->GMMove(
new_pos.x,
new_pos.y,
new_pos.z,
new_pos.w,
set_new_guard_spot
);
}
return true;
} else if(summon_level == 2) {
+3 -1
View File
@@ -686,7 +686,7 @@ public:
float GetMovespeed() const { return IsRunning() ? GetRunspeed() : GetWalkspeed(); }
bool IsRunning() const { return m_is_running; }
void SetRunning(bool val) { m_is_running = val; }
virtual void GMMove(float x, float y, float z, float heading = 0.01);
virtual void GMMove(float x, float y, float z, float heading = 0.01, bool save_guard_spot = true);
virtual void GMMove(const glm::vec4 &position);
void SetDelta(const glm::vec4& delta);
void MakeSpawnUpdateNoDelta(PlayerPositionUpdateServer_Struct* spu);
@@ -1427,6 +1427,8 @@ protected:
int _GetRunSpeed() const;
int _GetFearSpeed() const;
Timer m_z_clip_check_timer;
virtual bool AI_EngagedCastCheck() { return(false); }
virtual bool AI_PursueCastCheck() { return(false); }
virtual bool AI_IdleCastCheck() { return(false); }
+8
View File
@@ -1071,6 +1071,14 @@ void Mob::AI_Process() {
}
if (engaged) {
if (IsNPC() && m_z_clip_check_timer.Check()) {
auto t = GetTarget();
if (t && DistanceNoZ(GetPosition(), t->GetPosition()) < 75 && std::abs(GetPosition().z - t->GetPosition().z) > 15 && !CheckLosFN(t)) {
GMMove(t->GetPosition().x, t->GetPosition().y, t->GetPosition().z, t->GetPosition().w);
FaceTarget(t);
}
}
if (!(m_PlayerState & static_cast<uint32>(PlayerState::Aggressive)))
SendAddPlayerState(PlayerState::Aggressive);
+1 -1
View File
@@ -90,7 +90,7 @@ uint32 Perl_Bot_CountBotItem(Bot* self, uint32 item_id)
return self->CountBotItem(item_id);
}
bool Perl_Bot_HasBotItem(Bot* self, uint32 item_id)
int16 Perl_Bot_HasBotItem(Bot* self, uint32 item_id)
{
return self->HasBotItem(item_id);
}
+6
View File
@@ -2755,6 +2755,11 @@ void Perl_Client_Signal(Client* self, int signal_id)
self->Signal(signal_id);
}
void Perl_Client_SignalClient(Client* self, int signal_id) // @categories Script Utility
{
self->Signal(signal_id);
}
std::string Perl_Client_GetGuildPublicNote(Client* self)
{
return self->GetGuildPublicNote();
@@ -3362,6 +3367,7 @@ void perl_register_client()
package.add("SetTitleSuffix", (void(*)(Client*, std::string, bool))&Perl_Client_SetTitleSuffix);
package.add("SetZoneFlag", &Perl_Client_SetZoneFlag);
package.add("Signal", &Perl_Client_Signal);
package.add("SignalClient", &Perl_Client_SignalClient);
package.add("SilentMessage", &Perl_Client_SilentMessage);
package.add("Sit", &Perl_Client_Sit);
package.add("SlotConvert2", &Perl_Client_SlotConvert2);
+10
View File
@@ -584,6 +584,16 @@ bool Perl_NPC_GetCombatState(NPC* self) // @categories Script Utility
return self->GetCombatEvent();
}
void Perl_NPC_SetSimpleRoamBox(NPC* self, float box_size) // @categories Script Utility
{
self->SetSimpleRoamBox(box_size);
}
void Perl_NPC_SetSimpleRoamBox(NPC* self, float box_size, float move_distance) // @categories Script Utility
{
self->SetSimpleRoamBox(box_size, move_distance);
}
void Perl_NPC_SetSimpleRoamBox(NPC* self, float box_size, float move_distance, int move_delay) // @categories Script Utility
{
self->SetSimpleRoamBox(box_size, move_distance, move_delay);
+13 -15
View File
@@ -781,7 +781,19 @@ float Mob::GetFixedZ(const glm::vec3 &destination, int32 z_find_offset) {
return new_z;
}
new_z = FindDestGroundZ(destination, (-GetZOffset() / 2));
new_z = FindDestGroundZ(destination, ((-GetZOffset() / 2) + z_find_offset));
if (RuleB(Map, MobPathingVisualDebug)) {
DrawDebugCoordinateNode(
fmt::format("{} search z node", GetCleanName()),
glm::vec4{
m_Position.x,
m_Position.y,
((-GetZOffset() / 2) + z_find_offset),
m_Position.w
}
);
}
if (new_z != BEST_Z_INVALID) {
new_z += GetZOffset();
@@ -790,20 +802,6 @@ float Mob::GetFixedZ(const glm::vec3 &destination, int32 z_find_offset) {
}
}
// prevent ceiling clipping
// if client is close in distance (not counting Z) and we clipped up into a ceiling
// this helps us snap back down (or up) if it were to happen
// other fixes were put in place to prevent clipping into the ceiling to begin with
if (std::abs(new_z - m_Position.z) > 15) {
LogFixZ("TRIGGER clipping detection");
auto t = GetTarget();
if (t && DistanceNoZ(GetPosition(), t->GetPosition()) < 20) {
new_z = FindDestGroundZ(t->GetPosition(), -t->GetZOffset());
new_z += GetZOffset();
GMMove(t->GetPosition().x, t->GetPosition().y, new_z, t->GetPosition().w);
}
}
auto duration = timer.elapsed();
LogFixZ("[{}] returned [{}] at [{}] [{}] [{}] - Took [{}]",