EQ2EMu/EQ2/source/WorldServer/World.cpp
image 41716c32fa Group fixes/support - requires characters table update
Fix #66

- Fixed crashing of the world server when a client in a group zones
- Tracking of players group_id in the characters table (for both sanity and tracking cross zone)
- Clients zoning now properly 'rejoin' their group
- Mutex locks added to group code, this makes groups more reliably work across different zones as they run on different threads (some behavior for example was some players would not see chat and others would)
2020-08-21 23:38:15 -04:00

2305 lines
No EOL
73 KiB
C++

/*
EQ2Emulator: Everquest II Server Emulator
Copyright (C) 2007 EQ2EMulator Development Team (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/>.
*/
#include <assert.h>
#include "World.h"
#include "Items/Items.h"
#include "Items/Items_ToV.h"
#include "Items/Items_DoV.h"
#include "Spells.h"
#include "client.h"
#include "WorldDatabase.h"
#include "../common/debug.h"
#include "races.h"
#include "classes.h"
#include "VisualStates.h"
#include "Appearances.h"
#include "Skills.h"
#include "LoginServer.h"
#include "Quests.h"
#include "Factions.h"
#include "Guilds/Guild.h"
#include "Collections/Collections.h"
#include "Achievements/Achievements.h"
#include "Recipes/Recipe.h"
#include "Rules/Rules.h"
#include "IRC/IRC.h"
#include "../common/Log.h"
#include "Traits/Traits.h"
#include "Chat/Chat.h"
#include "Tradeskills/Tradeskills.h"
#include "AltAdvancement/AltAdvancement.h"
#include "LuaInterface.h"
#include "HeroicOp/HeroicOp.h"
#include "RaceTypes/RaceTypes.h"
MasterQuestList master_quest_list;
MasterItemList master_item_list;
MasterSpellList master_spell_list;
MasterTraitList master_trait_list;
MasterHeroicOPList master_ho_list;
MasterSkillList master_skill_list;
MasterFactionList master_faction_list;
MasterCollectionList master_collection_list;
MasterAchievementList master_achievement_list;
MasterRecipeList master_recipe_list;
MasterRecipeBookList master_recipebook_list;
MasterTradeskillEventsList master_tradeskillevent_list;
MasterAAList master_aa_list;
MasterRaceTypeList race_types_list;
MasterAANodeList master_tree_nodes;
ClientList client_list;
ZoneList zone_list;
ZoneAuth zone_auth;
int32 Spawn::next_id = 1;
int32 WorldDatabase::next_id = 0;
Commands commands;
Variables variables;
VisualStates visual_states;
Appearances master_appearance_list;
Classes classes;
Races races;
map<int16,OpcodeManager*>EQOpcodeManager;
map<int16, int16> EQOpcodeVersions;
WorldDatabase database;
GuildList guild_list;
IRC irc;
Chat chat;
extern ConfigReader configReader;
extern LoginServer loginserver;
extern World world;
extern RuleManager rule_manager;
World::World() : save_time_timer(300000), time_tick_timer(3000), vitality_timer(3600000), player_stats_timer(60000), server_stats_timer(60000), /*remove_grouped_player(30000),*/ guilds_timer(60000), lotto_players_timer(500) {
save_time_timer.Start();
time_tick_timer.Start();
vitality_timer.Start();
player_stats_timer.Start();
server_stats_timer.Start();
//remove_grouped_player.Start();
guilds_timer.Start();
lotto_players_timer.Start();
xp_rate = -1;
ts_xp_rate = -1;
vitality_frequency = 0xFFFFFFFF;
vitality_amount = -1;
last_checked_time = 0;
items_loaded = false;
spells_loaded = false;
achievments_loaded = false;
merchant_inventory_items.clear();
MHouseZones.SetName("World::m_houseZones");
MPlayerHouses.SetName("World::m_playerHouses");
}
World::~World(){
// At this point the log system is already shut down so no calls to LogWrite are allowed in any of the functions called by this deconstructor
DeleteSpawns();
if(database.GetStatus() == database.Connected)
WriteServerStatistics();
RemoveServerStatistics();
DeleteMerchantsInfo();
MutexMap<int32, LottoPlayer*>::iterator itr = lotto_players.begin();
while (itr.Next())
safe_delete(itr->second);
map<int32, HouseZone*>::iterator itr2;
for (itr2 = m_houseZones.begin(); itr2 != m_houseZones.end(); itr2++)
safe_delete(itr2->second);
m_houseZones.clear();
tov_itemstat_conversion.clear();
PurgeStartingLists();
}
void World::init(){
WorldDatabase::next_id = database.GetMaxHotBarID();
LogWrite(COMMAND__DEBUG, 1, "Command", "-Loading Commands...");
database.LoadCommandList();
LogWrite(COMMAND__DEBUG, 1, "Command", "-Load Commands complete!");
LogWrite(FACTION__DEBUG, 1, "Faction", "-Loading Factions...");
database.LoadFactionList();
LogWrite(FACTION__DEBUG, 1, "Faction", "-Load Factions complete!...");
LogWrite(SKILL__DEBUG, 1, "Skill", "-Loading Skills...");
database.LoadSkills();
LogWrite(SKILL__DEBUG, 1, "Skill", "-Load Skills complete...");
LogWrite(WORLD__DEBUG, 1, "World", "-Loading `variables`...");
database.LoadGlobalVariables();
LogWrite(WORLD__DEBUG, 1, "World", "-Load `variables` complete!");
LogWrite(WORLD__DEBUG, 1, "World", "-Loading `appearances`...");
database.LoadAppearanceMasterList();
LogWrite(WORLD__DEBUG, 1, "World", "-Load `appearances` complete!");
LogWrite(WORLD__DEBUG, 1, "World", "-Loading `visual_states`...");
database.LoadVisualStates();
LogWrite(WORLD__DEBUG, 1, "World", "-Load `visual states` complete!");
LoadStartingLists();
LogWrite(WORLD__DEBUG, 1, "World", "-Setting system parameters...");
Variable* var = variables.FindVariable("gametime");
const char* time_string = 0;
char default_time[] = "0/0/3800 8:30";
if(var)
time_string = var->GetValue();
if(!time_string)
time_string = default_time;
int year, month, day, hour, minute;
sscanf (time_string, "%d/%d/%d %d:%d", &month, &day, &year, &hour, &minute);
LogWrite(WORLD__DEBUG, 3, "World", "--Setting World Time to %s...", time_string);
world_time.month = month;
world_time.day = day;
world_time.year = year;
world_time.hour = hour;
world_time.minute = minute;
LogWrite(WORLD__DEBUG, 3, "World", "--Loading Vitality Information...");
LoadVitalityInformation();
LogWrite(WORLD__DEBUG, 3, "World", "--Loading Server Statistics...");
database.LoadServerStatistics();
LogWrite(WORLD__DEBUG, 3, "World", "--Setting Server Start Time...");
UpdateServerStatistic(STAT_SERVER_START_TIME, Timer::GetUnixTimeStamp(), true);
LogWrite(WORLD__DEBUG, 3, "World", "--Resetting Accepted Connections to 0...");
UpdateServerStatistic(STAT_SERVER_ACCEPTED_CONNECTION, 0, true);
LogWrite(WORLD__DEBUG, 3, "World", "--Resetting Active Zones to 0...");
UpdateServerStatistic(STAT_SERVER_NUM_ACTIVE_ZONES, 0, true);
// Clear all online players at server startup
LogWrite(WORLD__DEBUG, 3, "World", "--Resetting characters online flags...");
database.ToggleCharacterOnline();
LogWrite(WORLD__DEBUG, 1, "World", "-Set system parameters complete!");
LogWrite(RULESYS__DEBUG, 1, "Rules", "-Loading Rule Sets...");
database.LoadRuleSets();
LogWrite(RULESYS__DEBUG, 1, "Rules", "-Load Rule Sets complete!");
LogWrite(CHAT__DEBUG, 1, "IRC", "-Starting IRC thread...");
LoadItemBlueStats();
//PopulateTOVStatMap();
group_buff_updates.Start(rule_manager.GetGlobalRule(R_Client, GroupSpellsTimer)->GetInt32());
irc.Start();
}
PacketStruct* World::GetWorldTime(int16 version){
PacketStruct* packet = configReader.getStruct("WS_GameWorldTime", version);
if(packet){
packet->setDataByName("year", world_time.year);
packet->setDataByName("month", world_time.month);
packet->setDataByName("day", world_time.day);
packet->setDataByName("hour", world_time.hour);
packet->setDataByName("minute", world_time.minute);
packet->setDataByName("unknown", 250);
packet->setDataByName("unix_time", Timer::GetUnixTimeStamp());
packet->setDataByName("unknown2", 1);
}
return packet;
}
float World::GetXPRate(){
if(xp_rate >= 0)
return xp_rate;
xp_rate = rule_manager.GetGlobalRule(R_Player, XPMultiplier)->GetFloat();
LogWrite(WORLD__DEBUG, 0, "World", "Setting Global XP Rate to: %.2f", xp_rate);
return xp_rate;
}
float World::GetTSXPRate()
{
if(ts_xp_rate >= 0)
return ts_xp_rate;
ts_xp_rate = rule_manager.GetGlobalRule(R_Player, TSXPMultiplier)->GetFloat();
LogWrite(WORLD__DEBUG, 0, "World", "Setting Global Tradeskill XP Rate to: %.2f", ts_xp_rate);
return ts_xp_rate;
}
void World::Process(){
if(last_checked_time > Timer::GetCurrentTime2())
return;
last_checked_time = Timer::GetCurrentTime2() + 1000;
if(save_time_timer.Check())
database.SaveWorldTime(&world_time);
if(time_tick_timer.Check())
WorldTimeTick();
if(vitality_timer.Check())
UpdateVitality();
if (player_stats_timer.Check())
WritePlayerStatistics();
if (server_stats_timer.Check())
WriteServerStatistics();
/*if(remove_grouped_player.Check())
CheckRemoveGroupedPlayer();*/
if (group_buff_updates.Check())
GetGroupManager()->UpdateGroupBuffs();
if (guilds_timer.Check())
SaveGuilds();
if (lotto_players_timer.Check())
CheckLottoPlayers();
}
vector<Variable*>* World::GetClientVariables(){
return variables.GetVariables("cl_");
}
void World::LoadVitalityInformation()
{
int32 timestamp = Timer::GetUnixTimeStamp();
int32 diff = 0;
// fetch vitalitytimer value from `variables` table
Variable* timer_var = variables.FindVariable("vitalitytimer");
if(timer_var)
{
try
{
diff = timestamp - atoul(timer_var->GetValue());
diff *= 1000; //convert seconds to milliseconds
}
catch(...)
{
LogWrite(WORLD__ERROR, 0, "World", "Error parsing vitalitytimer, value: %s", timer_var->GetValue());
}
}
// Now using Rules System to set vitality parameters
vitality_amount = rule_manager.GetGlobalRule(R_Player, VitalityAmount)->GetFloat();
vitality_frequency = rule_manager.GetGlobalRule(R_Player, VitalityFrequency)->GetInt32();
vitality_frequency *= 1000; //convert seconds to milliseconds
if(diff >= vitality_frequency)
UpdateVitality(); //update now
else
vitality_timer.SetTimer(vitality_frequency - diff);
}
void World::UpdateVitality()
{
// push new vitalitytimer to `variables` table
database.UpdateVitality(Timer::GetUnixTimeStamp(), vitality_amount);
if(vitality_timer.GetDuration() != vitality_frequency)
vitality_timer.SetTimer(vitality_frequency);
zone_list.UpdateVitality(vitality_amount);
}
void ZoneList::UpdateVitality(float amount)
{
list<ZoneServer*>::iterator zone_iter;
ZoneServer* tmp = 0;
MZoneList.readlock(__FUNCTION__, __LINE__);
for(zone_iter=zlist.begin(); zone_iter!=zlist.end(); zone_iter++)
{
tmp = *zone_iter;
if(tmp)
tmp->UpdateVitality(amount);
}
MZoneList.releasereadlock(__FUNCTION__, __LINE__);
}
void World::WorldTimeTick(){
world_time.minute++;
//I know it looks complicated, but the nested ifs are to avoid checking all of them every 3 seconds
if(world_time.minute >= 60){ // >= in case of bad time from db
world_time.minute = 0;
world_time.hour++;
if(world_time.hour >= 24){
world_time.hour = 0;
world_time.day++;
if(world_time.day>=30){
world_time.day = 0;
world_time.month++;
if(world_time.month >= 12){
world_time.month = 0;
world_time.year++;
}
}
}
}
}
ZoneList::ZoneList() {
MZoneList.SetName("ZoneList::MZoneList");
}
ZoneList::~ZoneList() {
list<ZoneServer*>::iterator zone_iter;
ZoneServer* zs = 0;
for(zone_iter=zlist.begin(); zone_iter!=zlist.end();){
zs = *zone_iter;
zone_iter = zlist.erase(zone_iter);
safe_delete(zs);
}
}
void ZoneList::CheckFriendList(Client* client) {
LogWrite(WORLD__DEBUG, 0, "World", "Sending FriendList...");
MClientList.lock();
map<string,Client*>::iterator itr;
for(itr = client_map.begin(); itr != client_map.end(); itr++){
if(itr->second != client && itr->second){
if(itr->second->GetPlayer()->IsFriend(client->GetPlayer()->GetName())){
itr->second->SendFriendList();
itr->second->Message(CHANNEL_COLOR_CHAT_RELATIONSHIP, "Friend: %s has logged in.", client->GetPlayer()->GetName());
}
}
}
MClientList.unlock();
}
void ZoneList::CheckFriendZoned(Client* client){
MClientList.lock();
map<string,Client*>::iterator itr;
for(itr = client_map.begin(); itr != client_map.end(); itr++){
if(itr->second != client && itr->second){
if(itr->second->GetPlayer()->IsFriend(client->GetPlayer()->GetName())){
itr->second->SendFriendList();
}
}
}
MClientList.unlock();
}
bool ZoneList::HandleGlobalChatMessage(Client* from, char* to, int16 channel, const char* message, const char* channel_name){
if (!from) {
LogWrite(WORLD__ERROR, 0, "World", "HandleGlobalChatMessage() called with an invalid client");
return false;
}
if(channel == CHANNEL_TELL){
Client* find_client = zone_list.GetClientByCharName(to);
if(!find_client || find_client->GetPlayer()->IsIgnored(from->GetPlayer()->GetName()))
return false;
else if(find_client == from)
{
from->Message(CHANNEL_COLOR_RED,"You must be very lonely...(ERROR: Cannot send tell to self)");
}
else
{
PacketStruct* packet = configReader.getStruct("WS_HearChat", from->GetVersion());
if(packet){
packet->setMediumStringByName("from", from->GetPlayer()->GetName());
packet->setMediumStringByName("to", find_client->GetPlayer()->GetName());
packet->setDataByName("channel", CHANNEL_TELL);
packet->setDataByName("from_spawn_id", 0xFFFFFFFF);
packet->setDataByName("to_spawn_id", 0xFFFFFFFF);
packet->setDataByName("unknown2", 1, 1);
packet->setDataByName("show_bubble", 1);
packet->setDataByName("understood", 1);
packet->setDataByName("time", 2);
packet->setMediumStringByName("message", message);
if(channel_name)
packet->setMediumStringByName("channel_name", channel_name);
EQ2Packet* outpacket = packet->serialize();
//DumpPacket(outpacket);
find_client->QueuePacket(outpacket->Copy());
from->QueuePacket(outpacket);
safe_delete(packet);
}
if (find_client->GetPlayer()->get_character_flag(CF_AFK)) {
PacketStruct* packet2 = configReader.getStruct("WS_HearChat", from->GetVersion());
if (packet2) {
packet2->setMediumStringByName("from", find_client->GetPlayer()->GetName());
packet2->setMediumStringByName("to", from->GetPlayer()->GetName());
packet2->setDataByName("channel", CHANNEL_TELL);
packet2->setDataByName("from_spawn_id", 0xFFFFFFFF);
packet2->setDataByName("to_spawn_id", 0xFFFFFFFF);
packet2->setDataByName("unknown2", 1, 1);
packet2->setDataByName("show_bubble", 1);
packet2->setDataByName("understood", 1);
packet2->setDataByName("time", 2);
packet2->setMediumStringByName("message", find_client->GetPlayer()->GetAwayMessage().c_str());
if (channel_name)
packet2->setMediumStringByName("channel_name", channel_name);
EQ2Packet* outpacket = packet2->serialize();
from->QueuePacket(outpacket->Copy());
find_client->QueuePacket(outpacket);
safe_delete(packet2);
}
}
}
}
else if(channel == CHANNEL_GROUP) {
GroupMemberInfo* gmi = from->GetPlayer()->GetGroupMemberInfo();
if(gmi)
world.GetGroupManager()->GroupMessage(gmi->group_id, message);
}
else{
list<ZoneServer*>::iterator zone_iter;
ZoneServer* zs = 0;
MZoneList.readlock(__FUNCTION__, __LINE__);
for(zone_iter=zlist.begin(); zone_iter!=zlist.end();zone_iter++){
zs = *zone_iter;
if(zs)
zs->HandleChatMessage(from->GetPlayer(), to, channel, message, 0, channel_name);
}
MZoneList.releasereadlock(__FUNCTION__, __LINE__);
}
return true;
}
void ZoneList::LoadSpellProcess(){
list<ZoneServer*>::iterator zone_iter;
ZoneServer* zone = 0;
MZoneList.readlock(__FUNCTION__, __LINE__);
for (zone_iter=zlist.begin(); zone_iter!=zlist.end();zone_iter++){
zone = *zone_iter;
if (zone)
zone->LoadSpellProcess();
}
MZoneList.releasereadlock(__FUNCTION__, __LINE__);
}
void ZoneList::DeleteSpellProcess(){
list<ZoneServer*>::iterator zone_iter;
ZoneServer* zone = 0;
MZoneList.readlock(__FUNCTION__, __LINE__);
for (zone_iter=zlist.begin(); zone_iter!=zlist.end();zone_iter++){
zone = *zone_iter;
if (zone)
zone->DeleteSpellProcess();
}
MZoneList.releasereadlock(__FUNCTION__, __LINE__);
}
void ZoneList::HandleGlobalBroadcast(const char* message) {
list<ZoneServer*>::iterator zone_iter;
ZoneServer* zone = 0;
MZoneList.readlock(__FUNCTION__, __LINE__);
for (zone_iter=zlist.begin(); zone_iter!=zlist.end();zone_iter++){
zone = *zone_iter;
if (zone)
zone->HandleBroadcast(message);
}
MZoneList.releasereadlock(__FUNCTION__, __LINE__);
}
void ZoneList::HandleGlobalAnnouncement(const char* message) {
list<ZoneServer*>::iterator zone_iter;
ZoneServer* zone = 0;
MZoneList.readlock(__FUNCTION__, __LINE__);
for (zone_iter=zlist.begin(); zone_iter!=zlist.end();zone_iter++){
zone = *zone_iter;
if (zone)
zone->HandleAnnouncement(message);
}
MZoneList.releasereadlock(__FUNCTION__, __LINE__);
}
int32 ZoneList::Count(){
return zlist.size();
}
void ZoneList::Add(ZoneServer* zone) {
MZoneList.writelock(__FUNCTION__, __LINE__);
zlist.push_back(zone);
MZoneList.releasewritelock(__FUNCTION__, __LINE__);
}
void ZoneList::Remove(ZoneServer* zone) {
MZoneList.writelock(__FUNCTION__, __LINE__);
zlist.remove(zone);
MZoneList.releasewritelock(__FUNCTION__, __LINE__);
}
ZoneServer* ZoneList::Get(const char* zone_name, bool loadZone) {
list<ZoneServer*>::iterator zone_iter;
ZoneServer* tmp = 0;
ZoneServer* ret = 0;
MZoneList.readlock(__FUNCTION__, __LINE__);
for(zone_iter=zlist.begin(); zone_iter!=zlist.end(); zone_iter++){
tmp = *zone_iter;
if (!tmp->isZoneShuttingDown() && !tmp->IsInstanceZone() && strlen(zone_name) == strlen(tmp->GetZoneName()) &&
strncasecmp(tmp->GetZoneName(), zone_name, strlen(zone_name))==0){
if(tmp->NumPlayers() < 30 || tmp->IsCityZone())
ret = tmp;
break;
}
}
MZoneList.releasereadlock(__FUNCTION__, __LINE__);
if(!ret )
{
if ( loadZone )
{
ret = new ZoneServer(zone_name);
database.LoadZoneInfo(ret);
ret->Init();
}
}
return ret;
}
ZoneServer* ZoneList::Get(int32 id, bool loadZone) {
list<ZoneServer*>::iterator zone_iter;
ZoneServer* tmp = 0;
ZoneServer* ret = 0;
MZoneList.readlock(__FUNCTION__, __LINE__);
for(zone_iter=zlist.begin(); zone_iter!=zlist.end(); zone_iter++){
tmp = *zone_iter;
if(!tmp->isZoneShuttingDown() && !tmp->IsInstanceZone() && tmp->GetZoneID() == id){
if(tmp->NumPlayers() < 30 || tmp->IsCityZone())
ret = tmp;
break;
}
}
MZoneList.releasereadlock(__FUNCTION__, __LINE__);
if(ret)
tmp = ret;
else if (loadZone) {
string* zonename = database.GetZoneName(id);
if(zonename){
tmp = new ZoneServer(zonename->c_str());
database.LoadZoneInfo(tmp);
tmp->Init();
safe_delete(zonename);
}
}
return tmp;
}
void ZoneList::SendZoneList(Client* client) {
list<ZoneServer*>::iterator zone_iter;
ZoneServer* tmp = 0;
MZoneList.readlock(__FUNCTION__, __LINE__);
int zonesListed = 0;
for(zone_iter=zlist.begin(); zone_iter!=zlist.end(); zone_iter++){
tmp = *zone_iter;
if ( zonesListed > 20 )
{
client->Message(CHANNEL_COLOR_YELLOW,"Reached max zone list of 20.");
break;
}
zonesListed++;
client->Message(CHANNEL_COLOR_YELLOW,"Zone(ID): %s(%i), Instance ID: %i, Description: %s.",tmp->GetZoneName(),tmp->GetZoneID(),
tmp->GetInstanceID(),tmp->GetZoneDescription());
}
MZoneList.releasereadlock(__FUNCTION__, __LINE__);
}
ZoneServer* ZoneList::GetByInstanceID(int32 id, int32 zone_id) {
list<ZoneServer*>::iterator zone_iter;
ZoneServer* tmp = 0;
ZoneServer* ret = 0;
MZoneList.readlock(__FUNCTION__, __LINE__);
if ( id > 0 )
{
for(zone_iter=zlist.begin(); zone_iter!=zlist.end(); zone_iter++){
tmp = *zone_iter;
if(tmp->GetInstanceID() == id){
ret = tmp;
break;
}
}
}
MZoneList.releasereadlock(__FUNCTION__, __LINE__);
if(ret)
tmp = ret;
else if ( zone_id > 0 ){
string* zonename = database.GetZoneName(zone_id);
if(zonename){
tmp = new ZoneServer(zonename->c_str());
// the player is trying to preload an already existing instance but it isn't loaded
if ( id > 0 )
tmp->SetupInstance(id);
database.LoadZoneInfo(tmp);
tmp->Init();
safe_delete(zonename);
}
}
return tmp;
}
ZoneServer* ZoneList::GetByLowestPopulation(int32 zone_id) {
ZoneServer* ret = 0;
ZoneServer* zone = 0;
int32 clients = 0;
list<ZoneServer*>::iterator itr;
MZoneList.readlock(__FUNCTION__, __LINE__);
if (zone_id) {
for (itr = zlist.begin(); itr != zlist.end(); itr++) {
zone = *itr;
if (zone) {
// check the zone id's
if (zone->GetZoneID() == zone_id) {
// check this zones client count
if (clients == 0 || zone->GetClientCount() < clients) {
ret = zone;
clients = zone->GetClientCount();
}
}
}
}
}
MZoneList.releasereadlock(__FUNCTION__, __LINE__);
return ret;
}
bool ZoneList::ClientConnected(int32 account_id){
bool ret = false;
map<string, Client*>::iterator itr;
MClientList.lock();
for(itr=client_map.begin(); itr != client_map.end(); itr++){
if(itr->second && itr->second->GetAccountID() == account_id && (itr->second->GetPlayer()->GetActivityStatus() & ACTIVITY_STATUS_LINKDEAD) == 0){
ret = true;
break;
}
else if(!itr->second){
client_map.erase(itr);
if(client_map.size() > 0){
itr=client_map.begin();
if(itr == client_map.end()){
if(itr->second && itr->second->GetAccountID() == account_id && (itr->second->GetPlayer()->GetActivityStatus() & ACTIVITY_STATUS_LINKDEAD) == 0)
ret = true;
break;
}
}
else
break;
}
}
MClientList.unlock();
return ret;
}
void ZoneList::ReloadClientQuests(){
list<ZoneServer*>::iterator zone_iter;
ZoneServer* tmp = 0;
MZoneList.readlock(__FUNCTION__, __LINE__);
for(zone_iter=zlist.begin(); zone_iter!=zlist.end(); zone_iter++){
tmp = *zone_iter;
if(tmp)
tmp->ReloadClientQuests();
}
MZoneList.releasereadlock(__FUNCTION__, __LINE__);
}
void ZoneList::ProcessWhoQuery(vector<string>* queries, ZoneServer* zone, vector<Entity*>* players, bool isGM){
Entity* player = 0;
bool add_player = true;
bool found_match = false;
int8 lower = 0;
int8 upper = 0;
vector<Entity*> tmpPlayers;
vector<Entity*>::iterator spawn_iter;
if(!zone->isZoneShuttingDown()){
tmpPlayers = zone->GetPlayers();
for(spawn_iter = tmpPlayers.begin(); spawn_iter!=tmpPlayers.end(); spawn_iter++){
player = *spawn_iter;
add_player = true;
Client* find_client = zone_list.GetClientByCharName(player->GetName());
if (find_client == NULL) continue;
int flags = find_client->GetPlayer()->GetInfoStruct()->flags;
int flags2 = find_client->GetPlayer()->GetInfoStruct()->flags2;
for(int32 i=0;add_player && queries && i<queries->size();i++){
found_match = false;
if(queries->at(i) == "ALL")
continue;
if(queries->at(i).length() > 3 && classes.GetClassID(queries->at(i).c_str()) > 0){
if(player->GetAdventureClass() != classes.GetClassID(queries->at(i).c_str()))
add_player = false;
found_match = true;
}
else if(queries->at(i).length() > 2 && races.GetRaceID(queries->at(i).c_str()) > 0){
if(player->GetRace() != races.GetRaceID(queries->at(i).c_str()))
add_player = false;
found_match = true;
}
if(!found_match && queries->at(i) == "GOOD"){
if(player->GetDeity() != 1)
add_player = false;
found_match = true;
}
else if(!found_match && queries->at(i) == "EVIL"){
if(player->GetDeity() == 1)
add_player = false;
found_match = true;
}
if((queries->at(i) == "GUIDE") && (find_client->GetAdminStatus() > 0) && ((find_client->GetAdminStatus() >> 4) < 5))
found_match = true;
else if((queries->at(i) == "GM") && ((find_client->GetAdminStatus() >> 4) > 4))
found_match = true;
else if((queries->at(i) == "LFG") && (flags & (1 << CF_LFG)))
found_match = true;
else if((queries->at(i) == "LFW") && (flags & (1 << CF_LFW)))
found_match = true;
else if((queries->at(i) == "ROLEPLAYING") && (flags & (1 << CF_ROLEPLAYING)))
found_match = true;
else if(strspn(queries->at(i).c_str(),"0123456789") == queries->at(i).length()){
try{
if(lower == 0)
lower = atoi(queries->at(i).c_str());
else
upper = atoi(queries->at(i).c_str());
}
catch(...){}
found_match = true;
}
if(!found_match){
string name = string(player->GetName());
name = ToUpper(name);
if(name.find(queries->at(i)) == name.npos)
add_player = false;
}
}
if(lower > 0 && upper > 0){
if(player->GetLevel() < lower || player->GetLevel() > upper)
add_player = false;
}
else if(lower > 0){
if(player->GetLevel() != lower)
add_player = false;
}
if((flags2 & (1 << (CF_GM_HIDDEN - 32))) && !isGM) {
add_player = false;
found_match = true;
}
if(add_player)
players->push_back(player);
}
}
}
void ZoneList::ProcessWhoQuery(const char* query, Client* client){
list<ZoneServer*>::iterator zone_iter;
vector<Entity*> players;
vector<Entity*>::iterator spawn_iter;
Entity* player = 0;
//for now display all clients
bool all = false;
vector<string>* queries = 0;
bool isGM = ((client->GetAdminStatus() >> 4) > 4);
if(query){
string query_string = string(query);
query_string = ToUpper(query_string);
queries = SplitString(query_string, ' ');
}
if(queries && queries->size() > 0 && queries->at(0) == "ALL")
all = true;
if(all){
MZoneList.readlock(__FUNCTION__, __LINE__);
for(zone_iter=zlist.begin(); zone_iter!=zlist.end(); zone_iter++){
ZoneServer* tmp = *zone_iter;
ProcessWhoQuery(queries, tmp, &players, isGM);
}
MZoneList.releasereadlock(__FUNCTION__, __LINE__);
}
else{
ProcessWhoQuery(queries, client->GetCurrentZone(), &players, isGM);
}
PacketStruct* packet = configReader.getStruct("WS_WhoQueryReply", client->GetVersion());
if(packet){
packet->setDataByName("account_id", client->GetAccountID());
packet->setDataByName("unknown", 0xFFFFFFFF);
int8 num_characters = players.size();
int8 max_who_results = 10;
int8 max_who_results_status_override = 100;
Variable* var = variables.FindVariable("max_who_results_status_override");
if ( var ){
max_who_results_status_override = atoi(var->GetValue());
}
// AdnaeDMorte
if ( client->GetAdminStatus() >= max_who_results_status_override ){
client->Message(CHANNEL_COLOR_RED, "** ADMIN-MODE ** ");
}
Variable* var1 = variables.FindVariable("max_who_results");
if ( var1 ){
max_who_results = atoi(var1->GetValue());
}
if(num_characters > max_who_results && client->GetAdminStatus() < max_who_results_status_override){
num_characters = max_who_results;
packet->setDataByName("response", 3); //response 1 = error message, 3 == capped
}
else
packet->setDataByName("response", 2);
packet->setArrayLengthByName("num_characters", num_characters);
packet->setDataByName("unknown10", 1);
int i=0;
for(spawn_iter = players.begin(); spawn_iter!=players.end(); spawn_iter++, i++){
if(i == num_characters)
break;
player = *spawn_iter;
Client* find_client = zone_list.GetClientByCharName(player->GetName());
int flags = find_client->GetPlayer()->GetInfoStruct()->flags;
int flags2 = find_client->GetPlayer()->GetInfoStruct()->flags2;
packet->setArrayDataByName("char_name", player->GetName(), i);
packet->setArrayDataByName("level", player->GetLevel(), i);
packet->setArrayDataByName("admin_level", ((flags2 & (1 << (CF_HIDE_STATUS - 32))) && !isGM)?0:(find_client->GetAdminStatus() >> 4), i);
packet->setArrayDataByName("class", player->GetAdventureClass(), i);
packet->setArrayDataByName("unknown4", 0xFF, i); //probably tradeskill class
packet->setArrayDataByName("flags", (((flags >> CF_ANONYMOUS) & 1) << 0 ) |
(((flags >> CF_LFG) & 1) << 1 ) |
(((flags >> CF_ANONYMOUS) & 1) << 2 ) |
/*(((flags >> CF_HIDDEN) & 1) << 3 ) |*/
(((flags >> CF_ROLEPLAYING) & 1) << 4 ) |
(((flags >> CF_AFK) & 1) << 5 ) |
(((flags >> CF_LFW) & 1) << 6 ) /*|
(((flags >> CF_NOTA) & 1) << 7 )*/, i);
packet->setArrayDataByName("race", player->GetRace(), i);
if(player->GetZone() && player->GetZone()->GetZoneDescription())
packet->setArrayDataByName("zone", player->GetZone()->GetZoneDescription(), i);
if(player->appearance.sub_title) {
int32 sub_title_length = strlen(player->appearance.sub_title);
char tmp_title[255];
int32 index = 0;
int32 index_tmp = 0;
while (index < sub_title_length) {
if (player->appearance.sub_title[index] != '<' && player->appearance.sub_title[index] != '>') {
memcpy(tmp_title + index_tmp, player->appearance.sub_title + index, 1);
index_tmp++;
}
index++;
}
tmp_title[index_tmp] = '\0';
packet->setArrayDataByName("guild", tmp_title, i);
}
}
client->QueuePacket(packet->serialize());
safe_delete(packet);
}
}
bool ZoneList::DepopFinished(){
list<ZoneServer*>::iterator zone_iter;
MZoneList.readlock(__FUNCTION__, __LINE__);
bool finished_depop = true;
for(zone_iter=zlist.begin(); zone_iter!=zlist.end(); zone_iter++){
if(!(*zone_iter)->FinishedDepop())
finished_depop = false;
}
MZoneList.releasereadlock(__FUNCTION__, __LINE__);
return finished_depop;
}
void ZoneList::Depop(){
list<ZoneServer*>::iterator zone_iter;
MZoneList.readlock(__FUNCTION__, __LINE__);
for(zone_iter=zlist.begin(); zone_iter!=zlist.end(); zone_iter++){
(*zone_iter)->Depop();
}
MZoneList.releasereadlock(__FUNCTION__, __LINE__);
}
void ZoneList::Repop(){
list<ZoneServer*>::iterator zone_iter;
MZoneList.readlock(__FUNCTION__, __LINE__);
for(zone_iter=zlist.begin(); zone_iter!=zlist.end(); zone_iter++){
(*zone_iter)->Depop(false, true);
}
MZoneList.releasereadlock(__FUNCTION__, __LINE__);
}
void ZoneList::ReloadSpawns() {
MZoneList.readlock(__FUNCTION__, __LINE__);
list<ZoneServer*>::iterator itr;
for (itr = zlist.begin(); itr != zlist.end(); itr++)
(*itr)->ReloadSpawns();
MZoneList.releasereadlock(__FUNCTION__, __LINE__);
}
bool World::ReportBug(string data, char* player_name, int32 account_id, const char* spawn_name, int32 spawn_id, int32 zone_id){
//loginserver
vector<string> list;
int32 offset = 0;
int32 old_offset = 0;
while((offset = data.find(7, old_offset+1)) < 0xFFFFFFFF){
if(old_offset > 0)
list.push_back(data.substr(old_offset+1, offset-old_offset-1));
else
list.push_back(data.substr(old_offset, offset));
old_offset = offset;
}
if(list.size() > 0 && list.size() < 7){
string output = "Invalid bug list:\n";
for(int32 i=0;i<list.size();i++)
output = output.append("\t").append(list[i]).append("\n");
LogWrite(WORLD__ERROR, 0, "World", "%s", output.c_str());
return false;
}
ServerPacket* outpack = new ServerPacket(ServerOP_BugReport, sizeof(BugReport));
BugReport* report = (BugReport*)outpack->pBuffer;
if (list.size() < 7) {
strncpy(report->category, "AutoBug", 7);
strncpy(report->subcategory, "AutoGenerate", 12);
strncpy(report->causes_crash, "N", 1);
strncpy(report->reproducible, "Y", 1);
strncpy(report->summary, data.c_str(), data.length() > 127 ? 127 : data.length());
strncpy(report->description, data.c_str(), data.length() > 1999 ? 1999 : data.length());
strncpy(report->version, "CUR", 3);
}
else
{
strncpy(report->category, list[0].c_str(), list[0].length() > 63 ? 63 : list[0].length());
strncpy(report->subcategory, list[1].c_str(), list[1].length() > 63 ? 63 : list[1].length());
strncpy(report->causes_crash, list[2].c_str(), list[2].length() > 63 ? 63 : list[2].length());
strncpy(report->reproducible, list[3].c_str(), list[3].length() > 63 ? 63 : list[3].length());
strncpy(report->summary, list[4].c_str(), list[4].length() > 127 ? 127 : list[4].length());
strncpy(report->description, list[5].c_str(), list[5].length() > 1999 ? 1999 : list[5].length());
strncpy(report->version, list[6].c_str(), list[6].length() > 31 ? 31 : list[6].length());
}
strncpy(report->player, player_name, strlen(player_name) > 63 ? 63 : strlen(player_name));
strncpy(report->spawn_name, spawn_name, strlen(spawn_name) > 63 ? 63 : strlen(spawn_name));
report->spawn_id = spawn_id;
report->account_id = account_id;
report->zone_id = zone_id;
loginserver.SendPacket(outpack);
database.SaveBugReport(report->category, report->subcategory, report->causes_crash, report->reproducible, report->summary, report->description, report->version, report->player, account_id, spawn_name, spawn_id, zone_id);
safe_delete(outpack);
return true;
}
void ZoneList::WritePlayerStatistics() {
list<ZoneServer*>::iterator zone_itr;
MZoneList.readlock(__FUNCTION__, __LINE__);
for (zone_itr = zlist.begin(); zone_itr != zlist.end(); zone_itr++)
(*zone_itr)->WritePlayerStatistics();
MZoneList.releasereadlock(__FUNCTION__, __LINE__);
}
void ZoneList::ShutDownZones(){
LogWrite(WORLD__INFO, 0, "World", "Shutting down all zones, please wait...");
list<ZoneServer*>::iterator zone_itr;
int32 size = 0;
MZoneList.readlock(__FUNCTION__, __LINE__);
for (zone_itr = zlist.begin(); zone_itr != zlist.end(); zone_itr++){
(*zone_itr)->Shutdown();
}
size = zlist.size();
MZoneList.releasereadlock(__FUNCTION__, __LINE__);
while(size > 0){
Sleep(10);
MZoneList.readlock(__FUNCTION__, __LINE__);
size = zlist.size();
MZoneList.releasereadlock(__FUNCTION__, __LINE__);
}
LogWrite(WORLD__INFO, 0, "World", "Zone shutdown complete");
}
void ZoneList::ReloadMail() {
map<string, Client*>::iterator itr;
MClientList.writelock(__FUNCTION__, __LINE__);
for (itr = client_map.begin(); itr != client_map.end(); itr++) {
itr->second->GetPlayer()->DeleteMail();
database.LoadPlayerMail(itr->second);
}
MClientList.releasewritelock(__FUNCTION__, __LINE__);
}
void World::AddSpawnScript(int32 id, const char* name){
MSpawnScripts.lock();
if(name)
spawn_scripts[id] = string(name);
MSpawnScripts.unlock();
}
void World::AddSpawnEntryScript(int32 id, const char* name){
MSpawnScripts.lock();
if(name)
spawnentry_scripts[id] = string(name);
MSpawnScripts.unlock();
}
void World::AddSpawnLocationScript(int32 id, const char* name){
MSpawnScripts.lock();
if(name)
spawnlocation_scripts[id] = string(name);
MSpawnScripts.unlock();
}
void World::AddZoneScript(int32 id, const char* name) {
MZoneScripts.lock();
if (name)
zone_scripts[id] = string(name);
MZoneScripts.unlock();
}
const char* World::GetSpawnScript(int32 id){
LogWrite(SPAWN__TRACE, 1, "Spawn", "Enter %s", __FUNCTION__);
const char* ret = 0;
MSpawnScripts.lock();
if(spawn_scripts.count(id) > 0)
ret = spawn_scripts[id].c_str();
MSpawnScripts.unlock();
LogWrite(SPAWN__TRACE, 1, "Spawn", "Exit %s", __FUNCTION__);
return ret;
}
const char* World::GetSpawnEntryScript(int32 id){
LogWrite(SPAWN__TRACE, 1, "Spawn", "Enter %s", __FUNCTION__);
const char* ret = 0;
MSpawnScripts.lock();
if(spawnentry_scripts.count(id) > 0)
ret = spawnentry_scripts[id].c_str();
MSpawnScripts.unlock();
LogWrite(SPAWN__TRACE, 1, "Spawn", "Exit %s", __FUNCTION__);
return ret;
}
const char* World::GetSpawnLocationScript(int32 id){
LogWrite(SPAWN__TRACE, 1, "Spawn", "Enter %s", __FUNCTION__);
const char* ret = 0;
MSpawnScripts.lock();
if(spawnlocation_scripts.count(id) > 0)
ret = spawnlocation_scripts[id].c_str();
MSpawnScripts.unlock();
LogWrite(SPAWN__TRACE, 1, "Spawn", "Exit %s", __FUNCTION__);
return ret;
}
const char* World::GetZoneScript(int32 id) {
const char* ret = 0;
MZoneScripts.lock();
if (zone_scripts.count(id) > 0)
ret = zone_scripts[id].c_str();
MZoneScripts.unlock();
return ret;
}
void World::ResetSpawnScripts(){
MSpawnScripts.lock();
spawn_scripts.clear();
spawnentry_scripts.clear();
spawnlocation_scripts.clear();
MSpawnScripts.unlock();
}
void World::ResetZoneScripts() {
MZoneScripts.lock();
zone_scripts.clear();
MZoneScripts.unlock();
}
vector<MerchantItemInfo>* World::GetMerchantItemList(int32 merchant_id, int8 merchant_type, Player* player)
{
vector<MerchantItemInfo>* ret = 0;
MMerchantList.lock();
if(merchant_info.count(merchant_id) > 0)
{
MerchantInfo* info = merchant_info[merchant_id];
vector<MerchantItemInfo>::iterator itr;
int32 inventory_id = 0;
Item* item = 0;
for(int i=info->inventory_ids.size()-1;i>=0;i--)
{
inventory_id = info->inventory_ids[i];
if(merchant_inventory_items.count(inventory_id) > 0)
{
for(itr = merchant_inventory_items[inventory_id].begin(); itr != merchant_inventory_items[inventory_id].end(); itr++)
{
if(!ret)
ret = new vector<MerchantItemInfo>;
item = master_item_list.GetItem((*itr).item_id);
// if NOT spell merchant, OR
// skill req is any skill, OR player has the skill, AND
// skill req2 is any skill, OR player has the skill2
if(item && ( (merchant_type & MERCHANT_TYPE_SPELLS) == 0 || ( (item->generic_info.skill_req1 == 0xFFFFFFFF || player->GetSkills()->HasSkill(item->generic_info.skill_req1)) && (item->generic_info.skill_req2 == 0xFFFFFFFF || player->GetSkills()->HasSkill(item->generic_info.skill_req2)) ) ) )
(*ret).push_back(*itr);
}
}
}
}
MMerchantList.unlock();
return ret;
}
vector<MerchantItemInfo>* World::GetMerchantList(int32 merchant_id){
vector<MerchantItemInfo>* ret = 0;
MMerchantList.lock();
if(merchant_info.count(merchant_id) > 0){
MerchantInfo* info = merchant_info[merchant_id];
map<int32, int16>::iterator itr;
int32 inventory_id = 0;
for(int i=info->inventory_ids.size()-1;i>=0;i--){
inventory_id = info->inventory_ids[i];
if(merchant_inventory_items.count(inventory_id) > 0){
ret = &merchant_inventory_items[inventory_id];
}
}
}
MMerchantList.unlock();
return ret;
}
void World::AddMerchantItem(int32 inventory_id, MerchantItemInfo ItemInfo){
MMerchantList.lock();
merchant_inventory_items[inventory_id].push_back(ItemInfo);
MMerchantList.unlock();
}
void World::DeleteMerchantItems(){
MMerchantList.lock();
merchant_inventory_items.clear();
MMerchantList.unlock();
}
void World::RemoveMerchantItem(int32 inventory_id, int32 item_id){
MMerchantList.lock();
if(merchant_inventory_items.count(inventory_id) > 0) {
vector<MerchantItemInfo>::iterator itr;
for(itr = merchant_inventory_items[inventory_id].begin(); itr != merchant_inventory_items[inventory_id].end(); itr++){
if ((*itr).item_id == item_id) {
merchant_inventory_items[inventory_id].erase(itr);
break;
}
}
}
MMerchantList.unlock();
}
int16 World::GetMerchantItemQuantity(int32 merchant_id, int32 item_id){
int16 amount = 0;
int32 inventory_id = GetInventoryID(merchant_id, item_id);
if(inventory_id > 0){
MMerchantList.lock();
vector<MerchantItemInfo>::iterator itr;
for(itr = merchant_inventory_items[inventory_id].begin(); itr != merchant_inventory_items[inventory_id].end(); itr++){
if ((*itr).item_id == item_id)
amount = (*itr).quantity;
}
MMerchantList.unlock();
}
return amount;
}
int32 World::GetInventoryID(int32 merchant_id, int32 item_id){
int32 ret = 0;
MMerchantList.lock();
if(merchant_info.count(merchant_id) > 0){
MerchantInfo* info = merchant_info[merchant_id];
vector<MerchantItemInfo>::iterator itr;
int32 inventory_id = 0;
for(int i=info->inventory_ids.size()-1;i>=0;i--){
inventory_id = info->inventory_ids[i];
if(merchant_inventory_items.count(inventory_id) > 0){
for(itr = merchant_inventory_items[inventory_id].begin(); itr != merchant_inventory_items[inventory_id].end(); itr++){
if((*itr).item_id == item_id){
ret = inventory_id;
break;
}
}
if(ret > 0)
break;
}
}
}
MMerchantList.unlock();
return ret;
}
void World::DecreaseMerchantQuantity(int32 merchant_id, int32 item_id, int16 amount){
int16 total_left = GetMerchantItemQuantity(merchant_id, item_id);
if(total_left > 0 && total_left < 0xFFFF){
int32 inventory_id = GetInventoryID(merchant_id, item_id);
if(inventory_id > 0){
MMerchantList.lock();
vector<MerchantItemInfo>::iterator itr;
for(itr = merchant_inventory_items[inventory_id].begin(); itr != merchant_inventory_items[inventory_id].end(); itr++){
if ((*itr).item_id == item_id) {
if(total_left <= amount) {
merchant_inventory_items[inventory_id].erase(itr);
amount = 0;
break;
}
else
(*itr).quantity -= amount;
amount = (*itr).quantity;
}
}
MMerchantList.unlock();
}
}
}
MerchantInfo* World::GetMerchantInfo(int32 merchant_id){
MerchantInfo* ret = 0;
MMerchantList.lock();
if(merchant_info.count(merchant_id) > 0)
ret = merchant_info[merchant_id];
MMerchantList.unlock();
return ret;
}
void World::AddMerchantInfo(int32 merchant_id, MerchantInfo* info){
MMerchantList.lock();
if(merchant_info.count(merchant_id) > 0){
safe_delete(merchant_info[merchant_id]);
}
merchant_info[merchant_id] = info;
MMerchantList.unlock();
}
map<int32, MerchantInfo*>* World::GetMerchantInfo() {
return &merchant_info;
}
void World::DeleteMerchantsInfo(){
MMerchantList.lock();
map<int32, MerchantInfo*>::iterator itr;
for(itr = merchant_info.begin(); itr != merchant_info.end(); itr++){
safe_delete(itr->second);
}
merchant_info.clear();
MMerchantList.unlock();
}
void World::DeleteSpawns(){
//reloading = true;
//ClearLootTables();
/*
map<int32, NPC*>::iterator npc_list_iter;
for(npc_list_iter=npc_list.begin();npc_list_iter!=npc_list.end();npc_list_iter++) {
safe_delete(npc_list_iter->second);
}
npc_list.clear();
map<int32, Object*>::iterator object_list_iter;
for(object_list_iter=object_list.begin();object_list_iter!=object_list.end();object_list_iter++) {
safe_delete(object_list_iter->second);
}
object_list.clear();
map<int32, GroundSpawn*>::iterator groundspawn_list_iter;
for(groundspawn_list_iter=groundspawn_list.begin();groundspawn_list_iter!=groundspawn_list.end();groundspawn_list_iter++) {
safe_delete(groundspawn_list_iter->second);
}
groundspawn_list.clear();
map<int32, Widget*>::iterator widget_list_iter;
for(widget_list_iter=widget_list.begin();widget_list_iter!=widget_list.end();widget_list_iter++) {
safe_delete(widget_list_iter->second);
}
widget_list.clear();
map<int32, Sign*>::iterator sign_list_iter;
for(sign_list_iter=sign_list.begin();sign_list_iter!=sign_list.end();sign_list_iter++) {
safe_delete(sign_list_iter->second);
}
sign_list.clear();*/
map<int32, AppearanceData*>::iterator appearance_list_iter;
for(appearance_list_iter=npc_appearance_list.begin();appearance_list_iter!=npc_appearance_list.end();appearance_list_iter++) {
safe_delete(appearance_list_iter->second);
}
npc_appearance_list.clear();
/*
map<int32, vector<EntityCommand*>* >::iterator command_list_iter;
for(command_list_iter=entity_command_list.begin();command_list_iter!=entity_command_list.end();command_list_iter++) {
vector<EntityCommand*>* v = command_list_iter->second;
if(v){
for(int32 i=0;i<v->size();i++){
safe_delete(v->at(i));
}
safe_delete(v);
}
}
entity_command_list.clear();
*/
//DeleteGroundSpawnItems();
//DeleteTransporters();
//DeleteTransporterMaps();
}
void World::ReloadGuilds() {
guild_list.GetGuilds()->clear(true);
database.LoadGuilds();
}
int8 World::GetClassID(const char* name){
return classes.GetClassID(name);
}
void World::WritePlayerStatistics() {
zone_list.WritePlayerStatistics();
}
void World::WriteServerStatistics() {
map<int32, Statistic*>::iterator itr;
Statistic* stat = 0;
for (itr = server_statistics.begin(); itr != server_statistics.end(); itr++) {
stat = itr->second;
if (stat->save_needed) {
stat->save_needed = false;
database.WriteServerStatistic(stat);
}
}
database.WriteServerStatisticsNeededQueries();
}
void World::AddServerStatistic(int32 stat_id, sint32 stat_value, int32 stat_date) {
if (server_statistics.count(stat_id) == 0) {
Statistic* stat = new Statistic;
stat->stat_id = stat_id;
stat->stat_value = stat_value;
stat->stat_date = stat_date;
stat->save_needed = false;
server_statistics[stat_id] = stat;
}
}
void World::UpdateServerStatistic(int32 stat_id, sint32 stat_value, bool overwrite) {
if (server_statistics.count(stat_id) == 0)
AddServerStatistic(stat_id, stat_value, 0);
Statistic* stat = server_statistics[stat_id];
overwrite == true ? stat->stat_value = stat_value : stat->stat_value += stat_value;
stat->stat_date = Timer::GetUnixTimeStamp();
stat->save_needed = true;
}
sint32 World::GetServerStatisticValue(int32 stat_id) {
if (server_statistics.count(stat_id) > 0)
return server_statistics[stat_id]->stat_value;
return 0;
}
void World::RemoveServerStatistics() {
map<int32, Statistic*>::iterator stat_itr;
for (stat_itr = server_statistics.begin(); stat_itr != server_statistics.end(); stat_itr++)
safe_delete(stat_itr->second);
server_statistics.clear();
}
void World::SendGroupQuests(PlayerGroup* group, Client* client){
return;
/*if(!group)
return;
GroupMemberInfo* info = 0;
MGroups.readlock(__FUNCTION__, __LINE__);
deque<GroupMemberInfo*>::iterator itr;
for(itr = group->members.begin(); itr != group->members.end(); itr++){
info = *itr;
if(info->client){
LogWrite(PLAYER__DEBUG, 0, "Player", "Send Quest Journal...");
info->client->SendQuestJournal(false, client);
client->SendQuestJournal(false, info->client);
}
}
MGroups.releasereadlock(__FUNCTION__, __LINE__);*/
}
/*void World::CheckRemoveGroupedPlayer(){
map<GroupMemberInfo*, int32>::iterator itr;
GroupMemberInfo* found = 0;
MGroups.readlock(__FUNCTION__, __LINE__);
for(itr = group_removal_pending.begin(); itr != group_removal_pending.end(); itr++){
if(itr->second < Timer::GetCurrentTime2()){
found = itr->first;
break;
}
}
MGroups.releasereadlock(__FUNCTION__, __LINE__);
if(found){
if(!found->client || (found->client && found->client->IsConnected() == false))
DeleteGroupMember(found);
else{
MGroups.writelock(__FUNCTION__, __LINE__);
group_removal_pending.erase(found);
MGroups.releasewritelock(__FUNCTION__, __LINE__);
}
}
}*/
void World::RejoinGroup(Client* client, int32 group_id){
if (!group_id) // no need if no group id!
return;
world.GetGroupManager()->GroupLock(__FUNCTION__, __LINE__);
PlayerGroup* group = world.GetGroupManager()->GetGroup(group_id);
deque<GroupMemberInfo*>* members = 0;
if (group)
members = group->GetMembers();
string name = string(client->GetPlayer()->GetName());
if (!members)
{
// group does not exist!
Query query;
query.AddQueryAsync(client->GetCharacterID(), &database, Q_INSERT, "UPDATE characters set group_id = 0 where id = %u",
client->GetCharacterID());
LogWrite(PLAYER__ERROR, 0, "Player", "Group did not exist for player %s to group id %i, async query to group_id = 0.", name.c_str(), group_id);
world.GetGroupManager()->ReleaseGroupLock(__FUNCTION__, __LINE__);
return;
}
deque<GroupMemberInfo*>::iterator itr;
GroupMemberInfo* info = 0;
bool match = false;
group->MGroupMembers.writelock();
for (itr = members->begin(); itr != members->end(); itr++) {
info = *itr;
if (info && info->name == name)
{
info->client = client;
info->member = client->GetPlayer();
client->GetPlayer()->SetGroup(group);
client->GetPlayer()->SetGroupMemberInfo(info);
client->GetPlayer()->UpdateGroupMemberInfo();
LogWrite(PLAYER__DEBUG, 0, "Player", "Identified group match for player %s to group id %u", name.c_str(), group_id);
match = true;
break;
}
}
group->MGroupMembers.releasewritelock();
// must be done after cause it needs a readlock
if (match)
group->SendGroupUpdate();
if (!match)
LogWrite(PLAYER__ERROR, 0, "Player", "Identified group match for player %s to group id %u, however the player name was not present in the group! May be an old group id that has been re-used.", name.c_str(), group_id);
world.GetGroupManager()->ReleaseGroupLock(__FUNCTION__, __LINE__);
}
void World::AddBonuses(ItemStatsValues* values, int16 type, sint32 value, Entity* entity){
if(values){
switch(type){
case ITEM_STAT_STR:{
values->str += value;
break;
}
case ITEM_STAT_STA:{
values->sta += value;
break;
}
case ITEM_STAT_AGI:{
values->agi += value;
break;
}
case ITEM_STAT_WIS:{
values->wis += value;
break;
}
case ITEM_STAT_INT:{
values->int_ += value;
break;
}
case ITEM_STAT_VS_SLASH:{
values->vs_slash += value;
break;
}
case ITEM_STAT_VS_CRUSH:{
values->vs_crush += value;
break;
}
case ITEM_STAT_VS_PIERCE:{
values->vs_pierce += value;
break;
}
case ITEM_STAT_VS_HEAT:{
values->vs_heat += value;
break;
}
case ITEM_STAT_VS_COLD:{
values->vs_cold += value;
break;
}
case ITEM_STAT_VS_MAGIC:{
values->vs_magic += value;
break;
}
case ITEM_STAT_VS_MENTAL:{
values->vs_mental += value;
break;
}
case ITEM_STAT_VS_DIVINE:{
values->vs_divine += value;
break;
}
case ITEM_STAT_VS_DISEASE:{
values->vs_disease += value;
break;
}
case ITEM_STAT_VS_POISON:{
values->vs_poison += value;
break;
}
case ITEM_STAT_HEALTH:{
values->health += value;
break;
}
case ITEM_STAT_POWER:{
values->power += value;
break;
}
case ITEM_STAT_CONCENTRATION:{
values->concentration += value;
break;
}
case ITEM_STAT_ABILITY_MODIFIER:{
values->ability_modifier += value;
break;
}
case ITEM_STAT_CRITICALMITIGATION:{
values->criticalmitigation += value;
break;
}
case ITEM_STAT_EXTRASHIELDBLOCKCHANCE:{
values->extrashieldblockchance += value;
break;
}
case ITEM_STAT_BENEFICIALCRITCHANCE:{
values->beneficialcritchance += value;
break;
}
case ITEM_STAT_CRITBONUS:{
values->critbonus += value;
break;
}
/*case ITEM_STAT_POTENCY:{
values->potency += value;
break;
}*/
case ITEM_STAT_HATEGAINMOD:{
values->hategainmod += value;
break;
}
case ITEM_STAT_ABILITYREUSESPEED:{
values->abilityreusespeed += value;
break;
}
case ITEM_STAT_ABILITYCASTINGSPEED:{
values->abilitycastingspeed += value;
break;
}
case ITEM_STAT_ABILITYRECOVERYSPEED:{
values->abilityrecoveryspeed += value;
break;
}
case ITEM_STAT_SPELLREUSESPEED:{
values->spellreusespeed += value;
break;
}
case ITEM_STAT_SPELLMULTIATTACKCHANCE:{
values->spellmultiattackchance += value;
break;
}
case ITEM_STAT_DPS:{
values->dps += value;
break;
}
case ITEM_STAT_ATTACKSPEED:{
values->attackspeed += value;
break;
}
case ITEM_STAT_MULTIATTACKCHANCE:{
values->multiattackchance += value;
break;
}
case ITEM_STAT_AEAUTOATTACKCHANCE:{
values->aeautoattackchance += value;
break;
}
case ITEM_STAT_STRIKETHROUGH:{
values->strikethrough += value;
break;
}
case ITEM_STAT_ACCURACY:{
values->accuracy += value;
break;
}
/*case ITEM_STAT_OFFENSIVESPEED:{
values->offensivespeed += value;
break;
}*/
default: {
if (entity)
entity->stats[type] += value;
break;
}
}
}
}
void World::CreateGuild(const char* guild_name, Client* leader, int32 group_id) {
deque<GroupMemberInfo*>::iterator itr;
GroupMemberInfo* gmi;
Guild *guild;
assert(guild_name);
guild = new Guild();
guild->SetName(guild_name);
guild->SetFormedDate(Timer::GetUnixTimeStamp());
database.LoadGuildDefaultRanks(guild);
database.LoadGuildDefaultEventFilters(guild);
database.SaveGuild(guild, true);
database.SaveGuildEvents(guild);
database.SaveGuildRanks(guild);
database.SaveGuildEventFilters(guild);
database.SaveGuildRecruiting(guild);
guild_list.AddGuild(guild);
if (leader && !leader->GetPlayer()->GetGuild())
guild->AddNewGuildMember(leader, 0, GUILD_RANK_LEADER);
database.SaveGuildMembers(guild);
if (leader && group_id > 0) {
GetGroupManager()->GroupLock(__FUNCTION__, __LINE__);
deque<GroupMemberInfo*>* members = GetGroupManager()->GetGroupMembers(group_id);
for (itr = members->begin(); itr != members->end(); itr++) {
gmi = *itr;
if (gmi->client && gmi->client != leader && !gmi->client->GetPlayer()->GetGuild())
guild->InvitePlayer(gmi->client, leader->GetPlayer()->GetName());
}
GetGroupManager()->ReleaseGroupLock(__FUNCTION__, __LINE__);
}
}
void World::SaveGuilds() {
MutexMap<int32, Guild*>* guilds = guild_list.GetGuilds();
MutexMap<int32, Guild*>::iterator itr = guilds->begin();
while (itr.Next()) {
Guild* guild = itr.second;
if (guild->GetSaveNeeded())
database.SaveGuild(guild);
if (guild->GetMemberSaveNeeded())
database.SaveGuildMembers(guild);
if (guild->GetEventsSaveNeeded())
database.SaveGuildEvents(guild);
if (guild->GetRanksSaveNeeded())
database.SaveGuildRanks(guild);
if (guild->GetEventFiltersSaveNeeded())
database.SaveGuildEventFilters(guild);
if (guild->GetPointsHistorySaveNeeded())
database.SaveGuildPointsHistory(guild);
if (guild->GetRecruitingSaveNeeded())
database.SaveGuildRecruiting(guild);
}
}
void World::PickRandomLottoDigits(int32* digits) {
if (digits) {
for (int32 i = 0; i < 6; i++) {
bool found = true;
int32 num = 0;
while (found) {
num = ((int32)rand() % 36) + 1;
for (int32 j = 0; j < 6; j++) {
if (digits[j] == num)
break;
if (j == 5)
found = false;
}
}
digits[i] = num;
}
}
}
void World::AddLottoPlayer(int32 character_id, int32 end_time) {
LottoPlayer* lp;
if (lotto_players.count(character_id) == 0) {
lp = new LottoPlayer;
lotto_players.Put(character_id, lp);
}
else
lp = lotto_players.Get(character_id);
lp->end_time = end_time;
lp->num_matches = 0;
lp->set = false;
}
void World::RemoveLottoPlayer(int32 character_id) {
if (lotto_players.count(character_id) > 0)
lotto_players.erase(character_id, false, true);
}
void World::SetLottoPlayerNumMatches(int32 character_id, int8 num_matches) {
if (lotto_players.count(character_id) > 0) {
lotto_players.Get(character_id)->num_matches = num_matches;
lotto_players.Get(character_id)->set = true;
}
}
void World::CheckLottoPlayers() {
MutexMap<int32, LottoPlayer*>::iterator itr = lotto_players.begin();
while (itr.Next()) {
LottoPlayer* lp = itr->second;
if (Timer::GetCurrentTime2() >= lp->end_time && lp->set) {
int8 num_matches = lp->num_matches;
LogWrite(PLAYER__DEBUG, 0, "Player", "Num matches: %u", lp->num_matches);
Client* client = zone_list.GetClientByCharID(itr->first);
if (client && num_matches >= 2) {
if (num_matches == 2) {
client->SimpleMessage(CHANNEL_COLOR_YELLOW, "You receive 10 silver.");
client->SendPopupMessage(0, "Congratulations! You have won 10 silver!", "", 2, 0xFF, 0xFF, 0x99);
client->GetPlayer()->AddCoins(1000);
client->GetPlayer()->GetZone()->SendCastSpellPacket(869, client->GetPlayer());
}
else if (num_matches == 3) {
client->SimpleMessage(CHANNEL_COLOR_YELLOW, "You receive 50 silver.");
client->SendPopupMessage(0, "Congratulations! You have won 50 silver!", "", 2, 0xFF, 0xFF, 0x99);
client->GetPlayer()->AddCoins(5000);
client->GetPlayer()->GetZone()->SendCastSpellPacket(870, client->GetPlayer());
}
else if (num_matches == 4) {
client->SimpleMessage(CHANNEL_COLOR_YELLOW, "You receive 2 gold 50 silver.");
client->SendPopupMessage(0, "Congratulations! You have won 2 gold 50 silver!", "", 2, 0xFF, 0xFF, 0x99);
client->GetPlayer()->AddCoins(25000);
client->GetPlayer()->GetZone()->SendCastSpellPacket(871, client->GetPlayer());
}
else if (num_matches == 5) {
client->SimpleMessage(CHANNEL_COLOR_YELLOW, "You receive 25 gold.");
client->SendPopupMessage(0, "Congratulations! You have won 25 gold!", "", 2, 0xFF, 0xFF, 0x99);
client->GetPlayer()->AddCoins(250000);
client->GetPlayer()->GetZone()->SendCastSpellPacket(872, client->GetPlayer());
}
else if (num_matches == 6) {
Variable* var = variables.FindVariable("gambling_current_jackpot");
if (var) {
int64 jackpot = 0;
try {
jackpot = atoul(var->GetValue());
}
catch (...) {
jackpot = 10000;
}
char coin_message[128];
char message[512];
char announcement[512];
memset(coin_message, 0, sizeof(coin_message));
memset(message, 0, sizeof(message));
memset(announcement, 0, sizeof(announcement));
sprintf(coin_message, "%s", client->GetCoinMessage(jackpot).c_str());
sprintf(message, "Congratulations! You have won %s!", coin_message);
sprintf(announcement, "%s as won the jackpot containing a total of %s!", client->GetPlayer()->GetName(), coin_message);
client->Message(CHANNEL_COLOR_YELLOW, "You receive %s.", coin_message);
client->SendPopupMessage(0, message, "", 2, 0xFF, 0xFF, 0x99);
zone_list.HandleGlobalAnnouncement(announcement);
client->GetPlayer()->AddCoins(jackpot);
client->GetPlayer()->GetZone()->SendCastSpellPacket(843, client->GetPlayer());
client->GetPlayer()->GetZone()->SendCastSpellPacket(1413, client->GetPlayer());
}
}
}
RemoveLottoPlayer(itr->first);
}
}
}
void World::AddHouseZone(int32 id, string name, int64 cost_coins, int32 cost_status, int64 upkeep_coins, int32 upkeep_status, int8 vault_slots, int8 alignment, int8 guild_level, int32 zone_id, int32 exit_zone_id, float exit_x, float exit_y, float exit_z, float exit_heading) {
MHouseZones.writelock(__FUNCTION__, __LINE__);
if (m_houseZones.count(id) == 0) {
HouseZone* hz = new HouseZone;
//ZeroMemory(hz, sizeof(HouseZone));
hz->id = id;
hz->name = name;
hz->cost_coin = cost_coins;
hz->cost_status = cost_status;
hz->upkeep_coin = upkeep_coins;
hz->upkeep_status = upkeep_status;
hz->vault_slots = vault_slots;
hz->alignment = alignment;
hz->guild_level = guild_level;
hz->zone_id = zone_id;
hz->exit_zone_id = exit_zone_id;
hz->exit_x = exit_x;
hz->exit_y = exit_y;
hz->exit_z = exit_z;
hz->exit_heading = exit_heading;
m_houseZones[id] = hz;
}
else {
LogWrite(WORLD__ERROR, 0, "Housing", "Duplicate house id (%u) for %s", id, name.c_str());
}
MHouseZones.releasewritelock(__FUNCTION__, __LINE__);
}
HouseZone* World::GetHouseZone(int32 id) {
HouseZone* ret = 0;
MHouseZones.readlock(__FUNCTION__, __LINE__);
if (m_houseZones.count(id) > 0)
ret = m_houseZones[id];
MHouseZones.releasereadlock(__FUNCTION__, __LINE__);
return ret;
}
void World::AddPlayerHouse(int32 char_id, int32 house_id, int64 unique_id, int32 instance_id, int32 upkeep_due, int64 escrow_coins, int32 escrow_status, string player_name) {
MPlayerHouses.writelock(__FUNCTION__, __LINE__);
if (m_playerHouses.count(house_id) == 0 || m_playerHouses[house_id].count(char_id) == 0) {
PlayerHouse* ph = new PlayerHouse;
ph->house_id = house_id;
ph->unique_id = unique_id;
ph->instance_id = instance_id;
ph->escrow_coins = escrow_coins;
ph->escrow_status = escrow_status;
ph->upkeep_due = upkeep_due;
ph->player_name = player_name;
ReloadHouseData(ph);
m_playerHouses[house_id][char_id] = ph;
}
MPlayerHouses.releasewritelock(__FUNCTION__, __LINE__);
}
void World::ReloadHouseData(PlayerHouse* ph)
{
database.LoadDeposits(ph);
database.LoadHistory(ph);
}
PlayerHouse* World::GetPlayerHouseByHouseID(int32 char_id, int32 house_id) {
PlayerHouse* ret = 0;
MPlayerHouses.readlock(__FUNCTION__, __LINE__);
if (m_playerHouses.count(house_id) > 0 && m_playerHouses[house_id].count(char_id) > 0)
ret = m_playerHouses[house_id][char_id];
MPlayerHouses.releasereadlock(__FUNCTION__, __LINE__);
return ret;
}
PlayerHouse* World::GetPlayerHouseByUniqueID(int64 unique_id) {
PlayerHouse* ret = 0;
MPlayerHouses.readlock(__FUNCTION__, __LINE__);
map<int32, map<int32, PlayerHouse*> >::iterator itr;
for (itr = m_playerHouses.begin(); itr != m_playerHouses.end(); itr++) {
map<int32, PlayerHouse*>::iterator itr2;
for (itr2 = itr->second.begin(); itr2 != itr->second.end(); itr2++) {
if (itr2->second->unique_id == unique_id) {
ret = itr2->second;
break;
}
}
if (ret)
break;
}
MPlayerHouses.releasereadlock(__FUNCTION__, __LINE__);
return ret;
}
PlayerHouse* World::GetPlayerHouseByInstanceID(int32 instance_id) {
PlayerHouse* ret = 0;
MPlayerHouses.readlock(__FUNCTION__, __LINE__);
map<int32, map<int32, PlayerHouse*> >::iterator itr;
for (itr = m_playerHouses.begin(); itr != m_playerHouses.end(); itr++) {
map<int32, PlayerHouse*>::iterator itr2;
for (itr2 = itr->second.begin(); itr2 != itr->second.end(); itr2++) {
if (itr2->second->instance_id == instance_id) {
ret = itr2->second;
break;
}
}
if (ret)
break;
}
MPlayerHouses.releasereadlock(__FUNCTION__, __LINE__);
return ret;
}
vector<PlayerHouse*> World::GetAllPlayerHouses(int32 char_id) {
vector<PlayerHouse*> ret;
MPlayerHouses.readlock(__FUNCTION__, __LINE__);
map<int32, map<int32, PlayerHouse*> >::iterator itr;
for (itr = m_playerHouses.begin(); itr != m_playerHouses.end(); itr++) {
if (itr->second.count(char_id) > 0)
ret.push_back(itr->second[char_id]);
}
MPlayerHouses.releasereadlock(__FUNCTION__, __LINE__);
return ret;
}
vector<PlayerHouse*> World::GetAllPlayerHousesByHouseID(int32 house_id) {
vector<PlayerHouse*> ret;
MPlayerHouses.readlock(__FUNCTION__, __LINE__);
if (m_houseZones.count(house_id) > 0) {
map<int32, PlayerHouse*>::iterator itr;
for (itr = m_playerHouses[house_id].begin(); itr != m_playerHouses[house_id].end(); itr++)
ret.push_back(itr->second);
}
MPlayerHouses.releasereadlock(__FUNCTION__, __LINE__);
return ret;
}
void World::PopulateTOVStatMap() {
//This function populates a map that converts changed CoE to ToV stats
tov_itemstat_conversion[0] = TOV_ITEM_STAT_HPREGEN;
tov_itemstat_conversion[1] = TOV_ITEM_STAT_MANAREGEN;
tov_itemstat_conversion[2] = TOV_ITEM_STAT_HPREGENPPT;
tov_itemstat_conversion[3] = TOV_ITEM_STAT_MPREGENPPT;
tov_itemstat_conversion[4] = TOV_ITEM_STAT_COMBATHPREGENPPT;
tov_itemstat_conversion[5] = TOV_ITEM_STAT_COMBATMPREGENPPT;
tov_itemstat_conversion[6] = TOV_ITEM_STAT_MAXHP;
tov_itemstat_conversion[7] = TOV_ITEM_STAT_MAXHPPERC;
tov_itemstat_conversion[8] = TOV_ITEM_STAT_MAXHPPERCFINAL;
tov_itemstat_conversion[9] = TOV_ITEM_STAT_SPEED;
tov_itemstat_conversion[10] = TOV_ITEM_STAT_SLOW;
tov_itemstat_conversion[11] = TOV_ITEM_STAT_MOUNTSPEED;
tov_itemstat_conversion[12] = TOV_ITEM_STAT_MOUNTAIRSPEED;
tov_itemstat_conversion[13] = TOV_ITEM_STAT_LEAPSPEED;
tov_itemstat_conversion[14] = TOV_ITEM_STAT_LEAPTIME;
tov_itemstat_conversion[15] = TOV_ITEM_STAT_GLIDEEFFICIENCY;
tov_itemstat_conversion[16] = TOV_ITEM_STAT_OFFENSIVESPEED;
tov_itemstat_conversion[17] = TOV_ITEM_STAT_ATTACKSPEED;
tov_itemstat_conversion[18] = 698;
tov_itemstat_conversion[19] = TOV_ITEM_STAT_MAXMANA;
tov_itemstat_conversion[20] = TOV_ITEM_STAT_MAXMANAPERC;
tov_itemstat_conversion[21] = TOV_ITEM_STAT_MAXATTPERC;
tov_itemstat_conversion[22] = TOV_ITEM_STAT_BLURVISION;
tov_itemstat_conversion[23] = TOV_ITEM_STAT_MAGICLEVELIMMUNITY;
tov_itemstat_conversion[24] = TOV_ITEM_STAT_HATEGAINMOD;
tov_itemstat_conversion[25] = TOV_ITEM_STAT_COMBATEXPMOD;
tov_itemstat_conversion[26] = TOV_ITEM_STAT_TRADESKILLEXPMOD;
tov_itemstat_conversion[27] = TOV_ITEM_STAT_ACHIEVEMENTEXPMOD;
tov_itemstat_conversion[28] = TOV_ITEM_STAT_SIZEMOD;
tov_itemstat_conversion[29] = TOV_ITEM_STAT_DPS;
tov_itemstat_conversion[30] = 698;
tov_itemstat_conversion[31] = TOV_ITEM_STAT_STEALTH;
tov_itemstat_conversion[32] = TOV_ITEM_STAT_INVIS;
tov_itemstat_conversion[33] = TOV_ITEM_STAT_SEESTEALTH;
tov_itemstat_conversion[34] = TOV_ITEM_STAT_SEEINVIS;
tov_itemstat_conversion[35] = TOV_ITEM_STAT_EFFECTIVELEVELMOD;
tov_itemstat_conversion[36] = TOV_ITEM_STAT_RIPOSTECHANCE;
tov_itemstat_conversion[37] = TOV_ITEM_STAT_PARRYCHANCE;
tov_itemstat_conversion[38] = TOV_ITEM_STAT_DODGECHANCE;
tov_itemstat_conversion[39] = TOV_ITEM_STAT_AEAUTOATTACKCHANCE;
tov_itemstat_conversion[40] = 698;
tov_itemstat_conversion[41] = TOV_ITEM_STAT_MULTIATTACKCHANCE;
tov_itemstat_conversion[42] = 698;
tov_itemstat_conversion[43] = 698;
tov_itemstat_conversion[44] = 698;
tov_itemstat_conversion[45] = TOV_ITEM_STAT_SPELLMULTIATTACKCHANCE;
tov_itemstat_conversion[46] = 698;
tov_itemstat_conversion[47] = TOV_ITEM_STAT_FLURRY;
tov_itemstat_conversion[48] = 698;
tov_itemstat_conversion[49] = TOV_ITEM_STAT_MELEEDAMAGEMULTIPLIER;
tov_itemstat_conversion[50] = TOV_ITEM_STAT_EXTRAHARVESTCHANCE;
tov_itemstat_conversion[51] = TOV_ITEM_STAT_EXTRASHIELDBLOCKCHANCE;
tov_itemstat_conversion[52] = TOV_ITEM_STAT_ITEMHPREGENPPT;
tov_itemstat_conversion[53] = TOV_ITEM_STAT_ITEMPPREGENPPT;
tov_itemstat_conversion[54] = TOV_ITEM_STAT_MELEECRITCHANCE;
tov_itemstat_conversion[55] = TOV_ITEM_STAT_CRITAVOIDANCE;
tov_itemstat_conversion[56] = TOV_ITEM_STAT_BENEFICIALCRITCHANCE;
tov_itemstat_conversion[57] = TOV_ITEM_STAT_CRITBONUS;
tov_itemstat_conversion[58] = 698;
tov_itemstat_conversion[59] = TOV_ITEM_STAT_POTENCY;
tov_itemstat_conversion[60] = 698;
tov_itemstat_conversion[61] = TOV_ITEM_STAT_UNCONSCIOUSHPMOD;
tov_itemstat_conversion[62] = TOV_ITEM_STAT_ABILITYREUSESPEED;
tov_itemstat_conversion[63] = TOV_ITEM_STAT_ABILITYRECOVERYSPEED;
tov_itemstat_conversion[64] = TOV_ITEM_STAT_ABILITYCASTINGSPEED;
tov_itemstat_conversion[65] = TOV_ITEM_STAT_SPELLREUSESPEED;
tov_itemstat_conversion[66] = TOV_ITEM_STAT_MELEEWEAPONRANGE;
tov_itemstat_conversion[67] = TOV_ITEM_STAT_RANGEDWEAPONRANGE;
tov_itemstat_conversion[68] = TOV_ITEM_STAT_FALLINGDAMAGEREDUCTION;
tov_itemstat_conversion[69] = TOV_ITEM_STAT_RIPOSTEDAMAGE;
tov_itemstat_conversion[70] = TOV_ITEM_STAT_MINIMUMDEFLECTIONCHANCE;
tov_itemstat_conversion[71] = TOV_ITEM_STAT_MOVEMENTWEAVE;
tov_itemstat_conversion[72] = TOV_ITEM_STAT_COMBATHPREGEN;
tov_itemstat_conversion[73] = TOV_ITEM_STAT_COMBATMANAREGEN;
tov_itemstat_conversion[74] = TOV_ITEM_STAT_CONTESTSPEEDBOOST;
tov_itemstat_conversion[75] = TOV_ITEM_STAT_TRACKINGAVOIDANCE;
tov_itemstat_conversion[76] = TOV_ITEM_STAT_STEALTHINVISSPEEDMOD;
tov_itemstat_conversion[77] = TOV_ITEM_STAT_LOOT_COIN;
tov_itemstat_conversion[78] = TOV_ITEM_STAT_ARMORMITIGATIONINCREASE;
tov_itemstat_conversion[79] = TOV_ITEM_STAT_AMMOCONSERVATION;
tov_itemstat_conversion[80] = TOV_ITEM_STAT_STRIKETHROUGH;
tov_itemstat_conversion[81] = TOV_ITEM_STAT_STATUSBONUS;
tov_itemstat_conversion[82] = TOV_ITEM_STAT_ACCURACY;
tov_itemstat_conversion[83] = TOV_ITEM_STAT_COUNTERSTRIKE;
tov_itemstat_conversion[84] = TOV_ITEM_STAT_SHIELDBASH;
tov_itemstat_conversion[85] = TOV_ITEM_STAT_WEAPONDAMAGEBONUS;
tov_itemstat_conversion[86] = 698;
tov_itemstat_conversion[87] = TOV_ITEM_STAT_WEAPONDAMAGEBONUSMELEEONLY;
tov_itemstat_conversion[88] = TOV_ITEM_STAT_ADDITIONALRIPOSTECHANCE;
tov_itemstat_conversion[89] = TOV_ITEM_STAT_PVPTOUGHNESS;
tov_itemstat_conversion[90] = TOV_ITEM_STAT_PVPLETHALITY;
tov_itemstat_conversion[91] = TOV_ITEM_STAT_STAMINABONUS;
tov_itemstat_conversion[92] = TOV_ITEM_STAT_WISDOMMITBONUS;
tov_itemstat_conversion[93] = TOV_ITEM_STAT_HEALRECEIVE;
tov_itemstat_conversion[94] = TOV_ITEM_STAT_HEALRECEIVEPERC;
tov_itemstat_conversion[95] = TOV_ITEM_STAT_PVPCRITICALMITIGATION;
tov_itemstat_conversion[96] = TOV_ITEM_STAT_BASEAVOIDANCEBONUS;
tov_itemstat_conversion[97] = TOV_ITEM_STAT_INCOMBATSAVAGERYREGEN;
tov_itemstat_conversion[98] = TOV_ITEM_STAT_OUTOFCOMBATSAVAGERYREGEN;
tov_itemstat_conversion[99] = TOV_ITEM_STAT_SAVAGERYREGEN;
tov_itemstat_conversion[100] = TOV_ITEM_STAT_SAVAGERYGAINMOD;
tov_itemstat_conversion[101] = TOV_ITEM_STAT_MAXSAVAGERYLEVEL;
}
int32 World::LoadItemBlueStats() {
Query query;
MYSQL_ROW row;
int32 count = 0;
MYSQL_RES* result = query.RunQuery2(Q_SELECT, "SELECT version_range1,version_range2,emu_stat,name,stat from itemstats");
if (result && mysql_num_rows(result) > 0) {
while (result && (row = mysql_fetch_row(result))) {
count++;
if (atoi(row[0]) >= 63119) //KA
ka_itemstat_conversion[atoi(row[2])] = atoi(row[4]);
else if (atoi(row[0]) >= 57101) // ToV
tov_itemstat_conversion[atoi(row[2])] = atoi(row[4]);
else if (atoi(row[0]) >= 1193) // CoE
coe_itemstat_conversion[atoi(row[2])] = atoi(row[4]);
else if (atoi(row[0]) >= 1096) // DoV
dov_itemstat_conversion[atoi(row[2])] = atoi(row[4]);
}
}
return count;
}
sint16 World::GetItemStatTOVValue(sint16 subtype) {
return (tov_itemstat_conversion[subtype] - 600);
}
sint16 World::GetItemStatDOVValue(sint16 subtype) {
return (dov_itemstat_conversion[subtype] - 600);
}
sint16 World::GetItemStatCOEValue(sint16 subtype) {
return (coe_itemstat_conversion[subtype] - 600);
}
sint16 World::GetItemStatKAValue(sint16 subtype) {
return (ka_itemstat_conversion[subtype] - 600);
}
bool World::CheckTempBugCRC(char* msg)
{
MBugReport.writelock();
sint32 crc = GetItemNameCrc(std::string(msg));
if (bug_report_crc.count(crc) > 0)
{
MBugReport.releasewritelock();
return true;
}
else
bug_report_crc.insert(make_pair(crc, true));
MBugReport.releasewritelock();
return false;
}
#ifdef WIN32
ulong World::GetCurrentThreadID(){
return GetCurrentThreadId();
}
int64 World::GetThreadUsageCPUTime(){
HANDLE handle = GetCurrentThread();
int64 lpCreationTime;
int64 lpExitTime;
int64 lpKernelTime;
int64 lpUserTime;
if(GetThreadTimes(handle, (FILETIME*)&lpCreationTime, (FILETIME*)&lpExitTime, (FILETIME*)&lpKernelTime, (FILETIME*)&lpUserTime))
return lpKernelTime + lpUserTime;
return 0;
}
#else
#endif
void World::SyncCharacterAbilities(Client* client)
{
MStartingLists.readlock();
int8 baseClass = classes.GetBaseClass(client->GetPlayer()->GetAdventureClass());
int8 secondaryClass = classes.GetSecondaryBaseClass(client->GetPlayer()->GetAdventureClass());
int8 actualClass = client->GetPlayer()->GetAdventureClass();
int8 baseRace = client->GetPlayer()->GetRace();
bool reloadSpells = false;
multimap<int8, multimap<int8, StartingSkill>*>::iterator skill_itr = starting_skills.begin();
multimap<int8, multimap<int8, StartingSpell>*>::iterator spell_itr = starting_spells.begin();
bool isComplete = false;
int8 wait_iterations = 0; // wait 5 iterations and give up if db takes too long
do
{
bool isProcessing = false;
if (skill_itr != starting_skills.end())
{
isProcessing = true;
// race = 255 is wildcard all, otherwise needs to match the race id
if (skill_itr->first == 255 || skill_itr->first == baseRace)
{
multimap<int8, StartingSkill>::iterator child_itr;
for (child_itr = skill_itr->second->begin(); child_itr != skill_itr->second->end(); child_itr++)
{
// class = 255 is wildcard all, otherwise needs to match the class id
if (child_itr->first == 255 ||
child_itr->first == baseClass ||
child_itr->first == secondaryClass ||
child_itr->first == actualClass)
{
if (!client->GetPlayer()->skill_list.HasSkill(child_itr->second.skill_id))
{
Query query;
LogWrite(PLAYER__DEBUG, 0, "Player", "Adding skill %i for race: %i, class: %i for char_id: %u", child_itr->second.skill_id, baseRace, baseClass, client->GetCharacterID());
query.AddQueryAsync(client->GetCharacterID(), &database, Q_INSERT, "INSERT IGNORE INTO character_skills (char_id, skill_id, current_val, max_val) VALUES (%u, %u, %u, %u)",
client->GetCharacterID(), child_itr->second.skill_id, child_itr->second.current_val, child_itr->second.max_val);
client->GetPlayer()->AddSkill(child_itr->second.skill_id, child_itr->second.current_val, child_itr->second.max_val);
}
}
}
}
skill_itr++;
}
if (spell_itr != starting_spells.end())
{
isProcessing = true;
// race = 255 is wildcard all, otherwise needs to match the race id
if (spell_itr->first == 255 || spell_itr->first == baseRace)
{
multimap<int8, StartingSpell>::iterator child_itr;
for (child_itr = spell_itr->second->begin(); child_itr != spell_itr->second->end(); child_itr++)
{
// class = 255 is wildcard all, otherwise needs to match the class id
if (child_itr->first == 255 ||
child_itr->first == baseClass ||
child_itr->first == secondaryClass ||
child_itr->first == actualClass)
{
if (!client->GetPlayer()->HasSpell(child_itr->second.spell_id, child_itr->second.tier, true))
{
Query query;
LogWrite(PLAYER__DEBUG, 0, "Player", "Adding spell %i for race: %i, class: %i for char_id: %u", child_itr->second.spell_id, baseRace, baseClass, client->GetCharacterID());
// knowledge_slot is a signed int in the DB
query.AddQueryAsync(client->GetCharacterID(), &database, Q_INSERT, "INSERT IGNORE INTO character_spells (char_id, spell_id, tier, knowledge_slot) VALUES (%u, %u, %u, %i)",
client->GetCharacterID(), child_itr->second.spell_id, child_itr->second.tier, child_itr->second.knowledge_slot);
// reload spells, we don't know the spellbook or timer info
reloadSpells = true;
}
}
}
}
spell_itr++;
}
// poor mans async kill just in case, we don't want a client stuck here, they can get the spells on next zone
if (!isProcessing)
{
bool isDBActive = database.IsActiveQuery(client->GetCharacterID());
if (isDBActive && wait_iterations < 5)
{
wait_iterations++;
}
else
isComplete = true;
}
} while (!isComplete);
MStartingLists.releasereadlock();
if (reloadSpells)
database.LoadCharacterSpells(client->GetCharacterID(), client->GetPlayer());
}
void World::LoadStartingLists()
{
LogWrite(WORLD__DEBUG, 1, "World", "-Loading `starting_skills`...");
database.LoadStartingSkills(this);
LogWrite(WORLD__DEBUG, 1, "World", "-Loading `starting_spells`...");
database.LoadStartingSpells(this);
}
void World::PurgeStartingLists()
{
MStartingLists.writelock();
multimap<int8, multimap<int8, StartingSkill>*>::iterator skill_itr;
for (skill_itr = starting_skills.begin(); skill_itr != starting_skills.end(); skill_itr++)
{
multimap<int8, StartingSkill>* tmpMap = skill_itr->second;
safe_delete(tmpMap);
}
starting_skills.clear();
multimap<int8, multimap<int8, StartingSpell>*>::iterator spell_itr;
for (spell_itr = starting_spells.begin(); spell_itr != starting_spells.end(); spell_itr++)
{
multimap<int8, StartingSpell>* tmpMap = spell_itr->second;
safe_delete(tmpMap);
}
starting_spells.clear();
MStartingLists.releasewritelock();
}