
Broker/Vault (Fix #10): - Traditional broker integrated (/frombroker), GM itemsearch is still available via /itemsearch. AoM client cannot sell/use shop, can only buy from broker. - DoF and KoS client now support House Shop (sell inventory, vault to broker). - House containers now function like actual containers and can be stored in the vault which will allow placement in the home (to serve as a merchant). - Sales crate merchant windows in player houses implemented for broker. - Peering URI's added for broker/shop support: /addseller, /removeseller, /additemsale, /removeitemsale - House vault and vault slots implemented for DoF, KoS and AoM clients. Stability and Functionality: - World Database now has seq_character_items (Sequence) to sync self and peers instantiating new unique IDs for character items. This is to avoid conflict/overriding another unique id. - spawn and spawn_houses tables have a lua_script that can now be defined outside the existing spawn_scripts table for a singular spawn. - Fixed a watchdog/hangup when clearing hate lists inside a spawn list lock. - Item unique id is being transitioned to int64 (although older clients still only support int32, clients later on are int64). - Fixed so that spells that have no duration will no longer be added to maintained effects. - Fixed spell cleanup, maintained effect does not remain on the Player causing a crash. - Fixed spell conflicts to that check all targets are applicable for the spell. - Fixed issues with maintained effects or spell effects stacking repeatedly. - Fixed assigning items to non inventory slots when 'adding' an item to the Player. - Fixed locking orders between maintained effects and spell effects to avoid deadlocks. - Fixed entering house and visiting houses, targetting of the house door is now enforced server side. - Item locking is now enforced by the type of locking (eg. house placement, crafting, shop list for sale). Locks no longer override/interfere with each other. - Additional logging around spell casting and targets. - Spawns/Objects/Widgets so on related to houses now have their own sub tables _houses, eg. spawn_houses, spawn_npc_houses, spawn_object_houses, so on to avoid conflicting with existing tables non-house. - new LUA Functions: ShowShopWindow(Player, FromSpawn) - opens shop window for player (if in their house) for listing, pricing items, retrieving sales log and coin, etc. SetSpawnHouseScript(Target, LuaScript) - Utilized in the item script 'placed' function to set the spawned house item's lua script. SendBook(Target, ItemID) - Sends the book to the target player based on the item id. GetPickupItemID(Spawn) - Gets the item id that the house spawn would represent SetHouseCharacterID(Spawn, CharID) - Sets the house spawn character id (should be used on Spawn). Set CharID to 0 to set to the current houses character id. - Updated LUA Functions: StartHeroicOpportunity(Caster, ClassID, OverrideTarget) - OverrideTarget now available to change the heroic opportunity target HasItem(Player, ItemID, IncludeBank) - No parameter change, include bank, which serves as 'all' should now work correctly (previously did not check bank/other negative slots), default is false. - Slash Commands Added: /sle - Fix #41, Set Location Entry (DB command for setting spawn location entry values such as offsets and overrides) /store_list_item - used to list an item for broker shop /store_set_price - used for setting items price for shop /store_set_price_local - used for inventory items price for shop /store_start_selling - Begin selling from inventory for shop /store_stop_selling - Stop selling from inventory /store_unlist_item - used to unlist an item from broker shop /close_store_keep_selling - Closes store shop window, but keep selling from inventory while in house /cancel_store - cancel selling from inventory
182 lines
5.5 KiB
C++
182 lines
5.5 KiB
C++
/*
|
||
EQ2Emulator: Everquest II Server Emulator
|
||
Copyright (C) 2005 - 2026 EQ2EMulator Development Team (http://www.eq2emu.com formerly http://www.eq2emulator.net)
|
||
|
||
This file is part of EQ2Emulator.
|
||
|
||
EQ2Emulator is free software: you can redistribute it and/or modify
|
||
it under the terms of the GNU General Public License as published by
|
||
the Free Software Foundation, either version 3 of the License, or
|
||
(at your option) any later version.
|
||
|
||
EQ2Emulator is distributed in the hope that it will be useful,
|
||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||
GNU General Public License for more details.
|
||
|
||
You should have received a copy of the GNU General Public License
|
||
along with EQ2Emulator. If not, see <http://www.gnu.org/licenses/>.
|
||
*/
|
||
|
||
#pragma once
|
||
|
||
#include <cstdint>
|
||
#include <functional>
|
||
#include <unordered_map>
|
||
#include <vector>
|
||
#include <mutex>
|
||
#include <string>
|
||
#include <optional>
|
||
#include <shared_mutex>
|
||
#include "../../common/types.h"
|
||
|
||
// Key = (character_id, unique_id)
|
||
using Key = std::pair<int32, int64>;
|
||
|
||
class Item;
|
||
class Client;
|
||
|
||
struct SaleItem {
|
||
int64 unique_id;
|
||
int32 character_id;
|
||
int32 house_id;
|
||
int64 item_id;
|
||
int64 cost_copper;
|
||
bool for_sale;
|
||
sint32 inv_slot_id;
|
||
int16 slot_id;
|
||
int16 count;
|
||
bool from_inventory;
|
||
string creator;
|
||
};
|
||
|
||
struct SellerInfo {
|
||
int32 character_id;
|
||
std::string seller_name;
|
||
int64 house_id;
|
||
bool sale_enabled;
|
||
bool sell_from_inventory;
|
||
int64 coin_session;
|
||
int64 coin_total;
|
||
};
|
||
|
||
struct SellerLog {
|
||
int64 log_id; // auto_increment PK
|
||
std::string timestamp; // e.g. "October 20, 2005, 05:29:33 AM"
|
||
std::string message; // the human-readable text
|
||
};
|
||
|
||
class BrokerManager {
|
||
public:
|
||
BrokerManager();
|
||
|
||
//–– Player management
|
||
void AddSeller(int32 cid,
|
||
const std::string& name,
|
||
int32 house_id,
|
||
bool sale_enabled,
|
||
bool sell_from_inventory);
|
||
|
||
void LoadSeller(int32 cid,
|
||
const std::string& name,
|
||
int32 house_id,
|
||
bool sale_enabled,
|
||
bool sell_from_inventory,
|
||
int64 coin_session,
|
||
int64 coin_total);
|
||
|
||
int64 ResetSellerSessionCoins(int32 cid);
|
||
void AddSellerSessionCoins(int32 cid, uint64 session);
|
||
void RemoveSeller(int32 character_id, bool peerCacheOnly = false);
|
||
|
||
//–– Item management
|
||
void AddItem(const SaleItem& item, bool peerCacheOnly = false);
|
||
void LoadItem(const SaleItem& item);
|
||
|
||
//–– Activate / deactivate sale flag
|
||
void SetSaleStatus(int32 cid, int64 uid, bool for_sale);
|
||
bool IsItemListed(int32 cid, int64 uid);
|
||
void SetSalePrice(int32 cid, int64 uid, int64 price);
|
||
int64 GetSalePrice(int32 cid, int64 uid);
|
||
|
||
//–– Remove quantity
|
||
void RemoveItem(int32 cid, int64 uid, int16 quantity = 1);
|
||
|
||
//–– Attempt to buy (atomic DB + in-memory + broadcast)
|
||
bool BuyItem(Client* buyer, int32 seller_cid, int64 uid, int32 quantity);
|
||
bool IsItemForSale(int32 seller_cid, int64 uid) const;
|
||
|
||
//–– Called when a peer notifies that an item was sold/removed (in-memory only)
|
||
void OnPeerRemoveItem(int32 character_id, int64 unique_id);
|
||
|
||
//–– Queries
|
||
std::vector<SaleItem> GetActiveForSaleItems(int32 cid) const;
|
||
std::optional<SaleItem> GetActiveItem(int32 cid, int64 uid) const;
|
||
bool IsSellingItems(int32 cid, bool vaultOnly = false) const;
|
||
std::vector<SaleItem> GetInactiveItems(int32 cid) const;
|
||
//–– Global search API (only active_items_)
|
||
vector<Item*>* GetItems(
|
||
const std::string& name,
|
||
int64 itype,
|
||
int64 ltype,
|
||
int64 btype,
|
||
int64 minprice,
|
||
int64 maxprice,
|
||
int8 minskill,
|
||
int8 maxskill,
|
||
const std::string& seller,
|
||
const std::string& adornment,
|
||
int8 mintier,
|
||
int8 maxtier,
|
||
int16 minlevel,
|
||
int16 maxlevel,
|
||
int8 itemclass
|
||
) const;
|
||
|
||
//–– UI helper: get (unique_id, cost) for active items
|
||
std::vector<std::pair<int64,int32>> GetUniqueIDsAndCost(int32 cid) const;
|
||
|
||
std::optional<SellerInfo> GetSellerInfo(int32 character_id) const;
|
||
//–– Lookup seller name
|
||
std::string GetSellerName(int32 cid) const;
|
||
bool IsSaleEnabled(int32 cid) const;
|
||
bool CanSellFromInventory(int32 cid) const;
|
||
int32 GetHouseID(int32 cid) const;
|
||
|
||
bool IsItemFromInventory(int32 cid, int64 uid) const;
|
||
void LockActiveItemsForClient(Client* client) const;
|
||
|
||
std::string GetShopPurchaseMessage(const std::string& buyer_name, const std::string& item_desc, int16 quantity, int64 coin);
|
||
void LogSale(int32 character_id, const std::string& buyer_name,const std::string& item_desc, int16 quantity, int64 coin);
|
||
void LogSaleMessage(int32 character_id,const std::string& log_message);
|
||
std::vector<SellerLog> GetSellerLog(int32 character_id) const;
|
||
static std::string EscapeSQLString(const std::string& s) {
|
||
std::string out;
|
||
out.reserve(s.size() * 2);
|
||
for (char c : s) {
|
||
if (c == '\'') out += "''";
|
||
else out += c;
|
||
}
|
||
return out;
|
||
}
|
||
private:
|
||
mutable std::shared_mutex mtx_;
|
||
std::unordered_map<int32,SellerInfo> players_;
|
||
std::unordered_map<
|
||
int32,
|
||
std::unordered_map<int64_t, SaleItem>
|
||
> active_items_by_char_;
|
||
std::unordered_map<
|
||
int32,
|
||
std::unordered_map<int64_t, SaleItem>
|
||
> inactive_items_by_char_;
|
||
|
||
//–– DB sync (async writes)
|
||
void SavePlayerToDB(const SellerInfo& p);
|
||
void SaveItemToDB(const SaleItem& i);
|
||
void UpdateItemInDB(const SaleItem& i);
|
||
void DeleteItemFromDB(int32 character_id, int64 unique_id);
|
||
void DeleteCharacterItemFromDB(int32 cid, int64 uid);
|
||
void UpdateCharacterItemDB(int32 cid, int64 uid, int16 count);
|
||
void DeletePlayerFromDB(int32 cid);
|
||
}; |