Procyon Updates

Fix  - Put additional protection to rest of player_quests calls, using read/write locks now also
Fix  - Added rule R_Loot, SkipLootGrayMob, default is on "1".  Set to "0" to allow chests and 'non body' or 'non quest' drops from gray mobs
Fix  - Removed charge based items when charges depleted
Fix  - Added World Time LUA Functions
int16 = GetWorldTimeYear()
sint32 = GetWorldTimeMonth()
sint32 = GetWorldTimeHour()
sint32 = GetWorldTimeMinute()
SetWorldTime(int16 years, sint32 months, sint32 hours, sint32 minutes)
SendTimeUpdate()

- Additionally fixed camping and logging back in immediately, there was a 30 second delay.  That is no longer the case.
- Fixed effective level updating on level changes, this prevents the unexpected purple inventory and mentor level display
This commit is contained in:
Image 2021-07-19 21:25:31 -04:00
parent c1f748c7d6
commit f169cb6d6e
17 changed files with 357 additions and 129 deletions

View file

@ -2094,13 +2094,20 @@ void Commands::Process(int32 index, EQ2_16BitString* command_parms, Client* clie
Item* item = player->item_list.GetItemFromIndex(item_index);
if(!item)
LogWrite(PLAYER__WARNING, 0, "Command", "%s: Item %s (%i) was used, however after the item looks to be removed.", client->GetPlayer()->GetName(), itemName.c_str(), item_id);
else if(!item->generic_info.display_charges && item->generic_info.max_charges == 1)
else if(!item->generic_info.display_charges && item->generic_info.max_charges == 1) {
client->Message(CHANNEL_NARRATIVE, "%s is out of charges. It has been removed.", item->name.c_str());
client->RemoveItem(item, 1); // end of a set of charges OR an item that uses a stack count of actual item quantity
}
else
{
item->details.count--; // charges
item->save_needed = true;
client->QueuePacket(item->serialize(client->GetVersion(), false, client->GetPlayer()));
if(!item->details.count) {
client->Message(CHANNEL_NARRATIVE, "%s is out of charges. It has been removed.", item->name.c_str());
client->RemoveItem(item, 1); // end of a set of charges OR an item that uses a stack count of actual item quantity
}
}
}
else
@ -2779,7 +2786,7 @@ void Commands::Process(int32 index, EQ2_16BitString* command_parms, Client* clie
{
Entity* ent = (Entity*)spawn;
ent->SetLootCoins(0);
ent->ClearLootList();
ent->ClearLoot();
spawn->GetZone()->AddLoot((NPC*)spawn);
client->Message(CHANNEL_COLOR_YELLOW, "Spawn %u active loot purged and reloaded.", spawn->GetDatabaseID());
}
@ -3295,6 +3302,8 @@ void Commands::Process(int32 index, EQ2_16BitString* command_parms, Client* clie
}
case COMMAND_SETTIME:{
if(sep && sep->arg[0]){
world.MWorldTime.writelock(__FUNCTION__, __LINE__);
sscanf (sep->arg[0], "%d:%d", &world.GetWorldTimeStruct()->hour, &world.GetWorldTimeStruct()->minute);
if(sep->arg[1] && sep->IsNumber(1))
world.GetWorldTimeStruct()->month = atoi(sep->arg[1]) - 1; //zero based indexes
@ -3302,6 +3311,7 @@ void Commands::Process(int32 index, EQ2_16BitString* command_parms, Client* clie
world.GetWorldTimeStruct()->day = atoi(sep->arg[2]) - 1; //zero based indexes
if(sep->arg[3] && sep->IsNumber(3))
world.GetWorldTimeStruct()->year = atoi(sep->arg[3]);
world.MWorldTime.releasewritelock(__FUNCTION__, __LINE__);
client->GetCurrentZone()->SendTimeUpdateToAllClients();
}
else{

View file

@ -884,7 +884,6 @@ void LoginServer::SendInfo() {
#ifdef _DEBUG
lsi->servertype = 4;
#endif
int8 tmppass[16];
string passwdSha512 = sha512(net.GetWorldPassword());
memcpy(lsi->password, (char*)passwdSha512.c_str(), passwdSha512.length());
strcpy(lsi->address, net.GetWorldAddress());
@ -965,6 +964,7 @@ int32 LoginServer::DetermineCharacterLoginRequest ( UsertoWorldRequest_Struct* u
if(status < 100 && zone_list.ClientConnected(utwr->lsaccountid))
status = -9;
if(status < 0){
LogWrite(WORLD__ERROR, 0, "World", "Login Rejected based on PLAY_ERROR (UserStatus) (MinStatus: %i), UserStatus: %i, CharID: %i",loginserver.minLockedStatus,status,utwr->char_id );
switch(status){
case -10:
utwrs->response = PLAY_ERROR_CHAR_NOT_LOADED;

View file

@ -417,7 +417,7 @@ int EQ2Emu_lua_SpawnSet(lua_State* state) {
int8 num_args = (int8)lua_interface->GetNumberOfArgs(state);
int8 index = 0;
if(num_args >= 5)
{
temporary_flag = lua_interface->GetBooleanValue(state, 5); // this used to be false, but no one bothered to set it temporary, we don't need to update the DB
@ -12319,6 +12319,9 @@ int EQ2Emu_lua_SetRailID(lua_State* state) {
}
int EQ2Emu_lua_IsZoneLoading(lua_State* state) {
if (!lua_interface)
return 0;
ZoneServer* zone = lua_interface->GetZone(state);
lua_interface->ResetFunctionStack(state);
@ -12329,6 +12332,9 @@ int EQ2Emu_lua_IsZoneLoading(lua_State* state) {
return 0;
}
int EQ2Emu_lua_IsRunning(lua_State* state) {
if (!lua_interface)
return 0;
Spawn* spawn = lua_interface->GetSpawn(state);
lua_interface->ResetFunctionStack(state);
@ -12359,4 +12365,88 @@ int EQ2Emu_lua_GetZoneLockoutTimer(lua_State* state) {
return 1;
}
return 0;
}
}
int EQ2Emu_lua_SetWorldTime(lua_State* state) {
if (!lua_interface)
return 0;
int16 newYear = lua_interface->GetInt16Value(state, 1);
sint32 newMonth = lua_interface->GetInt16Value(state, 2);
int16 newHour = lua_interface->GetInt16Value(state, 3);
int16 newMinute = lua_interface->GetInt16Value(state, 4);
lua_interface->ResetFunctionStack(state);
world.MWorldTime.writelock(__FUNCTION__, __LINE__);
world.GetWorldTimeStruct()->year = newYear;
world.GetWorldTimeStruct()->month = newMonth;
world.GetWorldTimeStruct()->hour = newHour;
world.GetWorldTimeStruct()->minute = newMinute;
world.MWorldTime.releasewritelock(__FUNCTION__, __LINE__);
return 0;
}
int EQ2Emu_lua_GetWorldTimeYear(lua_State* state) {
if (!lua_interface)
return 0;
lua_interface->ResetFunctionStack(state);
world.MWorldTime.readlock(__FUNCTION__, __LINE__);
lua_interface->SetInt32Value(state, world.GetWorldTimeStruct()->year);
world.MWorldTime.releasereadlock(__FUNCTION__, __LINE__);
return 1;
}
int EQ2Emu_lua_GetWorldTimeMonth(lua_State* state) {
if (!lua_interface)
return 0;
lua_interface->ResetFunctionStack(state);
world.MWorldTime.readlock(__FUNCTION__, __LINE__);
lua_interface->SetSInt32Value(state, world.GetWorldTimeStruct()->month);
world.MWorldTime.releasereadlock(__FUNCTION__, __LINE__);
return 1;
}
int EQ2Emu_lua_GetWorldTimeHour(lua_State* state) {
if (!lua_interface)
return 0;
lua_interface->ResetFunctionStack(state);
world.MWorldTime.readlock(__FUNCTION__, __LINE__);
lua_interface->SetSInt32Value(state, world.GetWorldTimeStruct()->hour);
world.MWorldTime.releasereadlock(__FUNCTION__, __LINE__);
return 1;
}
int EQ2Emu_lua_GetWorldTimeMinute(lua_State* state) {
if (!lua_interface)
return 0;
lua_interface->ResetFunctionStack(state);
world.MWorldTime.readlock(__FUNCTION__, __LINE__);
lua_interface->SetSInt32Value(state, world.GetWorldTimeStruct()->minute);
world.MWorldTime.releasereadlock(__FUNCTION__, __LINE__);
return 1;
}
int EQ2Emu_lua_SendTimeUpdate(lua_State* state) {
if (!lua_interface)
return 0;
lua_interface->ResetFunctionStack(state);
world.SendTimeUpdate();
return 0;
}

View file

@ -595,4 +595,11 @@ int EQ2Emu_lua_IsZoneLoading(lua_State* state);
int EQ2Emu_lua_IsRunning(lua_State* state);
int EQ2Emu_lua_GetZoneLockoutTimer(lua_State* state);
int EQ2Emu_lua_SetWorldTime(lua_State* state);
int EQ2Emu_lua_GetWorldTimeYear(lua_State* state);
int EQ2Emu_lua_GetWorldTimeMonth(lua_State* state);
int EQ2Emu_lua_GetWorldTimeHour(lua_State* state);
int EQ2Emu_lua_GetWorldTimeMinute(lua_State* state);
int EQ2Emu_lua_SendTimeUpdate(lua_State* state);
#endif

View file

@ -1424,6 +1424,13 @@ void LuaInterface::RegisterFunctions(lua_State* state) {
lua_register(state, "IsRunning", EQ2Emu_lua_IsRunning);
lua_register(state, "GetZoneLockoutTimer", EQ2Emu_lua_GetZoneLockoutTimer);
lua_register(state, "SetWorldTime", EQ2Emu_lua_SetWorldTime);
lua_register(state, "GetWorldTimeYear", EQ2Emu_lua_GetWorldTimeYear);
lua_register(state, "GetWorldTimeMonth", EQ2Emu_lua_GetWorldTimeMonth);
lua_register(state, "GetWorldTimeHour", EQ2Emu_lua_GetWorldTimeHour);
lua_register(state, "GetWorldTimeMinute", EQ2Emu_lua_GetWorldTimeMinute);
lua_register(state, "SendTimeUpdate", EQ2Emu_lua_SendTimeUpdate);
}
void LuaInterface::LogError(const char* error, ...) {

View file

@ -224,7 +224,7 @@ EQ2Packet* Player::Move(float x, float y, float z, int16 version, float heading)
}
void Player::DestroyQuests(){
MPlayerQuests.lock();
MPlayerQuests.writelock(__FUNCTION__, __LINE__);
map<int32, Quest*>::iterator itr;
for(itr = completed_quests.begin(); itr != completed_quests.end(); itr++){
safe_delete(itr->second);
@ -238,7 +238,7 @@ void Player::DestroyQuests(){
safe_delete(itr->second);
}
pending_quests.clear();
MPlayerQuests.unlock();
MPlayerQuests.releasewritelock(__FUNCTION__, __LINE__);
}
PlayerInfo* Player::GetPlayerInfo(){
@ -3870,6 +3870,10 @@ float Player::CalculateXP(Spawn* victim){
}
switch(GetArrowColor(victim->GetLevel())){
case ARROW_COLOR_GRAY:
LogWrite(PLAYER__DEBUG, 5, "XP", "Gray Arrow = No XP");
return 0.0f;
break;
case ARROW_COLOR_GREEN:
multiplier = 3.25;
LogWrite(PLAYER__DEBUG, 5, "XP", "Green Arrow Multiplier = %.2f", multiplier);
@ -4256,19 +4260,19 @@ void Player::RemoveSpawn(Spawn* spawn)
vector<int32> Player::GetQuestIDs(){
vector<int32> ret;
map<int32, Quest*>::iterator itr;
MPlayerQuests.lock();
MPlayerQuests.readlock(__FUNCTION__, __LINE__);
for(itr = player_quests.begin(); itr != player_quests.end(); itr++){
if(itr->second)
ret.push_back(itr->second->GetQuestID());
}
MPlayerQuests.unlock();
MPlayerQuests.releasereadlock(__FUNCTION__, __LINE__);
return ret;
}
vector<Quest*>* Player::CheckQuestsItemUpdate(Item* item){
vector<Quest*>* quest_updates = 0;
map<int32, Quest*>::iterator itr;
MPlayerQuests.lock();
MPlayerQuests.readlock(__FUNCTION__, __LINE__);
for(itr = player_quests.begin(); itr != player_quests.end(); itr++){
if(itr->second && itr->second->CheckQuestItemUpdate(item->details.item_id, item->details.count)){
if(!quest_updates)
@ -4276,14 +4280,14 @@ vector<Quest*>* Player::CheckQuestsItemUpdate(Item* item){
quest_updates->push_back(itr->second);
}
}
MPlayerQuests.unlock();
MPlayerQuests.releasereadlock(__FUNCTION__, __LINE__);
return quest_updates;
}
void Player::CheckQuestsCraftUpdate(Item* item, int32 qty){
map<int32, Quest*>::iterator itr;
vector<Quest*>* update_list = new vector<Quest*>;
MPlayerQuests.lock();
MPlayerQuests.readlock(__FUNCTION__, __LINE__);
for(itr = player_quests.begin(); itr != player_quests.end(); itr++){
if(itr->second){
if(item && qty > 0){
@ -4293,7 +4297,7 @@ void Player::CheckQuestsCraftUpdate(Item* item, int32 qty){
}
}
}
MPlayerQuests.unlock();
MPlayerQuests.releasereadlock(__FUNCTION__, __LINE__);
if(update_list && update_list->size() > 0){
Client* client = GetZone()->GetClientBySpawn(this);
if(client){
@ -4310,7 +4314,7 @@ void Player::CheckQuestsCraftUpdate(Item* item, int32 qty){
void Player::CheckQuestsHarvestUpdate(Item* item, int32 qty){
map<int32, Quest*>::iterator itr;
vector<Quest*>* update_list = new vector<Quest*>;
MPlayerQuests.lock();
MPlayerQuests.readlock(__FUNCTION__, __LINE__);
for(itr = player_quests.begin(); itr != player_quests.end(); itr++){
if(itr->second){
if(item && qty > 0){
@ -4320,7 +4324,7 @@ void Player::CheckQuestsHarvestUpdate(Item* item, int32 qty){
}
}
}
MPlayerQuests.unlock();
MPlayerQuests.releasereadlock(__FUNCTION__, __LINE__);
if(update_list && update_list->size() > 0){
Client* client = GetZone()->GetClientBySpawn(this);
if(client){
@ -4337,7 +4341,7 @@ void Player::CheckQuestsHarvestUpdate(Item* item, int32 qty){
vector<Quest*>* Player::CheckQuestsSpellUpdate(Spell* spell) {
vector<Quest*>* quest_updates = 0;
map<int32, Quest*>::iterator itr;
MPlayerQuests.lock();
MPlayerQuests.readlock(__FUNCTION__, __LINE__);
for (itr = player_quests.begin(); itr != player_quests.end(); itr++){
if (itr->second && itr->second->CheckQuestSpellUpdate(spell)) {
if (!quest_updates)
@ -4345,7 +4349,7 @@ vector<Quest*>* Player::CheckQuestsSpellUpdate(Spell* spell) {
quest_updates->push_back(itr->second);
}
}
MPlayerQuests.unlock();
MPlayerQuests.releasereadlock(__FUNCTION__, __LINE__);
return quest_updates;
}
@ -4358,7 +4362,7 @@ PacketStruct* Player::GetQuestJournalPacket(bool all_quests, int16 version, int3
map<int32, Quest*> total_quests = player_quests;
if(all_quests && completed_quests.size() > 0)
total_quests.insert(completed_quests.begin(), completed_quests.end());
MPlayerQuests.lock();
MPlayerQuests.readlock(__FUNCTION__, __LINE__);
if(total_quests.size() > 0){
map<string, int16> quest_types;
map<int32, Quest*>::iterator itr;
@ -4470,7 +4474,7 @@ PacketStruct* Player::GetQuestJournalPacket(bool all_quests, int16 version, int3
//packet->setDataByName("unknown4", 0);
packet->setDataByName("visible_quest_id", current_quest_id);
}
MPlayerQuests.unlock();
MPlayerQuests.releasereadlock(__FUNCTION__, __LINE__);
packet->setDataByName("player_crc", crc);
packet->setDataByName("player_name", GetName());
packet->setDataByName("used_quests", total_quests_num - total_completed_quests);
@ -4577,51 +4581,51 @@ PacketStruct* Player::GetQuestJournalPacket(Quest* quest, int16 version, int32 c
Quest* Player::SetStepComplete(int32 id, int32 step){
Quest* ret = 0;
MPlayerQuests.lock();
MPlayerQuests.readlock(__FUNCTION__, __LINE__);
if(player_quests.count(id) > 0){
if(player_quests[id]->SetStepComplete(step))
ret = player_quests[id];
}
MPlayerQuests.unlock();
MPlayerQuests.releasereadlock(__FUNCTION__, __LINE__);
return ret;
}
Quest* Player::AddStepProgress(int32 quest_id, int32 step, int32 progress) {
Quest* ret = 0;
MPlayerQuests.lock();
MPlayerQuests.readlock(__FUNCTION__, __LINE__);
if (player_quests.count(quest_id) > 0) {
if (player_quests[quest_id]->AddStepProgress(step, progress))
ret = player_quests[quest_id];
}
MPlayerQuests.unlock();
MPlayerQuests.releasereadlock(__FUNCTION__, __LINE__);
return ret;
}
int32 Player::GetStepProgress(int32 quest_id, int32 step_id) {
int32 ret = 0;
MPlayerQuests.lock();
MPlayerQuests.readlock(__FUNCTION__, __LINE__);
if (player_quests.count(quest_id) > 0)
ret = player_quests[quest_id]->GetStepProgress(step_id);
MPlayerQuests.unlock();
MPlayerQuests.releasereadlock(__FUNCTION__, __LINE__);
return ret;
}
void Player::RemoveQuest(int32 id, bool delete_quest){
MPlayerQuests.lock();
MPlayerQuests.writelock(__FUNCTION__, __LINE__);
if(delete_quest){
safe_delete(player_quests[id]);
}
player_quests.erase(id);
MPlayerQuests.unlock();
MPlayerQuests.releasewritelock(__FUNCTION__, __LINE__);
SendQuestRequiredSpawns(id);
}
vector<Quest*>* Player::CheckQuestsLocationUpdate(){
vector<Quest*>* quest_updates = 0;
map<int32, Quest*>::iterator itr;
MPlayerQuests.lock();
MPlayerQuests.readlock(__FUNCTION__, __LINE__);
for(itr = player_quests.begin(); itr != player_quests.end(); itr++){
if(itr->second && itr->second->CheckQuestLocationUpdate(GetX(), GetY(), GetZ(), (GetZone() ? GetZone()->GetZoneID() : 0))){
if(!quest_updates)
@ -4629,14 +4633,14 @@ vector<Quest*>* Player::CheckQuestsLocationUpdate(){
quest_updates->push_back(itr->second);
}
}
MPlayerQuests.unlock();
MPlayerQuests.releasereadlock(__FUNCTION__, __LINE__);
return quest_updates;
}
vector<Quest*>* Player::CheckQuestsFailures(){
vector<Quest*>* quest_failures = 0;
map<int32, Quest*>::iterator itr;
MPlayerQuests.lock();
MPlayerQuests.readlock(__FUNCTION__, __LINE__);
for(itr = player_quests.begin(); itr != player_quests.end(); itr++){
if(itr->second && itr->second->GetQuestFailures()->size() > 0){
if(!quest_failures)
@ -4644,14 +4648,14 @@ vector<Quest*>* Player::CheckQuestsFailures(){
quest_failures->push_back(itr->second);
}
}
MPlayerQuests.unlock();
MPlayerQuests.releasereadlock(__FUNCTION__, __LINE__);
return quest_failures;
}
vector<Quest*>* Player::CheckQuestsKillUpdate(Spawn* spawn, bool update){
vector<Quest*>* quest_updates = 0;
map<int32, Quest*>::iterator itr;
MPlayerQuests.lock();
MPlayerQuests.readlock(__FUNCTION__, __LINE__);
for(itr = player_quests.begin(); itr != player_quests.end(); itr++){
if(itr->second && itr->second->CheckQuestKillUpdate(spawn, update)){
if(!quest_updates)
@ -4659,14 +4663,14 @@ vector<Quest*>* Player::CheckQuestsKillUpdate(Spawn* spawn, bool update){
quest_updates->push_back(itr->second);
}
}
MPlayerQuests.unlock();
MPlayerQuests.releasereadlock(__FUNCTION__, __LINE__);
return quest_updates;
}
vector<Quest*>* Player::CheckQuestsChatUpdate(Spawn* spawn){
vector<Quest*>* quest_updates = 0;
map<int32, Quest*>::iterator itr;
MPlayerQuests.lock();
MPlayerQuests.readlock(__FUNCTION__, __LINE__);
for(itr = player_quests.begin(); itr != player_quests.end(); itr++){
if(itr->second && itr->second->CheckQuestChatUpdate(spawn->GetDatabaseID())){
if(!quest_updates)
@ -4674,54 +4678,46 @@ vector<Quest*>* Player::CheckQuestsChatUpdate(Spawn* spawn){
quest_updates->push_back(itr->second);
}
}
MPlayerQuests.unlock();
MPlayerQuests.releasereadlock(__FUNCTION__, __LINE__);
return quest_updates;
}
int16 Player::GetTaskGroupStep(int32 quest_id){
Quest* quest = 0;
int16 step = 0;
MPlayerQuests.lock();
MPlayerQuests.readlock(__FUNCTION__, __LINE__);
if(player_quests.count(quest_id) > 0){
quest = player_quests[quest_id];
step = quest->GetTaskGroupStep();
}
MPlayerQuests.unlock();
MPlayerQuests.releasereadlock(__FUNCTION__, __LINE__);
return step;
}
bool Player::GetQuestStepComplete(int32 quest_id, int32 step_id){
bool ret = false;
MPlayerQuests.lock();
MPlayerQuests.readlock(__FUNCTION__, __LINE__);
if(player_quests.count(quest_id) > 0){
Quest* quest = player_quests[quest_id];
if ( quest != NULL )
ret = quest->GetQuestStepCompleted(step_id);
}
MPlayerQuests.unlock();
MPlayerQuests.releasereadlock(__FUNCTION__, __LINE__);
return ret;
}
int16 Player::GetQuestStep(int32 quest_id){
Quest* quest = 0;
int16 step = 0;
MPlayerQuests.lock();
MPlayerQuests.readlock(__FUNCTION__, __LINE__);
if(player_quests.count(quest_id) > 0){
quest = player_quests[quest_id];
step = quest->GetQuestStep();
}
MPlayerQuests.unlock();
MPlayerQuests.releasereadlock(__FUNCTION__, __LINE__);
return step;
}
void Player::LockQuests(){
MPlayerQuests.lock();
}
void Player::UnlockQuests(){
MPlayerQuests.unlock();
}
map<int32, Quest*>* Player::GetPlayerQuests(){
return &player_quests;
}
@ -4777,19 +4773,19 @@ int8 Player::CheckQuestFlag(Spawn* spawn){
vector<int32>* quests = spawn->GetProvidedQuests();
Quest* quest = 0;
for(int32 i=0;i<quests->size();i++){
MPlayerQuests.lock();
MPlayerQuests.readlock(__FUNCTION__, __LINE__);
if(player_quests.count(quests->at(i)) > 0){
if(player_quests[quests->at(i)]->GetCompleted() && player_quests[quests->at(i)]->GetQuestReturnNPC() == spawn->GetDatabaseID()){
ret = 2;
MPlayerQuests.unlock();
MPlayerQuests.releasereadlock(__FUNCTION__, __LINE__);
break;
}
}
MPlayerQuests.unlock();
MPlayerQuests.releasereadlock(__FUNCTION__, __LINE__);
if (CanReceiveQuest(quests->at(i))){
MPlayerQuests.lock();
master_quest_list.LockQuests();
quest = master_quest_list.GetQuest(quests->at(i), false);
MPlayerQuests.unlock();
master_quest_list.UnlockQuests();
if(quest){
int8 color = quest->GetFeatherColor();
// purple
@ -4810,12 +4806,12 @@ int8 Player::CheckQuestFlag(Spawn* spawn){
}
}
map<int32, Quest*>::iterator itr;
MPlayerQuests.lock();
MPlayerQuests.readlock(__FUNCTION__, __LINE__);
for(itr = player_quests.begin(); itr != player_quests.end(); itr++){
if(itr->second->CheckQuestChatUpdate(spawn->GetDatabaseID(), false))
ret = 2;
}
MPlayerQuests.unlock();
MPlayerQuests.releasereadlock(__FUNCTION__, __LINE__);
if(ret > 0)
current_quest_flagged[spawn] = true;
return ret;
@ -4824,9 +4820,9 @@ int8 Player::CheckQuestFlag(Spawn* spawn){
bool Player::CanReceiveQuest(int32 quest_id){
bool passed = true;
int32 x;
MPlayerQuests.lock();
master_quest_list.LockQuests();
Quest* quest = master_quest_list.GetQuest(quest_id, false);
MPlayerQuests.unlock();
master_quest_list.UnlockQuests();
if (!quest)
passed = false;
//check if quest is already completed, and not repeatable
@ -6770,4 +6766,13 @@ void Player::SetMentorStats(int32 effective_level, int32 target_char_id)
}
}
GetEquipmentList()->SendEquippedItems(this);
}
void Player::SetLevel(int16 level, bool setUpdateFlags) {
if(!GetGroupMemberInfo() || GetGroupMemberInfo()->mentor_target_char_id == 0) {
GetInfoStruct()->set_effective_level(level);
}
SetInfo(&appearance.level, level, setUpdateFlags);
SetXP(0);
SetNeededXP();
}

View file

@ -583,11 +583,7 @@ public:
void ClearRemovedSpawn(Spawn* spawn);
bool ShouldSendSpawn(Spawn* spawn);
Client* client = 0;
void SetLevel(int16 level, bool setUpdateFlags = true) {
SetInfo(&appearance.level, level, setUpdateFlags);
SetXP(0);
SetNeededXP();
}
void SetLevel(int16 level, bool setUpdateFlags = true);
Spawn* GetSpawnWithPlayerID(int32 id){
Spawn* spawn = 0;
@ -666,8 +662,6 @@ public:
map<int32, Quest*> player_quests;
map<int32, Quest*>* GetPlayerQuests();
map<int32, Quest*>* GetCompletedPlayerQuests();
void LockQuests();
void UnlockQuests();
void SetFactionValue(int32 faction_id, sint32 value){
factions.SetFactionValue(faction_id, value);
}
@ -995,6 +989,8 @@ public:
{
reset_mentorship = true;
}
Mutex MPlayerQuests;
private:
bool reset_mentorship;
bool range_attack;
@ -1009,7 +1005,6 @@ private:
map<int32, map<int32, bool> > pending_loot_items;
Mutex MSpellsBook;
Mutex MRecipeBook;
Mutex MPlayerQuests;
map<Spawn*, bool> current_quest_flagged;
PlayerFaction factions;
map<int32, Quest*> completed_quests;

View file

@ -324,6 +324,7 @@ void RuleManager::Init()
RULE_INIT(R_Loot, AllowChestUnlockByDropTime, "1"); // when set to 1 we will start a countdown timer to allow anyone to loot once ChestUnlockedTimeDrop elapsed
RULE_INIT(R_Loot, ChestUnlockedTimeTrap, "600"); // time in seconds, 10 minutes by default
RULE_INIT(R_Loot, AllowChestUnlockByTrapTime, "1"); // when set to 1 we will allow unlocking the chest to all players after the trap is triggered (or chest is open) and period ChestUnlockedTimeTrap elapsed
RULE_INIT(R_Loot, SkipLootGrayMob, "1");
RULE_INIT(R_Spells, NoInterruptBaseChance, "50");
RULE_INIT(R_Spells, EnableFizzleSpells, "1"); // enables/disables the 'fizzling' of spells based on can_fizzle in the spells table. This also enables increasing specialized skills for classes based on spells/abilities.
@ -340,6 +341,7 @@ void RuleManager::Init()
RULE_INIT(R_Expansion, GlobalHolidayFlag, "0");
RULE_INIT(R_World, DatabaseVersion, "0");
#undef RULE_INIT
}

View file

@ -195,7 +195,9 @@ enum RuleType {
GlobalExpansionFlag,
GlobalHolidayFlag,
DatabaseVersion
DatabaseVersion,
SkipLootGrayMob
};
class Rule {

View file

@ -910,6 +910,24 @@ public:
MLootItems.unlock();
}
void ClearNonBodyLoot() {
MLootItems.lock();
vector<Item*>::iterator itr;
for (itr = loot_items.begin(); itr != loot_items.end();) {
Item* itm = *itr;
if(!itm->IsBodyDrop())
{
itr = loot_items.erase(itr);
safe_delete(itm);
}
else
itr++;
}
MLootItems.unlock();
}
int32 GetLootCoins() {
return loot_coins;
}
@ -919,15 +937,6 @@ public:
void AddLootCoins(int32 coins) {
loot_coins += coins;
}
void ClearLootList() {
vector<Item*>::iterator itr;
for (itr = loot_items.begin(); itr != loot_items.end(); itr++)
safe_delete(*itr);
loot_items.clear();
}
Spawn* GetTarget();
void SetTarget(Spawn* spawn);
Spawn* GetLastAttacker();

View file

@ -226,6 +226,7 @@ void World::init(){
PacketStruct* World::GetWorldTime(int16 version){
MWorldTime.readlock(__FUNCTION__, __LINE__);
PacketStruct* packet = configReader.getStruct("WS_GameWorldTime", version);
if(packet){
packet->setDataByName("year", world_time.year);
@ -237,6 +238,7 @@ PacketStruct* World::GetWorldTime(int16 version){
packet->setDataByName("unix_time", Timer::GetUnixTimeStamp());
packet->setDataByName("unknown2", 1);
}
MWorldTime.releasereadlock(__FUNCTION__, __LINE__);
return packet;
}
@ -263,10 +265,21 @@ void World::Process(){
if(last_checked_time > Timer::GetCurrentTime2())
return;
last_checked_time = Timer::GetCurrentTime2() + 1000;
if(save_time_timer.Check())
{
MWorldTime.readlock(__FUNCTION__, __LINE__);
database.SaveWorldTime(&world_time);
MWorldTime.releasereadlock(__FUNCTION__, __LINE__);
}
if(time_tick_timer.Check())
{
MWorldTime.writelock(__FUNCTION__, __LINE__);
WorldTimeTick();
MWorldTime.releasewritelock(__FUNCTION__, __LINE__);
}
if(vitality_timer.Check())
UpdateVitality();
if (player_stats_timer.Check())
@ -350,7 +363,23 @@ void ZoneList::UpdateVitality(float amount)
MZoneList.releasereadlock(__FUNCTION__, __LINE__);
}
void ZoneList::SendTimeUpdate()
{
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->isZoneShuttingDown())
tmp->WorldTimeUpdateTrigger();
}
MZoneList.releasereadlock(__FUNCTION__, __LINE__);
}
// should already be ran inside MWorldTime
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
@ -661,7 +690,7 @@ bool ZoneList::ClientConnected(int32 account_id){
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){
if(itr->second && itr->second->GetAccountID() == account_id && itr->second->getConnection() && itr->second->getConnection()->GetState() != CLOSING && itr->second->getConnection()->GetState() != CLOSED && (itr->second->GetPlayer()->GetActivityStatus() & ACTIVITY_STATUS_LINKDEAD) == 0){
ret = true;
break;
}
@ -2542,4 +2571,9 @@ Map* World::GetMap(std::string zoneFile, int32 client_version)
MWorldMaps.releasereadlock();
return nullptr;
}
void World::SendTimeUpdate()
{
zone_list.SendTimeUpdate();
}

View file

@ -490,6 +490,8 @@ class ZoneList {
void ReloadSpawns();
void WatchdogHeartbeat();
void SendTimeUpdate();
private:
Mutex MClientList;
Mutex MZoneList;
@ -633,6 +635,13 @@ public:
void LoadMaps(std::string zoneFile);
Map* GetMap(std::string zoneFile, int32 client_version);
void SendTimeUpdate();
// just in case we roll over a time as to not send bad times to clients (days before hours, hours before minutes as examples)
Mutex MWorldTime;
static sint64 newValue;
private:
int32 suppressed_warning = 0;

View file

@ -2513,7 +2513,7 @@ void WorldDatabase::SaveCharacterQuests(Client* client){
Query query;
map<int32, Quest*>::iterator itr;
master_quest_list.LockQuests(); //prevent reloading until we are done
client->GetPlayer()->LockQuests(); //prevent all quest modifications until we are done
client->GetPlayer()->MPlayerQuests.writelock(__FUNCTION__, __LINE__); //prevent all quest modifications until we are done
map<int32, Quest*>* quests = client->GetPlayer()->GetPlayerQuests();
for(itr = quests->begin(); itr != quests->end(); itr++){
if(client->GetCurrentQuestID() == itr->first){
@ -2540,7 +2540,7 @@ void WorldDatabase::SaveCharacterQuests(Client* client){
itr->second->SetSaveNeeded(false);
}
}
client->GetPlayer()->UnlockQuests();
client->GetPlayer()->MPlayerQuests.releasewritelock(__FUNCTION__, __LINE__);
master_quest_list.UnlockQuests();
}

View file

@ -149,7 +149,6 @@ Client::Client(EQStream* ieqs) : pos_update(125), quest_pos_timer(2000), lua_deb
connected_to_zone = false;
connected = false;
camp_timer = 0;
disconnect_timer = 0;
client_zoning = false;
player_pos_changed = false;
++numclients;
@ -209,6 +208,28 @@ Client::Client(EQStream* ieqs) : pos_update(125), quest_pos_timer(2000), lua_deb
}
Client::~Client() {
RemoveClientFromZone();
//let the stream factory know were done with this stream
if (eqs) {
eqs->Close();
try {
eqs->ReleaseFromUse();
}
catch (...) {}
}
eqs = NULL;
//safe_delete(autobootup_timeout);
safe_delete(CLE_keepalive_timer);
safe_delete(connect);
--numclients;
UpdateWindowTitle(0);
}
void Client::RemoveClientFromZone() {
if (current_zone && player) {
if (player->GetGroupMemberInfo())
{
@ -225,29 +246,7 @@ Client::~Client() {
if (player)
zone_list.RemoveClientFromMap(player->GetName(), this);
//let the stream factory know were done with this stream
if (eqs) {
eqs->Close();
try {
eqs->ReleaseFromUse();
}
catch (...) {}
}
eqs = NULL;
//safe_delete(autobootup_timeout);
safe_delete(disconnect_timer);
safe_delete(camp_timer);
safe_delete(CLE_keepalive_timer);
safe_delete(connect);
--numclients;
MDeletePlayer.writelock(__FUNCTION__, __LINE__);
if (player && !player->GetPendingDeletion())
safe_delete(player);
MDeletePlayer.releasewritelock(__FUNCTION__, __LINE__);
safe_delete(search_items);
safe_delete(current_rez.expire_timer);
safe_delete(pending_last_name);
@ -259,9 +258,14 @@ Client::~Client() {
delete tmp;
SetTempPlacementSpawn(nullptr);
}
UpdateWindowTitle(0);
MDeletePlayer.writelock(__FUNCTION__, __LINE__);
if (player && !player->GetPendingDeletion())
safe_delete(player);
MDeletePlayer.releasewritelock(__FUNCTION__, __LINE__);
}
void Client::QueuePacket(EQ2Packet* app, bool attemptedCombine) {
if (eqs) {
if (!eqs->CheckActive()) {
@ -383,6 +387,7 @@ void Client::SendLoginInfo() {
map<int32, Quest*>::iterator itr;
Quest* quest = 0;
GetPlayer()->MPlayerQuests.readlock(__FUNCTION__, __LINE__);
for (itr = player->player_quests.begin(); itr != player->player_quests.end(); itr++) {
quest = itr->second;
if (quest->IsTracked()) {
@ -392,6 +397,7 @@ void Client::SendLoginInfo() {
QueuePacket(itr->second->QuestJournalReply(version, GetNameCRC(), player));
}
}
GetPlayer()->MPlayerQuests.releasereadlock(__FUNCTION__, __LINE__);
// SendAchievementsList();
@ -1214,6 +1220,7 @@ bool Client::HandlePacket(EQApplicationPacket* app) {
if (packet && packet->LoadPacketData(app->pBuffer, app->size)) {
int32 quest_id = packet->getType_int32_ByName("quest_id");
bool hidden = packet->getType_int8_ByName("visible") == 1 ? false : true;
GetPlayer()->MPlayerQuests.readlock(__FUNCTION__, __LINE__);
map<int32, Quest*>* player_quests = player->GetPlayerQuests();
if (player_quests) {
if (player_quests->count(quest_id) > 0)
@ -1223,6 +1230,7 @@ bool Client::HandlePacket(EQApplicationPacket* app) {
}
else
LogWrite(CCLIENT__ERROR, 0, "Client", "OP_QuestJournalSetVisibleMsg error: Unable to get player(%s) quests", player->GetName());
GetPlayer()->MPlayerQuests.releasereadlock(__FUNCTION__, __LINE__);
safe_delete(packet);
}
@ -2238,10 +2246,12 @@ bool Client::HandlePacket(EQApplicationPacket* app) {
continue;
LogWrite(CCLIENT__DEBUG, 5, "Client", "quest_id = %u", id);
bool tracked = packet->getType_int8_ByName("quest_tracked_0", i) == 1 ? true : false;
GetPlayer()->MPlayerQuests.writelock(__FUNCTION__, __LINE__);
if (player->player_quests.count(id) > 0) {
player->player_quests[id]->SetTracked(tracked);
player->player_quests[id]->SetSaveNeeded(true);
}
GetPlayer()->MPlayerQuests.releasewritelock(__FUNCTION__, __LINE__);
}
safe_delete(packet);
@ -3022,7 +3032,7 @@ bool Client::Process(bool zone_process) {
if(GetPlayer()->GetRegionMap())
GetPlayer()->GetRegionMap()->TicRegionsNearSpawn(this->GetPlayer(), regionDebugMessaging ? this : nullptr);
if(player_pos_changed && IsReadyForUpdates() && ( !disconnect_timer || !disconnect_timer->Enabled())) {
if(player_pos_changed && IsReadyForUpdates()) {
//GetPlayer()->CalculateLocation();
client_list.CheckPlayersInvisStatus(this);
GetCurrentZone()->SendPlayerPositionChanges(GetPlayer());
@ -3045,13 +3055,6 @@ bool Client::Process(bool zone_process) {
lua_interface->UpdateDebugClients(this);
if (quest_pos_timer.Check())
CheckPlayerQuestsLocationUpdate();
if (camp_timer && camp_timer->Check() && getConnection()) {
ResetSendMail();
getConnection()->SendDisconnect(false);
safe_delete(camp_timer);
disconnect_timer = new Timer(2000);
disconnect_timer->Start();
}
if (player->GetSkills()->HasSkillUpdates()) {
vector<Skill*>* skills = player->GetSkills()->GetSkillUpdates();
if (skills) {
@ -3066,10 +3069,6 @@ bool Client::Process(bool zone_process) {
safe_delete(skills);
}
}
if (disconnect_timer && disconnect_timer->Check()) {
safe_delete(disconnect_timer);
ret = false;
}
m_resurrect.writelock(__FUNCTION__, __LINE__);
if (current_rez.should_delete || (current_rez.expire_timer && current_rez.expire_timer->Check(false))) {
safe_delete(current_rez.expire_timer);
@ -3096,6 +3095,7 @@ bool Client::Process(bool zone_process) {
MQuestTimers.writelock(__FUNCTION__, __LINE__);
if (quest_timers.size() > 0) {
vector<int32>::iterator itr;
GetPlayer()->MPlayerQuests.readlock(__FUNCTION__, __LINE__);
map<int32, Quest*>* player_quests = player->GetPlayerQuests();
for (itr = quest_timers.begin(); itr != quest_timers.end(); itr++) {
if (player_quests->count(*itr) > 0 && player_quests->at(*itr)->GetStepTimer() != 0) {
@ -3106,10 +3106,11 @@ bool Client::Process(bool zone_process) {
}
}
else {
quest_timers.erase(itr);
itr = quest_timers.erase(itr);
break;
}
}
GetPlayer()->MPlayerQuests.releasereadlock(__FUNCTION__, __LINE__);
}
MQuestTimers.releasewritelock(__FUNCTION__, __LINE__);
@ -3119,6 +3120,14 @@ bool Client::Process(bool zone_process) {
if (player->ControlFlagsChanged())
player->SendControlFlagUpdates(this);
if (camp_timer && camp_timer->Check()) {
ResetSendMail();
if(getConnection())
getConnection()->SendDisconnect(false);
safe_delete(camp_timer);
ret = false;
}
if (!eqs || (eqs && !eqs->CheckActive()))
ret = false;
@ -4338,11 +4347,6 @@ void Client::ChangeLevel(int16 old_level, int16 new_level) {
}
if (player->GetLevel() != new_level) {
if(!player->GetGroupMemberInfo() || !player->GetGroupMemberInfo()->mentor_target_char_id)
{
player->GetInfoStruct()->set_effective_level(new_level);
}
player->SetLevel(new_level);
if (player->GetGroupMemberInfo()) {
player->UpdateGroupMemberInfo();
@ -5454,13 +5458,15 @@ void Client::AddPendingQuest(Quest* quest, bool forced) {
}
Quest* Client::GetActiveQuest(int32 quest_id) {
Quest* quest = 0;
GetPlayer()->MPlayerQuests.readlock(__FUNCTION__, __LINE__);
if (player->player_quests.count(quest_id) > 0) {
LogWrite(CCLIENT__DEBUG, 0, "Client", "Found %u active quests for char_id: %u", player->player_quests.count(quest_id), player->GetCharacterID());
return player->player_quests[quest_id];
quest = player->player_quests[quest_id];
}
return 0;
GetPlayer()->MPlayerQuests.releasereadlock(__FUNCTION__, __LINE__);
return quest;
}
void Client::AcceptQuest(int32 id) {
@ -5515,6 +5521,7 @@ void Client::SetPlayerQuest(Quest* quest, map<int32, int32>* progress) {
}
void Client::AddPlayerQuest(Quest* quest, bool call_accepted, bool send_packets) {
GetPlayer()->MPlayerQuests.writelock(__FUNCTION__, __LINE__);
if (player->player_quests.count(quest->GetQuestID()) > 0) {
if (player->player_quests[quest->GetQuestID()]->GetQuestFlags() > 0)
quest->SetQuestFlags(player->player_quests[quest->GetQuestID()]->GetQuestFlags());
@ -5522,6 +5529,8 @@ void Client::AddPlayerQuest(Quest* quest, bool call_accepted, bool send_packets)
RemovePlayerQuest(quest->GetQuestID(), false, false);
}
player->player_quests[quest->GetQuestID()] = quest;
GetPlayer()->MPlayerQuests.releasewritelock(__FUNCTION__, __LINE__);
quest->SetPlayer(player);
current_quest_id = quest->GetQuestID();
if (send_packets && quest->GetQuestGiver() > 0)
@ -5550,13 +5559,17 @@ void Client::AddPlayerQuest(Quest* quest, bool call_accepted, bool send_packets)
void Client::RemovePlayerQuest(int32 id, bool send_update, bool delete_quest) {
if (current_quest_id == id)
current_quest_id = 0;
GetPlayer()->MPlayerQuests.writelock(__FUNCTION__, __LINE__);
if (player->player_quests.count(id) > 0) {
if (delete_quest) {
player->player_quests[id]->SetDeleted(true);
database.DeleteCharacterQuest(id, GetCharacterID(), player->GetCompletedPlayerQuests()->count(id) > 0);
}
if (send_update && player->player_quests[id]->GetQuestGiver() > 0)
GetCurrentZone()->SendSpawnChangesByDBID(player->player_quests[id]->GetQuestGiver(), this, false, true);
int32 quest_giver = player->player_quests[id]->GetQuestGiver();
GetPlayer()->MPlayerQuests.releasewritelock(__FUNCTION__, __LINE__);
if (send_update && quest_giver > 0)
GetCurrentZone()->SendSpawnChangesByDBID(quest_giver, this, false, true);
if (send_update) {
LogWrite(CCLIENT__DEBUG, 0, "Client", "Send Quest Journal...");
SendQuestJournal(false, 0, true);
@ -5568,6 +5581,10 @@ void Client::RemovePlayerQuest(int32 id, bool send_update, bool delete_quest) {
GetCurrentZone()->SendAllSpawnsForVisChange(this);
}
}
else {
// if we don't have any quests to count then release the write lock
GetPlayer()->MPlayerQuests.releasewritelock(__FUNCTION__, __LINE__);
}
}

View file

@ -146,6 +146,7 @@ public:
Client(EQStream* ieqs);
~Client();
void RemoveClientFromZone();
bool Process(bool zone_process = false);
void Disconnect(bool send_disconnect = true);
void SetConnected(bool val){ connected = val; }
@ -546,7 +547,6 @@ private:
Timer* CLE_keepalive_timer;
Timer* connect;
Timer* camp_timer;
Timer* disconnect_timer;
bool connected;
bool ready_for_spawns;
bool ready_for_updates;

View file

@ -1414,9 +1414,10 @@ bool ZoneServer::Process()
if(lua_interface)
lua_interface->Process();
world.MWorldTime.readlock(__FUNCTION__, __LINE__);
int hour = world.GetWorldTimeStruct()->hour;
int minute = world.GetWorldTimeStruct()->minute;
world.MWorldTime.releasereadlock(__FUNCTION__, __LINE__);
if (!isDusk && (hour >= 19 || hour < 8)) {//((hour > dusk_hour || hour < dawn_hour) || ((dusk_hour == hour && minute >= dusk_minute) || (hour == dawn_hour && minute < dawn_minute)))) {
isDusk = true;
@ -2350,6 +2351,31 @@ void ZoneServer::ProcessSpawnLocations()
}
void ZoneServer::AddLoot(NPC* npc, Spawn* killer){
// this function is ran twice, first on spawn of mob, then at death of mob (gray mob check and no_drop_quest_completed_id check)
// first we see if the skipping of gray mobs loot is enabled, then we move all non body drops
if(killer)
{
int8 skip_loot_gray_mob_flag = rule_manager.GetGlobalRule(R_Loot, SkipLootGrayMob)->GetInt8();
if(skip_loot_gray_mob_flag) {
Player* player = 0;
if(killer->IsPlayer())
player = (Player*)killer;
else if(killer->IsPet()) {
Spawn* owner = ((Entity*)killer)->GetOwner();
if(owner->IsPlayer())
player = (Player*)owner;
}
if(player) {
int8 difficulty = player->GetArrowColor(npc->GetLevel());
if(difficulty == ARROW_COLOR_GRAY) {
npc->ClearNonBodyLoot();
}
}
}
}
// check for starting loot of Spawn and death of Spawn loot (no_drop_quest_completed_id)
vector<int32> loot_tables = GetSpawnLootList(npc->GetDatabaseID(), GetZoneID(), npc->GetLevel(), race_types_list.GetRaceType(npc->GetModelType()), npc);
if(loot_tables.size() > 0){
vector<LootDrop*>* loot_drops = 0;
@ -4135,7 +4161,20 @@ void ZoneServer::SendCalculatedXP(Player* player, Spawn* victim){
group->MGroupMembers.readlock(__FUNCTION__, __LINE__);
deque<GroupMemberInfo*>* members = group->GetMembers();
deque<GroupMemberInfo*>::iterator itr;
bool skipGrayMob = false;
for (itr = members->begin(); itr != members->end(); itr++) {
GroupMemberInfo* gmi = *itr;
if (gmi->client) {
Player* group_member = gmi->client->GetPlayer();
if(group_member && group_member->GetArrowColor(victim->GetLevel()) == ARROW_COLOR_GRAY) {
skipGrayMob = true;
break;
}
}
}
for (itr = members->begin(); !skipGrayMob && itr != members->end(); itr++) {
GroupMemberInfo* gmi = *itr;
if (gmi->client) {
Player* group_member = gmi->client->GetPlayer();

View file

@ -679,6 +679,8 @@ public:
void QueueDefaultCommand(int32 spawn_id, std::string command, float distance);
void ProcessQueuedStateCommands();
void UpdateClientSpawnMap(Player* player, Client* client);
void WorldTimeUpdateTrigger() { sync_game_time_timer.Trigger(); }
private:
#ifndef WIN32
pthread_t ZoneThread;