1
0

LUA Spell Scripts updated to have a queue much like SpawnScripts, but extended to LuaSpell for tracking procs and lifetime of the lua state

Need to avoid crashes/overrun of the lua stack.
This commit is contained in:
Emagi 2024-09-04 06:44:04 -04:00
parent 56e43288bf
commit 14003ee3a4
7 changed files with 200 additions and 124 deletions

View File

@ -1662,7 +1662,12 @@ void Entity::AddProc(int8 type, float chance, Item* item, LuaSpell* spell, int8
proc->chance = chance;
proc->item = item;
proc->spell = spell;
proc->spellid = spell->spell->GetSpellID();
if(spell) {
spell->has_proc = true;
}
if(spell && spell->spell) {
proc->spellid = spell->spell->GetSpellID();
}
proc->health_ratio = hp_ratio;
proc->below_health = below_health;
proc->damage_type = damage_type;
@ -1701,12 +1706,12 @@ bool Entity::CastProc(Proc* proc, int8 type, Spawn* target) {
lua_State* state = 0;
bool item_proc = false;
int8 num_args = 3;
Mutex* mutex = 0;
if (proc->spell) {
state = proc->spell->state;
}
else if (proc->item) {
state = lua_interface->GetItemScript(proc->item->GetItemScript());
state = lua_interface->GetItemScript(proc->item->GetItemScript(), true, true);
item_proc = true;
}
@ -1714,6 +1719,13 @@ bool Entity::CastProc(Proc* proc, int8 type, Spawn* target) {
LogWrite(COMBAT__ERROR, 0, "Proc", "No valid lua_State* found");
return false;
}
if(item_proc) {
mutex = lua_interface->GetItemScriptMutex(proc->item->GetItemScript());
}
if(mutex)
mutex->readlock(__FUNCTION__, __LINE__);
if(proc->extended_version) {
lua_getglobal(state, "proc_ext");
@ -1721,7 +1733,7 @@ bool Entity::CastProc(Proc* proc, int8 type, Spawn* target) {
else {
lua_getglobal(state, "proc");
}
if (lua_isfunction(state, -1)) {
if (lua_isfunction(state, lua_gettop(state))) {
if (item_proc) {
num_args++;
lua_interface->SetItemValue(state, proc->item);
@ -1730,7 +1742,10 @@ bool Entity::CastProc(Proc* proc, int8 type, Spawn* target) {
lua_interface->SetSpawnValue(state, this);
lua_interface->SetSpawnValue(state, target);
lua_interface->SetInt32Value(state, type);
lua_interface->SetInt32Value(state, proc->damage_type);
if(proc->extended_version) {
lua_interface->SetInt32Value(state, proc->damage_type);
}
/*
Add spell data from db in case of a spell proc here...
@ -1768,12 +1783,19 @@ bool Entity::CastProc(Proc* proc, int8 type, Spawn* target) {
if (lua_pcall(state, num_args, 0, 0) != 0) {
LogWrite(COMBAT__ERROR, 0, "Proc", "Unable to call the proc function for spell %i tier %i", proc->spell->spell->GetSpellID(), proc->spell->spell->GetSpellTier());
lua_pop(state, 1);
if(mutex)
mutex->releasereadlock(__FUNCTION__, __LINE__);
if(item_proc)
lua_interface->UseItemScript(proc->item->GetItemScript(), state, false);
return false;
}
}
lua_interface->ResetFunctionStack(state);
if(mutex)
mutex->releasereadlock(__FUNCTION__);
if(item_proc)
lua_interface->UseItemScript(proc->item->GetItemScript(), state, false);
return true;
}

View File

@ -12248,7 +12248,6 @@ int EQ2Emu_lua_GetSpell(lua_State* state) {
if((lua_spell = lua_interface->GetSpell(custom_lua_script.c_str())) == nullptr)
{
LogWrite(LUA__WARNING, 0, "LUA", "GetSpell(%u, %u, '%s'), custom lua script not loaded, attempting to load.", spell_id, spell_tier, custom_lua_script.c_str());
lua_interface->LoadLuaSpell(custom_lua_script);
}
}
else

View File

@ -147,11 +147,12 @@ void LuaInterface::DestroySpells() {
MSpells.lock();
for(itr = spells.begin(); itr != spells.end(); itr++){
MSpellDelete.lock();
RemoveCurrentSpell(itr->second->state, false);
RemoveCurrentSpell(itr->second->state, itr->second, false);
MSpellDelete.unlock();
lua_close(itr->second->state);
safe_delete(itr->second);
}
spell_scripts.clear();
spells.clear();
MSpells.unlock();
}
@ -256,61 +257,6 @@ void LuaInterface::ReloadSpells() {
database.LoadSpellScriptData();
}
bool LuaInterface::LoadLuaSpell(const char* name) {
LuaSpell* spell = 0;
string lua_script = string(name);
if (lua_script.find(".lua") == string::npos)
lua_script.append(".lua");
lua_State* state = LoadLuaFile(lua_script.c_str());
if(state){
spell = new LuaSpell;
spell->file_name = lua_script;
spell->state = state;
spell->spell = 0;
spell->caster = 0;
spell->initial_target = 0;
spell->resisted = false;
spell->has_damaged = false;
spell->is_damage_spell = false;
spell->interrupted = false;
spell->last_spellattack_hit = false;
spell->crit = false;
spell->MSpellTargets.SetName("LuaSpell.MSpellTargets");
spell->cancel_after_all_triggers = false;
spell->num_triggers = 0;
spell->num_calls = 0;
spell->is_recast_timer = false;
spell->had_triggers = false;
spell->had_dmg_remaining = false;
spell->slot_pos = 0;
spell->damage_remaining = 0;
spell->effect_bitmask = 0;
spell->restored = false;
spell->initial_caster_char_id = 0;
spell->initial_target_char_id = 0;
MSpells.lock();
if (spells.count(lua_script) > 0) {
SetLuaUserDataStale(spells[lua_script]);
MSpellDelete.lock();
RemoveCurrentSpell(spells[lua_script]->state, false);
MSpellDelete.unlock();
lua_close(spells[lua_script]->state);
safe_delete(spells[lua_script]);
}
spells[lua_script] = spell;
MSpells.unlock();
return true;
}
return false;
}
bool LuaInterface::LoadLuaSpell(string name) {
return LoadLuaSpell(name.c_str());
}
bool LuaInterface::LoadItemScript(string name) {
return LoadItemScript(name.c_str());
}
@ -555,6 +501,10 @@ bool LuaInterface::LoadRegionScript(string name) {
return LoadRegionScript(name.c_str());
}
LuaSpell* LuaInterface::LoadSpellScript(string name) {
return LoadSpellScript(name.c_str());
}
std::string LuaInterface::AddSpawnPointers(LuaSpell* spell, bool first_cast, bool precast, const char* function, SpellScriptTimer* timer, bool passLuaSpell, Spawn* altTarget) {
std::string functionCalled = string("");
if (function)
@ -580,7 +530,7 @@ std::string LuaInterface::AddSpawnPointers(LuaSpell* spell, bool first_cast, boo
LogWrite(SPELL__DEBUG, 0, "Spell", "LuaInterface::AddSpawnPointers spell %s (%u) function %s, caster %s.", spell->spell ? spell->spell->GetName() : "UnknownUnset", spell->spell ? spell->spell->GetSpellID() : 0, functionCalled.c_str(), spell->caster ? spell->caster->GetName() : "Unknown");
if (!lua_isfunction(spell->state, -1)){
if (!lua_isfunction(spell->state, lua_gettop(spell->state))){
lua_pop(spell->state, 1);
return string("");
}
@ -647,13 +597,28 @@ LuaSpell* LuaInterface::GetCurrentSpell(lua_State* state, bool needsLock) {
return spell;
}
void LuaInterface::RemoveCurrentSpell(lua_State* state, bool needsLock) {
void LuaInterface::RemoveCurrentSpell(lua_State* state, LuaSpell* cur_spell, bool needsLock, bool removeCurSpell) {
if(needsLock) {
MSpells.lock();
MSpellDelete.lock();
}
map<lua_State*, LuaSpell*>::iterator itr = current_spells.find(state);
if(itr != current_spells.end())
if(itr->second) {
MSpellScripts.readlock(__FUNCTION__, __LINE__);
map<string, map<lua_State*, LuaSpell*> >::iterator spell_script_itr = spell_scripts.find(cur_spell->file_name);
if(spell_script_itr != spell_scripts.end()) {
LogWrite(SPELL__DEBUG, 9, "Spell", "LuaInterface::RemoveCurrentSpell spell %s. Queue Entries %u.", cur_spell->file_name.c_str(), spell_script_itr->second.size());
Mutex* mutex = GetSpellScriptMutex(cur_spell->file_name.c_str());
mutex->writelock(__FUNCTION__, __LINE__);
map<lua_State*, LuaSpell*>::iterator spell_script_itr2 = spell_script_itr->second.find(state);
if(spell_script_itr2 != spell_script_itr->second.end()) {
spell_script_itr2->second = nullptr;
}
mutex->releasewritelock(__FUNCTION__, __LINE__);
}
MSpellScripts.releasereadlock(__FUNCTION__, __LINE__);
}
if(itr != current_spells.end() && removeCurSpell)
current_spells.erase(itr);
if(needsLock) {
MSpellDelete.unlock();
@ -664,10 +629,6 @@ void LuaInterface::RemoveCurrentSpell(lua_State* state, bool needsLock) {
bool LuaInterface::CallSpellProcess(LuaSpell* spell, int8 num_parameters, std::string customFunction) {
if(shutting_down || !spell || !spell->caster)
return false;
MSpells.lock();
current_spells[spell->state] = spell;
MSpells.unlock();
LogWrite(SPELL__DEBUG, 0, "Spell", "LuaInterface::CallSpellProcess spell %s (%u) function %s, caster %s.", spell->spell ? spell->spell->GetName() : "UnknownUnset", spell->spell ? spell->spell->GetSpellID() : 0, customFunction.c_str(), spell->caster->GetName());
@ -861,7 +822,7 @@ lua_State* LuaInterface::LoadLuaFile(const char* name) {
void LuaInterface::RemoveSpell(LuaSpell* spell, bool call_remove_function, bool can_delete, string reason, bool removing_all_spells) {
if(call_remove_function){
lua_getglobal(spell->state, "remove");
if (!lua_isfunction(spell->state, -1)){
if (!lua_isfunction(spell->state, lua_gettop(spell->state))){
lua_pop(spell->state, 1);
}
else {
@ -889,10 +850,6 @@ void LuaInterface::RemoveSpell(LuaSpell* spell, bool call_remove_function, bool
reason = "dead";
lua_pushstring(spell->state, (char*)reason.c_str());
MSpells.lock();
current_spells[spell->state] = spell;
MSpells.unlock();
lua_pcall(spell->state, 3, 0, 0);
ResetFunctionStack(spell->state);
@ -1672,7 +1629,7 @@ void LuaInterface::DeletePendingSpells(bool all) {
}
SetLuaUserDataStale(spell);
RemoveCurrentSpell(spell->state, false);
RemoveCurrentSpell(spell->state, spell, false);
safe_delete(spell);
}
}
@ -2056,47 +2013,49 @@ void LuaInterface::SetSpellValue(lua_State* state, LuaSpell* spell) {
lua_pushlightuserdata(state, spell_wrapper);
}
LuaSpell* LuaInterface::GetSpell(const char* name) {
LuaSpell* LuaInterface::LoadSpellScript(const char* name) {
LuaSpell* spell = nullptr;
string lua_script = string(name);
if (lua_script.find(".lua") == string::npos)
lua_script.append(".lua");
if(spells.count(lua_script) > 0)
{
LogWrite(LUA__DEBUG, 0, "LUA", "Found LUA Spell Script: '%s'", lua_script.c_str());
LuaSpell* spell = spells[lua_script];
LuaSpell* new_spell = new LuaSpell;
new_spell->state = spell->state;
new_spell->file_name = string(spell->file_name);
new_spell->timer = spell->timer;
new_spell->timer.Disable();
new_spell->resisted = false;
new_spell->is_damage_spell = false;
new_spell->has_damaged = false;
new_spell->interrupted = false;
new_spell->crit = false;
new_spell->last_spellattack_hit = false;
new_spell->MSpellTargets.SetName("LuaSpell.MSpellTargets");
new_spell->cancel_after_all_triggers = false;
new_spell->num_triggers = 0;
new_spell->num_calls = 0;
new_spell->is_recast_timer = false;
new_spell->had_triggers = false;
new_spell->had_dmg_remaining = false;
new_spell->slot_pos = 0;
new_spell->damage_remaining = 0;
new_spell->effect_bitmask = 0;
new_spell->caster = 0;
new_spell->initial_target = 0;
new_spell->spell = 0;
new_spell->restored = false;
new_spell->initial_caster_char_id = 0;
new_spell->initial_target_char_id = 0;
return new_spell;
}
else{
LogWrite(LUA__ERROR, 0, "LUA", "Error LUA Spell Script: '%s'", name);
return 0;
lua_State* state = LoadLuaFile(lua_script.c_str());
if(state) {
spell = new LuaSpell;
spell->file_name = lua_script;
spell->state = state;
spell->spell = 0;
spell->caster = 0;
spell->initial_target = 0;
spell->resisted = false;
spell->has_damaged = false;
spell->is_damage_spell = false;
spell->interrupted = false;
spell->last_spellattack_hit = false;
spell->crit = false;
spell->MSpellTargets.SetName("LuaSpell.MSpellTargets");
spell->cancel_after_all_triggers = false;
spell->num_triggers = 0;
spell->num_calls = 0;
spell->is_recast_timer = false;
spell->had_triggers = false;
spell->had_dmg_remaining = false;
spell->slot_pos = 0;
spell->damage_remaining = 0;
spell->effect_bitmask = 0;
spell->restored = false;
spell->has_proc = false;
spell->initial_caster_char_id = 0;
spell->initial_target_char_id = 0;
MSpells.lock();
current_spells[spell->state] = spell;
MSpells.unlock();
MSpellScripts.writelock(__FUNCTION__, __LINE__);
spell_scripts[lua_script][state] = spell;
MSpellScripts.releasewritelock(__FUNCTION__, __LINE__);
}
return spell;
}
Mutex* LuaInterface::GetItemScriptMutex(const char* name) {
@ -2143,6 +2102,17 @@ Mutex* LuaInterface::GetRegionScriptMutex(const char* name) {
return mutex;
}
Mutex* LuaInterface::GetSpellScriptMutex(const char* name) {
Mutex* mutex = 0;
if(spell_scripts_mutex.count(name) > 0)
mutex = spell_scripts_mutex[name];
if(!mutex){
mutex = new Mutex();
spell_scripts_mutex[name] = mutex;
}
return mutex;
}
void LuaInterface::UseItemScript(const char* name, lua_State* state, bool val) {
MItemScripts.writelock(__FUNCTION__, __LINE__);
item_scripts[name][state] = val;
@ -2268,7 +2238,7 @@ lua_State* LuaInterface::GetZoneScript(const char* name, bool create_new, bool u
}
if(!ret && create_new){
if(name && LoadZoneScript(name))
ret = GetZoneScript(name);
ret = GetZoneScript(name, create_new, use);
else{
LogError("Error LUA Zone Script '%s'", name);
return 0;
@ -2302,7 +2272,7 @@ lua_State* LuaInterface::GetRegionScript(const char* name, bool create_new, bool
}
if(!ret && create_new){
if(name && LoadRegionScript(name))
ret = GetRegionScript(name);
ret = GetRegionScript(name, create_new, use);
else{
LogError("Error LUA Zone Script '%s'", name);
return 0;
@ -2311,6 +2281,78 @@ lua_State* LuaInterface::GetRegionScript(const char* name, bool create_new, bool
return ret;
}
LuaSpell* LuaInterface::GetSpellScript(const char* name, bool create_new, bool use) {
map<string, map<lua_State*, LuaSpell*> >::iterator itr;
map<lua_State*, LuaSpell*>::iterator spell_script_itr;
LuaSpell* ret = 0;
Mutex* mutex = 0;
itr = spell_scripts.find(name);
if(itr != spell_scripts.end()) {
mutex = GetSpellScriptMutex(name);
mutex->readlock(__FUNCTION__, __LINE__);
for(spell_script_itr = itr->second.begin(); spell_script_itr != itr->second.end(); spell_script_itr++){
if(spell_script_itr->second == nullptr){ //not in use
if (use)
{
spell_script_itr->second = CreateSpellScript(name, spell_script_itr->first);
ret = spell_script_itr->second;
break; // don't keep iterating, we already have our result
}
}
}
mutex->releasereadlock(__FUNCTION__, __LINE__);
}
if(!ret && create_new){
if(!name || (ret = LoadSpellScript(name)) == nullptr) {
LogError("Error LUA Spell Script '%s'", name == nullptr ? "unknown" : name);
}
}
return ret;
}
LuaSpell* LuaInterface::CreateSpellScript(const char* name, lua_State* existState) {
LuaSpell* new_spell = new LuaSpell;
new_spell->state = existState;
new_spell->file_name = string(name);
new_spell->resisted = false;
new_spell->is_damage_spell = false;
new_spell->has_damaged = false;
new_spell->interrupted = false;
new_spell->crit = false;
new_spell->last_spellattack_hit = false;
new_spell->MSpellTargets.SetName("LuaSpell.MSpellTargets");
new_spell->cancel_after_all_triggers = false;
new_spell->num_triggers = 0;
new_spell->num_calls = 0;
new_spell->is_recast_timer = false;
new_spell->had_triggers = false;
new_spell->had_dmg_remaining = false;
new_spell->slot_pos = 0;
new_spell->damage_remaining = 0;
new_spell->effect_bitmask = 0;
new_spell->caster = 0;
new_spell->initial_target = 0;
new_spell->spell = 0;
new_spell->restored = false;
new_spell->has_proc = false;
new_spell->initial_caster_char_id = 0;
new_spell->initial_target_char_id = 0;
MSpells.lock();
current_spells[new_spell->state] = new_spell;
MSpells.unlock();
MSpellScripts.writelock(__FUNCTION__, __LINE__);
spell_scripts[std::string(name)][new_spell->state] = new_spell;
MSpellScripts.releasewritelock(__FUNCTION__, __LINE__);
return new_spell;
}
LuaSpell* LuaInterface::GetSpell(const char* name) {
return GetSpellScript(name, true);
}
bool LuaInterface::RunItemScript(string script_name, const char* function_name, Item* item, Spawn* spawn, Spawn* target, sint64* returnValue) {
if(!item)
return false;

View File

@ -102,6 +102,7 @@ struct LuaSpell{
Mutex MSpellTargets;
int32 effect_bitmask;
bool restored; // restored spell cross zone
std::atomic<bool> has_proc;
};
@ -183,8 +184,6 @@ public:
LuaInterface();
~LuaInterface();
int GetNumberOfArgs(lua_State* state);
bool LoadLuaSpell(const char* name);
bool LoadLuaSpell(string name);
bool LoadItemScript(string name);
bool LoadItemScript(const char* name);
bool LoadSpawnScript(string name);
@ -193,6 +192,8 @@ public:
bool LoadZoneScript(const char* name);
bool LoadRegionScript(string name);
bool LoadRegionScript(const char* name);
LuaSpell* LoadSpellScript(string name);
LuaSpell* LoadSpellScript(const char* name);
void RemoveSpell(LuaSpell* spell, bool call_remove_function = true, bool can_delete = true, string reason = "", bool removing_all_spells = false);
Spawn* GetSpawn(lua_State* state, int8 arg_num = 1);
Item* GetItem(lua_State* state, int8 arg_num = 1);
@ -233,7 +234,7 @@ public:
std::string AddSpawnPointers(LuaSpell* spell, bool first_cast, bool precast = false, const char* function = 0, SpellScriptTimer* timer = 0, bool passLuaSpell=false, Spawn* altTarget = 0);
LuaSpell* GetCurrentSpell(lua_State* state, bool needsLock = true);
void RemoveCurrentSpell(lua_State* state, bool needsLock = true);
void RemoveCurrentSpell(lua_State* state, LuaSpell* cur_spell, bool needsLock = true, bool removeCurSpell = true);
bool CallSpellProcess(LuaSpell* spell, int8 num_parameters, std::string functionCalled);
LuaSpell* GetSpell(const char* name);
void UseItemScript(const char* name, lua_State* state, bool val);
@ -244,6 +245,9 @@ public:
lua_State* GetSpawnScript(const char* name, bool create_new = true, bool use = false);
lua_State* GetZoneScript(const char* name, bool create_new = true, bool use = false);
lua_State* GetRegionScript(const char* name, bool create_new = true, bool use = false);
LuaSpell* GetSpellScript(const char* name, bool create_new = true, bool use = true);
LuaSpell* CreateSpellScript(const char* name, lua_State* existState);
Quest* LoadQuest(int32 id, const char* name, const char* type, const char* zone, int8 level, const char* description, char* script_name);
const char* GetScriptName(lua_State* state);
@ -286,6 +290,7 @@ public:
Mutex* GetItemScriptMutex(const char* name);
Mutex* GetZoneScriptMutex(const char* name);
Mutex* GetRegionScriptMutex(const char* name);
Mutex* GetSpellScriptMutex(const char* name);
Mutex* GetQuestMutex(Quest* quest);
void SetLuaSystemReloading(bool val) { lua_system_reloading = val; }
@ -326,6 +331,7 @@ private:
map<string, map<lua_State*, bool> > spawn_scripts;
map<string, map<lua_State*, bool> > zone_scripts;
map<string, map<lua_State*, bool> > region_scripts;
map<string, map<lua_State*, LuaSpell*> > spell_scripts;
map<int32, LuaSpell*> custom_spells;
std::deque<int32> custom_free_spell_ids;
@ -340,6 +346,7 @@ private:
map<string, Mutex*> zone_scripts_mutex;
map<int32, Mutex*> quests_mutex;
map<string, Mutex*> region_scripts_mutex;
map<string, Mutex*> spell_scripts_mutex;
Mutex MDebugClients;
Mutex MSpells;
@ -351,6 +358,7 @@ private:
Mutex MSpellDelete;
Mutex MCustomSpell;
Mutex MRegionScripts;
Mutex MSpellScripts;
mutable std::shared_mutex MLUAUserData;
};

View File

@ -217,6 +217,9 @@ void SpellProcess::Process(){
}
if (cast_timer->delete_timer) {
safe_delete(cast_timer->timer);
if(cast_timer->spell && !cast_timer->spell->has_proc) {
lua_interface->RemoveCurrentSpell(cast_timer->spell->state, cast_timer->spell, true, false);
}
cast_timers.Remove(cast_timer, true);
}
}
@ -650,7 +653,7 @@ bool SpellProcess::CastInstant(Spell* spell, Entity* caster, Entity* target, boo
if (!lua_spell->spell->IsCopiedSpell())
{
lua_getglobal(lua_spell->state, "customspell");
if (lua_isfunction(lua_spell->state, -1)) {
if (lua_isfunction(lua_spell->state, lua_gettop(lua_spell->state))) {
lua_pop(lua_spell->state, 1);
Spell* tmpSpell = lua_spell->spell;
lua_spell->spell = new Spell(lua_spell->spell);
@ -1094,7 +1097,7 @@ void SpellProcess::ProcessSpell(ZoneServer* zone, Spell* spell, Entity* caster,
if (!customSpell && !lua_spell->spell->IsCopiedSpell())
{
lua_getglobal(lua_spell->state, "customspell");
if (lua_isfunction(lua_spell->state, -1)) {
if (lua_isfunction(lua_spell->state, lua_gettop(lua_spell->state))) {
lua_pop(lua_spell->state, 1);
Spell* tmpSpell = lua_spell->spell;
lua_spell->spell = new Spell(lua_spell->spell);
@ -1637,6 +1640,9 @@ void SpellProcess::ProcessSpell(ZoneServer* zone, Spell* spell, Entity* caster,
DeleteSpell(lua_spell);
return;
}
if(!lua_spell->has_proc) {
lua_interface->RemoveCurrentSpell(lua_spell->state, lua_spell, true, false);
}
}
if(caster)
@ -2969,7 +2975,7 @@ void SpellProcess::DeleteSpell(LuaSpell* spell)
}
lua_interface->SetLuaUserDataStale(spell);
lua_interface->RemoveCurrentSpell(spell->state, true);
lua_interface->RemoveCurrentSpell(spell->state, spell, true);
DeleteActiveSpell(spell);
}

View File

@ -4508,7 +4508,7 @@ int32 WorldDatabase::LoadSpellScriptData() {
while (result && (row = mysql_fetch_row(result))) {
if (row[0] && strlen(row[0]) > 0) {
if (lua_interface->LoadLuaSpell(row[0]))
if (lua_interface->GetSpell(row[0]))
LogWrite(SPELL__DEBUG, 5, "Spells", "SpellScript: %s loaded.", row[0]);
}
}
@ -7787,7 +7787,6 @@ void WorldDatabase::LoadCharacterSpellEffects(int32 char_id, Client* client, int
if((lua_spell = lua_interface->GetSpell(lua_file.c_str())) == nullptr)
{
LogWrite(LUA__WARNING, 0, "LUA", "WorldDatabase::LoadCharacterSpellEffects: GetSpell(%u, %u, '%s'), custom lua script not loaded, when attempting to load.", spell_id, tier, lua_file.c_str());
lua_interface->LoadLuaSpell(lua_file);
}
}

View File

@ -11391,7 +11391,7 @@ bool Client::EntityCommandPrecheck(Spawn* spawn, const char* command) {
if (state_mutex)
state_mutex->writelock(__FUNCTION__, __LINE__);
lua_getglobal(state, "can_use_command");
if (lua_isfunction(state, -1)) {
if (lua_isfunction(state, lua_gettop(state))) {
lua_interface->SetSpawnValue(state, spawn);
lua_interface->SetSpawnValue(state, GetPlayer());
lua_interface->SetStringValue(state, command ? command : "");