From 5656ad22ce7c151ef740e0c542b0078dd85dbd7b Mon Sep 17 00:00:00 2001 From: Image <image.emagi@gmail.com> Date: Tue, 23 Mar 2021 22:08:29 -0400 Subject: [PATCH] Disabling buy item supported, few crash fixes Fix #326 - buy from merchant can have the 'buy' button disabled function buy_display_flags(Item, Spawn) return 128 end charactersProperties -> character_properties CREATE TABLE `character_properties` ( `charid` int(10) unsigned NOT NULL DEFAULT 0, `propname` varchar(64) NOT NULL DEFAULT '', `propvalue` varchar(64) NOT NULL DEFAULT '' ) ENGINE=InnoDB DEFAULT CHARSET=latin1; drop table charactersProperties; drop table charactersproperties; - some misc crash fixes, sql escape issue, etc. --- EQ2/source/WorldServer/Commands/Commands.cpp | 4 ++-- EQ2/source/WorldServer/Items/Items.h | 1 + EQ2/source/WorldServer/LuaFunctions.cpp | 2 +- EQ2/source/WorldServer/WorldDatabase.cpp | 14 +++++++---- EQ2/source/WorldServer/client.cpp | 25 +++++++++++++++++++- EQ2/source/WorldServer/zoneserver.cpp | 9 ++----- 6 files changed, 40 insertions(+), 15 deletions(-) diff --git a/EQ2/source/WorldServer/Commands/Commands.cpp b/EQ2/source/WorldServer/Commands/Commands.cpp index 4d44aca88..32d24f3d3 100644 --- a/EQ2/source/WorldServer/Commands/Commands.cpp +++ b/EQ2/source/WorldServer/Commands/Commands.cpp @@ -3095,13 +3095,13 @@ void Commands::Process(int32 index, EQ2_16BitString* command_parms, Client* clie } case COMMAND_USE: { Spawn* target = cmdTarget; - if (target->IsWidget()) + if (target && target->IsWidget()) ((Widget*)target)->HandleUse(client, "use"); break; } case COMMAND_OPEN: { Spawn* target = cmdTarget; - if (target->IsWidget()) + if (target && target->IsWidget()) ((Widget*)target)->HandleUse(client, "Open", WIDGET_TYPE_DOOR); break; } diff --git a/EQ2/source/WorldServer/Items/Items.h b/EQ2/source/WorldServer/Items/Items.h index 1d8186d9e..8ba7624ef 100644 --- a/EQ2/source/WorldServer/Items/Items.h +++ b/EQ2/source/WorldServer/Items/Items.h @@ -603,6 +603,7 @@ extern MasterItemList master_item_list; #define DISPLAY_FLAG_NO_GUILD_STATUS 8 #define DISPLAY_FLAG_NO_BUYBACK 16 #define DISPLAY_FLAG_NOT_FOR_SALE 64 +#define DISPLAY_FLAG_NO_BUY 128 // disables buying on merchant 'buy' list #pragma pack(1) struct ItemStatsValues{ diff --git a/EQ2/source/WorldServer/LuaFunctions.cpp b/EQ2/source/WorldServer/LuaFunctions.cpp index 3a31d7e2a..9d68b2064 100644 --- a/EQ2/source/WorldServer/LuaFunctions.cpp +++ b/EQ2/source/WorldServer/LuaFunctions.cpp @@ -396,7 +396,7 @@ int EQ2Emu_lua_SendStateCommand(lua_State* state) { } else { - spawn->GetZone()->SendStateCommand(spawn, new_state); + spawn->GetZone()->QueueStateCommandToClients(spawn->GetID(), new_state); lua_interface->SetBooleanValue(state, true); return 1; } diff --git a/EQ2/source/WorldServer/WorldDatabase.cpp b/EQ2/source/WorldServer/WorldDatabase.cpp index e3ad8f790..185723f86 100644 --- a/EQ2/source/WorldServer/WorldDatabase.cpp +++ b/EQ2/source/WorldServer/WorldDatabase.cpp @@ -1864,11 +1864,11 @@ bool WorldDatabase::UpdateCharacterTimeStamp(int32 account_id, int32 character_i bool WorldDatabase::insertCharacterProperty(Client* client, char* propName, char* propValue) { Query query; - string update_status = string("update charactersProperties set propvalue='%s' where charid=%i and propname='%s'"); + string update_status = string("update character_properties set propvalue='%s' where charid=%i and propname='%s'"); query.RunQuery2(Q_UPDATE, update_status.c_str(), propValue, client->GetCharacterID(), propName); if (!query.GetAffectedRows()) { - query.RunQuery2(Q_UPDATE, "insert into charactersProperties (charid, propname, propvalue) values(%i, '%s', '%s')", client->GetCharacterID(), propName, propValue); + query.RunQuery2(Q_UPDATE, "insert into character_properties (charid, propname, propvalue) values(%i, '%s', '%s')", client->GetCharacterID(), propName, propValue); if (query.GetErrorNumber() && query.GetError() && query.GetErrorNumber() < 0xFFFFFFFF) { LogWrite(WORLD__ERROR, 0, "World", "Error in insertCharacterProperty query '%s': %s", query.GetQuery(), query.GetError()); return false; @@ -1881,7 +1881,7 @@ bool WorldDatabase::loadCharacterProperties(Client* client) { Query query; MYSQL_ROW row; int32 id = 0; - MYSQL_RES* result = query.RunQuery2(Q_SELECT, "SELECT propname, propvalue FROM charactersProperties where charid = %i", client->GetCharacterID()); + MYSQL_RES* result = query.RunQuery2(Q_SELECT, "SELECT propname, propvalue FROM character_properties where charid = %i", client->GetCharacterID()); // no character found if (result == NULL) { LogWrite(PLAYER__ERROR, 0, "Player", "Error loading character properties for '%s'", client->GetPlayer()->GetName()); @@ -2766,12 +2766,14 @@ void WorldDatabase::SaveZoneInfo(int32 zone_id, const char* field, const char* v int32 WorldDatabase::GetZoneID(const char* name) { int32 zone_id = 0; Query query; - MYSQL_RES* result = query.RunQuery2(Q_SELECT, "SELECT `id` FROM zones WHERE `name`='%s'", name); + char* escaped = getEscapeString(name); + MYSQL_RES* result = query.RunQuery2(Q_SELECT, "SELECT `id` FROM zones WHERE `name`=\"%s\"", escaped); if (result && mysql_num_rows(result) > 0) { MYSQL_ROW row; row = mysql_fetch_row(result); zone_id = atoi(row[0]); } + safe_delete_array(escaped); return zone_id; } @@ -4941,6 +4943,10 @@ int32 WorldDatabase::LoadQuests(){ total++; master_quest_list.AddQuest(id, quest); } + else + { + LogWrite(QUEST__ERROR, 5, "Quests", "\tFAILED LOADING QUEST: '%s' (%u), check that script file exists/permissions correct: %s", name, id, (script && strlen(script)) ? script : "Not Set, Missing!"); + } } } LogWrite(QUEST__DEBUG, 0, "Quest", "\tLoaded %i Quest(s)", total); diff --git a/EQ2/source/WorldServer/client.cpp b/EQ2/source/WorldServer/client.cpp index 32732c8e4..855c31fe2 100644 --- a/EQ2/source/WorldServer/client.cpp +++ b/EQ2/source/WorldServer/client.cpp @@ -6570,7 +6570,11 @@ void Client::BuyBack(int32 item_id, int16 quantity) { else item->details.count = closest->quantity; } - if (!player->item_list.HasFreeSlot() && !player->item_list.CanStack(item)) + bool itemAdded = false; + sint64 dispFlags = 0; + if (item && item->GetItemScript() && lua_interface && lua_interface->RunItemScript(item->GetItemScript(), "buyback_display_flags", item, player, &dispFlags) && (dispFlags & DISPLAY_FLAG_NO_BUY)) + SimpleMessage(CHANNEL_NARRATIVE, "You do not meet all the requirements to buy this item."); + else if (!player->item_list.HasFreeSlot() && !player->item_list.CanStack(item)) SimpleMessage(CHANNEL_COLOR_RED, "You do not have any slots available for this item."); else if (player->RemoveCoins(closest->quantity * closest->price)) { bool removed = false; @@ -6590,6 +6594,7 @@ void Client::BuyBack(int32 item_id, int16 quantity) { closest->save_needed = true; } AddItem(item); + itemAdded = true; //EQ2Packet* outapp = player->SendInventoryUpdate(GetVersion()); //if(outapp) //QueuePacket(outapp); @@ -6604,6 +6609,9 @@ void Client::BuyBack(int32 item_id, int16 quantity) { } else SimpleMessage(CHANNEL_COLOR_RED, "You cannot afford this item."); + + if(!itemAdded) + safe_delete(item); } } @@ -6645,6 +6653,12 @@ void Client::BuyItem(int32 item_id, int16 quantity) { if (quantity > total_available) quantity = total_available; } + sint64 dispFlags = 0; + if (master_item->GetItemScript() && lua_interface && lua_interface->RunItemScript(master_item->GetItemScript(), "buy_display_flags", master_item, player, &dispFlags) && (dispFlags & DISPLAY_FLAG_NO_BUY)) + { + SimpleMessage(CHANNEL_NARRATIVE, "You do not meet all the requirements to buy this item."); + return; + } if(quantity < 1) { SimpleMessage(CHANNEL_COLOR_RED, "Merchant does not have item for purchase (quantity < 1)."); @@ -7019,6 +7033,10 @@ void Client::SendBuyMerchantList(bool sell) { packet->setArrayDataByName("quantity", ItemInfo.quantity, i); packet->setArrayDataByName("unknown5", 255, i); packet->setArrayDataByName("stack_size2", item->stack_count, i); + + sint64 dispFlags = 0; + if (item->GetItemScript() && lua_interface && lua_interface->RunItemScript(item->GetItemScript(), "buy_display_flags", item, player, &dispFlags)) + packet->setArrayDataByName("display_flags", (int8)dispFlags, i); std::string overrideValueStr; // classic client isn't properly tracking this field, DoF we don't have it identified yet, but no field to cause any issues (can add later if identified) @@ -7275,6 +7293,11 @@ void Client::SendBuyBackList(bool sell) { if (item_difficulty < 0) item_difficulty *= -1; packet->setArrayDataByName("item_difficulty", item_difficulty, i); + + sint64 dispFlags = 0; + if (master_item->GetItemScript() && lua_interface && lua_interface->RunItemScript(master_item->GetItemScript(), "buyback_display_flags", master_item, player, &dispFlags)) + packet->setArrayDataByName("display_flags", (int8)dispFlags, i); + if (buyback->quantity == 1) packet->setArrayDataByName("quantity", 0xFFFF, i); else diff --git a/EQ2/source/WorldServer/zoneserver.cpp b/EQ2/source/WorldServer/zoneserver.cpp index f84562d36..b1a3ef403 100644 --- a/EQ2/source/WorldServer/zoneserver.cpp +++ b/EQ2/source/WorldServer/zoneserver.cpp @@ -1882,9 +1882,6 @@ void ZoneServer::SendSpawnChanges(){ Spawn* spawn = 0; MSpawnList.readlock(__FUNCTION__, __LINE__); - - int32 max_updates = 100; - MutexList<int32>::iterator spawn_iter = changed_spawns.begin(); int count = 0; while(spawn_iter.Next()){ @@ -1895,9 +1892,6 @@ void ZoneServer::SendSpawnChanges(){ } if (!spawn) changed_spawns.Remove(spawn_iter->value); - - if(count >= max_updates) - break; } //changed_spawns.clear() is not thread safe, advise we rely on what was removed and continue on, get any others in next batch @@ -1906,7 +1900,8 @@ void ZoneServer::SendSpawnChanges(){ MClientList.readlock(__FUNCTION__, __LINE__); for (client_itr = clients.begin(); client_itr != clients.end(); client_itr++) { client = *client_itr; - client->SendSpawnChanges(spawns_to_send); + if(client) + client->SendSpawnChanges(spawns_to_send); } MClientList.releasereadlock(__FUNCTION__, __LINE__);