diff options
Diffstat (limited to 'src')
36 files changed, 6462 insertions, 0 deletions
diff --git a/src/game/AchievementMgr.cpp b/src/game/AchievementMgr.cpp new file mode 100644 index 00000000000..e4e26dec111 --- /dev/null +++ b/src/game/AchievementMgr.cpp @@ -0,0 +1,910 @@ +/* + * Copyright (C) 2005-2008 MaNGOS <http://getmangos.com/> + * + * This program 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 2 of the License, or + * (at your option) any later version. + * + * This program 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 this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "AchievementMgr.h" +#include "Common.h" +#include "Player.h" +#include "WorldPacket.h" +#include "Database/DBCEnums.h" +#include "ObjectMgr.h" +#include "Guild.h" +#include "Database/DatabaseEnv.h" +#include "GameEvent.h" +#include "World.h" +#include "SpellMgr.h" + +const CriteriaCastSpellRequirement AchievementMgr::criteriaCastSpellRequirements[CRITERIA_CAST_SPELL_REQ_COUNT] = + { + {5272, 3057, 0, 0}, + {5273, 2784, 0, 0}, + {5752, 9099, 0, 0}, + {5753, 8403, 0, 0}, + {5772, 0, 0, RACE_GNOME}, + {5774, 0, 0, RACE_BLOODELF}, + {5775, 0, 0, RACE_DRAENEI}, + {5776, 0, 0, RACE_DWARF}, + {5777, 0, 0, RACE_HUMAN}, + {5778, 0, 0, RACE_NIGHTELF}, + {5779, 0, 0, RACE_ORC}, + {5780, 0, 0, RACE_TAUREN}, + {5781, 0, 0, RACE_TROLL}, + {5782, 0, 0, RACE_UNDEAD_PLAYER}, + {6225, 5661, 0, 0}, + {6226, 26044, 0, 0}, + {6228, 739, 0, 0}, + {6229, 927, 0, 0}, + {6230, 1444, 0, 0}, + {6231, 8140, 0, 0}, + {6232, 5489, 0, 0}, + {6233,12336, 0, 0}, + {6234, 1351, 0, 0}, + {6235, 5484, 0, 0}, + {6236, 1182, 0, 0}, + {6237, 0, CLASS_DEATH_KNIGHT, RACE_ORC}, + {6238, 0, CLASS_WARRIOR, RACE_HUMAN}, + {6239, 0, CLASS_SHAMAN, RACE_TAUREN}, + {6240, 0, CLASS_DRUID, RACE_NIGHTELF}, + {6241, 0, CLASS_ROGUE, RACE_UNDEAD_PLAYER}, + {6242, 0, CLASS_HUNTER, RACE_TROLL}, + {6243, 0, CLASS_MAGE, RACE_GNOME}, + {6244, 0, CLASS_PALADIN, RACE_DWARF}, + {6245, 0, CLASS_WARLOCK, RACE_BLOODELF}, + {6246, 0, CLASS_PRIEST, RACE_DRAENEI}, + {6312, 0, CLASS_WARLOCK, RACE_GNOME}, + {6313, 0, CLASS_DEATH_KNIGHT, RACE_HUMAN}, + {6314, 0, CLASS_PRIEST, RACE_NIGHTELF}, + {6315, 0, CLASS_SHAMAN, RACE_ORC}, + {6316, 0, CLASS_DRUID, RACE_TAUREN}, + {6317, 0, CLASS_ROGUE, RACE_TROLL}, + {6318, 0, CLASS_WARRIOR, RACE_UNDEAD_PLAYER}, + {6319, 0, CLASS_MAGE, RACE_BLOODELF}, + {6320, 0, CLASS_PALADIN, RACE_DRAENEI}, + {6321, 0, CLASS_HUNTER, RACE_DWARF}, + {6662, 31261, 0, 0} + }; + +const AchievementReward AchievementMgr::achievementRewards[ACHIEVEMENT_REWARD_COUNT] = + { + // achievementId, horde titleid, alliance titleid, itemid + {45, 0, 0, 43348}, + {46, 78, 78, 0}, + {230, 72, 72, 0}, + {456, 139, 139, 0}, + {614, 0, 0, 44223}, + {619, 0, 0, 44224}, + {714, 47, 47, 0}, + {762, 130, 130, 0}, + {870, 127, 126, 0}, + {871, 144, 144, 0}, + {876, 0, 0, 43349}, + {907, 48, 48, 0}, + {913, 74, 74, 0}, + {942, 79, 79, 0}, + {943, 79, 79, 0}, + {945, 131, 131, 0}, + {948, 130, 130, 0}, + {953, 132, 132, 0}, + {978, 81, 81, 0}, + {1015, 77, 77, 0}, + {1021, 0, 0, 40643}, + {1038, 75, 75, 0}, + {1039, 76, 76, 0}, + {1163, 128, 128, 0}, + {1174, 82, 82, 0}, + {1175, 72, 72, 0}, + {1250, 0, 0, 40653}, + {1400, 120, 120, 0}, + {1402, 122, 122, 0}, + {1516, 83, 83, 0}, + {1563, 84, 84, 0}, + {1656, 124, 124, 0}, + {1657, 124, 124, 0}, + {1658, 129, 129, 0}, + {1681, 125, 125, 43300}, + {1682, 125, 125, 43300}, + {1683, 133, 133, 0}, + {1684, 133, 133, 0}, + {1691, 134, 134, 0}, + {1692, 134, 134, 0}, + {1693, 135, 135, 0}, + {1707, 135, 135, 0}, + {1784, 84, 84, 0}, + {1793, 137, 137, 0}, + {1956, 0, 0, 43824}, + {2051, 140, 140, 0}, + {2054, 121, 121, 0}, + {2096, 0, 0, 44430}, + {2136, 0, 0, 0},// <- TODO: find item for spell 59961 + {2137, 0, 0, 0},// <- TODO: find item for spell 60021 + {2138, 0, 0, 0},// <- TODO: find item for spell 59976 + {2143, 0, 0, 44178}, + {2144, 0, 0, 0},// <- TODO: find item for spell 60024 + {2145, 0, 0, 0},// <- TODO: find item for spell 60024 + {2186, 141, 141, 0}, + {2187, 142, 142, 0}, + {2188, 143, 143, 0} + }; + +AchievementMgr::AchievementMgr(Player *player) +{ + m_player = player; +} + +AchievementMgr::~AchievementMgr() +{ +} + +void AchievementMgr::SaveToDB() +{ + if(!m_completedAchievements.empty()) + { + bool need_execute = false; + std::ostringstream ssdel; + std::ostringstream ssins; + for(CompletedAchievementMap::iterator iter = m_completedAchievements.begin(); iter!=m_completedAchievements.end(); iter++) + { + if(!iter->second.changed) + continue; + + /// first new/changed record prefix + if(!need_execute) + { + ssdel << "DELETE FROM character_achievement WHERE guid = " << GetPlayer()->GetGUIDLow() << " AND achievement IN ("; + ssins << "INSERT INTO character_achievement (guid, achievement, date) VALUES "; + need_execute = true; + } + /// next new/changed record prefix + else + { + ssdel << ", "; + ssins << ", "; + } + + // new/changed record data + ssdel << iter->first; + ssins << "("<<GetPlayer()->GetGUIDLow() << ", " << iter->first << ", " << uint64(iter->second.date) << ")"; + + /// mark as saved in db + iter->second.changed = false; + } + + if(need_execute) + ssdel << ")"; + + if(need_execute) + { + CharacterDatabase.BeginTransaction (); + CharacterDatabase.Execute( ssdel.str().c_str() ); + CharacterDatabase.Execute( ssins.str().c_str() ); + CharacterDatabase.CommitTransaction (); + } + } + + if(!m_criteriaProgress.empty()) + { + /// prepare deleting and insert + bool need_execute = false; + std::ostringstream ssdel; + std::ostringstream ssins; + for(CriteriaProgressMap::iterator iter = m_criteriaProgress.begin(); iter!=m_criteriaProgress.end(); ++iter) + { + if(!iter->second.changed) + continue; + + /// first new/changed record prefix + if(!need_execute) + { + ssdel << "DELETE FROM character_achievement_progress WHERE guid = " << GetPlayer()->GetGUIDLow() << " AND criteria IN ("; + ssins << "INSERT INTO character_achievement_progress (guid, criteria, counter, date) VALUES "; + need_execute = true; + } + /// next new/changed record prefix + else + { + ssdel << ", "; + ssins << ", "; + } + + // new/changed record data + ssdel << iter->first; + ssins << "(" << GetPlayer()->GetGUIDLow() << ", " << iter->first << ", " << iter->second.counter << ", " << iter->second.date << ")"; + + /// mark as saved in db + iter->second.changed = false; + } + + if(need_execute) + ssdel << ")"; + + if(need_execute) + { + CharacterDatabase.BeginTransaction (); + CharacterDatabase.Execute( ssdel.str().c_str() ); + CharacterDatabase.Execute( ssins.str().c_str() ); + CharacterDatabase.CommitTransaction (); + } + } +} + +void AchievementMgr::LoadFromDB(QueryResult *achievementResult, QueryResult *criteriaResult) +{ + if(achievementResult) + { + do + { + Field *fields = achievementResult->Fetch(); + CompletedAchievementData& ca = m_completedAchievements[fields[0].GetUInt32()]; + ca.date = time_t(fields[1].GetUInt64()); + ca.changed = false; + } while(achievementResult->NextRow()); + delete achievementResult; + } + + if(criteriaResult) + { + do + { + Field *fields = criteriaResult->Fetch(); + + uint32 id = fields[0].GetUInt32(); + uint32 counter = fields[1].GetUInt32(); + time_t date = time_t(fields[2].GetUInt64()); + + AchievementCriteriaEntry const* criteria = sAchievementCriteriaStore.LookupEntry(id); + if(!criteria || criteria->timeLimit && date + criteria->timeLimit < time(NULL)) + continue; + + CriteriaProgress& progress = m_criteriaProgress[id]; + progress.counter = counter; + progress.date = date; + progress.changed = false; + } while(criteriaResult->NextRow()); + delete criteriaResult; + } + +} + +void AchievementMgr::SendAchievementEarned(AchievementEntry const* achievement) +{ + sLog.outString("AchievementMgr::SendAchievementEarned(%u)", achievement->ID); + + const char *msg = "|Hplayer:$N|h[$N]|h has earned the achievement $a!"; + if(Guild* guild = objmgr.GetGuildById(GetPlayer()->GetGuildId())) + { + WorldPacket data(SMSG_MESSAGECHAT, 200); + data << uint8(CHAT_MSG_ACHIEVEMENT); + data << uint8(CHAT_MSG_GUILD_ACHIEVEMENT); + data << uint32(LANG_UNIVERSAL); + data << uint64(GetPlayer()->GetGUID()); + data << uint32(5); + data << uint64(GetPlayer()->GetGUID()); + data << uint32(strlen(msg)+1); + data << msg; + data << uint8(0); + data << uint32(achievement->ID); + guild->BroadcastPacket(&data); + } + if(achievement->flags & (ACHIEVEMENT_FLAG_REALM_FIRST_KILL|ACHIEVEMENT_FLAG_REALM_FIRST_REACH)) + { + // broadcast realm first reached + WorldPacket data(SMSG_SERVER_FIRST_ACHIEVEMENT, strlen(GetPlayer()->GetName())+1+8+4+4); + data << GetPlayer()->GetName(); + data << uint64(GetPlayer()->GetGUID()); + data << uint32(achievement->ID); + data << uint32(0); // 1=link supplied string as player name, 0=display plain string + sWorld.SendGlobalMessage(&data); + } + else + { + WorldPacket data(SMSG_MESSAGECHAT, 200); + data << uint8(CHAT_MSG_ACHIEVEMENT); + data << uint32(LANG_UNIVERSAL); + data << uint64(GetPlayer()->GetGUID()); + data << uint32(5); + data << uint64(GetPlayer()->GetGUID()); + data << uint32(strlen(msg)+1); + data << msg; + data << uint8(0); + data << uint32(achievement->ID); + GetPlayer()->SendMessageToSet(&data, true); + + } + WorldPacket data(SMSG_ACHIEVEMENT_EARNED, 8+4+8); + data.append(GetPlayer()->GetPackGUID()); + data << uint32(achievement->ID); + data << uint32(secsToTimeBitFields(time(NULL))); + data << uint32(0); + GetPlayer()->SendMessageToSet(&data, true); +} + +void AchievementMgr::SendCriteriaUpdate(uint32 id, CriteriaProgress const* progress) +{ + WorldPacket data(SMSG_CRITERIA_UPDATE, 8+4+8); + data << uint32(id); + + // the counter is packed like a packed Guid + data.appendPackGUID(progress->counter); + + data.append(GetPlayer()->GetPackGUID()); + data << uint32(0); + data << uint32(secsToTimeBitFields(progress->date)); + data << uint32(0); // timer 1 + data << uint32(0); // timer 2 + GetPlayer()->SendMessageToSet(&data, true); +} + +/** + * called at player login. The player might have fulfilled some achievements when the achievement system wasn't working yet + */ +void AchievementMgr::CheckAllAchievementCriteria() +{ + // suppress sending packets + for(uint32 i=0; i<ACHIEVEMENT_CRITERIA_TYPE_TOTAL; i++) + UpdateAchievementCriteria(AchievementCriteriaTypes(i)); +} + +/** + * this function will be called whenever the user might have done a criteria relevant action + */ +void AchievementMgr::UpdateAchievementCriteria(AchievementCriteriaTypes type, uint32 miscvalue1, uint32 miscvalue2, Unit *unit, uint32 time) +{ + sLog.outString("AchievementMgr::UpdateAchievementCriteria(%u, %u, %u, %u)", type, miscvalue1, miscvalue2, time); + AchievementCriteriaEntryList const& achievementCriteriaList = objmgr.GetAchievementCriteriaByType(type); + for(AchievementCriteriaEntryList::const_iterator i = achievementCriteriaList.begin(); i!=achievementCriteriaList.end(); ++i) + { + AchievementCriteriaEntry const *achievementCriteria = (*i); + + // don't update already completed criteria + if(IsCompletedCriteria(achievementCriteria)) + continue; + + if(achievementCriteria->groupFlag & ACHIEVEMENT_CRITERIA_GROUP_NOT_IN_GROUP && GetPlayer()->GetGroup()) + continue; + + AchievementEntry const *achievement = sAchievementStore.LookupEntry(achievementCriteria->referredAchievement); + if(!achievement) + continue; + + if(achievement->factionFlag == ACHIEVEMENT_FACTION_FLAG_HORDE && GetPlayer()->GetTeam() != HORDE || + achievement->factionFlag == ACHIEVEMENT_FACTION_FLAG_ALLIANCE && GetPlayer()->GetTeam() != ALLIANCE) + continue; + + switch (type) + { + case ACHIEVEMENT_CRITERIA_TYPE_REACH_LEVEL: + SetCriteriaProgress(achievementCriteria, GetPlayer()->getLevel()); + break; + case ACHIEVEMENT_CRITERIA_TYPE_BUY_BANK_SLOT: + SetCriteriaProgress(achievementCriteria, GetPlayer()->GetByteValue(PLAYER_BYTES_2, 2)+1); + break; + case ACHIEVEMENT_CRITERIA_TYPE_KILL_CREATURE: + // AchievementMgr::UpdateAchievementCriteria might also be called on login - skip in this case + if(!miscvalue1) + continue; + if(achievementCriteria->kill_creature.creatureID != miscvalue1) + continue; + SetCriteriaProgress(achievementCriteria, miscvalue2, true); + break; + case ACHIEVEMENT_CRITERIA_TYPE_REACH_SKILL_LEVEL: + if(uint32 skillvalue = GetPlayer()->GetBaseSkillValue(achievementCriteria->reach_skill_level.skillID)) + SetCriteriaProgress(achievementCriteria, skillvalue); + break; + case ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_QUEST_COUNT: + { + uint32 counter =0; + for(QuestStatusMap::iterator itr = GetPlayer()->getQuestStatusMap().begin(); itr!=GetPlayer()->getQuestStatusMap().end(); itr++) + if(itr->second.m_rewarded) + counter++; + SetCriteriaProgress(achievementCriteria, counter); + break; + } + case ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_QUESTS_IN_ZONE: + { + uint32 counter =0; + for(QuestStatusMap::iterator itr = GetPlayer()->getQuestStatusMap().begin(); itr!=GetPlayer()->getQuestStatusMap().end(); itr++) + { + Quest const* quest = objmgr.GetQuestTemplate(itr->first); + if(itr->second.m_rewarded && quest->GetZoneOrSort() == achievementCriteria->complete_quests_in_zone.zoneID) + counter++; + } + SetCriteriaProgress(achievementCriteria, counter); + break; + } + case ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_DAILY_QUEST: + // AchievementMgr::UpdateAchievementCriteria might also be called on login - skip in this case + if(!miscvalue1) + continue; + SetCriteriaProgress(achievementCriteria, miscvalue1, true); + break; + case ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_BATTLEGROUND: + // AchievementMgr::UpdateAchievementCriteria might also be called on login - skip in this case + if(!miscvalue1) + continue; + if(GetPlayer()->GetMapId() != achievementCriteria->complete_battleground.mapID) + continue; + SetCriteriaProgress(achievementCriteria, miscvalue1, true); + break; + case ACHIEVEMENT_CRITERIA_TYPE_LEARN_SPELL: + if(GetPlayer()->HasSpell(achievementCriteria->learn_spell.spellID)) + SetCriteriaProgress(achievementCriteria, 1); + break; + case ACHIEVEMENT_CRITERIA_TYPE_DEATH_AT_MAP: + // AchievementMgr::UpdateAchievementCriteria might also be called on login - skip in this case + if(!miscvalue1) + continue; + if(GetPlayer()->GetMapId() != achievementCriteria->death_at_map.mapID) + continue; + SetCriteriaProgress(achievementCriteria, 1, true); + break; + case ACHIEVEMENT_CRITERIA_TYPE_KILLED_BY_CREATURE: + // AchievementMgr::UpdateAchievementCriteria might also be called on login - skip in this case + if(!miscvalue1) + continue; + if(miscvalue1 != achievementCriteria->killed_by_creature.creatureEntry) + continue; + SetCriteriaProgress(achievementCriteria, 1, true); + break; + case ACHIEVEMENT_CRITERIA_TYPE_KILLED_BY_PLAYER: + // AchievementMgr::UpdateAchievementCriteria might also be called on login - skip in this case + if(!miscvalue1) + continue; + SetCriteriaProgress(achievementCriteria, 1, true); + break; + case ACHIEVEMENT_CRITERIA_TYPE_FALL_WITHOUT_DYING: + { + // AchievementMgr::UpdateAchievementCriteria might also be called on login - skip in this case + if(!miscvalue1) + continue; + if(achievement->ID == 1260) + { + if(Player::GetDrunkenstateByValue(GetPlayer()->GetDrunkValue()) != DRUNKEN_SMASHED) + continue; + // TODO: hardcoding eventid is bad, it can differ from DB to DB - maye implement something using HolidayNames.dbc? + if(!gameeventmgr.IsActiveEvent(26)) + continue; + } + // miscvalue1 is the ingame fallheight*100 as stored in dbc + SetCriteriaProgress(achievementCriteria, miscvalue1); + break; + } + case ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_QUEST: + if(GetPlayer()->GetQuestRewardStatus(achievementCriteria->complete_quest.questID)) + SetCriteriaProgress(achievementCriteria, 1); + break; + case ACHIEVEMENT_CRITERIA_TYPE_USE_ITEM: + // AchievementMgr::UpdateAchievementCriteria might also be called on login - skip in this case + if(!miscvalue1) + continue; + if(achievementCriteria->use_item.itemID != miscvalue1) + continue; + SetCriteriaProgress(achievementCriteria, 1, true); + break; + case ACHIEVEMENT_CRITERIA_TYPE_OWN_ITEM: + // speedup for non-login case + if(miscvalue1 && achievementCriteria->own_item.itemID!=miscvalue1) + continue; + SetCriteriaProgress(achievementCriteria, GetPlayer()->GetItemCount(achievementCriteria->own_item.itemID, true)); + break; + case ACHIEVEMENT_CRITERIA_TYPE_LOOT_ITEM: + // You _have_ to loot that item, just owning it when logging in does _not_ count! + if(!miscvalue1) + continue; + if(miscvalue1 != achievementCriteria->own_item.itemID) + continue; + SetCriteriaProgress(achievementCriteria, miscvalue2, true); + break; + case ACHIEVEMENT_CRITERIA_TYPE_BE_SPELL_TARGET: + case ACHIEVEMENT_CRITERIA_TYPE_BE_SPELL_TARGET2: + if (!miscvalue1 || miscvalue1 != achievementCriteria->be_spell_target.spellID) + continue; + SetCriteriaProgress(achievementCriteria, 1, true); + break; + case ACHIEVEMENT_CRITERIA_TYPE_CAST_SPELL: + if (!miscvalue1 || miscvalue1 != achievementCriteria->cast_spell.spellID) + continue; + SetCriteriaProgress(achievementCriteria, 1, true); + break; + case ACHIEVEMENT_CRITERIA_TYPE_CAST_SPELL2: + { + if (!miscvalue1 || miscvalue1 != achievementCriteria->cast_spell.spellID) + continue; + // those requirements couldn't be found in the dbc + + const CriteriaCastSpellRequirement *requirement = NULL; + for (uint32 i=0; i<CRITERIA_CAST_SPELL_REQ_COUNT; i++) + { + if (criteriaCastSpellRequirements[i].achievementCriteriaId == achievementCriteria->ID) + { + requirement = &criteriaCastSpellRequirements[i]; + break; + } + } + + if (requirement) + { + if (!unit) + continue; + + if (requirement->creatureEntry && unit->GetEntry() != requirement->creatureEntry) + continue; + + if (requirement->playerRace && (unit->GetTypeId() != TYPEID_PLAYER || unit->getRace()!=requirement->playerRace)) + continue; + + if (requirement->playerClass && (unit->GetTypeId() != TYPEID_PLAYER || unit->getClass()!=requirement->playerClass)) + continue; + } + SetCriteriaProgress(achievementCriteria, 1, true); + break; + } + case ACHIEVEMENT_CRITERIA_TYPE_LEARN_SKILLLINE_SPELLS: + { + uint32 spellCount = 0; + for (PlayerSpellMap::const_iterator spellIter = GetPlayer()->GetSpellMap().begin(); + spellIter != GetPlayer()->GetSpellMap().end(); + spellIter++) + { + for(SkillLineAbilityMap::const_iterator skillIter = spellmgr.GetBeginSkillLineAbilityMap(spellIter->first); + skillIter != spellmgr.GetEndSkillLineAbilityMap(spellIter->first); + skillIter++) + { + if(skillIter->second->skillId == achievementCriteria->learn_skilline_spell.skillLine) + spellCount++; + } + } + SetCriteriaProgress(achievementCriteria, spellCount); + break; + } + case ACHIEVEMENT_CRITERIA_TYPE_VISIT_BARBER_SHOP: + { + // skip for login case + if(!miscvalue1) + continue; + SetCriteriaProgress(achievementCriteria, 1); + break; + } + case ACHIEVEMENT_CRITERIA_TYPE_GAIN_REPUTATION: + { + int32 reputation = GetPlayer()->GetReputation(achievementCriteria->gain_reputation.factionID); + if (reputation > 0) + SetCriteriaProgress(achievementCriteria, reputation); + break; + } + case ACHIEVEMENT_CRITERIA_TYPE_GAIN_EXALTED_REPUTATION: + { + uint32 counter = 0; + const FactionStateList factionStateList = GetPlayer()->GetFactionStateList(); + for (FactionStateList::const_iterator iter = factionStateList.begin(); iter!= factionStateList.end(); iter++) + { + if(GetPlayer()->ReputationToRank(iter->second.Standing) >= REP_EXALTED) + ++counter; + } + SetCriteriaProgress(achievementCriteria, counter); + break; + } + case ACHIEVEMENT_CRITERIA_TYPE_EXPLORE_AREA: + { + WorldMapOverlayEntry const* worldOverlayEntry = sWorldMapOverlayStore.LookupEntry(achievementCriteria->explore_area.areaReference); + if(!worldOverlayEntry) + break; + + int32 exploreFlag = GetAreaFlagByAreaID(worldOverlayEntry->areatableID); + if(exploreFlag < 0) + break; + + uint32 playerIndexOffset = uint32(exploreFlag) / 32; + uint32 mask = 1<< (uint32(exploreFlag) % 32); + + if(GetPlayer()->GetUInt32Value(PLAYER_EXPLORED_ZONES_1 + playerIndexOffset) & mask) + SetCriteriaProgress(achievementCriteria, 1); + break; + } + + } + if(IsCompletedCriteria(achievementCriteria)) + CompletedCriteria(achievementCriteria); + } +} + +bool AchievementMgr::IsCompletedCriteria(AchievementCriteriaEntry const* achievementCriteria) +{ + AchievementEntry const* achievement = sAchievementStore.LookupEntry(achievementCriteria->referredAchievement); + if(!achievement) + return false; + + // counter can never complete + if(achievement->flags & ACHIEVEMENT_FLAG_COUNTER) + return false; + + if(achievement->flags & (ACHIEVEMENT_FLAG_REALM_FIRST_REACH | ACHIEVEMENT_FLAG_REALM_FIRST_KILL)) + { + // someone on this realm has already completed that achievement + if(objmgr.allCompletedAchievements.find(achievement->ID)!=objmgr.allCompletedAchievements.end()) + return false; + } + + CriteriaProgressMap::const_iterator itr = m_criteriaProgress.find(achievementCriteria->ID); + if(itr == m_criteriaProgress.end()) + return false; + + CriteriaProgress const* progress = &itr->second; + + switch(achievementCriteria->requiredType) + { + case ACHIEVEMENT_CRITERIA_TYPE_REACH_LEVEL: + if(achievement->ID == 467 && GetPlayer()->getClass() != CLASS_SHAMAN || + achievement->ID == 466 && GetPlayer()->getClass() != CLASS_DRUID || + achievement->ID == 465 && GetPlayer()->getClass() != CLASS_PALADIN || + achievement->ID == 464 && GetPlayer()->getClass() != CLASS_PRIEST || + achievement->ID == 463 && GetPlayer()->getClass() != CLASS_WARLOCK || + achievement->ID == 462 && GetPlayer()->getClass() != CLASS_HUNTER || + achievement->ID == 461 && GetPlayer()->getClass() != CLASS_DEATH_KNIGHT || + achievement->ID == 460 && GetPlayer()->getClass() != CLASS_MAGE || + achievement->ID == 459 && GetPlayer()->getClass() != CLASS_WARRIOR || + achievement->ID == 458 && GetPlayer()->getClass() != CLASS_ROGUE || + + achievement->ID == 1404 && GetPlayer()->getRace() != RACE_GNOME || + achievement->ID == 1405 && GetPlayer()->getRace() != RACE_BLOODELF || + achievement->ID == 1406 && GetPlayer()->getRace() != RACE_DRAENEI || + achievement->ID == 1407 && GetPlayer()->getRace() != RACE_DWARF || + achievement->ID == 1408 && GetPlayer()->getRace() != RACE_HUMAN || + achievement->ID == 1409 && GetPlayer()->getRace() != RACE_NIGHTELF || + achievement->ID == 1410 && GetPlayer()->getRace() != RACE_ORC || + achievement->ID == 1411 && GetPlayer()->getRace() != RACE_TAUREN || + achievement->ID == 1412 && GetPlayer()->getRace() != RACE_TROLL || + achievement->ID == 1413 && GetPlayer()->getRace() != RACE_UNDEAD_PLAYER ) + return false; + return progress->counter >= achievementCriteria->reach_level.level; + case ACHIEVEMENT_CRITERIA_TYPE_BUY_BANK_SLOT: + return progress->counter >= achievementCriteria->buy_bank_slot.numberOfSlots; + case ACHIEVEMENT_CRITERIA_TYPE_KILL_CREATURE: + return progress->counter >= achievementCriteria->kill_creature.creatureCount; + case ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_ACHIEVEMENT: + return m_completedAchievements.find(achievementCriteria->complete_achievement.linkedAchievement) != m_completedAchievements.end(); + case ACHIEVEMENT_CRITERIA_TYPE_REACH_SKILL_LEVEL: + return progress->counter >= achievementCriteria->reach_skill_level.skillLevel; + case ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_QUESTS_IN_ZONE: + return progress->counter >= achievementCriteria->complete_quests_in_zone.questCount; + case ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_DAILY_QUEST: + return progress->counter >= achievementCriteria->complete_daily_quest.questCount; + case ACHIEVEMENT_CRITERIA_TYPE_LEARN_SPELL: + return progress->counter >= 1; + case ACHIEVEMENT_CRITERIA_TYPE_FALL_WITHOUT_DYING: + return progress->counter >= achievementCriteria->fall_without_dying.fallHeight; + case ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_QUEST: + return progress->counter >= 1; + case ACHIEVEMENT_CRITERIA_TYPE_USE_ITEM: + return progress->counter >= achievementCriteria->use_item.itemCount; + case ACHIEVEMENT_CRITERIA_TYPE_OWN_ITEM: + return progress->counter >= achievementCriteria->own_item.itemCount; + case ACHIEVEMENT_CRITERIA_TYPE_LOOT_ITEM: + return progress->counter >= achievementCriteria->loot_item.itemCount; + case ACHIEVEMENT_CRITERIA_TYPE_BE_SPELL_TARGET: + case ACHIEVEMENT_CRITERIA_TYPE_BE_SPELL_TARGET2: + return progress->counter >= achievementCriteria->be_spell_target.spellCount; + case ACHIEVEMENT_CRITERIA_TYPE_CAST_SPELL: + case ACHIEVEMENT_CRITERIA_TYPE_CAST_SPELL2: + return progress->counter >= achievementCriteria->cast_spell.castCount; + case ACHIEVEMENT_CRITERIA_TYPE_LEARN_SKILLLINE_SPELLS: + return progress->counter >= achievementCriteria->learn_skilline_spell.spellCount; + case ACHIEVEMENT_CRITERIA_TYPE_VISIT_BARBER_SHOP: + return progress->counter >= achievementCriteria->visit_barber.numberOfVisits; + case ACHIEVEMENT_CRITERIA_TYPE_GAIN_REPUTATION: + return progress->counter >= achievementCriteria->gain_reputation.reputationAmount; + case ACHIEVEMENT_CRITERIA_TYPE_GAIN_EXALTED_REPUTATION: + return progress->counter >= achievementCriteria->gain_exalted_reputation.numberOfExaltedFactions; + case ACHIEVEMENT_CRITERIA_TYPE_EXPLORE_AREA: + return progress->counter >= 1; + + // handle all statistic-only criteria here + case ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_BATTLEGROUND: + case ACHIEVEMENT_CRITERIA_TYPE_DEATH_AT_MAP: + case ACHIEVEMENT_CRITERIA_TYPE_KILLED_BY_CREATURE: + case ACHIEVEMENT_CRITERIA_TYPE_KILLED_BY_PLAYER: + return false; + } + return false; +} + +void AchievementMgr::CompletedCriteria(AchievementCriteriaEntry const* criteria) +{ + AchievementEntry const* achievement = sAchievementStore.LookupEntry(criteria->referredAchievement); + if(!achievement) + return; + // counter can never complete + if(achievement->flags & ACHIEVEMENT_FLAG_COUNTER) + return; + + if(criteria->completionFlag & ACHIEVEMENT_CRITERIA_COMPLETE_FLAG_ALL || GetAchievementCompletionState(achievement)==ACHIEVEMENT_COMPLETED_COMPLETED_NOT_STORED) + { + CompletedAchievement(achievement); + } +} + +// TODO: achievement 705 requires 4 criteria to be fulfilled +AchievementCompletionState AchievementMgr::GetAchievementCompletionState(AchievementEntry const* entry) +{ + if(m_completedAchievements.find(entry->ID)!=m_completedAchievements.end()) + return ACHIEVEMENT_COMPLETED_COMPLETED_STORED; + + bool foundOutstanding = false; + for (uint32 entryId = 0; entryId<sAchievementCriteriaStore.GetNumRows(); entryId++) + { + AchievementCriteriaEntry const* criteria = sAchievementCriteriaStore.LookupEntry(entryId); + if(!criteria || criteria->referredAchievement!= entry->ID) + continue; + + if(IsCompletedCriteria(criteria) && criteria->completionFlag & ACHIEVEMENT_CRITERIA_COMPLETE_FLAG_ALL) + return ACHIEVEMENT_COMPLETED_COMPLETED_NOT_STORED; + + // found an umcompleted criteria, but DONT return false yet - there might be a completed criteria with ACHIEVEMENT_CRITERIA_COMPLETE_FLAG_ALL + if(!IsCompletedCriteria(criteria)) + foundOutstanding = true; + } + if(foundOutstanding) + return ACHIEVEMENT_COMPLETED_NONE; + else + return ACHIEVEMENT_COMPLETED_COMPLETED_NOT_STORED; +} + +void AchievementMgr::SetCriteriaProgress(AchievementCriteriaEntry const* entry, uint32 newValue, bool relative) +{ + sLog.outString("AchievementMgr::SetCriteriaProgress(%u, %u)", entry->ID, newValue); + CriteriaProgress *progress = NULL; + + CriteriaProgressMap::iterator iter = m_criteriaProgress.find(entry->ID); + + if(iter == m_criteriaProgress.end()) + { + progress = &m_criteriaProgress[entry->ID]; + progress->counter = 0; + progress->date = time(NULL); + } + else + { + progress = &iter->second; + if(relative) + newValue += progress->counter; + if(progress->counter == newValue) + return; + progress->counter = newValue; + } + + progress->changed = true; + + if(entry->timeLimit) + { + time_t now = time(NULL); + if(progress->date + entry->timeLimit < now) + { + progress->counter = 1; + } + // also it seems illogical, the timeframe will be extended at every criteria update + progress->date = now; + } + SendCriteriaUpdate(entry->ID,progress); +} + +void AchievementMgr::CompletedAchievement(AchievementEntry const* achievement) +{ + sLog.outString("AchievementMgr::CompletedAchievement(%u)", achievement->ID); + if(achievement->flags & ACHIEVEMENT_FLAG_COUNTER || m_completedAchievements.find(achievement->ID)!=m_completedAchievements.end()) + return; + + SendAchievementEarned(achievement); + CompletedAchievementData& ca = m_completedAchievements[achievement->ID]; + ca.date = time(NULL); + ca.changed = true; + + // don't insert for ACHIEVEMENT_FLAG_REALM_FIRST_KILL since otherwise only the first group member would reach that achievement + // TODO: where do set this instead? + if(!(achievement->flags & ACHIEVEMENT_FLAG_REALM_FIRST_KILL)) + objmgr.allCompletedAchievements.insert(achievement->ID); + + UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_ACHIEVEMENT); + + // reward items and titles + AchievementReward const* reward = NULL; + for (uint32 i=0; i<ACHIEVEMENT_REWARD_COUNT; i++) + { + if (achievementRewards[i].achievementId == achievement->ID) + { + reward = &achievementRewards[i]; + break; + } + } + + if (reward) + { + sLog.outString("achiev %u, title= %u, %u", reward->achievementId, reward->titleId[0], reward->titleId[1]); + uint32 titleId = reward->titleId[GetPlayer()->GetTeam() == HORDE?0:1]; + if(CharTitlesEntry const* titleEntry = sCharTitlesStore.LookupEntry(titleId)) + GetPlayer()->SetTitle(titleEntry); + + if (reward->itemId) + { + ItemPrototype const *pProto = objmgr.GetItemPrototype( reward->itemId ); + + if(!pProto) + { + GetPlayer()->SendEquipError( EQUIP_ERR_ITEM_NOT_FOUND, NULL, NULL ); + return; + } + + ItemPosCountVec dest; + uint32 no_space = 0; + uint8 msg = GetPlayer()->CanStoreNewItem( NULL_BAG, NULL_SLOT, dest, reward->itemId, 1, &no_space ); + + if( msg != EQUIP_ERR_OK ) + { + GetPlayer()->SendEquipError( msg, NULL, NULL ); + return; + } + Item* pItem = GetPlayer()->StoreNewItem( dest, reward->itemId, true); + + if(!pItem) + { + GetPlayer()->SendEquipError( EQUIP_ERR_ITEM_NOT_FOUND, NULL, NULL ); + return; + } + } + } +} + +void AchievementMgr::SendAllAchievementData() +{ + // since we don't know the exact size of the packed GUIDs this is just an approximation + WorldPacket data(SMSG_ALL_ACHIEVEMENT_DATA, 4*2+m_completedAchievements.size()*4*2+m_completedAchievements.size()*7*4); + BuildAllDataPacket(&data); + GetPlayer()->GetSession()->SendPacket(&data); +} + +void AchievementMgr::SendRespondInspectAchievements(Player* player) +{ + // since we don't know the exact size of the packed GUIDs this is just an approximation + WorldPacket data(SMSG_RESPOND_INSPECT_ACHIEVEMENTS, 4+4*2+m_completedAchievements.size()*4*2+m_completedAchievements.size()*7*4); + data.append(GetPlayer()->GetPackGUID()); + BuildAllDataPacket(&data); + player->GetSession()->SendPacket(&data); +} + +/** + * used by both SMSG_ALL_ACHIEVEMENT_DATA and SMSG_RESPOND_INSPECT_ACHIEVEMENT + */ +void AchievementMgr::BuildAllDataPacket(WorldPacket *data) +{ + for(CompletedAchievementMap::const_iterator iter = m_completedAchievements.begin(); iter!=m_completedAchievements.end(); ++iter) + { + *data << uint32(iter->first); + *data << uint32(secsToTimeBitFields(iter->second.date)); + } + *data << int32(-1); + + for(CriteriaProgressMap::const_iterator iter = m_criteriaProgress.begin(); iter!=m_criteriaProgress.end(); ++iter) + { + *data << uint32(iter->first); + data->appendPackGUID(iter->second.counter); + data->append(GetPlayer()->GetPackGUID()); + *data << uint32(0); + *data << uint32(secsToTimeBitFields(iter->second.date)); + *data << uint32(0); + *data << uint32(0); + } + + *data << int32(-1); +} diff --git a/src/game/AchievementMgr.h b/src/game/AchievementMgr.h new file mode 100644 index 00000000000..6392a9fc647 --- /dev/null +++ b/src/game/AchievementMgr.h @@ -0,0 +1,101 @@ +/* + * Copyright (C) 2005-2008 MaNGOS <http://getmangos.com/> + * + * This program 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 2 of the License, or + * (at your option) any later version. + * + * This program 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 this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +#ifndef __MANGOS_ACHIEVEMENTMGR_H +#define __MANGOS_ACHIEVEMENTMGR_H + +#include "Common.h" +#include "Database/DBCEnums.h" +#include "Database/DBCStores.h" +#include "Database/DatabaseEnv.h" + +#define CRITERIA_CAST_SPELL_REQ_COUNT 46 +#define ACHIEVEMENT_REWARD_COUNT 57 + +struct CriteriaProgress +{ + uint32 counter; + time_t date; + bool changed; +}; + +struct CriteriaCastSpellRequirement +{ + uint32 achievementCriteriaId; + uint32 creatureEntry; + uint8 playerClass; + uint8 playerRace; +}; + +struct AchievementReward +{ + uint32 achievementId; + uint32 titleId[2]; + uint32 itemId; +}; + +struct CompletedAchievementData +{ + time_t date; + bool changed; +}; + +typedef UNORDERED_MAP<uint32, CriteriaProgress> CriteriaProgressMap; +typedef UNORDERED_MAP<uint32, CompletedAchievementData> CompletedAchievementMap; + +class Unit; +class Player; +class WorldPacket; + +enum AchievementCompletionState +{ + ACHIEVEMENT_COMPLETED_NONE, + ACHIEVEMENT_COMPLETED_COMPLETED_NOT_STORED, + ACHIEVEMENT_COMPLETED_COMPLETED_STORED, +}; + +class AchievementMgr +{ + public: + AchievementMgr(Player* pl); + ~AchievementMgr(); + + void LoadFromDB(QueryResult *achievementResult, QueryResult *criteriaResult); + void SaveToDB(); + void UpdateAchievementCriteria(AchievementCriteriaTypes type, uint32 miscvalue1=0, uint32 miscvalue2=0, Unit *unit=NULL, uint32 time=0); + void CheckAllAchievementCriteria(); + void SendAllAchievementData(); + void SendRespondInspectAchievements(Player* player); + Player* GetPlayer() { return m_player;} + + private: + void SendAchievementEarned(AchievementEntry const* achievement); + void SendCriteriaUpdate(uint32 id, CriteriaProgress const* progress); + void SetCriteriaProgress(AchievementCriteriaEntry const* entry, uint32 newValue, bool relative=false); + void CompletedCriteria(AchievementCriteriaEntry const* entry); + void CompletedAchievement(AchievementEntry const* entry); + bool IsCompletedCriteria(AchievementCriteriaEntry const* entry); + AchievementCompletionState GetAchievementCompletionState(AchievementEntry const* entry); + void BuildAllDataPacket(WorldPacket *data); + + Player* m_player; + CriteriaProgressMap m_criteriaProgress; + CompletedAchievementMap m_completedAchievements; + static const CriteriaCastSpellRequirement criteriaCastSpellRequirements[]; + static const AchievementReward achievementRewards[]; +}; +#endif diff --git a/src/game/Calendar.cpp b/src/game/Calendar.cpp new file mode 100644 index 00000000000..cebf7252e78 --- /dev/null +++ b/src/game/Calendar.cpp @@ -0,0 +1,17 @@ +/* + * Copyright (C) 2005-2008 MaNGOS <http://getmangos.com/> + * + * This program 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 2 of the License, or + * (at your option) any later version. + * + * This program 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 this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ diff --git a/src/game/Calendar.h b/src/game/Calendar.h new file mode 100644 index 00000000000..94e4ff103f5 --- /dev/null +++ b/src/game/Calendar.h @@ -0,0 +1,26 @@ +/* + * Copyright (C) 2005-2008 MaNGOS <http://getmangos.com/> + * + * This program 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 2 of the License, or + * (at your option) any later version. + * + * This program 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 this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef MANGOS_CALENDAR_H +#define MANGOS_CALENDAR_H + +class Calendar +{ + +}; +#endif diff --git a/src/game/CalendarHandler.cpp b/src/game/CalendarHandler.cpp new file mode 100644 index 00000000000..9c69e3a91f6 --- /dev/null +++ b/src/game/CalendarHandler.cpp @@ -0,0 +1,118 @@ +/* + * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/> + * + * This program 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 2 of the License, or + * (at your option) any later version. + * + * This program 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 this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "Common.h" +#include "Log.h" +#include "Player.h" +#include "WorldPacket.h" +#include "WorldSession.h" +#include "Opcodes.h" + +void WorldSession::HandleCalendarGetCalendar(WorldPacket &recv_data) +{ + sLog.outDebug("WORLD: CMSG_CALENDAR_GET_CALENDAR"); + recv_data.hexlike(); +} + +void WorldSession::HandleCalendarGetEvent(WorldPacket &recv_data) +{ + sLog.outDebug("WORLD: CMSG_CALENDAR_GET_EVENT"); + recv_data.hexlike(); +} + +void WorldSession::HandleCalendarGuildFilter(WorldPacket &recv_data) +{ + sLog.outDebug("WORLD: CMSG_CALENDAR_GUILD_FILTER"); + recv_data.hexlike(); +} + +void WorldSession::HandleCalendarArenaTeam(WorldPacket &recv_data) +{ + sLog.outDebug("WORLD: CMSG_CALENDAR_ARENA_TEAM"); + recv_data.hexlike(); +} + +void WorldSession::HandleCalendarAddEvent(WorldPacket &recv_data) +{ + sLog.outDebug("WORLD: CMSG_CALENDAR_ADD_EVENT"); + recv_data.hexlike(); +} + +void WorldSession::HandleCalendarUpdateEvent(WorldPacket &recv_data) +{ + sLog.outDebug("WORLD: CMSG_CALENDAR_UPDATE_EVENT"); + recv_data.hexlike(); +} + +void WorldSession::HandleCalendarRemoveEvent(WorldPacket &recv_data) +{ + sLog.outDebug("WORLD: CMSG_CALENDAR_REMOVE_EVENT"); + recv_data.hexlike(); +} + +void WorldSession::HandleCalendarCopyEvent(WorldPacket &recv_data) +{ + sLog.outDebug("WORLD: CMSG_CALENDAR_COPY_EVENT"); + recv_data.hexlike(); +} + +void WorldSession::HandleCalendarEventInvite(WorldPacket &recv_data) +{ + sLog.outDebug("WORLD: CMSG_CALENDAR_EVENT_INVITE"); + recv_data.hexlike(); +} + +void WorldSession::HandleCalendarEventRsvp(WorldPacket &recv_data) +{ + sLog.outDebug("WORLD: CMSG_CALENDAR_EVENT_RSVP"); + recv_data.hexlike(); +} + +void WorldSession::HandleCalendarEventRemoveInvite(WorldPacket &recv_data) +{ + sLog.outDebug("WORLD: CMSG_CALENDAR_EVENT_REMOVE_INVITE"); + recv_data.hexlike(); +} + +void WorldSession::HandleCalendarEventStatus(WorldPacket &recv_data) +{ + sLog.outDebug("WORLD: CMSG_CALENDAR_EVENT_STATUS"); + recv_data.hexlike(); +} + +void WorldSession::HandleCalendarEventModeratorStatus(WorldPacket &recv_data) +{ + sLog.outDebug("WORLD: CMSG_CALENDAR_EVENT_MODERATOR_STATUS"); + recv_data.hexlike(); +} + +void WorldSession::HandleCalendarComplain(WorldPacket &recv_data) +{ + sLog.outDebug("WORLD: CMSG_CALENDAR_COMPLAIN"); + recv_data.hexlike(); +} + +void WorldSession::HandleCalendarGetNumPending(WorldPacket &recv_data) +{ + sLog.outDebug("WORLD: CMSG_CALENDAR_GET_NUM_PENDING"); + recv_data.hexlike(); + + WorldPacket data(SMSG_CALENDAR_SEND_NUM_PENDING, 4); + data << uint32(0); // 0 - no pending invites, 1 - some pending invites + SendPacket(&data); +} diff --git a/src/game/Vehicle.cpp b/src/game/Vehicle.cpp new file mode 100644 index 00000000000..4e1153a166b --- /dev/null +++ b/src/game/Vehicle.cpp @@ -0,0 +1,102 @@ +/* + * Copyright (C) 2005-2008 MaNGOS <http://getmangos.com/> + * + * This program 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 2 of the License, or + * (at your option) any later version. + * + * This program 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 this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "Common.h" +#include "Log.h" +#include "WorldSession.h" +#include "WorldPacket.h" +#include "ObjectMgr.h" +#include "SpellMgr.h" +#include "Vehicle.h" +#include "MapManager.h" +#include "SpellAuras.h" +#include "Unit.h" +#include "Util.h" + +Vehicle::Vehicle() : Creature(), m_vehicleId(0) +{ + m_isVehicle = true; + m_updateFlag = (UPDATEFLAG_LOWGUID | UPDATEFLAG_HIGHGUID | UPDATEFLAG_LIVING | UPDATEFLAG_HAS_POSITION | UPDATEFLAG_VEHICLE); +} + +Vehicle::~Vehicle() +{ + if(m_uint32Values) // only for fully created Object + ObjectAccessor::Instance().RemoveObject(this); +} + +void Vehicle::AddToWorld() +{ + ///- Register the vehicle for guid lookup + if(!IsInWorld()) ObjectAccessor::Instance().AddObject(this); + Unit::AddToWorld(); +} + +void Vehicle::RemoveFromWorld() +{ + ///- Remove the vehicle from the accessor + if(IsInWorld()) ObjectAccessor::Instance().RemoveObject(this); + ///- Don't call the function for Creature, normal mobs + totems go in a different storage + Unit::RemoveFromWorld(); +} + +void Vehicle::setDeathState(DeathState s) // overwrite virtual Creature::setDeathState and Unit::setDeathState +{ + Creature::setDeathState(s); +} + +void Vehicle::Update(uint32 diff) +{ + Creature::Update(diff); +} + +bool Vehicle::Create(uint32 guidlow, Map *map, uint32 Entry, uint32 vehicleId, uint32 team) +{ + SetMapId(map->GetId()); + SetInstanceId(map->GetInstanceId()); + + Object::_Create(guidlow, Entry, HIGHGUID_VEHICLE); + + if(!InitEntry(Entry, team)) + return false; + + m_defaultMovementType = IDLE_MOTION_TYPE; + + AIM_Initialize(); + + SetVehicleId(vehicleId); + + SetUInt32Value(UNIT_NPC_FLAGS, UNIT_NPC_FLAG_SPELLCLICK); + SetFloatValue(UNIT_FIELD_HOVERHEIGHT, 1.0f); + + CreatureInfo const *ci = GetCreatureInfo(); + setFaction(team == ALLIANCE ? ci->faction_A : ci->faction_H); + SetMaxHealth(ci->maxhealth); + SelectLevel(ci); + SetHealth(GetMaxHealth()); + + return true; +} + +void Vehicle::Dismiss() +{ + SendObjectDeSpawnAnim(GetGUID()); + CombatStop(); + CleanupsBeforeDelete(); + AddObjectToRemoveList(); +} diff --git a/src/game/Vehicle.h b/src/game/Vehicle.h new file mode 100644 index 00000000000..7fd8b60c40a --- /dev/null +++ b/src/game/Vehicle.h @@ -0,0 +1,58 @@ +/* + * Copyright (C) 2005-2008 MaNGOS <http://getmangos.com/> + * + * This program 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 2 of the License, or + * (at your option) any later version. + * + * This program 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 this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef MANGOSSERVER_VEHICLE_H +#define MANGOSSERVER_VEHICLE_H + +#include "ObjectDefines.h" +#include "Creature.h" +#include "Unit.h" + +class Vehicle : public Creature +{ + public: + explicit Vehicle(); + virtual ~Vehicle(); + + void AddToWorld(); + void RemoveFromWorld(); + + bool Create (uint32 guidlow, Map *map, uint32 Entry, uint32 vehicleId, uint32 team); + + void setDeathState(DeathState s); // overwrite virtual Creature::setDeathState and Unit::setDeathState + void Update(uint32 diff); // overwrite virtual Creature::Update and Unit::Update + + uint32 GetVehicleId() { return m_vehicleId; } + void SetVehicleId(uint32 vehicleid) { m_vehicleId = vehicleid; } + + void Dismiss(); + + protected: + uint32 m_vehicleId; + + private: + void SaveToDB(uint32, uint8) // overwrited of Creature::SaveToDB - don't must be called + { + assert(false); + } + void DeleteFromDB() // overwrited of Creature::DeleteFromDB - don't must be called + { + assert(false); + } +}; +#endif diff --git a/src/mangosd/CliRunnable.cpp b/src/mangosd/CliRunnable.cpp new file mode 100644 index 00000000000..7fe2ab784a6 --- /dev/null +++ b/src/mangosd/CliRunnable.cpp @@ -0,0 +1,355 @@ +/* + * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/> + * + * Copyright (C) 2008 Trinity <http://www.trinitycore.org/> + * + * This program 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 2 of the License, or + * (at your option) any later version. + * + * This program 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 this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/// \addtogroup Trinityd +/// @{ +/// \file + +#include "Common.h" +#include "Language.h" +#include "Log.h" +#include "World.h" +#include "ScriptCalls.h" +#include "ObjectMgr.h" +#include "WorldSession.h" +#include "Config/ConfigEnv.h" +#include "Util.h" +#include "AccountMgr.h" +#include "CliRunnable.h" +#include "MapManager.h" +#include "Player.h" +#include "Chat.h" + +void utf8print(const char* str) +{ +#if PLATFORM == PLATFORM_WINDOWS + wchar_t wtemp_buf[6000]; + size_t wtemp_len = 6000-1; + if(!Utf8toWStr(str,strlen(str),wtemp_buf,wtemp_len)) + return; + + char temp_buf[6000]; + CharToOemBuffW(&wtemp_buf[0],&temp_buf[0],wtemp_len+1); + printf(temp_buf); +#else + printf(str); +#endif +} + +/// Delete a user account and all associated characters in this realm +/// \todo This function has to be enhanced to respect the login/realm split (delete char, delete account chars in realm, delete account chars in realm then delete account +bool ChatHandler::HandleAccountDeleteCommand(const char* args) +{ + if(!*args) + return false; + + ///- Get the account name from the command line + char *account_name_str=strtok ((char*)args," "); + if (!account_name_str) + return false; + + std::string account_name = account_name_str; + if(!AccountMgr::normilizeString(account_name)) + { + PSendSysMessage(LANG_ACCOUNT_NOT_EXIST,account_name.c_str()); + SetSentErrorMessage(true); + return false; + } + + uint32 account_id = accmgr.GetId(account_name); + if(!account_id) + { + PSendSysMessage(LANG_ACCOUNT_NOT_EXIST,account_name.c_str()); + SetSentErrorMessage(true); + return false; + } + + /// Commands not recommended call from chat, but support anyway + if(m_session) + { + uint32 targetSecurity = accmgr.GetSecurity(account_id); + + /// can delete only for account with less security + /// This is also reject self apply in fact + if (targetSecurity >= m_session->GetSecurity()) + { + SendSysMessage (LANG_YOURS_SECURITY_IS_LOW); + SetSentErrorMessage (true); + return false; + } + } + + AccountOpResult result = accmgr.DeleteAccount(account_id); + switch(result) + { + case AOR_OK: + PSendSysMessage(LANG_ACCOUNT_DELETED,account_name.c_str()); + break; + case AOR_NAME_NOT_EXIST: + PSendSysMessage(LANG_ACCOUNT_NOT_EXIST,account_name.c_str()); + SetSentErrorMessage(true); + return false; + case AOR_DB_INTERNAL_ERROR: + PSendSysMessage(LANG_ACCOUNT_NOT_DELETED_SQL_ERROR,account_name.c_str()); + SetSentErrorMessage(true); + return false; + default: + PSendSysMessage(LANG_ACCOUNT_NOT_DELETED,account_name.c_str()); + SetSentErrorMessage(true); + return false; + } + + return true; +} + +bool ChatHandler::HandleCharacterDeleteCommand(const char* args) +{ + if(!*args) + return false; + + char *character_name_str = strtok((char*)args," "); + if(!character_name_str) + return false; + + std::string character_name = character_name_str; + if(!normalizePlayerName(character_name)) + return false; + + uint64 character_guid; + uint32 account_id; + + Player *player = objmgr.GetPlayer(character_name.c_str()); + if(player) + { + character_guid = player->GetGUID(); + account_id = player->GetSession()->GetAccountId(); + player->GetSession()->KickPlayer(); + } + else + { + character_guid = objmgr.GetPlayerGUIDByName(character_name); + if(!character_guid) + { + PSendSysMessage(LANG_NO_PLAYER,character_name.c_str()); + SetSentErrorMessage(true); + return false; + } + + account_id = objmgr.GetPlayerAccountIdByGUID(character_guid); + } + + std::string account_name; + accmgr.GetName (account_id,account_name); + + Player::DeleteFromDB(character_guid, account_id, true); + PSendSysMessage(LANG_CHARACTER_DELETED,character_name.c_str(),GUID_LOPART(character_guid),account_name.c_str(), account_id); + return true; +} + +/// Exit the realm +bool ChatHandler::HandleServerExitCommand(const char* args) +{ + SendSysMessage(LANG_COMMAND_EXIT); + World::StopNow(SHUTDOWN_EXIT_CODE); + return true; +} + +/// Display info on users currently in the realm +bool ChatHandler::HandleAccountOnlineListCommand(const char* args) +{ + ///- Get the list of accounts ID logged to the realm + QueryResult *resultDB = CharacterDatabase.Query("SELECT name,account FROM characters WHERE online > 0"); + if (!resultDB) + return true; + + ///- Display the list of account/characters online + SendSysMessage("====================================================================="); + SendSysMessage(LANG_ACCOUNT_LIST_HEADER); + SendSysMessage("====================================================================="); + + ///- Circle through accounts + do + { + Field *fieldsDB = resultDB->Fetch(); + std::string name = fieldsDB[0].GetCppString(); + uint32 account = fieldsDB[1].GetUInt32(); + + ///- Get the username, last IP and GM level of each account + // No SQL injection. account is uint32. + // 0 1 2 3 + QueryResult *resultLogin = loginDatabase.PQuery("SELECT username, last_ip, gmlevel, expansion FROM account WHERE id = '%u'",account); + + if(resultLogin) + { + Field *fieldsLogin = resultLogin->Fetch(); + PSendSysMessage("|%15s| %20s | %15s |%4d|%5d|", + fieldsLogin[0].GetString(),name.c_str(),fieldsLogin[1].GetString(),fieldsLogin[2].GetUInt32(),fieldsLogin[3].GetUInt32()); + + delete resultLogin; + } + else + PSendSysMessage(LANG_ACCOUNT_LIST_ERROR,name.c_str()); + + }while(resultDB->NextRow()); + + delete resultDB; + + SendSysMessage("====================================================================="); + return true; +} + +/// Create an account +bool ChatHandler::HandleAccountCreateCommand(const char* args) +{ + if(!*args) + return false; + + ///- %Parse the command line arguments + char *szAcc = strtok((char*)args, " "); + char *szPassword = strtok(NULL, " "); + if(!szAcc || !szPassword) + return false; + + // normilized in accmgr.CreateAccount + std::string account_name = szAcc; + std::string password = szPassword; + + AccountOpResult result = accmgr.CreateAccount(account_name, password); + switch(result) + { + case AOR_OK: + PSendSysMessage(LANG_ACCOUNT_CREATED,account_name.c_str()); + break; + case AOR_NAME_TOO_LONG: + SendSysMessage(LANG_ACCOUNT_TOO_LONG); + SetSentErrorMessage(true); + return false; + case AOR_NAME_ALREDY_EXIST: + SendSysMessage(LANG_ACCOUNT_ALREADY_EXIST); + SetSentErrorMessage(true); + return false; + case AOR_DB_INTERNAL_ERROR: + PSendSysMessage(LANG_ACCOUNT_NOT_CREATED_SQL_ERROR,account_name.c_str()); + SetSentErrorMessage(true); + return false; + default: + PSendSysMessage(LANG_ACCOUNT_NOT_CREATED,account_name.c_str()); + SetSentErrorMessage(true); + return false; + } + + return true; +} + +/// Set the level of logging +bool ChatHandler::HandleServerSetLogLevelCommand(const char *args) +{ + if(!*args) + return false; + + char *NewLevel = strtok((char*)args, " "); + if (!NewLevel) + return false; + + sLog.SetLogLevel(NewLevel); + return true; +} + +/// @} + +#ifdef linux +// Non-blocking keypress detector, when return pressed, return 1, else always return 0 +int kb_hit_return() +{ + struct timeval tv; + fd_set fds; + tv.tv_sec = 0; + tv.tv_usec = 0; + FD_ZERO(&fds); + FD_SET(STDIN_FILENO, &fds); + select(STDIN_FILENO+1, &fds, NULL, NULL, &tv); + return FD_ISSET(STDIN_FILENO, &fds); +} +#endif + +/// %Thread start +void CliRunnable::run() +{ + ///- Init new SQL thread for the world database (one connection call enough) + WorldDatabase.ThreadStart(); // let thread do safe mySQL requests + + char commandbuf[256]; + + ///- Display the list of available CLI functions then beep + sLog.outString(); + + if(sConfig.GetBoolDefault("BeepAtStart", true)) + printf("\a"); // \a = Alert + + // print this here the first time + // later it will be printed after command queue updates + printf("TC>"); + + ///- As long as the World is running (no World::m_stopEvent), get the command line and handle it + while (!World::IsStopped()) + { + fflush(stdout); + #ifdef linux + while (!kb_hit_return() && !World::IsStopped()) + // With this, we limit CLI to 10commands/second + usleep(100); + if (World::IsStopped()) + break; + #endif + char *command_str = fgets(commandbuf,sizeof(commandbuf),stdin); + if (command_str != NULL) + { + for(int x=0;command_str[x];x++) + if(command_str[x]=='\r'||command_str[x]=='\n') + { + command_str[x]=0; + break; + } + + + if(!*command_str) + { + printf("TC>"); + continue; + } + + std::string command; + if(!consoleToUtf8(command_str,command)) // convert from console encoding to utf8 + { + printf("TC>"); + continue; + } + + sWorld.QueueCliCommand(&utf8print,command.c_str()); + } + else if (feof(stdin)) + { + World::StopNow(SHUTDOWN_EXIT_CODE); + } + } + + ///- End the database thread + WorldDatabase.ThreadEnd(); // free mySQL thread resources +} diff --git a/src/mangosd/CliRunnable.h b/src/mangosd/CliRunnable.h new file mode 100644 index 00000000000..c3c1792b6e8 --- /dev/null +++ b/src/mangosd/CliRunnable.h @@ -0,0 +1,35 @@ +/* + * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/> + * + * Copyright (C) 2008 Trinity <http://www.trinitycore.org/> + * + * This program 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 2 of the License, or + * (at your option) any later version. + * + * This program 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 this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/// \addtogroup Trinityd +/// @{ +/// \file + +#ifndef __CLIRUNNABLE_H +#define __CLIRUNNABLE_H + +/// Command Line Interface handling thread +class CliRunnable : public ZThread::Runnable +{ + public: + void run(); +}; +#endif +/// @} diff --git a/src/mangosd/Main.cpp b/src/mangosd/Main.cpp new file mode 100644 index 00000000000..5d497a0e0c2 --- /dev/null +++ b/src/mangosd/Main.cpp @@ -0,0 +1,165 @@ +/* + * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/> + * + * Copyright (C) 2008 Trinity <http://www.trinitycore.org/> + * + * This program 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 2 of the License, or + * (at your option) any later version. + * + * This program 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 this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/// \addtogroup Trinityd Trinity Daemon +/// @{ +/// \file + +#include "Common.h" +#include "Database/DatabaseEnv.h" +#include "Config/ConfigEnv.h" +#include "Log.h" +#include "Master.h" + +#ifndef _TRINITY_CORE_CONFIG +# define _TRINITY_CORE_CONFIG "TrinityCore.conf" +#endif //_TRINITY_CORE_CONFIG + +// Format is YYYYMMDDRR where RR is the change in the conf file +// for that day. +#ifndef _TRINITY_CORE_CONFVER +# define _TRINITY_CORE_CONFVER 2008022901 +#endif //_TRINITY_CORE_CONFVER + +#ifdef WIN32 +#include "ServiceWin32.h" +char serviceName[] = "Trinityd"; +char serviceLongName[] = "Trinity core service"; +char serviceDescription[] = "Massive Network Game Object Server"; +/* + * -1 - not in service mode + * 0 - stopped + * 1 - running + * 2 - paused + */ +int m_ServiceStatus = -1; +#endif + +DatabaseType WorldDatabase; ///< Accessor to the world database +DatabaseType CharacterDatabase; ///< Accessor to the character database +DatabaseType loginDatabase; ///< Accessor to the realm/login database + +uint32 realmID; ///< Id of the realm + +/// Print out the usage string for this program on the console. +void usage(const char *prog) +{ + sLog.outString("Usage: \n %s [<options>]\n" + " -c config_file use config_file as configuration file\n\r" + #ifdef WIN32 + " Running as service functions:\n\r" + " --service run as service\n\r" + " -s install install service\n\r" + " -s uninstall uninstall service\n\r" + #endif + ,prog); +} + +/// Launch the Trinity server +extern int main(int argc, char **argv) +{ + ///- Command line parsing to get the configuration file name + char const* cfg_file = _TRINITY_CORE_CONFIG; + int c=1; + while( c < argc ) + { + if( strcmp(argv[c],"-c") == 0) + { + if( ++c >= argc ) + { + sLog.outError("Runtime-Error: -c option requires an input argument"); + usage(argv[0]); + return 1; + } + else + cfg_file = argv[c]; + } + + #ifdef WIN32 + //////////// + //Services// + //////////// + if( strcmp(argv[c],"-s") == 0) + { + if( ++c >= argc ) + { + sLog.outError("Runtime-Error: -s option requires an input argument"); + usage(argv[0]); + return 1; + } + if( strcmp(argv[c],"install") == 0) + { + if (WinServiceInstall()) + sLog.outString("Installing service"); + return 1; + } + else if( strcmp(argv[c],"uninstall") == 0) + { + if(WinServiceUninstall()) + sLog.outString("Uninstalling service"); + return 1; + } + else + { + sLog.outError("Runtime-Error: unsupported option %s",argv[c]); + usage(argv[0]); + return 1; + } + } + if( strcmp(argv[c],"--service") == 0) + { + WinServiceRun(); + } + //// + #endif + ++c; + } + + if (!sConfig.SetSource(cfg_file)) + { + sLog.outError("Could not find configuration file %s.", cfg_file); + return 1; + } + sLog.outString("Using configuration file %s.", cfg_file); + + uint32 confVersion = sConfig.GetIntDefault("ConfVersion", 0); + if (confVersion < _TRINITY_CORE_CONFVER) + { + sLog.outError("*****************************************************************************"); + sLog.outError(" WARNING: Your trinitycore.conf version indicates your conf file is out of date!"); + sLog.outError(" Please check for updates, as your current default values may cause"); + sLog.outError(" strange behavior."); + sLog.outError("*****************************************************************************"); + clock_t pause = 3000 + clock(); + + while (pause > clock()) {} + } + + ///- and run the 'Master' + /// \todo Why do we need this 'Master'? Can't all of this be in the Main as for Realmd? + return sMaster.Run(); + + // at sMaster return function exist with codes + // 0 - normal shutdown + // 1 - shutdown at error + // 2 - restart command used, this code can be used by restarter for restart Trinityd +} + +/// @} diff --git a/src/mangosd/Makefile.am b/src/mangosd/Makefile.am new file mode 100644 index 00000000000..ad1b78d9033 --- /dev/null +++ b/src/mangosd/Makefile.am @@ -0,0 +1,85 @@ +# Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/> +# +# Copyright (C) 2008 Trinity <http://www.trinitycore.org/> +# +# This program 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 2 of the License, or +# (at your option) any later version. +# +# This program 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 this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +## Process this file with automake to produce Makefile.in + +## Build world list daemon as standalone program +bin_PROGRAMS = trinity-core + +## Preprocessor flags +trinity_core_CPPFLAGS = \ +$(MYSQL_INCLUDES) \ +$(POSTGRE_INCLUDES) \ +$(TRINI_INCLUDES) \ +-I$(top_srcdir)/dep/include \ +-I$(top_srcdir)/src/shared \ +-I$(top_srcdir)/src/framework \ +-I$(top_srcdir)/src/game \ +-D_TRINITY_CORE_CONFIG='"$(sysconfdir)/trinitycore.conf"' + +## Sources +trinity_core_SOURCES = \ +$(srcdir)/CliRunnable.cpp \ +$(srcdir)/CliRunnable.h \ +$(srcdir)/Main.cpp \ +$(srcdir)/Master.cpp \ +$(srcdir)/Master.h \ +$(srcdir)/RASocket.cpp \ +$(srcdir)/RASocket.h \ +$(srcdir)/WorldRunnable.cpp \ +$(srcdir)/WorldRunnable.h + +## Convenience libs to add +trinity_core_LDADD = \ +$(top_builddir)/src/game/libgame.a \ +$(top_builddir)/src/shared/libshared.a \ +$(top_builddir)/src/shared/vmap/libvmaps.a \ +$(top_builddir)/src/framework/libmangosframework.a \ +$(top_builddir)/dep/src/sockets/libmangossockets.a \ +$(top_builddir)/dep/src/zthread/libZThread.la \ +$(top_builddir)/dep/src/g3dlite/libg3dlite.a + +if USE_TSCRIPTS +trinity_core_LDADD += $(top_builddir)/src/bindings/scripts/libtrinityscript.la +else +trinity_core_LDADD += $(top_builddir)/src/bindings/interface/libtrinityscript.la +endif + +## Linker flags +trinity_core_LDFLAGS = $(MYSQL_LIBS) $(POSTGRE_LIBS) $(ZLIB) $(COMPATLIB) $(SSLLIB) $(TRINI_LIBS) -export-dynamic + +## Additional files to install +sysconf_DATA = \ + trinitycore.conf.dist + +EXTRA_DIST = \ + trinitycore.conf.dist + +## Prevend overwrite of the config file, if its already installed +install-data-hook: + @list='$(sysconf_DATA)'; for p in $$list; do \ + dest=`echo $$p | sed -e s/.dist//`; \ + if test -f $(DESTDIR)$(sysconfdir)/$$dest; then \ + echo "$@ will not overwrite existing $(DESTDIR)$(sysconfdir)/$$dest"; \ + else \ + echo " $(INSTALL_DATA) $(srcdir)/$$p $(DESTDIR)$(sysconfdir)/$$dest"; \ + $(INSTALL_DATA) $(srcdir)/$$p $(DESTDIR)$(sysconfdir)/$$dest; \ + fi; \ + done + + diff --git a/src/mangosd/Master.cpp b/src/mangosd/Master.cpp new file mode 100644 index 00000000000..eea1606256c --- /dev/null +++ b/src/mangosd/Master.cpp @@ -0,0 +1,517 @@ +/* + * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/> + * + * Copyright (C) 2008 Trinity <http://www.trinitycore.org/> + * + * This program 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 2 of the License, or + * (at your option) any later version. + * + * This program 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 this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/** \file + \ingroup Trinityd +*/ + +#include <ace/OS_NS_signal.h> + +#include "WorldSocketMgr.h" +#include "Common.h" +#include "Master.h" +#include "WorldSocket.h" +#include "WorldRunnable.h" +#include "World.h" +#include "Log.h" +#include "Timer.h" +#include "Policies/SingletonImp.h" +#include "SystemConfig.h" +#include "Config/ConfigEnv.h" +#include "Database/DatabaseEnv.h" +#include "CliRunnable.h" +#include "RASocket.h" +#include "ScriptCalls.h" +#include "Util.h" + +#include "sockets/TcpSocket.h" +#include "sockets/Utility.h" +#include "sockets/Parse.h" +#include "sockets/Socket.h" +#include "sockets/SocketHandler.h" +#include "sockets/ListenSocket.h" + +#ifdef WIN32 +#include "ServiceWin32.h" +extern int m_ServiceStatus; +#endif + +/// \todo Warning disabling not useful under VC++2005. Can somebody say on which compiler it is useful? +#pragma warning(disable:4305) + +INSTANTIATE_SINGLETON_1( Master ); + +volatile uint32 Master::m_masterLoopCounter = 0; + +class FreezeDetectorRunnable : public ZThread::Runnable +{ +public: + FreezeDetectorRunnable() { _delaytime = 0; } + uint32 m_loops, m_lastchange; + uint32 w_loops, w_lastchange; + uint32 _delaytime; + void SetDelayTime(uint32 t) { _delaytime = t; } + void run(void) + { + if(!_delaytime) + return; + sLog.outString("Starting up anti-freeze thread (%u seconds max stuck time)...",_delaytime/1000); + m_loops = 0; + w_loops = 0; + m_lastchange = 0; + w_lastchange = 0; + while(!World::IsStopped()) + { + ZThread::Thread::sleep(1000); + uint32 curtime = getMSTime(); + //DEBUG_LOG("anti-freeze: time=%u, counters=[%u; %u]",curtime,Master::m_masterLoopCounter,World::m_worldLoopCounter); + + // There is no Master anymore + // TODO: clear the rest of the code +// // normal work +// if(m_loops != Master::m_masterLoopCounter) +// { +// m_lastchange = curtime; +// m_loops = Master::m_masterLoopCounter; +// } +// // possible freeze +// else if(getMSTimeDiff(m_lastchange,curtime) > _delaytime) +// { +// sLog.outError("Main/Sockets Thread hangs, kicking out server!"); +// *((uint32 volatile*)NULL) = 0; // bang crash +// } + + // normal work + if(w_loops != World::m_worldLoopCounter) + { + w_lastchange = curtime; + w_loops = World::m_worldLoopCounter; + } + // possible freeze + else if(getMSTimeDiff(w_lastchange,curtime) > _delaytime) + { + sLog.outError("World Thread hangs, kicking out server!"); + *((uint32 volatile*)NULL) = 0; // bang crash + } + } + sLog.outString("Anti-freeze thread exiting without problems."); + } +}; + +class RARunnable : public ZThread::Runnable +{ +public: + uint32 numLoops, loopCounter; + + RARunnable () + { + uint32 socketSelecttime = sWorld.getConfig (CONFIG_SOCKET_SELECTTIME); + numLoops = (sConfig.GetIntDefault ("MaxPingTime", 30) * (MINUTE * 1000000 / socketSelecttime)); + loopCounter = 0; + } + + void + checkping () + { + // ping if need + if ((++loopCounter) == numLoops) + { + loopCounter = 0; + sLog.outDetail ("Ping MySQL to keep connection alive"); + delete WorldDatabase.Query ("SELECT 1 FROM command LIMIT 1"); + delete loginDatabase.Query ("SELECT 1 FROM realmlist LIMIT 1"); + delete CharacterDatabase.Query ("SELECT 1 FROM bugreport LIMIT 1"); + } + } + + void + run (void) + { + SocketHandler h; + + // Launch the RA listener socket + ListenSocket<RASocket> RAListenSocket (h); + bool usera = sConfig.GetBoolDefault ("Ra.Enable", false); + + if (usera) + { + port_t raport = sConfig.GetIntDefault ("Ra.Port", 3443); + std::string stringip = sConfig.GetStringDefault ("Ra.IP", "0.0.0.0"); + ipaddr_t raip; + if (!Utility::u2ip (stringip, raip)) + sLog.outError ("Trinity RA can not bind to ip %s", stringip.c_str ()); + else if (RAListenSocket.Bind (raip, raport)) + sLog.outError ("Trinity RA can not bind to port %d on %s", raport, stringip.c_str ()); + else + { + h.Add (&RAListenSocket); + + sLog.outString ("Starting Remote access listner on port %d on %s", raport, stringip.c_str ()); + } + } + + // Socket Selet time is in microseconds , not miliseconds!! + uint32 socketSelecttime = sWorld.getConfig (CONFIG_SOCKET_SELECTTIME); + + // if use ra spend time waiting for io, if not use ra ,just sleep + if (usera) + while (!World::IsStopped()) + { + h.Select (0, socketSelecttime); + checkping (); + } + else + while (!World::IsStopped()) + { + ZThread::Thread::sleep (static_cast<unsigned long> (socketSelecttime / 1000)); + checkping (); + } + } +}; + +Master::Master() +{ +} + +Master::~Master() +{ +} + +/// Main function +int Master::Run() +{ + sLog.outString( "%s (core-daemon)", _FULLVERSION ); + sLog.outString( "<Ctrl-C> to stop.\n" ); + + sLog.outTitle( " ______ __"); + sLog.outTitle( "/\\__ _\\ __ __/\\ \\__"); + sLog.outTitle( "\\/_/\\ \\/ _ __ /\\_\\ ___ /\\_\\ \\ ,_\\ __ __"); + sLog.outTitle( " \\ \\ \\/\\`'__\\/\\ \\ /' _ `\\/\\ \\ \\ \\/ /\\ \\/\\ \\"); + sLog.outTitle( " \\ \\ \\ \\ \\/ \\ \\ \\/\\ \\/\\ \\ \\ \\ \\ \\_\\ \\ \\_\\ \\"); + sLog.outTitle( " \\ \\_\\ \\_\\ \\ \\_\\ \\_\\ \\_\\ \\_\\ \\__\\\\/`____ \\"); + sLog.outTitle( " \\/_/\\/_/ \\/_/\\/_/\\/_/\\/_/\\/__/ `/___/> \\"); + sLog.outTitle( " C O R E /\\___/"); + sLog.outTitle( "http://TrinityCore.org \\/__/\n"); + + /// worldd PID file creation + std::string pidfile = sConfig.GetStringDefault("PidFile", ""); + if(!pidfile.empty()) + { + uint32 pid = CreatePIDFile(pidfile); + if( !pid ) + { + sLog.outError( "Cannot create PID file %s.\n", pidfile.c_str() ); + return 1; + } + + sLog.outString( "Daemon PID: %u\n", pid ); + } + + ///- Start the databases + if (!_StartDB()) + return 1; + + ///- Initialize the World + sWorld.SetInitialWorldSettings(); + + ///- Catch termination signals + _HookSignals(); + + ///- Launch WorldRunnable thread + ZThread::Thread t(new WorldRunnable); + t.setPriority ((ZThread::Priority )2); + + // set server online + loginDatabase.PExecute("UPDATE realmlist SET color = 0, population = 0 WHERE id = '%d'",realmID); + +#ifdef WIN32 + if (sConfig.GetBoolDefault("Console.Enable", true) && (m_ServiceStatus == -1)/* need disable console in service mode*/) +#else + if (sConfig.GetBoolDefault("Console.Enable", true)) +#endif + { + ///- Launch CliRunnable thread + ZThread::Thread td1(new CliRunnable); + } + + ZThread::Thread td2(new RARunnable); + + ///- Handle affinity for multiple processors and process priority on Windows + #ifdef WIN32 + { + HANDLE hProcess = GetCurrentProcess(); + + uint32 Aff = sConfig.GetIntDefault("UseProcessors", 0); + if(Aff > 0) + { + ULONG_PTR appAff; + ULONG_PTR sysAff; + + if(GetProcessAffinityMask(hProcess,&appAff,&sysAff)) + { + ULONG_PTR curAff = Aff & appAff; // remove non accessible processors + + if(!curAff ) + { + sLog.outError("Processors marked in UseProcessors bitmask (hex) %x not accessible for Trinityd. Accessible processors bitmask (hex): %x",Aff,appAff); + } + else + { + if(SetProcessAffinityMask(hProcess,curAff)) + sLog.outString("Using processors (bitmask, hex): %x", curAff); + else + sLog.outError("Can't set used processors (hex): %x",curAff); + } + } + sLog.outString(); + } + + bool Prio = sConfig.GetBoolDefault("ProcessPriority", false); + +// if(Prio && (m_ServiceStatus == -1)/* need set to default process priority class in service mode*/) + if(Prio) + { + if(SetPriorityClass(hProcess,HIGH_PRIORITY_CLASS)) + sLog.outString("TrinityCore process priority class set to HIGH"); + else + sLog.outError("ERROR: Can't set Trinityd process priority class."); + sLog.outString(); + } + } + #endif + + uint32 realCurrTime, realPrevTime; + realCurrTime = realPrevTime = getMSTime(); + + uint32 socketSelecttime = sWorld.getConfig(CONFIG_SOCKET_SELECTTIME); + + // maximum counter for next ping + uint32 numLoops = (sConfig.GetIntDefault( "MaxPingTime", 30 ) * (MINUTE * 1000000 / socketSelecttime)); + uint32 loopCounter = 0; + + ///- Start up freeze catcher thread + uint32 freeze_delay = sConfig.GetIntDefault("MaxCoreStuckTime", 0); + if(freeze_delay) + { + FreezeDetectorRunnable *fdr = new FreezeDetectorRunnable(); + fdr->SetDelayTime(freeze_delay*1000); + ZThread::Thread t(fdr); + t.setPriority(ZThread::High); + } + + ///- Launch the world listener socket + port_t wsport = sWorld.getConfig (CONFIG_PORT_WORLD); + std::string bind_ip = sConfig.GetStringDefault ("BindIP", "0.0.0.0"); + + if (sWorldSocketMgr->StartNetwork (wsport, bind_ip.c_str ()) == -1) + { + sLog.outError ("Failed to start network"); + World::StopNow(ERROR_EXIT_CODE); + // go down and shutdown the server + } + + sWorldSocketMgr->Wait (); + + // set server offline + loginDatabase.PExecute("UPDATE realmlist SET color = 2 WHERE id = '%d'",realmID); + + ///- Remove signal handling before leaving + _UnhookSignals(); + + // when the main thread closes the singletons get unloaded + // since worldrunnable uses them, it will crash if unloaded after master + t.wait(); + td2.wait (); + + ///- Clean database before leaving + clearOnlineAccounts(); + + ///- Wait for delay threads to end + CharacterDatabase.HaltDelayThread(); + WorldDatabase.HaltDelayThread(); + loginDatabase.HaltDelayThread(); + + sLog.outString( "Halting process..." ); + + #ifdef WIN32 + if (sConfig.GetBoolDefault("Console.Enable", true)) + { + // this only way to terminate CLI thread exist at Win32 (alt. way exist only in Windows Vista API) + //_exit(1); + // send keyboard input to safely unblock the CLI thread + INPUT_RECORD b[5]; + HANDLE hStdIn = GetStdHandle(STD_INPUT_HANDLE); + b[0].EventType = KEY_EVENT; + b[0].Event.KeyEvent.bKeyDown = TRUE; + b[0].Event.KeyEvent.uChar.AsciiChar = 'X'; + b[0].Event.KeyEvent.wVirtualKeyCode = 'X'; + b[0].Event.KeyEvent.wRepeatCount = 1; + + b[1].EventType = KEY_EVENT; + b[1].Event.KeyEvent.bKeyDown = FALSE; + b[1].Event.KeyEvent.uChar.AsciiChar = 'X'; + b[1].Event.KeyEvent.wVirtualKeyCode = 'X'; + b[1].Event.KeyEvent.wRepeatCount = 1; + + b[2].EventType = KEY_EVENT; + b[2].Event.KeyEvent.bKeyDown = TRUE; + b[2].Event.KeyEvent.dwControlKeyState = 0; + b[2].Event.KeyEvent.uChar.AsciiChar = '\r'; + b[2].Event.KeyEvent.wVirtualKeyCode = VK_RETURN; + b[2].Event.KeyEvent.wRepeatCount = 1; + b[2].Event.KeyEvent.wVirtualScanCode = 0x1c; + + b[3].EventType = KEY_EVENT; + b[3].Event.KeyEvent.bKeyDown = FALSE; + b[3].Event.KeyEvent.dwControlKeyState = 0; + b[3].Event.KeyEvent.uChar.AsciiChar = '\r'; + b[3].Event.KeyEvent.wVirtualKeyCode = VK_RETURN; + b[3].Event.KeyEvent.wVirtualScanCode = 0x1c; + b[3].Event.KeyEvent.wRepeatCount = 1; + DWORD numb; + BOOL ret = WriteConsoleInput(hStdIn, b, 4, &numb); + } + #endif + + // for some unknown reason, unloading scripts here and not in worldrunnable + // fixes a memory leak related to detaching threads from the module + UnloadScriptingModule(); + + // Exit the process with specified return value + return World::GetExitCode(); +} + +/// Initialize connection to the databases +bool Master::_StartDB() +{ + ///- Get world database info from configuration file + std::string dbstring; + if(!sConfig.GetString("WorldDatabaseInfo", &dbstring)) + { + sLog.outError("Database not specified in configuration file"); + return false; + } + sLog.outString("World Database: %s", dbstring.c_str()); + + ///- Initialise the world database + if(!WorldDatabase.Initialize(dbstring.c_str())) + { + sLog.outError("Cannot connect to world database %s",dbstring.c_str()); + return false; + } + + if(!sConfig.GetString("CharacterDatabaseInfo", &dbstring)) + { + sLog.outError("Character Database not specified in configuration file"); + return false; + } + sLog.outString("Character Database: %s", dbstring.c_str()); + + ///- Initialise the Character database + if(!CharacterDatabase.Initialize(dbstring.c_str())) + { + sLog.outError("Cannot connect to Character database %s",dbstring.c_str()); + return false; + } + + ///- Get login database info from configuration file + if(!sConfig.GetString("LoginDatabaseInfo", &dbstring)) + { + sLog.outError("Login database not specified in configuration file"); + return false; + } + + ///- Initialise the login database + sLog.outString("Login Database: %s", dbstring.c_str() ); + if(!loginDatabase.Initialize(dbstring.c_str())) + { + sLog.outError("Cannot connect to login database %s",dbstring.c_str()); + return false; + } + + ///- Get the realm Id from the configuration file + realmID = sConfig.GetIntDefault("RealmID", 0); + if(!realmID) + { + sLog.outError("Realm ID not defined in configuration file"); + return false; + } + sLog.outString("Realm running as realm ID %d", realmID); + + ///- Clean the database before starting + clearOnlineAccounts(); + + sWorld.LoadDBVersion(); + + sLog.outString("Using %s", sWorld.GetDBVersion()); + return true; +} + +/// Clear 'online' status for all accounts with characters in this realm +void Master::clearOnlineAccounts() +{ + // Cleanup online status for characters hosted at current realm + /// \todo Only accounts with characters logged on *this* realm should have online status reset. Move the online column from 'account' to 'realmcharacters'? + loginDatabase.PExecute( + "UPDATE account SET online = 0 WHERE online > 0 " + "AND id IN (SELECT acctid FROM realmcharacters WHERE realmid = '%d')",realmID); + + + CharacterDatabase.Execute("UPDATE characters SET online = 0 WHERE online<>0"); +} + +/// Handle termination signals +void Master::_OnSignal(int s) +{ + switch (s) + { + case SIGINT: + World::StopNow(RESTART_EXIT_CODE); + break; + case SIGTERM: + #ifdef _WIN32 + case SIGBREAK: + #endif + World::StopNow(SHUTDOWN_EXIT_CODE); + break; + } + + signal(s, _OnSignal); +} + +/// Define hook '_OnSignal' for all termination signals +void Master::_HookSignals() +{ + signal(SIGINT, _OnSignal); + signal(SIGTERM, _OnSignal); + #ifdef _WIN32 + signal(SIGBREAK, _OnSignal); + #endif +} + +/// Unhook the signals before leaving +void Master::_UnhookSignals() +{ + signal(SIGINT, 0); + signal(SIGTERM, 0); + #ifdef _WIN32 + signal(SIGBREAK, 0); + #endif +} diff --git a/src/mangosd/Master.h b/src/mangosd/Master.h new file mode 100644 index 00000000000..2485dd456b1 --- /dev/null +++ b/src/mangosd/Master.h @@ -0,0 +1,52 @@ +/* + * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/> + * + * Copyright (C) 2008 Trinity <http://www.trinitycore.org/> + * + * This program 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 2 of the License, or + * (at your option) any later version. + * + * This program 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 this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/// \addtogroup Trinityd +/// @{ +/// \file + +#ifndef _MASTER_H +#define _MASTER_H + +#include "Common.h" +#include "Policies/Singleton.h" + +/// Start the server +class Master +{ + public: + Master(); + ~Master(); + int Run(); + static volatile uint32 m_masterLoopCounter; + + private: + bool _StartDB(); + + void _HookSignals(); + void _UnhookSignals(); + static void _OnSignal(int s); + + void clearOnlineAccounts(); +}; + +#define sMaster Trinity::Singleton<Master>::Instance() +#endif +/// @} diff --git a/src/mangosd/RASocket.cpp b/src/mangosd/RASocket.cpp new file mode 100644 index 00000000000..f953dc3f592 --- /dev/null +++ b/src/mangosd/RASocket.cpp @@ -0,0 +1,259 @@ +/* + * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/> + * + * Copyright (C) 2008 Trinity <http://www.trinitycore.org/> + * + * This program 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 2 of the License, or + * (at your option) any later version. + * + * This program 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 this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/** \file + \ingroup Trinityd +*/ + +#include "Common.h" +#include "Database/DatabaseEnv.h" +#include "Log.h" +#include "RASocket.h" +#include "World.h" +#include "Config/ConfigEnv.h" +#include "Util.h" +#include "AccountMgr.h" + +/// \todo Make this thread safe if in the future 2 admins should be able to log at the same time. +SOCKET r; + +#define dropclient {Sendf("I'm busy right now, come back later."); \ + SetCloseAndDelete(); \ + return; \ + } + +uint32 iSession=0; ///< Session number (incremented each time a new connection is made) +unsigned int iUsers=0; ///< Number of active administrators + +typedef int(* pPrintf)(const char*,...); + +void ParseCommand(CliCommandHolder::Print*, char*command); + +/// RASocket constructor +RASocket::RASocket(ISocketHandler &h): TcpSocket(h) +{ + + ///- Increment the session number + iSess =iSession++ ; + + ///- Get the config parameters + bSecure = sConfig.GetBoolDefault( "RA.Secure", true ); + iMinLevel = sConfig.GetIntDefault( "RA.MinLevel", 3 ); + + ///- Initialize buffer and data + iInputLength=0; + buff=new char[RA_BUFF_SIZE]; + stage=NONE; +} + +/// RASocket destructor +RASocket::~RASocket() +{ + ///- Delete buffer and decrease active admins count + delete [] buff; + + sLog.outRALog("Connection was closed.\n"); + + if(stage==OK) + iUsers--; +} + +/// Accept an incoming connection +void RASocket::OnAccept() +{ + std::string ss=GetRemoteAddress(); + sLog.outRALog("Incoming connection from %s.\n",ss.c_str()); + ///- If there is already an active admin, drop the connection + if(iUsers) + dropclient + + ///- Else print Motd + Sendf("%s\r\n",sWorld.GetMotd()); +} + +/// Read data from the network +void RASocket::OnRead() +{ + ///- Read data and check input length + TcpSocket::OnRead(); + + unsigned int sz=ibuf.GetLength(); + if(iInputLength+sz>=RA_BUFF_SIZE) + { + sLog.outRALog("Input buffer overflow, possible DOS attack.\n"); + SetCloseAndDelete(); + return; + } + + ///- If there is already an active admin (other than you), drop the connection + if(stage!=OK && iUsers) + dropclient + + char *inp = new char [sz+1]; + ibuf.Read(inp,sz); + + /// \todo Can somebody explain this 'Linux bugfix'? + if(stage==NONE) + if(sz>4) //linux remote telnet + if(memcmp(inp ,"USER ",5)) + { + delete [] inp;return; + printf("lin bugfix"); + } //linux bugfix + + ///- Discard data after line break or line feed + bool gotenter=false; + unsigned int y=0; + for(;y<sz;y++) + if(inp[y]=='\r'||inp[y]=='\n') + { + gotenter=true; + break; + } + + //No buffer overflow (checked above) + memcpy(&buff[iInputLength],inp,y); + iInputLength+=y; + delete [] inp; + if(gotenter) + { + + buff[iInputLength]=0; + iInputLength=0; + switch(stage) + { + /// <ul> <li> If the input is 'USER <username>' + case NONE: + if(!memcmp(buff,"USER ",5)) //got "USER" cmd + { + szLogin=&buff[5]; + + ///- Get the gmlevel and password from the account table + std::string login = szLogin; + + ///- Convert Account name to Upper Format + AccountMgr::normilizeString(login); + + ///- Escape the Login to allow quotes in names + loginDatabase.escape_string(login); + + QueryResult* result = loginDatabase.PQuery("SELECT gmlevel FROM account WHERE username = '%s'",login.c_str()); + + ///- If the user is not found, deny access + if(!result) + { + Sendf("-No such user.\r\n"); + sLog.outRALog("User %s does not exist.\n",szLogin.c_str()); + if(bSecure)SetCloseAndDelete(); + } + else + { + Field *fields = result->Fetch(); + + //szPass=fields[0].GetString(); + + ///- if gmlevel is too low, deny access + if(fields[0].GetUInt32()<iMinLevel) + { + Sendf("-Not enough privileges.\r\n"); + sLog.outRALog("User %s has no privilege.\n",szLogin.c_str()); + if(bSecure)SetCloseAndDelete(); + } else + { + stage=LG; + } + delete result; + } + } + break; + ///<li> If the input is 'PASS <password>' (and the user already gave his username) + case LG: + if(!memcmp(buff,"PASS ",5)) //got "PASS" cmd + { //login+pass ok + ///- If password is correct, increment the number of active administrators + std::string login = szLogin; + std::string pw = &buff[5]; + + AccountMgr::normilizeString(login); + AccountMgr::normilizeString(pw); + loginDatabase.escape_string(login); + loginDatabase.escape_string(pw); + + QueryResult *check = loginDatabase.PQuery( + "SELECT 1 FROM account WHERE username = '%s' AND sha_pass_hash=SHA1(CONCAT(username,':','%s'))", + login.c_str(), pw.c_str()); + + if(check) + { + delete check; + r=GetSocket(); + stage=OK; + ++iUsers; + + Sendf("+Logged in.\r\n"); + sLog.outRALog("User %s has logged in.\n",szLogin.c_str()); + Sendf("TC>"); + } + else + { + ///- Else deny access + Sendf("-Wrong pass.\r\n"); + sLog.outRALog("User %s has failed to log in.\n",szLogin.c_str()); + if(bSecure)SetCloseAndDelete(); + } + } + break; + ///<li> If user is logged, parse and execute the command + case OK: + if(strlen(buff)) + { + sLog.outRALog("Got '%s' cmd.\n",buff); + sWorld.QueueCliCommand(&RASocket::zprint , buff); + } + else + Sendf("TC>"); + break; + ///</ul> + }; + + } +} + +/// Output function +void RASocket::zprint( const char * szText ) +{ + if( !szText ) + return; + + #ifdef RA_CRYPT + + char *megabuffer=strdup(szText); + unsigned int sz=strlen(megabuffer); + Encrypt(megabuffer,sz); + send(r,megabuffer,sz,0); + delete [] megabuffer; + + #else + + unsigned int sz=strlen(szText); + send(r,szText,sz,0); + + #endif +} diff --git a/src/mangosd/RASocket.h b/src/mangosd/RASocket.h new file mode 100644 index 00000000000..a164c9d3aa2 --- /dev/null +++ b/src/mangosd/RASocket.h @@ -0,0 +1,67 @@ +/* + * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/> + * + * Copyright (C) 2008 Trinity <http://www.trinitycore.org/> + * + * This program 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 2 of the License, or + * (at your option) any later version. + * + * This program 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 this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/// \addtogroup Trinityd +/// @{ +/// \file + +#ifndef _RASOCKET_H +#define _RASOCKET_H + +#include "Common.h" +#include "sockets/TcpSocket.h" + +#define RA_BUFF_SIZE 1024 + +class ISocketHandler; + +/// Remote Administration socket +class RASocket: public TcpSocket +{ + public: + + RASocket(ISocketHandler& h); + ~RASocket(); + + void OnAccept(); + void OnRead(); + + private: + + char * buff; + std::string szLogin; + uint32 iSess; + unsigned int iInputLength; + bool bLog; + bool bSecure; //kick on wrong pass, non exist. user, user with no priv + //will protect from DOS, bruteforce attacks + //some 'smart' protection must be added for more security + uint8 iMinLevel; + enum + { + NONE, //initial value + LG, //only login was entered + OK, //both login and pass were given, and they are correct and user have enough priv. + }stage; + + static void zprint( const char * szText ); +}; +#endif +/// @} diff --git a/src/mangosd/TrinityCore.ico b/src/mangosd/TrinityCore.ico Binary files differnew file mode 100644 index 00000000000..6f0a5721957 --- /dev/null +++ b/src/mangosd/TrinityCore.ico diff --git a/src/mangosd/WorldRunnable.cpp b/src/mangosd/WorldRunnable.cpp new file mode 100644 index 00000000000..b57dbc6bce2 --- /dev/null +++ b/src/mangosd/WorldRunnable.cpp @@ -0,0 +1,86 @@ +/* + * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/> + * + * Copyright (C) 2008 Trinity <http://www.trinitycore.org/> + * + * This program 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 2 of the License, or + * (at your option) any later version. + * + * This program 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 this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/** \file + \ingroup Trinityd +*/ + +#include "WorldSocketMgr.h" +#include "Common.h" +#include "World.h" +#include "WorldRunnable.h" +#include "Timer.h" +#include "ObjectAccessor.h" +#include "MapManager.h" + +#include "Database/DatabaseEnv.h" + +#ifdef WIN32 +#define WORLD_SLEEP_CONST 50 +#else +#define WORLD_SLEEP_CONST 100 //Is this still needed?? [On linux some time ago not working 50ms] +#endif + +/// Heartbeat for the World +void WorldRunnable::run() +{ + ///- Init new SQL thread for the world database + WorldDatabase.ThreadStart(); // let thread do safe mySQL requests (one connection call enough) + sWorld.InitResultQueue(); + + uint32 realCurrTime = 0; + uint32 realPrevTime = getMSTime(); + + uint32 prevSleepTime = 0; // used for balanced full tick time length near WORLD_SLEEP_CONST + + ///- While we have not World::m_stopEvent, update the world + while (!World::IsStopped()) + { + ++World::m_worldLoopCounter; + realCurrTime = getMSTime(); + + uint32 diff = getMSTimeDiff(realPrevTime,realCurrTime); + + sWorld.Update( diff ); + realPrevTime = realCurrTime; + + // diff (D0) include time of previous sleep (d0) + tick time (t0) + // we want that next d1 + t1 == WORLD_SLEEP_CONST + // we can't know next t1 and then can use (t0 + d1) == WORLD_SLEEP_CONST requirement + // d1 = WORLD_SLEEP_CONST - t0 = WORLD_SLEEP_CONST - (D0 - d0) = WORLD_SLEEP_CONST + d0 - D0 + if (diff <= WORLD_SLEEP_CONST+prevSleepTime) + { + prevSleepTime = WORLD_SLEEP_CONST+prevSleepTime-diff; + ZThread::Thread::sleep(prevSleepTime); + } + else + prevSleepTime = 0; + } + + sWorld.KickAll(); // save and kick all players + sWorld.UpdateSessions( 1 ); // real players unload required UpdateSessions call + + sWorldSocketMgr->StopNetwork(); + + MapManager::Instance().UnloadAll(); // unload all grids (including locked in memory) + + ///- End the database thread + WorldDatabase.ThreadEnd(); // free mySQL thread resources +} diff --git a/src/mangosd/WorldRunnable.h b/src/mangosd/WorldRunnable.h new file mode 100644 index 00000000000..8891186dec4 --- /dev/null +++ b/src/mangosd/WorldRunnable.h @@ -0,0 +1,35 @@ +/* + * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/> + * + * Copyright (C) 2008 Trinity <http://www.trinitycore.org/> + * + * This program 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 2 of the License, or + * (at your option) any later version. + * + * This program 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 this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/// \addtogroup Trinityd +/// @{ +/// \file + +#ifndef __WORLDRUNNABLE_H +#define __WORLDRUNNABLE_H + +/// Heartbeat thread for the World +class WorldRunnable : public ZThread::Runnable +{ + public: + void run(); +}; +#endif +/// @} diff --git a/src/mangosd/mangosd.conf.dist.in b/src/mangosd/mangosd.conf.dist.in new file mode 100644 index 00000000000..e7ef2ac7672 --- /dev/null +++ b/src/mangosd/mangosd.conf.dist.in @@ -0,0 +1,1294 @@ +########################################## +# Trinity Core worldd configuration file # +########################################## +ConfVersion=2008080101 + +################################################################################################################### +# CONNECTIONS AND DIRECTORIES +# +# RealmID +# RealmID must match the realmlist inside the realmd database +# +# DataDir +# Data directory setting. +# Important: DataDir needs to be quoted, as it is a string which may contain space characters. +# Example: "@prefix@/share/trinitycore" +# +# LogsDir +# Logs directory setting. +# Important: Logs dir must exists, or all logs need to be disabled +# Default: "" - no log directory prefix, if used log names isn't absolute path +# then logs will be stored in current directory for run program. +# +# +# LoginDatabaseInfo +# WorldDatabaseInfo +# CharacterDatabaseInfo +# Database connection settings for the world server. +# Default: hostname;port;username;password;database +# .;somenumber;username;password;database - use named pipes at Windows +# Named pipes: mySQL required adding "enable-named-pipe" to [mysqld] section my.ini +# .;/path/to/unix_socket;username;password;database - use Unix sockets at Unix/Linux +# Unix sockets: experimental, not tested +# +# MaxPingTime +# Settings for maximum database-ping interval (minutes between pings) +# +# WorldServerPort +# Default WorldServerPort +# +# BindIP +# Bind World Server to IP/hostname +# +################################################################################################################### + +RealmID = 1 +DataDir = "." +LogsDir = "" +LoginDatabaseInfo = "127.0.0.1;3306;trinity;trinity;realmd" +WorldDatabaseInfo = "127.0.0.1;3306;trinity;trinity;world" +CharacterDatabaseInfo = "127.0.0.1;3306;trinity;trinity;characters" +MaxPingTime = 30 +WorldServerPort = 8085 +BindIP = "0.0.0.0" + +################################################################################################################### +# SCRIPTING SETTINGS +# +# Locale +# Setting for current (DBC) locale to use +# +# EventAI Error reporting +# 0 - Only startup (Default) +# 1 - Startup errors and Runtime event errors +# 2 - Startup errors, Runtime event errors, and Creation errors +################################################################################################################### + +Locale = 0 +EAIErrorLevel = 2 + +################################################################################################################### +# PERFORMANCE SETINGS +# +# UseProcessors +# Used processors mask for multi-processors system (Used only at Windows) +# Default: 0 (selected by OS) +# number (bitmask value of selected processors) +# +# ProcessPriority +# Process priority setting (Used only at Windows) +# Default: 1 (HIGH) +# 0 (Normal) +# +# Compression +# Compression level for update packages sent to client (1..9) +# Default: 1 (speed) +# 9 (best compression) +# +# PlayerLimit +# Maximum number of players in the world. Excluding Mods, GM's and Admins +# Default: 100 +# 0 (for infinite players) +# -1 (for Mods, GM's and Admins only) +# -2 (for GM's and Admins only) +# -3 (for Admins only) +# +# SaveRespawnTimeImmediately +# Save respawn time for creatures at death and for gameobjects at use/open +# Default: 1 (save creature/gameobject respawn time without waiting grid unload) +# 0 (save creature/gameobject respawn time at grid unload) +# +# MaxOverspeedPings +# Maximum overspeed ping count before player kick (minimum is 2, 0 used for disable check) +# Default: 2 +# +# GridUnload +# Unload grids (if you have lot memory you can disable it to speed up player move to new grids second time) +# Default: 1 (unload grids) +# 0 (do not unload grids) +# +# SocketSelectTime +# Socket select time (in milliseconds) +# Default: 10000 +# +# GridCleanUpDelay +# Grid clean up delay (in milliseconds) +# Default: 300000 (5 min) +# +# MapUpdateInterval +# Map update interval (in milliseconds) +# Default: 100 +# +# ChangeWeatherInterval +# Weather update interval (in milliseconds) +# Default: 600000 (10 min) +# +# PlayerSaveInterval +# Player save interval (in milliseconds) +# Default: 900000 (15 min) +# +# vmap.enableLOS +# vmap.enableHeight +# Enable/Disable VMmap support for line of sight and height calculation +# Default: 1 (true) +# 0 (false) +# +# vmap.ignoreMapIds +# Map id that will be ignored by VMaps +# List of ids with delimiter ',' +# If more then one id is defined and spaces are included, the string has to be enclosed by " +# Example: "369,0,1,530" +# +# vmap.ignoreSpellIds +# These spells are ignored for LoS calculation +# List of ids with delimiter ',' +# +# DetectPosCollision +# Check final move position, summon position, etc for visible collision with other objects or +# wall (wall only if vmaps are enabled) +# Default: 1 (enable, required more CPU power usage) +# 0 (disable, less nice position selection but will less CPU power usage) +# +# TargetPosRecalculateRange +# Max distance from movement target point (+moving unit size) and targeted object (+size) +# after that new target movmeent point calculated. Max: melee attack range (5), min: contact range (0.5) +# More distance let have better performence, less distance let have more sensitive reaction at target move. +# Default: 1.5 +# +# UpdateUptimeInterval +# Update realm uptime period in minutes (for save data in 'uptime' table). Must be > 0 +# Default: 10 (minutes) +# +# MaxCoreStuckTime +# Periodically check if the process got freezed, if this is the case force crash after the specified +# amount of seconds. Must be > 0. Recommended > 10 secs if you use this. +# Default: 0 (Disabled) +# +# AddonChannel +# Permit/disable the use of the addon channel through the server +# (some client side addons can stop work correctly with disabled addon channel) +# Default: 1 (permit addon channel) +# 0 (do not permit addon channel) +# +################################################################################################################### + +UseProcessors = 0 +ProcessPriority = 1 +Compression = 1 +PlayerLimit = 100 +SaveRespawnTimeImmediately = 1 +MaxOverspeedPings = 2 +GridUnload = 1 +SocketSelectTime = 10000 +GridCleanUpDelay = 300000 +MapUpdateInterval = 100 +ChangeWeatherInterval = 600000 +PlayerSaveInterval = 900000 +vmap.enableLOS = 0 +vmap.enableHeight = 0 +vmap.ignoreMapIds = "369" +vmap.ignoreSpellIds = "7720" +DetectPosCollision = 1 +TargetPosRecalculateRange = 1.5 +UpdateUptimeInterval = 10 +MaxCoreStuckTime = 0 +AddonChannel = 1 + +################################################################################################################### +# SERVER LOGGING +# +# LogSQL +# Enable logging of GM commands - all SQL code will be written to a log file +# All commands are written to a file: YYYY-MM-DD_logSQL.sql +# If a new day starts (00:00:00) then a new file is created - the old file will not be deleted. +# Default: 1 - Write SQL code to logfile +# 0 - Do not log +# +# PidFile +# World daemon PID file +# Default: "" - do not create PID file +# "./worldd.pid" - create PID file (recommended name) +# +# LogLevel +# Server console level of logging +# 0 = Minimum; 1 = Basic&Error; 2 = Detail; 3 = Full/Debug +# Default: 3 +# +# LogTime +# Include time in server console output [hh:mm:ss] +# Default: 0 (no time) +# 1 (print time) +# +# LogFile +# Logfile name +# Default: "Server.log" +# "" - Empty name disable creating log file +# +# LogTimestamp +# Logfile with timestamp of server start in name +# Default: 0 - no timestamp in name +# 1 - add timestamp in name in form Logname_YYYY-MM-DD_HH-MM-SS.Ext for Logname.Ext +# +# LogFileLevel +# Server file level of logging +# 0 = Minimum; 1 = Error; 2 = Detail; 3 = Full/Debug +# Default: 0 +# +# LogFilter_TransportMoves +# LogFilter_CreatureMoves +# LogFilter_VisibilityChanges +# Log filters +# Default: 1 - not include with any log level +# 0 - include in log if log level permit +# +# WorldLogFile +# Packet logging file for the worldserver +# Default: "world.log" +# +# DBErrorLogFile +# Log file of DB errors detected at server run +# Default: "DBErrors.log" +# +# CharLogFile +# Character operations logfile name +# Default: "Char.log" +# "" - Empty name disable creating log file +# +# CharLogTimestamp +# Logfile with timestamp of server start in name +# Default: 0 - no timestamp in name +# 1 - add timestamp in name in form Logname_YYYY-MM-DD_HH-MM-SS.Ext for Logname.Ext +# +# CharLogDump +# Write character dump before deleting in Char.log +# For restoration, cut character data from log starting from +# line == START DUMP == to line == END DUMP == (without its) in file and load it using loadpdump command +# Default: 0 - don't include dumping chars to log +# 1 - include dumping chars to log +# +# GmLogFile +# Log file of gm commands +# Default: "gm_commands.log" +# "" - Empty name for disable +# +# GmLogTimestamp +# GM Logfile with timestamp of server start in name +# Default: 0 - no timestamp in name +# 1 - add timestamp in name in form Logname_YYYY-MM-DD_HH-MM-SS.Ext for Logname.Ext +# +# GmLogPerAccount +# GM Logfiles with GM account id (Note: logs not created if GmLogFile not set) +# Default: 0 - add gm log data to single log file +# 1 - add gm log data to account specific log files with name +# in form Logname_#ID_YYYY-MM-DD_HH-MM-SS.Ext +# or form Logname_#ID.Ext +# +# RaLogFile +# Log file of RA commands +# Default: "Ra.log" +# "" - Empty name for disable +# +# LogColors +# Color for messages (format "normal_color details_color debug_color error_color") +# Colors: 0 - BLACK, 1 - RED, 2 - GREEN, 3 - BROWN, 4 - BLUE, 5 - MAGENTA, 6 - CYAN, 7 - GREY, +# 8 - YELLOW, 9 - LRED, 10 - LGREEN, 11 - LBLUE, 12 - LMAGENTA, 13 - LCYAN, 14 - WHITE +# Default: "" - none colors +# Example: "13 7 11 9" +# +################################################################################################################### + +LogSQL = 1 +PidFile = "" +LogLevel = 1 +LogTime = 0 +LogFile = "Server.log" +LogTimestamp = 0 +LogFileLevel = 0 +LogFilter_TransportMoves = 1 +LogFilter_CreatureMoves = 1 +LogFilter_VisibilityChanges = 1 +WorldLogFile = "" +DBErrorLogFile = "db_errors.log" +CharLogFile = "characters.log" +CharLogTimestamp = 0 +CharLogDump = 0 +GmLogFile = "gm_commands.log" +GmLogTimestamp = 0 +GmLogPerAccount = 0 +RaLogFile = "ra_commands.log" +LogColors = "" + +################################################################################################################### +# SERVER SETTINGS +# +# GameType +# Server realm style +# 0 = NORMAL;1 = PVP; 4 = NORMAL; 6 = RP; 8 = RPPVP +# also custom type: 16 FFA_PVP (free for all pvp mode like arena PvP in all zones except rest +# activated places and sanctuaries) +# +# RealmZone +# Server realm zone (set allowed alphabet in character names/etc). See also Strict*Names options. +# +# 1 Development - any language (Default) +# 2 United States - extended-Latin +# 3 Oceanic - extended-Latin +# 4 Latin America - extended-Latin +# 5 Tournament - basic-Latin at create, any at login +# 6 Korea - East-Asian +# 7 Tournament - basic-Latin at create, any at login +# 8 English - extended-Latin +# 9 German - extended-Latin +# 10 French - extended-Latin +# 11 Spanish - extended-Latin +# 12 Russian - Cyrillic +# 13 Tournament - basic-Latin at create, any at login +# 14 Taiwan - East-Asian +# 15 Tournament - basic-Latin at create, any at login +# 16 China - East-Asian +# 17 CN1 - basic-Latin at create, any at login +# 18 CN2 - basic-Latin at create, any at login +# 19 CN3 - basic-Latin at create, any at login +# 20 CN4 - basic-Latin at create, any at login +# 21 CN5 - basic-Latin at create, any at login +# 22 CN6 - basic-Latin at create, any at login +# 23 CN7 - basic-Latin at create, any at login +# 24 CN8 - basic-Latin at create, any at login +# 25 Tournament - basic-Latin at create, any at login +# 26 Test Server - any language +# 27 Tournament - basic-Latin at create, any at login +# 28 QA Server - any language +# 29 CN9 - basic-Latin at create, any at login +# +# Expansion +# Allow server use content from expansion +# 2 - check expansion 2 maps existence, and if client support expansion 2 and account have +# expansion 2 setting then allow visit expansion 2 maps, allow create new class character) +# Default: 1 - check expansion 1 maps existence, and if client support expansion 1 and account have +# expansion 1 setting then allow visit expansion 1 maps, allow create new races character) +# 0 - not check expansion maps existence, not allow wisit its, not allow create new race or new class +# characters, ignore account expansion setting) +# +# DBC.Locale +# DBC Language Settings +# 0 = English; 1 = Korean; 2 = French; 3 = German; 4 = Chinese; 5 = Taiwanese; 6 = Spanish; 7 = Spanish Mexico +# 8 = Russian; 255 = Auto Detect (Default) +# +# DeclinedNames +# Allow russian clients to set and use declined names +# Default: 0 - do not use declined names, except when the Russian RealmZone is set +# 1 - use declined names +# +# StrictPlayerNames +# Limit player name to language specific symbols set, not allow create characters, and set rename request and disconnect at not allowed symbols name +# Default: 0 disable (but limited server timezone dependent client check) +# 1 basic latin characters (strict) +# 2 realm zone specific (strict). See RealmZone setting. +# Note: In any case if you want correctly see character name at client this client must have apporopriate fonts +# (included in client by default, with active official localization or custom localization fonts in clientdir/Fonts). +# 3 basic latin characters + server timezone specific +# +# StrictCharterNames +# Limit guild/arena team charter names to language specific symbols set, not allow create charters with allowed symbols in name +# Default: 0 disable +# 1 basic latin characters (strict) +# 2 realm zone specific (strict). See RealmZone setting. +# Note: In any case if you want correctly see character name at client this client must have apporopriate fonts +# (included in client by default, with active official localization or custom localization fonts in clientdir/Fonts). +# 3 basic latin characters + server timezone specific +# +# StrictPetNames +# Limit pet names to language specific symbols set +# Default: 0 disable +# 1 basic latin characters (strict) +# 2 realm zone specific (strict). See RealmZone setting. +# Note: In any case if you want correctly see character name at client this client must have apporopriate fonts +# (included in client by default, with active official localization or custom localization fonts in clientdir/Fonts). +# 3 basic latin characters + server timezone specific +# +# CharactersCreatingDisabled +# Disable characters creating for specific team or any (non-player accounts not affected) +# Default: 0 - enabled +# 1 - disabled only for Alliance +# 2 - disabled only for Horde +# 3 - disabled for both teams +# +# MaxWhoListReturns +# Set the maximum number of players returned in the /who list and interface. +# Default: 49 (stable) +# +# CharactersPerAccount +# Limit numbers of characters per account (at all realms). +# Note: this setting limit character creating at _current_ realm base at characters amount at all realms +# Default: 50 +# The number must be >= CharactersPerRealm +# +# CharactersPerRealm +# Limit numbers of characters for account at realm +# Default: 10 (client limitation) +# The number must be between 1 and 10 +# +# HeroicCharactersPerRealm +# Limit numbers of heroic class characters for account at realm +# Default: 1 +# The number must be between 0 (not allowed) and 10 +# +# MinLevelForHeroicCharacterCreating +# Limit creating heroic characters only for account with another character of specific level (ignored for GM accounts) +# 0 - not require any existed chaarcter +# 1 - require at least any character existed +# Default: 55 - default requirement +# +# +# SkipCinematics +# Disable in-game script movie at first character's login(allows to prevent buggy intro in case of custom start location coordinates) +# Default: 0 - show intro for each new characrer +# 1 - show intro only for first character of selected race +# 2 - disable intro show in all cases +# +# MaxPlayerLevel +# Max level that can be reached by player for experience (in range from 1 to 100). +# Change not recommended +# Default: 80 +# +# StartPlayerLevel +# Staring level that have character at creating (in range 1 to MaxPlayerLevel) +# Default: 1 +# +# StartHeroicPlayerLevel +# Staring level that have character of heroic class at creating (in range 1 to MaxPlayerLevel) +# Default: 55 +# +# StartPlayerMoney +# Amount of money that new players will start with. +# If you want to start with silver, use for example 100 (100 copper = 1 silver) +# Default: 0 +# +# MaxHonorPoints +# Max honor points that player can have. +# Default: 75000 +# +# StartHonorPoints +# Amount of honor that new players will start with +# Default: 0 +# +# MaxArenaPoints +# Max arena points that player can have. +# Default: 5000 +# +# StartArenaPoints +# Amount of arena points that new players will start with +# Default: 0 +# +# InstantLogout +# Enable or disable instant logout for security level (0..4) or high (NOT in combat/while dueling/while falling) +# Default: 1 (Mods/GMs/Admins) +# +# DisableWaterBreath +# Disable/enable waterbreathing for security level (0..4) or high +# Default: 4 (None) +# +# AllFlightPaths +# Players will start with all flight paths (Note: ALL flight paths, not only player's team) +# Default: 0 (true) +# 1 (false) +# +# AlwaysMaxSkillForLevel +# Players will automatically gain max level dependent (weapon/defense) skill when logging in, leveling up etc. +# Default: 0 (true) +# 1 (false) +# +# ActivateWeather +# Activate weather system +# Default: 1 (true) +# 0 (false) +# +# Battleground.CastDeserter +# Cast or not Deserter spell at player who leave battleground in progress +# Default: 1 (true) +# 0 (false) +# +# Battleground.QueueAnnouncer.Enable +# Enable queue announcer posting to chat +# Default: 1 (true) +# 0 (false) +# +# Battleground.QueueAnnouncer.PlayerOnly +# Enable queue announcer posting to chat +# Default: 0 (false) +# 1 (true) +# +# CastUnstuck +# Allow cast or not Unstuck spell at .start or client Help option use +# Default: 1 (true) +# 0 (false) +# +# Instance.IgnoreLevel +# Ignore level requirement to enter instance +# Default: 0 (false) +# 1 (true) +# +# Instance.IgnoreRaid +# Ignore raid requirement to enter instance +# Default: 0 (false) +# 1 (true) +# +# Instance.ResetTimeHour +# The hour of the day (0-23) when the global instance resets occur. +# Default: 4 +# +# Instance.UnloadDelay +# Unload the instance map from memory after some time if no players are inside. +# Default: 1800000 (miliseconds, i.e 30 minutes) +# 0 (instance maps are kept in memory until they are reset) +# +# Quests.LowLevelHideDiff +# Quest level difference to hide for player low level quests: +# if player_level > quest_level + LowLevelQuestsHideDiff then quest "!" mark not show for quest giver +# Default: 4 +# -1 (show all available quests marks) +# +# Quests.HighLevelHideDiff +# Quest level difference to hide for player high level quests: +# if player_level < quest_min_level - HighLevelQuestsHideDiff then quest "!" mark not show for quest giver +# Default: 7 +# -1 (show all available quests marks) +# +# MaxPrimaryTradeSkill +# Max count that player can learn the primary trade skill. +# Default: 2 +# Max : 10 +# +# MinPetitionSigns +# Min signatures count to creating guild (0..9). +# Default: 9 +# +# MaxGroupXPDistance +# Max distance to creature for group memeber to get XP at creature death. +# Default: 74 +# +# MailDeliveryDelay +# Mail delivery delay time for item sending +# Default: 3600 sec (1 hour) +# +# SkillChance.Prospecting +# For prospecting skillup impossible by default, but can be allowed as custom setting +# Default: 0 - no skilups +# 1 - skilups possible +# +# SkillChance.Milling +# For milling skillup impossible by default, but can be allowed as custom setting +# Default: 0 - no skilups +# 1 - skilups possible +# +# Event.Announce +# Default: 0 (false) +# 1 (true) +# +# BeepAtStart +# Beep at core start finished (mostly work only at Unix/Linux systems) +# Default: 1 (true) +# 0 (false) +# +# Motd +# Message of the Day. Displayed at worldlogin for every user ('@' for a newline). +# +################################################################################################################### + +GameType = 1 +RealmZone = 1 +Expansion = 2 +DBC.Locale = 255 +DeclinedNames = 0 +StrictPlayerNames = 0 +StrictCharterNames = 0 +StrictPetNames = 0 +MaxWhoListReturns = 49 +CharactersCreatingDisabled = 0 +CharactersPerAccount = 50 +CharactersPerRealm = 10 +HeroicCharactersPerRealm = 1 +MinLevelForHeroicCharacterCreating = 55 +SkipCinematics = 0 +MaxPlayerLevel = 80 +StartPlayerLevel = 1 +StartHeroicPlayerLevel = 55 +StartPlayerMoney = 0 +MaxHonorPoints = 75000 +StartHonorPoints = 0 +MaxArenaPoints = 5000 +StartArenaPoints = 0 +InstantLogout = 1 +DisableWaterBreath = 4 +AllFlightPaths = 0 +AlwaysMaxSkillForLevel = 0 +ActivateWeather = 1 +Battleground.CastDeserter = 1 +Battleground.QueueAnnouncer.Enable = 1 +Battleground.QueueAnnouncer.PlayerOnly = 0 +CastUnstuck = 1 +Instance.IgnoreLevel = 0 +Instance.IgnoreRaid = 0 +Instance.ResetTimeHour = 4 +Instance.UnloadDelay = 1800000 +Quests.LowLevelHideDiff = 4 +Quests.HighLevelHideDiff = 7 +MaxPrimaryTradeSkill = 2 +MinPetitionSigns = 9 +MaxGroupXPDistance = 74 +MailDeliveryDelay = 3600 +SkillChance.Prospecting = 0 +SkillChance.Milling = 0 +Event.Announce = 0 +BeepAtStart = 1 +Motd = "Welcome to a Trinity Core server." + +################################################################################################################### +# PLAYER INTERACTION +# +# AllowTwoSide.Accounts +# Allow or not accounts to create characters in the 2 teams in any game type. +# Default: 0 (Not allowed) +# 1 (Allowed) +# +# AllowTwoSide.Interaction.Chat +# AllowTwoSide.Interaction.Channel +# AllowTwoSide.Interaction.Group +# AllowTwoSide.Interaction.Guild +# AllowTwoSide.Interaction.Auction +# AllowTwoSide.Interaction.Mail +# Allow or not common :chat(say,yell);channel(chat)group(join)guild(join);merge all auction houses for players from +# different teams, send mail to different team. +# Default: 0 (Not allowed) +# 1 (Allowed) +# +# AllowTwoSide.WhoList +# Allow or not show player from both team in who list. +# Default: 0 (Not allowed) +# 1 (Allowed) +# +# AllowTwoSide.AddFriend +# Allow or not adding friends from other team in friend list. +# Default: 0 (Not allowed) +# 1 (Allowed) +# +# TalentsInspecting +# Allow other players see character talents in inspect dialog (Characters in Gamemaster mode can +# inspect talents always) +# Default: 1 (allow) +# 0 (not allow) +# +################################################################################################################### + +AllowTwoSide.Accounts = 0 +AllowTwoSide.Interaction.Chat = 0 +AllowTwoSide.Interaction.Channel = 0 +AllowTwoSide.Interaction.Group = 0 +AllowTwoSide.Interaction.Guild = 0 +AllowTwoSide.Interaction.Auction = 0 +AllowTwoSide.Interaction.Mail = 0 +AllowTwoSide.WhoList = 0 +AllowTwoSide.AddFriend = 0 +TalentsInspecting = 1 + +################################################################################################################### +# CREATURE SETTINGS +# +# ThreatRadius +# Radius for creature to evade after being pulled away from combat start point +# If ThreatRadius is less than creature aggro radius then aggro radius will be used +# Default: 100 yards +# +# Rate.Creature.Aggro +# Aggro radius percent or off. +# Default: 1 - 100% +# 1.5 - 150% +# 0 - off (0%) +# +# CreatureFamilyAssistanceRadius +# Creature family assistance radius +# Default: 10 +# 0 - off +# +# CreatureFamilyAssistanceDelay +# Reaction time for creature assistance call +# Default: 1500 (1.5s) +# +# WorldBossLevelDiff +# Difference for boss dynamic level with target +# Default: 3 +# +# Corpse.Decay.NORMAL +# Corpse.Decay.RARE +# Corpse.Decay.ELITE +# Corpse.Decay.RAREELITE +# Corpse.Decay.WORLDBOSS +# Seconds until creature corpse will decay without being looted or skinned. +# Default: 60, 300, 300, 300, 3600 +# +# Rate.Corpse.Decay.Looted +# Controls how long the creature corpse stays after it had been looted, as a multiplier of its Corpse.Decay.* config. +# Default: 0.1 +# +# Rate.Creature.Normal.Damage +# Rate.Creature.Elite.Elite.Damage +# Rate.Creature.Elite.RAREELITE.Damage +# Rate.Creature.Elite.WORLDBOSS.Damage +# Rate.Creature.Elite.RARE.Damage +# Creature Damage Rates. +# Examples: 2 - creatures will damage 2x, 1.7 - 1.7x. +# +# Rate.Creature.Normal.SpellDamage +# Rate.Creature.Elite.Elite.SpellDamage +# Rate.Creature.Elite.RAREELITE.SpellDamage +# Rate.Creature.Elite.WORLDBOSS.SpellDamag +# Rate.Creature.Elite.RARE.SpellDamage +# Creature Spell Damage Rates. +# Examples: 2 - creatures will damage with spells 2x, 1.7 - 1.7x. +# +# Rate.Creature.Normal.HP +# Rate.Creature.Elite.Elite.HP +# Rate.Creature.Elite.RAREELITE.HP +# Rate.Creature.Elite.WORLDBOSS.HP +# Rate.Creature.Elite.RARE.HP +# Creature Health Ammount Modifier. +# Examples: 2 - creatures have 2x health, 1.7 - 1.7x. +# +# ListenRange.Say +# Distance from player to listen text that creature (or other world object) say +# Default: 25 +# +# ListenRange.TextEmote +# Distance from player to listen textemote that creature (or other world object) say +# Default: 25 +# +# ListenRange.Yell +# Distance from player to listen text that creature (or other world object) yell +# Default: 300 +# +################################################################################################################### + +ThreatRadius = 100 +Rate.Creature.Aggro = 1 +CreatureFamilyAssistanceRadius = 10 +CreatureFamilyAssistanceDelay = 1500 +WorldBossLevelDiff = 3 +Corpse.Decay.NORMAL = 60 +Corpse.Decay.RARE = 300 +Corpse.Decay.ELITE = 300 +Corpse.Decay.RAREELITE = 300 +Corpse.Decay.WORLDBOSS = 3600 +Rate.Corpse.Decay.Looted = 0.1 +Rate.Creature.Normal.Damage = 1 +Rate.Creature.Elite.Elite.Damage = 1 +Rate.Creature.Elite.RAREELITE.Damage = 1 +Rate.Creature.Elite.WORLDBOSS.Damage = 1 +Rate.Creature.Elite.RARE.Damage = 1 +Rate.Creature.Normal.SpellDamage = 1 +Rate.Creature.Elite.Elite.SpellDamage = 1 +Rate.Creature.Elite.RAREELITE.SpellDamage = 1 +Rate.Creature.Elite.WORLDBOSS.SpellDamage = 1 +Rate.Creature.Elite.RARE.SpellDamage = 1 +Rate.Creature.Normal.HP = 1 +Rate.Creature.Elite.Elite.HP = 1 +Rate.Creature.Elite.RAREELITE.HP = 1 +Rate.Creature.Elite.WORLDBOSS.HP = 1 +Rate.Creature.Elite.RARE.HP = 1 +ListenRange.Say = 40 +ListenRange.TextEmote = 40 +ListenRange.Yell = 300 + +################################################################################################################### +# CHAT SETTINGS +# +# ChatFakeMessagePreventing +# Chat protection from creating fake messages using a lot spaces (other invisible symbols), +# not applied to addon language messages, but can prevent working old addons +# that use normal languages for sending data to another clients. +# Default: 0 (disible fake messages preventing) +# 1 (enabled fake messages preventing) +# +# ChatFlood.MessageCount +# Chat anti-flood protection, haste message count to activate protection +# Default: 10 +# 0 (disible anti-flood protection) +# +# ChatFlood.MessageDelay +# Chat anti-flood protection, minimum message delay to count message +# Default: 1 (in secs) +# +# ChatFlood.MuteTime +# Chat anti-flood protection, mute time at activation flood protection (not saved) +# Default: 10 (in secs) +# +# Channel.RestrictedLfg +# Restrict use LookupForGroup channel only registered in LFG tool players +# Default: 1 (allow join to channel only if active in LFG) +# 0 (allow join to channel in any time) +# +# Channel.SilentlyGMJoin +# Silently join GM characters (security level > 1) to channels +# Default: 0 (join announcement in normal way) +# 1 (GM join without announcement) +# +################################################################################################################### + +ChatFakeMessagePreventing = 0 +ChatFlood.MessageCount = 10 +ChatFlood.MessageDelay = 1 +ChatFlood.MuteTime = 10 +Channel.RestrictedLfg = 1 +Channel.SilentlyGMJoin = 0 + +################################################################################################################### +# GAME MASTER SETTINGS +# +# GM.LoginState +# GM mode at login +# Default: 2 (last save state) +# 0 (disable) +# 1 (enable) +# +# GM.AcceptTickets +# Is GM accepting tickets from player by default or not. +# Default: 2 (last save state) +# 0 (disable) +# 1 (enable) +# +# GM.Chat +# GM chat mode at login +# Default: 2 (last save state) +# 0 (disable) +# 1 (enable) +# +# GM.WhisperingTo +# Is GM accepting whispers from player by default or not. +# Default: 2 (last save state) +# 0 (disable) +# 1 (enable) +# +# GM.InGMList +# Is GM showed in GM list (if visible) in non-GM state (.gmoff) +# Default: 0 (false) +# 1 (true) +# +# GM.InWhoList +# Is GM showed in who list (if visible). +# Default: 0 (false) +# 1 (true) +# +# GM.LogTrade +# Include GM trade and trade slot enchanting operations in GM log if it enable +# Default: 1 (include) +# 0 (not include) +# +# GM.StartLevel +# GM starting level (1-100) +# Default: 1 +# +################################################################################################################### + +GM.LoginState = 2 +GM.AcceptTickets = 2 +GM.Chat = 2 +GM.WhisperingTo = 2 +GM.InGMList = 0 +GM.InWhoList = 0 +GM.LogTrade = 1 +GM.StartLevel = 70 + +################################################################################################################### +# VISIBILITY AND RADIUSES +# +# Visibility.GroupMode +# Group visibility modes +# Default: 0 (standard setting: only members from same group can 100% auto detect invisible player) +# 1 (raid members 100% auto detect invisible player from same raid) +# 2 (players from same team can 100% auto detect invisible player) +# +# Visibility.Distance.Creature +# Visibility.Distance.Player +# Visibility distance for different in game object +# Max limited by active player zone: ~ 333 +# Min limit dependent from objects +# Default: 132 (cell size) +# Min limit is max aggro radius (45) * Rate.Creature.Aggro +# +# Visibility.Distance.Object +# Visible distance for gameobject, dynobject, bodies, corpses, bones +# Min limit is iteraction distance (5) +# +# Visibility.Distance.InFlight +# Visible distance for player in flight +# Min limit is 0 (not show any objects) +# +# Visibility.Distance.Grey.Unit +# Visibility grey distance for creatures/players (fast changing objects) +# addition to appropriate object type Visibility.Distance.* use in case visibility removing to +# object (except corpse around distences) If � is distance and G is grey distance then object +# make visible if distance to it <= D but make non visible if distance > D+G +# Default: 1 (yard) +# +# Visibility.Distance.Grey.Object +# Visibility grey distance for dynobjects/gameobjects/corpses/creature bodies +# Default: 10 (yards) +# +# +################################################################################################################### + +Visibility.GroupMode = 0 +Visibility.Distance.Creature = 132 +Visibility.Distance.Player = 132 +Visibility.Distance.Object = 132 +Visibility.Distance.InFlight = 132 +Visibility.Distance.Grey.Unit = 1 +Visibility.Distance.Grey.Object = 10 + +################################################################################################################### +# SERVER RATES +# +# Rate.Health +# Rate.Mana +# Rate.Rage.Income +# Rate.Rage.Loss +# Rate.RunicPower.Income +# Rate.RunicPower.Loss +# Rate.Focus +# Health and power regeneration and rage income from damage. +# Default: 1 +# +# Rate.Skill.Discovery +# Skill Discovery Rates +# Default: 1 +# +# Rate.Drop.Item.Poor +# Rate.Drop.Item.Normal +# Rate.Drop.Item.Uncommon +# Rate.Drop.Item.Rare +# Rate.Drop.Item.Epic +# Rate.Drop.Item.Legendary +# Rate.Drop.Item.Artifact +# Rate.Drop.Item.Referenced +# Rate.Drop.Money +# Drop rates (items by quality and money) +# Default: 1 +# +# Rate.Drop.Money +# Drop rates +# Default: 1 +# +# Rate.XP.Kill +# Rate.XP.Quest +# Rate.XP.Explore +# XP rates +# Default: 1 +# +# Rate.XP.PastLevel70 +# XP needed per level past 70 (Rates below 1 not recommended) +# Default: 1 +# +# Rate.Rest.InGame +# Rate.Rest.Offline.InTavernOrCity +# Rate.Rest.Offline.InWilderness +# Resting points grow rates (1 - normal, 2 - double rate, 0.5 - half rate, etc) from standard values +# +# Rate.Damage.Fall +# Damage after fall rate. (1 - standard, 2 - double damage, 0.5 - half damage, etc) +# +# Rate.Auction.Time +# Rate.Auction.Deposit +# Rate.Auction.Cut +# Auction rates (auction time, deposit get at auction start, auction cut from price at auction end) +# +# Rate.Honor +# Honor gain rate +# +# Rate.Mining.Amount +# Rate.Mining.Next +# Mining Rates (Mining.Amount changes minimum/maximum usetimes of a deposit, +# Mining.Next changes chance to have next use of a deposit) +# +# Rate.Talent +# Talent Point rates +# Default: 1 +# +# Rate.Reputation.Gain +# Reputation Gain rate +# Default: 1 +# +# Rate.InstanceResetTime +# Multiplier for the number of days in between global raid/heroic instance resets. +# Default: 1 +# +# SkillGain.Crafting +# SkillGain.Defense +# SkillGain.Gathering +# SkillGain.Weapon +# crafting/defense/gathering/weapon skills gain at skill grow (1,2,...) +# Default: 1 +# +# SkillChance.Orange +# SkillChance.Yellow +# SkillChance.Green +# SkillChance.Grey +# Skill chance values (0..100) +# Default: 100-75-25-0 +# +# SkillChance.MiningSteps +# SkillChance.SkinningSteps +# For skinning and Mining chance decrease with skill level. +# Default: 0 - no decrease +# 75 - in 2 times each 75 skill points +# +# DurabilityLossChance.Damage +# Chance lost one from equiped items durability point at damage apply or receive. +# Default: 0.5 (100/0.5 = 200) Each 200 damage apply one from 19 possible equipped items +# +# DurabilityLossChance.Absorb +# Chance lost one from armor items durability point at damage absorb. +# Default: 0.5 (100/0.5 = 200) Each 200 absorbs apply one from 15 possible armor equipped items +# +# DurabilityLossChance.Parry +# Chance lost weapon durability point at parry. +# Default: 0.05 (100/0.05 = 2000) Each 2000 parry attacks main weapon lost point +# +# DurabilityLossChance.Block +# Chance lost sheild durability point at damage block. +# Default: 0.05 (100/0.05 = 2000) Each 2000 partly or full blocked attacks shield lost point +# +# Death.SicknessLevel +# Starting Character start gain sickness at spirit resurrection (1 min) +# Default: 11 +# -10 - character will have full time (10min) sickness at 1 level +# maxplayerlevel+1 - chaarcter will not have sickess at any level +# +# Death.CorpseReclaimDelay.PvP +# Death.CorpseReclaimDelay.PvE +# Enabled/disabled increase corpse reclaim delay at often PvP/PvE deaths +# Default: 1 (enabled) +# 0 (disabled) +# +################################################################################################################### + +Rate.Health = 1 +Rate.Mana = 1 +Rate.Rage.Income = 1 +Rate.Rage.Loss = 1 +Rate.RunicPower.Income = 1 +Rate.RunicPower.Loss = 1 +Rate.Focus = 1 +Rate.Skill.Discovery = 1 +Rate.Drop.Item.Poor = 1 +Rate.Drop.Item.Normal = 1 +Rate.Drop.Item.Uncommon = 1 +Rate.Drop.Item.Rare = 1 +Rate.Drop.Item.Epic = 1 +Rate.Drop.Item.Legendary = 1 +Rate.Drop.Item.Artifact = 1 +Rate.Drop.Item.Referenced = 1 +Rate.Drop.Money = 1 +Rate.XP.Kill = 1 +Rate.XP.Quest = 1 +Rate.XP.Explore = 1 +Rate.XP.PastLevel70 = 1 +Rate.Rest.InGame = 1 +Rate.Rest.Offline.InTavernOrCity = 1 +Rate.Rest.Offline.InWilderness = 1 +Rate.Damage.Fall = 1 +Rate.Auction.Time = 1 +Rate.Auction.Deposit = 1 +Rate.Auction.Cut = 1 +Rate.Honor = 1 +Rate.Mining.Amount = 1 +Rate.Mining.Next = 1 +Rate.Talent = 1 +Rate.Reputation.Gain = 1 +Rate.InstanceResetTime = 1 +SkillGain.Crafting = 1 +SkillGain.Defense = 1 +SkillGain.Gathering = 1 +SkillGain.Weapon = 1 +SkillChance.Orange = 100 +SkillChance.Yellow = 75 +SkillChance.Green = 25 +SkillChance.Grey = 0 +SkillChance.MiningSteps = 0 +SkillChance.SkinningSteps = 0 +DurabilityLossChance.Damage = 0.5 +DurabilityLossChance.Absorb = 0.5 +DurabilityLossChance.Parry = 0.05 +DurabilityLossChance.Block = 0.05 +Death.SicknessLevel = 11 +Death.CorpseReclaimDelay.PvP = 1 +Death.CorpseReclaimDelay.PvE = 1 + +################################################################################################################### +# +# Rated arena matches config +# +# MaxRatingDifference: the maximum rating difference between two groups in rated matches +# Default: 0 (disable, rating difference is discarded) +# +# RatingDiscardTimer: after the specified milliseconds has passed, +# rating information will be discarded when selecting teams for matches +# also initiates an update by this timer +# Default: 60000 +# +# AutoDistributePoints: set if arena points should be distributed automatically, or by GM command +# Default: 0 (disable) (recommended): use gm command or sql query to distribute the points +# 1 (enable): arena points are distributed automatically +# +# AutoDistributeInterval: how often should the distribution take place +# if automatic distribution is enabled +# in days +# Default: 7 (weekly) +# +################################################################################################################### + +Arena.MaxRatingDifference = 0 +Arena.RatingDiscardTimer = 60000 +Arena.AutoDistributePoints = 0 +Arena.AutoDistributeInterval = 7 + +################################################################################################################### +# +# Battleground config +# +# PrematureFinishTimer: the time to end the bg if there are less than minplayersperteam on one side +# in milliseconds +# Default: 300000 +# 0 - disable +# +################################################################################################################### + +BattleGround.PrematureFinishTimer = 300000 + + +################################################################################################################### +# +# NETWORK CONFIG +# +# Network.Threads +# Number of threads for network, recommend 1 thread per 1000 connections. +# Default: 1 +# +# Network.OutKBuff +# The size of the output kernel buffer used ( SO_SNDBUF socket option, tcp manual ). +# Default: -1 (Use system default setting) +# +# Network.OutUBuff +# Userspace buffer for output. This is amount of memory reserved per each connection. +# Default: 65536 +# +# Network.TcpNoDelay: +# TCP Nagle algorithm setting +# Default: 0 (enable Nagle algorithm, less traffic, more latency) +# 1 (TCP_NO_DELAY, disable Nagle algorithm, more traffic but less latency) +# +################################################################################################################### + +Network.Threads = 1 +Network.OutKBuff = -1 +Network.OutUBuff = 65536 +Network.TcpNodelay = 1 + +################################################################################################################### +# CONSOLE AND REMOTE ACCESS +# +# Console.Enable +# Enable console +# Default: 1 - on +# 0 - off +# +# Ra.Enable +# Enable remote console +# Default: 0 - off +# 1 - on +# +# Ra.IP +# Default remote console ip address, use 0.0.0.0 for every address +# +# Ra.Port +# Default remote console port +# +# Ra.MinLevel +# Minimum level that's required to login,3 by default +# +# Ra.Secure +# Kick client on wrong pass +# +################################################################################################################### + +Console.Enable = 1 +Ra.Enable = 0 +Ra.IP = 0.0.0.0 +Ra.Port = 3443 +Ra.MinLevel = 3 +Ra.Secure = 1 + +################################################################################################################### +# CUSTOM SERVER OPTIONS +# +# PlayerStart.AllReputation +# Players will start with most of the high level reputations that are needed for items, mounts etc. +# If there are any reputation faction you want to be added, just tell me. +# +# PlayerStart.AllSpells +# If enabled, players will start with all their class spells (not talents). Useful for instant 70 servers. +# You must import playercreateinfo_spell_custom.sql, it's included in the SQL folder. +# Default: 0 - off +# 1 - on +# +# PlayerStart.MapsExplored +# Players will start with all maps explored if enabled +# +# MusicInBattleground +# If enabled, "L70ETC - Power of the horde" will be played when BG starts ;) +# +# HonorPointsAfterDuel +# The amount of honor points the duel winner will get after a duel. +# Default: 0 - disable +# +# AlwaysMaxWeaponSkill +# Players will automatically gain max weapon/defense skill when logging in, leveling up etc. +# +# PvPToken.Enable +# Enable/disable PvP Token System. Players will get a token after slaying another player that gives honor. +# +# PvPToken.MapAllowType +# Where players can receive the pvp token +# 4 - In all maps +# 3 - In battlegrounds only +# 2 - In FFA areas only (gurubashi arena etc) +# 1 - In battlegrounds AND FFA areas only +# +# PvPToken.ItemID +# The item players will get after killing someone if PvP Token system is enabled. +# Default: 29434 - Badge of justice +# +# PvPToken.ItemCount +# Modify the item ID count - Default: 1 +# +# NoResetTalentsCost +# Enable or disable no cost when reseting talents +# +# ForbiddenMaps +# map ids that users below SEC_GAMEMASTER cannot enter, with delimiter ',' +# Default: "" +# example: "538,90" +# Note that it's HIGHLY DISCOURAGED to forbid starting maps (0, 1, 530)! +# +################################################################################################################### + +PlayerStart.AllReputation = 0 +PlayerStart.AllSpells = 0 +PlayerStart.MapsExplored = 0 +MusicInBattleground = 0 +HonorPointsAfterDuel = 0 +AlwaysMaxWeaponSkill = 0 +PvPToken.Enable = 0 +PvPToken.MapAllowType = 4 +PvPToken.ItemID = 29434 +PvPToken.ItemCount = 1 +NoResetTalentsCost = 0 + diff --git a/src/mangosd/mangosd.rc b/src/mangosd/mangosd.rc new file mode 100644 index 00000000000..4e6510c0407 --- /dev/null +++ b/src/mangosd/mangosd.rc @@ -0,0 +1,85 @@ +/* + * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/> + * + * This program 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 2 of the License, or + * (at your option) any later version. + * + * This program 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 this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "resource.h" + +#define APSTUDIO_READONLY_SYMBOLS +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 2 resource. +// +#include "windows.h" + +///////////////////////////////////////////////////////////////////////////// +#undef APSTUDIO_READONLY_SYMBOLS + +///////////////////////////////////////////////////////////////////////////// +// +// Icon +// + +// Icon with lowest ID value placed first to ensure application icon +// remains consistent on all systems. +IDI_APPICON ICON "TrinityCore.ico" + +///////////////////////////////////////////////////////////////////////////// +// Neutre (Par défaut système) resources + +#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_NEUSD) +#ifdef _WIN32 +LANGUAGE LANG_NEUTRAL, SUBLANG_SYS_DEFAULT +#pragma code_page(1252) +#endif //_WIN32 + +///////////////////////////////////////////////////////////////////////////// +// +// Version +// + +VS_VERSION_INFO VERSIONINFO + FILEVERSION 0,4,6743,685 + PRODUCTVERSION 0,4,6743,685 + FILEFLAGSMASK 0x17L +#ifdef _DEBUG + FILEFLAGS 0x1L +#else + FILEFLAGS 0x0L +#endif + FILEOS 0x0L + FILETYPE 0x0L + FILESUBTYPE 0x0L +BEGIN + BLOCK "StringFileInfo" + BEGIN + BLOCK "080004b0" + BEGIN + VALUE "FileDescription", "TrinityCore" + VALUE "FileVersion", "0, 4, 6743, 685" + VALUE "InternalName", "TrinityCore" + VALUE "LegalCopyright", "Copyright (C) 2008" + VALUE "OriginalFilename", "TrinityCore.exe" + VALUE "ProductName", "TrinityCore" + VALUE "ProductVersion", "0, 4, 6743, 685" + END + END + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0x800, 1200 + END +END +#endif diff --git a/src/trinitycore/monitor-mangosd b/src/mangosd/monitor-mangosd index a740ae5e8fa..a740ae5e8fa 100644 --- a/src/trinitycore/monitor-mangosd +++ b/src/mangosd/monitor-mangosd diff --git a/src/trinitycore/resource.h b/src/mangosd/resource.h index 7e7d8e4b76f..7e7d8e4b76f 100644 --- a/src/trinitycore/resource.h +++ b/src/mangosd/resource.h diff --git a/src/trinitycore/run-mangosd b/src/mangosd/run-mangosd index f307bd9e1ad..f307bd9e1ad 100644 --- a/src/trinitycore/run-mangosd +++ b/src/mangosd/run-mangosd diff --git a/src/realmd/AuthCodes.h b/src/realmd/AuthCodes.h new file mode 100644 index 00000000000..f322d7fea17 --- /dev/null +++ b/src/realmd/AuthCodes.h @@ -0,0 +1,75 @@ +/* + * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/> + * + * Copyright (C) 2008 Trinity <http://www.trinitycore.org/> + * + * This program 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 2 of the License, or + * (at your option) any later version. + * + * This program 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 this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/** \file + \ingroup realmd +*/ + +#ifndef _AUTHCODES_H +#define _AUTHCODES_H + +enum eAuthResults +{ + REALM_AUTH_SUCCESS = 0x00, + REALM_AUTH_FAILURE = 0x01, ///< Unable to connect + REALM_AUTH_UNKNOWN1 = 0x02, ///< Unable to connect + REALM_AUTH_ACCOUNT_BANNED = 0x03, ///< This <game> account has been closed and is no longer available for use. Please go to <site>/banned.html for further information. + REALM_AUTH_NO_MATCH = 0x04, ///< The information you have entered is not valid. Please check the spelling of the account name and password. If you need help in retrieving a lost or stolen password, see <site> for more information + REALM_AUTH_UNKNOWN2 = 0x05, ///< The information you have entered is not valid. Please check the spelling of the account name and password. If you need help in retrieving a lost or stolen password, see <site> for more information + REALM_AUTH_ACCOUNT_IN_USE = 0x06, ///< This account is already logged into <game>. Please check the spelling and try again. + REALM_AUTH_PREPAID_TIME_LIMIT = 0x07, ///< You have used up your prepaid time for this account. Please purchase more to continue playing + REALM_AUTH_SERVER_FULL = 0x08, ///< Could not log in to <game> at this time. Please try again later. + REALM_AUTH_WRONG_BUILD_NUMBER = 0x09, ///< Unable to validate game version. This may be caused by file corruption or interference of another program. Please visit <site> for more information and possible solutions to this issue. + REALM_AUTH_UPDATE_CLIENT = 0x0a, ///< Downloading + REALM_AUTH_UNKNOWN3 = 0x0b, ///< Unable to connect + REALM_AUTH_ACCOUNT_FREEZED = 0x0c, ///< This <game> account has been temporarily suspended. Please go to <site>/banned.html for further information + REALM_AUTH_UNKNOWN4 = 0x0d, ///< Unable to connect + REALM_AUTH_UNKNOWN5 = 0x0e, ///< Connected. + REALM_AUTH_PARENTAL_CONTROL = 0x0f ///< Access to this account has been blocked by parental controls. Your settings may be changed in your account preferences at <site> +}; + +enum LoginResult +{ + LOGIN_OK = 0x00, + LOGIN_FAILED = 0x01, + LOGIN_FAILED2 = 0x02, + LOGIN_BANNED = 0x03, + LOGIN_UNKNOWN_ACCOUNT = 0x04, + LOGIN_UNKNOWN_ACCOUNT3 = 0x05, + LOGIN_ALREADYONLINE = 0x06, + LOGIN_NOTIME = 0x07, + LOGIN_DBBUSY = 0x08, + LOGIN_BADVERSION = 0x09, + LOGIN_DOWNLOAD_FILE = 0x0A, + LOGIN_FAILED3 = 0x0B, + LOGIN_SUSPENDED = 0x0C, + LOGIN_FAILED4 = 0x0D, + LOGIN_CONNECTED = 0x0E, + LOGIN_PARENTALCONTROL = 0x0F, + LOGIN_LOCKED_ENFORCED = 0x10, +}; + +// we need to stick to 1 version or half of the stuff will work for someone +// others will not and opposite +// will only support WoW, WoW:TBC and WoW:WotLK 3.0.3 client build 9183... + +#define EXPECTED_TRINITY_CLIENT_BUILD {9183, 0} + +#endif diff --git a/src/realmd/AuthSocket.cpp b/src/realmd/AuthSocket.cpp new file mode 100644 index 00000000000..7168bcf700b --- /dev/null +++ b/src/realmd/AuthSocket.cpp @@ -0,0 +1,1094 @@ +/* + * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/> + * + * Copyright (C) 2008 Trinity <http://www.trinitycore.org/> + * + * This program 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 2 of the License, or + * (at your option) any later version. + * + * This program 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 this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/** \file + \ingroup realmd +*/ + +#include "Common.h" +#include "Database/DatabaseEnv.h" +#include "ByteBuffer.h" +#include "Config/ConfigEnv.h" +#include "Log.h" +#include "RealmList.h" +#include "AuthSocket.h" +#include "AuthCodes.h" +#include <openssl/md5.h> +#include "Auth/Sha1.h" +//#include "Util.h" -- for commented utf8ToUpperOnlyLatin + +extern RealmList m_realmList; + +extern DatabaseType dbRealmServer; + +#define ChunkSize 2048 + +enum eAuthCmd +{ + //AUTH_NO_CMD = 0xFF, + AUTH_LOGON_CHALLENGE = 0x00, + AUTH_LOGON_PROOF = 0x01, + AUTH_RECONNECT_CHALLENGE = 0x02, + AUTH_RECONNECT_PROOF = 0x03, + //update srv =4 + REALM_LIST = 0x10, + XFER_INITIATE = 0x30, + XFER_DATA = 0x31, + XFER_ACCEPT = 0x32, + XFER_RESUME = 0x33, + XFER_CANCEL = 0x34 +}; + +enum eStatus +{ + STATUS_CONNECTED = 0, + STATUS_AUTHED +}; + +// GCC have alternative #pragma pack(N) syntax and old gcc version not support pack(push,N), also any gcc version not support it at some paltform +#if defined( __GNUC__ ) +#pragma pack(1) +#else +#pragma pack(push,1) +#endif + +typedef struct AUTH_LOGON_CHALLENGE_C +{ + uint8 cmd; + uint8 error; + uint16 size; + uint8 gamename[4]; + uint8 version1; + uint8 version2; + uint8 version3; + uint16 build; + uint8 platform[4]; + uint8 os[4]; + uint8 country[4]; + uint32 timezone_bias; + uint32 ip; + uint8 I_len; + uint8 I[1]; +} sAuthLogonChallenge_C; + +//typedef sAuthLogonChallenge_C sAuthReconnectChallenge_C; +/* +typedef struct +{ + uint8 cmd; + uint8 error; + uint8 unk2; + uint8 B[32]; + uint8 g_len; + uint8 g[1]; + uint8 N_len; + uint8 N[32]; + uint8 s[32]; + uint8 unk3[16]; +} sAuthLogonChallenge_S; +*/ + +typedef struct AUTH_LOGON_PROOF_C +{ + uint8 cmd; + uint8 A[32]; + uint8 M1[20]; + uint8 crc_hash[20]; + uint8 number_of_keys; + uint8 unk; // Added in 1.12.x client branch +} sAuthLogonProof_C; +/* +typedef struct +{ + uint16 unk1; + uint32 unk2; + uint8 unk3[4]; + uint16 unk4[20]; +} sAuthLogonProofKey_C; +*/ +typedef struct AUTH_LOGON_PROOF_S +{ + uint8 cmd; + uint8 error; + uint8 M2[20]; + uint32 unk1; + uint32 unk2; + uint16 unk3; +} sAuthLogonProof_S; + +typedef struct AUTH_RECONNECT_PROOF_C +{ + uint8 cmd; + uint8 R1[16]; + uint8 R2[20]; + uint8 R3[20]; + uint8 number_of_keys; +} sAuthReconnectProof_C; + +typedef struct XFER_INIT +{ + uint8 cmd; // XFER_INITIATE + uint8 fileNameLen; // strlen(fileName); + uint8 fileName[5]; // fileName[fileNameLen] + uint64 file_size; // file size (bytes) + uint8 md5[MD5_DIGEST_LENGTH]; // MD5 +}XFER_INIT; + +typedef struct XFER_DATA +{ + uint8 opcode; + uint16 data_size; + uint8 data[ChunkSize]; +}XFER_DATA_STRUCT; + +typedef struct AuthHandler +{ + eAuthCmd cmd; + uint32 status; + bool (AuthSocket::*handler)(void); +}AuthHandler; + +// GCC have alternative #pragma pack() syntax and old gcc version not support pack(pop), also any gcc version not support it at some paltform +#if defined( __GNUC__ ) +#pragma pack() +#else +#pragma pack(pop) +#endif + +/// Launch a thread to transfer a patch to the client +class PatcherRunnable: public ZThread::Runnable +{ + public: + PatcherRunnable(class AuthSocket *); + void run(); + + private: + AuthSocket * mySocket; +}; + +typedef struct PATCH_INFO +{ + uint8 md5[MD5_DIGEST_LENGTH]; +}PATCH_INFO; + +/// Caches MD5 hash of client patches present on the server +class Patcher +{ + public: + typedef std::map<std::string, PATCH_INFO*> Patches; + ~Patcher(); + Patcher(); + Patches::const_iterator begin() const { return _patches.begin(); } + Patches::const_iterator end() const { return _patches.end(); } + void LoadPatchMD5(char*); + bool GetHash(char * pat,uint8 mymd5[16]); + + private: + void LoadPatchesInfo(); + Patches _patches; +}; + +const AuthHandler table[] = +{ + { AUTH_LOGON_CHALLENGE, STATUS_CONNECTED, &AuthSocket::_HandleLogonChallenge }, + { AUTH_LOGON_PROOF, STATUS_CONNECTED, &AuthSocket::_HandleLogonProof }, + { AUTH_RECONNECT_CHALLENGE, STATUS_CONNECTED, &AuthSocket::_HandleReconnectChallenge}, + { AUTH_RECONNECT_PROOF, STATUS_CONNECTED, &AuthSocket::_HandleReconnectProof }, + { REALM_LIST, STATUS_AUTHED, &AuthSocket::_HandleRealmList }, + { XFER_ACCEPT, STATUS_CONNECTED, &AuthSocket::_HandleXferAccept }, + { XFER_RESUME, STATUS_CONNECTED, &AuthSocket::_HandleXferResume }, + { XFER_CANCEL, STATUS_CONNECTED, &AuthSocket::_HandleXferCancel } +}; + +#define AUTH_TOTAL_COMMANDS sizeof(table)/sizeof(AuthHandler) + +///Holds the MD5 hash of client patches present on the server +Patcher PatchesCache; + +/// Constructor - set the N and g values for SRP6 +AuthSocket::AuthSocket(ISocketHandler &h) : TcpSocket(h) +{ + N.SetHexStr("894B645E89E1535BBDAD5B8B290650530801B18EBFBF5E8FAB3C82872A3E9BB7"); + g.SetDword(7); + _authed = false; + pPatch = NULL; + + _accountSecurityLevel = SEC_PLAYER; +} + +/// Close patch file descriptor before leaving +AuthSocket::~AuthSocket() +{ + ZThread::Guard<ZThread::Mutex> g(patcherLock); + if(pPatch) + fclose(pPatch); +} + +/// Accept the connection and set the s random value for SRP6 +void AuthSocket::OnAccept() +{ + sLog.outBasic("Accepting connection from '%s:%d'", + GetRemoteAddress().c_str(), GetRemotePort()); + + s.SetRand(s_BYTE_SIZE * 8); +} + +/// Read the packet from the client +void AuthSocket::OnRead() +{ + ///- Read the packet + TcpSocket::OnRead(); + uint8 _cmd; + while (1) + { + if (!ibuf.GetLength()) + return; + + ///- Get the command out of it + ibuf.SoftRead((char *)&_cmd, 1); // UQ1: No longer exists in new net code ??? + //ibuf.Read((char *)&_cmd, 1); + /*char *command = (char *)malloc(1); + + ibuf.Read(command, 1); + + _cmd = (uint8)command;*/ + // assert(0); + size_t i; + + ///- Circle through known commands and call the correct command handler + for (i=0;i<AUTH_TOTAL_COMMANDS; i++) + { + if ((uint8)table[i].cmd == _cmd && + (table[i].status == STATUS_CONNECTED || + (_authed && table[i].status == STATUS_AUTHED))) + { + DEBUG_LOG("[Auth] got data for cmd %u ibuf length %u", (uint32)_cmd, ibuf.GetLength()); + + if (!(*this.*table[i].handler)()) + { + DEBUG_LOG("Command handler failed for cmd %u ibuf length %u", (uint32)_cmd, ibuf.GetLength()); + return; + } + break; + } + } + + ///- Report unknown commands in the debug log + if (i==AUTH_TOTAL_COMMANDS) + { + DEBUG_LOG("[Auth] got unknown packet %u", (uint32)_cmd); + return; + } + } +} + +/// Make the SRP6 calculation from hash in dB +void AuthSocket::_SetVSFields(const std::string& rI) +{ + BigNumber I; + I.SetHexStr(rI.c_str()); + + //In case of leading zeroes in the rI hash, restore them + uint8 mDigest[SHA_DIGEST_LENGTH]; + memset(mDigest,0,SHA_DIGEST_LENGTH); + if (I.GetNumBytes() <= SHA_DIGEST_LENGTH) + memcpy(mDigest,I.AsByteArray(),I.GetNumBytes()); + + std::reverse(mDigest,mDigest+SHA_DIGEST_LENGTH); + + Sha1Hash sha; + sha.UpdateData(s.AsByteArray(), s.GetNumBytes()); + sha.UpdateData(mDigest, SHA_DIGEST_LENGTH); + sha.Finalize(); + BigNumber x; + x.SetBinary(sha.GetDigest(), sha.GetLength()); + v = g.ModExp(x, N); + // No SQL injection (username escaped) + const char *v_hex, *s_hex; + v_hex = v.AsHexStr(); + s_hex = s.AsHexStr(); + dbRealmServer.PExecute("UPDATE account SET v = '%s', s = '%s' WHERE username = '%s'",v_hex,s_hex, _safelogin.c_str() ); + OPENSSL_free((void*)v_hex); + OPENSSL_free((void*)s_hex); +} + +/// Logon Challenge command handler +bool AuthSocket::_HandleLogonChallenge() +{ + DEBUG_LOG("Entering _HandleLogonChallenge"); + if (ibuf.GetLength() < sizeof(sAuthLogonChallenge_C)) + return false; + + ///- Read the first 4 bytes (header) to get the length of the remaining of the packet + std::vector<uint8> buf; + buf.resize(4); + + ibuf.Read((char *)&buf[0], 4); + + EndianConvert(*((uint16*)(buf[0]))); + uint16 remaining = ((sAuthLogonChallenge_C *)&buf[0])->size; + DEBUG_LOG("[AuthChallenge] got header, body is %#04x bytes", remaining); + + if ((remaining < sizeof(sAuthLogonChallenge_C) - buf.size()) || (ibuf.GetLength() < remaining)) + return false; + + //No big fear of memory outage (size is int16, i.e. < 65536) + buf.resize(remaining + buf.size() + 1); + buf[buf.size() - 1] = 0; + sAuthLogonChallenge_C *ch = (sAuthLogonChallenge_C*)&buf[0]; + + // BigEndian code, nop in little endian case + // size already converted + EndianConvert(*((uint32*)(&ch->gamename[0]))); + EndianConvert(ch->build); + EndianConvert(*((uint32*)(&ch->platform[0]))); + EndianConvert(*((uint32*)(&ch->os[0]))); + EndianConvert(*((uint32*)(&ch->country[0]))); + EndianConvert(ch->timezone_bias); + EndianConvert(ch->ip); + + ///- Read the remaining of the packet + ibuf.Read((char *)&buf[4], remaining); + DEBUG_LOG("[AuthChallenge] got full packet, %#04x bytes", ch->size); + DEBUG_LOG("[AuthChallenge] name(%d): '%s'", ch->I_len, ch->I); + + ByteBuffer pkt; + + _login = (const char*)ch->I; + _build = ch->build; + + ///- Normalize account name + //utf8ToUpperOnlyLatin(_login); -- client already send account in expected form + + //Escape the user login to avoid further SQL injection + //Memory will be freed on AuthSocket object destruction + _safelogin=_login; + dbRealmServer.escape_string(_safelogin); + + pkt << (uint8) AUTH_LOGON_CHALLENGE; + pkt << (uint8) 0x00; + + ///- Verify that this IP is not in the ip_banned table + // No SQL injection possible (paste the IP address as passed by the socket) + dbRealmServer.Execute("DELETE FROM ip_banned WHERE unbandate<=UNIX_TIMESTAMP() AND unbandate<>bandate"); + + std::string address = GetRemoteAddress(); + dbRealmServer.escape_string(address); + QueryResult *result = dbRealmServer.PQuery( "SELECT * FROM ip_banned WHERE ip = '%s'",address.c_str()); + if(result) + { + pkt << (uint8)REALM_AUTH_ACCOUNT_BANNED; + sLog.outBasic("[AuthChallenge] Banned ip %s tries to login!",GetRemoteAddress().c_str ()); + delete result; + } + else + { + ///- Get the account details from the account table + // No SQL injection (escaped user name) + + result = dbRealmServer.PQuery("SELECT sha_pass_hash,id,locked,last_ip,gmlevel FROM account WHERE username = '%s'",_safelogin.c_str ()); + if( result ) + { + ///- If the IP is 'locked', check that the player comes indeed from the correct IP address + bool locked = false; + if((*result)[2].GetUInt8() == 1) // if ip is locked + { + DEBUG_LOG("[AuthChallenge] Account '%s' is locked to IP - '%s'", _login.c_str(), (*result)[3].GetString()); + DEBUG_LOG("[AuthChallenge] Player address is '%s'", GetRemoteAddress().c_str()); + if ( strcmp((*result)[3].GetString(),GetRemoteAddress().c_str()) ) + { + DEBUG_LOG("[AuthChallenge] Account IP differs"); + pkt << (uint8) REALM_AUTH_ACCOUNT_FREEZED; + locked=true; + } + else + { + DEBUG_LOG("[AuthChallenge] Account IP matches"); + } + } + else + { + DEBUG_LOG("[AuthChallenge] Account '%s' is not locked to ip", _login.c_str()); + } + + if (!locked) + { + //set expired bans to inactive + dbRealmServer.Execute("UPDATE account_banned SET active = 0 WHERE unbandate<=UNIX_TIMESTAMP() AND unbandate<>bandate"); + ///- If the account is banned, reject the logon attempt + QueryResult *banresult = dbRealmServer.PQuery("SELECT bandate,unbandate FROM account_banned WHERE id = %u AND active = 1", (*result)[1].GetUInt32()); + if(banresult) + { + if((*banresult)[0].GetUInt64() == (*banresult)[1].GetUInt64()) + { + pkt << (uint8) REALM_AUTH_ACCOUNT_BANNED; + sLog.outBasic("[AuthChallenge] Banned account %s tries to login!",_login.c_str ()); + } + else + { + pkt << (uint8) REALM_AUTH_ACCOUNT_FREEZED; + sLog.outBasic("[AuthChallenge] Temporarily banned account %s tries to login!",_login.c_str ()); + } + + delete banresult; + } + else + { + ///- Get the password from the account table, upper it, and make the SRP6 calculation + std::string rI = (*result)[0].GetCppString(); + _SetVSFields(rI); + + b.SetRand(19 * 8); + BigNumber gmod=g.ModExp(b, N); + B = ((v * 3) + gmod) % N; + + ASSERT(gmod.GetNumBytes() <= 32); + + BigNumber unk3; + unk3.SetRand(16*8); + + ///- Fill the response packet with the result + pkt << (uint8)REALM_AUTH_SUCCESS; + + // B may be calculated < 32B so we force minnimal length to 32B + pkt.append(B.AsByteArray(32), 32); // 32 bytes + pkt << (uint8)1; + pkt.append(g.AsByteArray(), 1); + pkt << (uint8)32; + pkt.append(N.AsByteArray(), 32); + pkt.append(s.AsByteArray(), s.GetNumBytes()); // 32 bytes + pkt.append(unk3.AsByteArray(), 16); + pkt << (uint8)0; // Added in 1.12.x client branch + + uint8 secLevel = (*result)[4].GetUInt8(); + _accountSecurityLevel = secLevel <= SEC_ADMINISTRATOR ? AccountTypes(secLevel) : SEC_ADMINISTRATOR; + + _localizationName.resize(4); + for(int i = 0; i <4; ++i) + _localizationName[i] = ch->country[4-i-1]; + + sLog.outBasic("[AuthChallenge] account %s is using '%c%c%c%c' locale (%u)", _login.c_str (), ch->country[3],ch->country[2],ch->country[1],ch->country[0], GetLocaleByName(_localizationName)); + } + } + delete result; + } + else //no account + { + pkt<< (uint8) REALM_AUTH_NO_MATCH; + } + } + SendBuf((char const*)pkt.contents(), pkt.size()); + return true; +} + +/// Logon Proof command handler +bool AuthSocket::_HandleLogonProof() +{ + DEBUG_LOG("Entering _HandleLogonProof"); + ///- Read the packet + if (ibuf.GetLength() < sizeof(sAuthLogonProof_C)) + return false; + sAuthLogonProof_C lp; + ibuf.Read((char *)&lp, sizeof(sAuthLogonProof_C)); + + ///- Check if the client has one of the expected version numbers + bool valid_version=false; + int accepted_versions[]=EXPECTED_TRINITY_CLIENT_BUILD; + for(int i=0;accepted_versions[i];i++) + { + if(_build==accepted_versions[i]) + { + valid_version=true; + break; + } + } + + /// <ul><li> If the client has no valid version + if(!valid_version) + { + ///- Check if we have the apropriate patch on the disk + + // 24 = len("./patches/65535enGB.mpq")+1 + char tmp[24]; + // No buffer overflow (fixed length of arguments) + sprintf(tmp,"./patches/%d%s.mpq",_build, _localizationName.c_str()); + // This will be closed at the destruction of the AuthSocket (client deconnection) + FILE *pFile=fopen(tmp,"rb"); + + if(!pFile) + { + ByteBuffer pkt; + pkt << (uint8) AUTH_LOGON_CHALLENGE; + pkt << (uint8) 0x00; + pkt << (uint8) REALM_AUTH_WRONG_BUILD_NUMBER; + DEBUG_LOG("[AuthChallenge] %u is not a valid client version!", _build); + DEBUG_LOG("[AuthChallenge] Patch %s not found",tmp); + SendBuf((char const*)pkt.contents(), pkt.size()); + return true; + } + else // have patch + { + pPatch=pFile; + XFER_INIT xferh; + + ///- Get the MD5 hash of the patch file (get it from preloaded Patcher cache or calculate it) + if(PatchesCache.GetHash(tmp,(uint8*)&xferh.md5)) + { + DEBUG_LOG("\n[AuthChallenge] Found precached patch info for patch %s",tmp); + } + else + { //calculate patch md5 + printf("\n[AuthChallenge] Patch info for %s was not cached.",tmp); + PatchesCache.LoadPatchMD5(tmp); + PatchesCache.GetHash(tmp,(uint8*)&xferh.md5); + } + + ///- Send a packet to the client with the file length and MD5 hash + uint8 data[2]={AUTH_LOGON_PROOF,REALM_AUTH_UPDATE_CLIENT}; + SendBuf((const char*)data,sizeof(data)); + + memcpy(&xferh,"0\x05Patch",7); + xferh.cmd=XFER_INITIATE; + fseek(pPatch,0,SEEK_END); + xferh.file_size=ftell(pPatch); + + SendBuf((const char*)&xferh,sizeof(xferh)); + return true; + } + } + /// </ul> + + ///- Continue the SRP6 calculation based on data received from the client + BigNumber A; + A.SetBinary(lp.A, 32); + + Sha1Hash sha; + sha.UpdateBigNumbers(&A, &B, NULL); + sha.Finalize(); + BigNumber u; + u.SetBinary(sha.GetDigest(), 20); + BigNumber S = (A * (v.ModExp(u, N))).ModExp(b, N); + + uint8 t[32]; + uint8 t1[16]; + uint8 vK[40]; + memcpy(t, S.AsByteArray(), 32); + for (int i = 0; i < 16; i++) + { + t1[i] = t[i*2]; + } + sha.Initialize(); + sha.UpdateData(t1, 16); + sha.Finalize(); + for (int i = 0; i < 20; i++) + { + vK[i*2] = sha.GetDigest()[i]; + } + for (int i = 0; i < 16; i++) + { + t1[i] = t[i*2+1]; + } + sha.Initialize(); + sha.UpdateData(t1, 16); + sha.Finalize(); + for (int i = 0; i < 20; i++) + { + vK[i*2+1] = sha.GetDigest()[i]; + } + K.SetBinary(vK, 40); + + uint8 hash[20]; + + sha.Initialize(); + sha.UpdateBigNumbers(&N, NULL); + sha.Finalize(); + memcpy(hash, sha.GetDigest(), 20); + sha.Initialize(); + sha.UpdateBigNumbers(&g, NULL); + sha.Finalize(); + for (int i = 0; i < 20; i++) + { + hash[i] ^= sha.GetDigest()[i]; + } + BigNumber t3; + t3.SetBinary(hash, 20); + + sha.Initialize(); + sha.UpdateData(_login); + sha.Finalize(); + uint8 t4[SHA_DIGEST_LENGTH]; + memcpy(t4, sha.GetDigest(), SHA_DIGEST_LENGTH); + + sha.Initialize(); + sha.UpdateBigNumbers(&t3, NULL); + sha.UpdateData(t4, SHA_DIGEST_LENGTH); + sha.UpdateBigNumbers(&s, &A, &B, &K, NULL); + sha.Finalize(); + BigNumber M; + M.SetBinary(sha.GetDigest(), 20); + + ///- Check if SRP6 results match (password is correct), else send an error + if (!memcmp(M.AsByteArray(), lp.M1, 20)) + { + sLog.outBasic("User '%s' successfully authenticated", _login.c_str()); + + ///- Update the sessionkey, last_ip, last login time and reset number of failed logins in the account table for this account + // No SQL injection (escaped user name) and IP address as received by socket + const char* K_hex = K.AsHexStr(); + dbRealmServer.PExecute("UPDATE account SET sessionkey = '%s', last_ip = '%s', last_login = NOW(), locale = '%u', failed_logins = 0 WHERE username = '%s'", K_hex, GetRemoteAddress().c_str(), GetLocaleByName(_localizationName), _safelogin.c_str() ); + OPENSSL_free((void*)K_hex); + + ///- Finish SRP6 and send the final result to the client + sha.Initialize(); + sha.UpdateBigNumbers(&A, &M, &K, NULL); + sha.Finalize(); + + sAuthLogonProof_S proof; + memcpy(proof.M2, sha.GetDigest(), 20); + proof.cmd = AUTH_LOGON_PROOF; + proof.error = 0; + proof.unk1 = 0x00800000; + proof.unk2 = 0x00; + proof.unk3 = 0x00; + + SendBuf((char *)&proof, sizeof(proof)); + + ///- Set _authed to true! + _authed = true; + } + else + { + char data[4]={AUTH_LOGON_PROOF,REALM_AUTH_NO_MATCH,3,0}; + SendBuf(data,sizeof(data)); + sLog.outBasic("[AuthChallenge] account %s tried to login with wrong password!",_login.c_str ()); + + uint32 MaxWrongPassCount = sConfig.GetIntDefault("WrongPass.MaxCount", 0); + if(MaxWrongPassCount > 0) + { + //Increment number of failed logins by one and if it reaches the limit temporarily ban that account or IP + dbRealmServer.PExecute("UPDATE account SET failed_logins = failed_logins + 1 WHERE username = '%s'",_safelogin.c_str()); + + if(QueryResult *loginfail = dbRealmServer.PQuery("SELECT id, failed_logins FROM account WHERE username = '%s'", _safelogin.c_str())) + { + Field* fields = loginfail->Fetch(); + uint32 failed_logins = fields[1].GetUInt32(); + + if( failed_logins >= MaxWrongPassCount ) + { + uint32 WrongPassBanTime = sConfig.GetIntDefault("WrongPass.BanTime", 600); + bool WrongPassBanType = sConfig.GetBoolDefault("WrongPass.BanType", false); + + if(WrongPassBanType) + { + uint32 acc_id = fields[0].GetUInt32(); + dbRealmServer.PExecute("INSERT INTO account_banned VALUES ('%u',UNIX_TIMESTAMP(),UNIX_TIMESTAMP()+'%u','Trinity realmd','Failed login autoban',1)", + acc_id, WrongPassBanTime); + sLog.outBasic("[AuthChallenge] account %s got banned for '%u' seconds because it failed to authenticate '%u' times", + _login.c_str(), WrongPassBanTime, failed_logins); + } + else + { + std::string current_ip = GetRemoteAddress(); + dbRealmServer.escape_string(current_ip); + dbRealmServer.PExecute("INSERT INTO ip_banned VALUES ('%s',UNIX_TIMESTAMP(),UNIX_TIMESTAMP()+'%u','Trinity realmd','Failed login autoban')", + current_ip.c_str(), WrongPassBanTime); + sLog.outBasic("[AuthChallenge] IP %s got banned for '%u' seconds because account %s failed to authenticate '%u' times", + current_ip.c_str(), WrongPassBanTime, _login.c_str(), failed_logins); + } + } + delete loginfail; + } + } + } + return true; +} + +/// Reconnect Challenge command handler +bool AuthSocket::_HandleReconnectChallenge() +{ + DEBUG_LOG("Entering _HandleReconnectChallenge"); + if (ibuf.GetLength() < sizeof(sAuthLogonChallenge_C)) + return false; + + ///- Read the first 4 bytes (header) to get the length of the remaining of the packet + std::vector<uint8> buf; + buf.resize(4); + + ibuf.Read((char *)&buf[0], 4); + + EndianConvert(*((uint16*)(buf[0]))); + uint16 remaining = ((sAuthLogonChallenge_C *)&buf[0])->size; + DEBUG_LOG("[ReconnectChallenge] got header, body is %#04x bytes", remaining); + + if ((remaining < sizeof(sAuthLogonChallenge_C) - buf.size()) || (ibuf.GetLength() < remaining)) + return false; + + //No big fear of memory outage (size is int16, i.e. < 65536) + buf.resize(remaining + buf.size() + 1); + buf[buf.size() - 1] = 0; + sAuthLogonChallenge_C *ch = (sAuthLogonChallenge_C*)&buf[0]; + + ///- Read the remaining of the packet + ibuf.Read((char *)&buf[4], remaining); + DEBUG_LOG("[ReconnectChallenge] got full packet, %#04x bytes", ch->size); + DEBUG_LOG("[ReconnectChallenge] name(%d): '%s'", ch->I_len, ch->I); + + _login = (const char*)ch->I; + _safelogin = _login; + + QueryResult *result = dbRealmServer.PQuery ("SELECT sessionkey FROM account WHERE username = '%s'", _safelogin.c_str ()); + + // Stop if the account is not found + if (!result) + { + sLog.outError("[ERROR] user %s tried to login and we cannot find his session key in the database.", _login.c_str()); + SetCloseAndDelete(); + return false; + } + + Field* fields = result->Fetch (); + K.SetHexStr (fields[0].GetString ()); + delete result; + + ///- Sending response + ByteBuffer pkt; + pkt << (uint8) AUTH_RECONNECT_CHALLENGE; + pkt << (uint8) 0x00; + _reconnectProof.SetRand(16*8); + pkt.append(_reconnectProof.AsByteBuffer()); // 16 bytes random + pkt << (uint64) 0x00 << (uint64) 0x00; // 16 bytes zeros + SendBuf((char const*)pkt.contents(), pkt.size()); + return true; +} + +/// Reconnect Proof command handler +bool AuthSocket::_HandleReconnectProof() +{ + DEBUG_LOG("Entering _HandleReconnectProof"); + ///- Read the packet + if (ibuf.GetLength() < sizeof(sAuthReconnectProof_C)) + return false; + if (_login.empty() || !_reconnectProof.GetNumBytes() || !K.GetNumBytes()) + return false; + sAuthReconnectProof_C lp; + ibuf.Read((char *)&lp, sizeof(sAuthReconnectProof_C)); + + BigNumber t1; + t1.SetBinary(lp.R1, 16); + + Sha1Hash sha; + sha.Initialize(); + sha.UpdateData(_login); + sha.UpdateBigNumbers(&t1, &_reconnectProof, &K, NULL); + sha.Finalize(); + + if (!memcmp(sha.GetDigest(), lp.R2, SHA_DIGEST_LENGTH)) + { + ///- Sending response + ByteBuffer pkt; + pkt << (uint8) AUTH_RECONNECT_PROOF; + pkt << (uint8) 0x00; + pkt << (uint16) 0x00; // 2 bytes zeros + SendBuf((char const*)pkt.contents(), pkt.size()); + + ///- Set _authed to true! + _authed = true; + + return true; + } + else + { + sLog.outError("[ERROR] user %s tried to login, but session invalid.", _login.c_str()); + SetCloseAndDelete(); + return false; + } +} + +/// %Realm List command handler +bool AuthSocket::_HandleRealmList() +{ + DEBUG_LOG("Entering _HandleRealmList"); + if (ibuf.GetLength() < 5) + return false; + + ibuf.Remove(5); + + ///- Get the user id (else close the connection) + // No SQL injection (escaped user name) + + QueryResult *result = dbRealmServer.PQuery("SELECT id,sha_pass_hash FROM account WHERE username = '%s'",_safelogin.c_str()); + if(!result) + { + sLog.outError("[ERROR] user %s tried to login and we cannot find him in the database.",_login.c_str()); + SetCloseAndDelete(); + return false; + } + + uint32 id = (*result)[0].GetUInt32(); + std::string rI = (*result)[1].GetCppString(); + delete result; + + ///- Update realm list if need + m_realmList.UpdateIfNeed(); + + ///- Circle through realms in the RealmList and construct the return packet (including # of user characters in each realm) + ByteBuffer pkt; + pkt << (uint32) 0; + pkt << (uint16) m_realmList.size(); + RealmList::RealmMap::const_iterator i; + for( i = m_realmList.begin(); i != m_realmList.end(); i++ ) + { + uint8 AmountOfCharacters; + + // No SQL injection. id of realm is controlled by the database. + result = dbRealmServer.PQuery( "SELECT numchars FROM realmcharacters WHERE realmid = '%d' AND acctid='%u'",i->second.m_ID,id); + if( result ) + { + Field *fields = result->Fetch(); + AmountOfCharacters = fields[0].GetUInt8(); + delete result; + } + else + AmountOfCharacters = 0; + + uint8 lock = (i->second.allowedSecurityLevel > _accountSecurityLevel) ? 1 : 0; + + pkt << i->second.icon; // realm type + pkt << lock; // if 1, then realm locked + pkt << i->second.color; // if 2, then realm is offline + pkt << i->first; + pkt << i->second.address; + pkt << i->second.populationLevel; + pkt << AmountOfCharacters; + pkt << i->second.timezone; // realm category + pkt << (uint8) 0x2C; // unk, may be realm number/id? + } + pkt << (uint8) 0x10; + pkt << (uint8) 0x00; + + ByteBuffer hdr; + hdr << (uint8) REALM_LIST; + hdr << (uint16)pkt.size(); + hdr.append(pkt); + + SendBuf((char const*)hdr.contents(), hdr.size()); + + // Set check field before possible relogin to realm + _SetVSFields(rI); + return true; +} + +/// Resume patch transfer +bool AuthSocket::_HandleXferResume() +{ + DEBUG_LOG("Entering _HandleXferResume"); + ///- Check packet length and patch existence + if (ibuf.GetLength()<9 || !pPatch) + { + sLog.outError("Error while resuming patch transfer (wrong packet)"); + return false; + } + + ///- Launch a PatcherRunnable thread starting at given patch file offset + uint64 start; + ibuf.Remove(1); + ibuf.Read((char*)&start,sizeof(start)); + fseek(pPatch,start,0); + + ZThread::Thread u(new PatcherRunnable(this)); + return true; +} + +/// Cancel patch transfer +bool AuthSocket::_HandleXferCancel() +{ + DEBUG_LOG("Entering _HandleXferCancel"); + + ///- Close and delete the socket + ibuf.Remove(1); //clear input buffer + + //ZThread::Thread::sleep(15); + SetCloseAndDelete(); + + return true; +} + +/// Accept patch transfer +bool AuthSocket::_HandleXferAccept() +{ + DEBUG_LOG("Entering _HandleXferAccept"); + + ///- Check packet length and patch existence + if (!pPatch) + { + sLog.outError("Error while accepting patch transfer (wrong packet)"); + return false; + } + + ///- Launch a PatcherRunnable thread, starting at the begining of the patch file + ibuf.Remove(1); //clear input buffer + fseek(pPatch,0,0); + + ZThread::Thread u(new PatcherRunnable(this)); + + return true; +} + +/// Check if there is lag on the connection to the client +bool AuthSocket::IsLag() +{ + return (TCP_BUFSIZE_READ-GetOutputLength()< 2*ChunkSize); +} + +PatcherRunnable::PatcherRunnable(class AuthSocket * as) +{ + mySocket=as; +} + +/// Send content of patch file to the client +void PatcherRunnable::run() +{ + ZThread::Guard<ZThread::Mutex> g(mySocket->patcherLock); + XFER_DATA_STRUCT xfdata; + xfdata.opcode = XFER_DATA; + + while(!feof(mySocket->pPatch) && mySocket->Ready()) + { + ///- Wait until output buffer is reasonably empty + while(mySocket->Ready() && mySocket->IsLag()) + { + ZThread::Thread::sleep(1); + } + ///- And send content of the patch file to the client + xfdata.data_size=fread(&xfdata.data,1,ChunkSize,mySocket->pPatch); + mySocket->SendBuf((const char*)&xfdata,xfdata.data_size +(sizeof(XFER_DATA_STRUCT)-ChunkSize)); + } +} + +/// Preload MD5 hashes of existing patch files on server +#ifndef _WIN32 +#include <dirent.h> +#include <errno.h> +void Patcher::LoadPatchesInfo() +{ + DIR * dirp; + //int errno; + struct dirent * dp; + dirp = opendir("./patches/"); + if(!dirp) + return; + while (dirp) + { + errno = 0; + if ((dp = readdir(dirp)) != NULL) + { + int l=strlen(dp->d_name); + if(l<8)continue; + if(!memcmp(&dp->d_name[l-4],".mpq",4)) + LoadPatchMD5(dp->d_name); + } + else + { + if(errno != 0) + { + closedir(dirp); + return; + } + break; + } + } + + if(dirp) + closedir(dirp); +} + +#else +void Patcher::LoadPatchesInfo() +{ + WIN32_FIND_DATA fil; + HANDLE hFil=FindFirstFile("./patches/*.mpq",&fil); + if(hFil==INVALID_HANDLE_VALUE) + return; //no patches were found + + do + { + LoadPatchMD5(fil.cFileName); + } + while(FindNextFile(hFil,&fil)); +} +#endif + +/// Calculate and store MD5 hash for a given patch file +void Patcher::LoadPatchMD5(char * szFileName) +{ + ///- Try to open the patch file + std::string path = "./patches/"; + path += szFileName; + FILE * pPatch=fopen(path.c_str(),"rb"); + sLog.outDebug("Loading patch info from %s\n",path.c_str()); + if(!pPatch) + { + sLog.outError("Error loading patch %s\n",path.c_str()); + return; + } + + ///- Calculate the MD5 hash + MD5_CTX ctx; + MD5_Init(&ctx); + uint8* buf = new uint8[512*1024]; + + while (!feof(pPatch)) + { + size_t read = fread(buf, 1, 512*1024, pPatch); + MD5_Update(&ctx, buf, read); + } + delete [] buf; + fclose(pPatch); + + ///- Store the result in the internal patch hash map + _patches[path] = new PATCH_INFO; + MD5_Final((uint8 *)&_patches[path]->md5 , &ctx); +} + +/// Get cached MD5 hash for a given patch file +bool Patcher::GetHash(char * pat,uint8 mymd5[16]) +{ + for( Patches::iterator i = _patches.begin(); i != _patches.end(); i++ ) + if(!stricmp(pat,i->first.c_str () )) + { + memcpy(mymd5,i->second->md5,16); + return true; + } + + return false; +} + +/// Launch the patch hashing mechanism on object creation +Patcher::Patcher() +{ + LoadPatchesInfo(); +} + +/// Empty and delete the patch map on termination +Patcher::~Patcher() +{ + for(Patches::iterator i = _patches.begin(); i != _patches.end(); i++ ) + delete i->second; +} diff --git a/src/realmd/AuthSocket.h b/src/realmd/AuthSocket.h new file mode 100644 index 00000000000..f704283c215 --- /dev/null +++ b/src/realmd/AuthSocket.h @@ -0,0 +1,86 @@ +/* + * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/> + * + * Copyright (C) 2008 Trinity <http://www.trinitycore.org/> + * + * This program 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 2 of the License, or + * (at your option) any later version. + * + * This program 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 this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/// \addtogroup realmd +/// @{ +/// \file + +#ifndef _AUTHSOCKET_H +#define _AUTHSOCKET_H + +#include "Common.h" +#include "Auth/BigNumber.h" +#include "sockets/TcpSocket.h" +#include "sockets/SocketHandler.h" +#include "sockets/ListenSocket.h" +#include "sockets/Utility.h" +#include "sockets/Parse.h" +#include "sockets/Socket.h" +#include "zthread/Mutex.h" + +/// Handle login commands +class AuthSocket: public TcpSocket +{ + public: + const static int s_BYTE_SIZE = 32; + + AuthSocket(ISocketHandler& h); + ~AuthSocket(); + + void OnAccept(); + void OnRead(); + + bool _HandleLogonChallenge(); + bool _HandleLogonProof(); + bool _HandleReconnectChallenge(); + bool _HandleReconnectProof(); + bool _HandleRealmList(); + //data transfer handle for patch + + bool _HandleXferResume(); + bool _HandleXferCancel(); + bool _HandleXferAccept(); + + void _SetVSFields(const std::string& rI); + + FILE *pPatch; + ZThread::Mutex patcherLock; + bool IsLag(); + + private: + + BigNumber N, s, g, v; + BigNumber b, B; + BigNumber K; + BigNumber _reconnectProof; + + bool _authed; + + std::string _login; + std::string _safelogin; + + // Since GetLocaleByName() is _NOT_ bijective, we have to store the locale as a string. Otherwise we can't differ + // between enUS and enGB, which is important for the patch system + std::string _localizationName; + uint16 _build; + AccountTypes _accountSecurityLevel; +}; +#endif +/// @} diff --git a/src/realmd/Main.cpp b/src/realmd/Main.cpp new file mode 100644 index 00000000000..1476efbd4ec --- /dev/null +++ b/src/realmd/Main.cpp @@ -0,0 +1,345 @@ +/* + * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/> + * + * Copyright (C) 2008 Trinity <http://www.trinitycore.org/> + * + * This program 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 2 of the License, or + * (at your option) any later version. + * + * This program 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 this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/// \addtogroup realmd Realm Daemon +/// @{ +/// \file + +#include "Common.h" +#include "Database/DatabaseEnv.h" +#include "RealmList.h" + +#include "Config/ConfigEnv.h" +#include "Log.h" +#include "sockets/ListenSocket.h" +#include "AuthSocket.h" +#include "SystemConfig.h" +#include "Util.h" + +// Format is YYYYMMDDRR where RR is the change in the conf file +// for that day. +#ifndef _REALMDCONFVERSION +# define _REALMDCONFVERSION 2007062001 +#endif + +#ifndef _TRINITY_REALM_CONFIG +# define _TRINITY_REALM_CONFIG "realmd.conf" +#endif //_TRINITY_REALM_CONFIG + +#ifdef WIN32 +#include "ServiceWin32.h" +char serviceName[] = "realmd"; +char serviceLongName[] = "Trinity realm service"; +char serviceDescription[] = "Massive Network Game Object Server"; +/* + * -1 - not in service mode + * 0 - stopped + * 1 - running + * 2 - paused + */ +int m_ServiceStatus = -1; +#endif + +bool StartDB(std::string &dbstring); +void UnhookSignals(); +void HookSignals(); + +bool stopEvent = false; ///< Setting it to true stops the server +RealmList m_realmList; ///< Holds the list of realms for this server + +DatabaseType dbRealmServer; ///< Accessor to the realm server database + +/// Print out the usage string for this program on the console. +void usage(const char *prog) +{ + sLog.outString("Usage: \n %s [<options>]\n" + " -c config_file use config_file as configuration file\n\r" + #ifdef WIN32 + " Running as service functions:\n\r" + " --service run as service\n\r" + " -s install install service\n\r" + " -s uninstall uninstall service\n\r" + #endif + ,prog); +} + +/// Launch the realm server +extern int main(int argc, char **argv) +{ + ///- Command line parsing to get the configuration file name + char const* cfg_file = _TRINITY_REALM_CONFIG; + int c=1; + while( c < argc ) + { + if( strcmp(argv[c],"-c") == 0) + { + if( ++c >= argc ) + { + sLog.outError("Runtime-Error: -c option requires an input argument"); + usage(argv[0]); + return 1; + } + else + cfg_file = argv[c]; + } + + #ifdef WIN32 + //////////// + //Services// + //////////// + if( strcmp(argv[c],"-s") == 0) + { + if( ++c >= argc ) + { + sLog.outError("Runtime-Error: -s option requires an input argument"); + usage(argv[0]); + return 1; + } + if( strcmp(argv[c],"install") == 0) + { + if (WinServiceInstall()) + sLog.outString("Installing service"); + return 1; + } + else if( strcmp(argv[c],"uninstall") == 0) + { + if(WinServiceUninstall()) + sLog.outString("Uninstalling service"); + return 1; + } + else + { + sLog.outError("Runtime-Error: unsupported option %s",argv[c]); + usage(argv[0]); + return 1; + } + } + if( strcmp(argv[c],"--service") == 0) + { + WinServiceRun(); + } + //// + #endif + ++c; + } + + if (!sConfig.SetSource(cfg_file)) + { + sLog.outError("Could not find configuration file %s.", cfg_file); + return 1; + } + sLog.outString("Using configuration file %s.", cfg_file); + + ///- Check the version of the configuration file + uint32 confVersion = sConfig.GetIntDefault("ConfVersion", 0); + if (confVersion < _REALMDCONFVERSION) + { + sLog.outError("*****************************************************************************"); + sLog.outError(" WARNING: Your trinityrealm.conf version indicates your conf file is out of date!"); + sLog.outError(" Please check for updates, as your current default values may cause"); + sLog.outError(" strange behavior."); + sLog.outError("*****************************************************************************"); + clock_t pause = 3000 + clock(); + + while (pause > clock()) {} + } + + sLog.outString( "%s (realm-daemon)", _FULLVERSION ); + sLog.outString( "<Ctrl-C> to stop.\n" ); + + /// realmd PID file creation + std::string pidfile = sConfig.GetStringDefault("PidFile", ""); + if(!pidfile.empty()) + { + uint32 pid = CreatePIDFile(pidfile); + if( !pid ) + { + sLog.outError( "Cannot create PID file %s.\n", pidfile.c_str() ); + return 1; + } + + sLog.outString( "Daemon PID: %u\n", pid ); + } + + ///- Initialize the database connection + std::string dbstring; + if(!StartDB(dbstring)) + return 1; + + ///- Get the list of realms for the server + m_realmList.Initialize(sConfig.GetIntDefault("RealmsStateUpdateDelay", 20)); + if (m_realmList.size() == 0) + { + sLog.outError("No valid realms specified."); + return 1; + } + + ///- Launch the listening network socket + port_t rmport = sConfig.GetIntDefault( "RealmServerPort", DEFAULT_REALMSERVER_PORT ); + std::string bind_ip = sConfig.GetStringDefault("BindIP", "0.0.0.0"); + + SocketHandler h; + ListenSocket<AuthSocket> authListenSocket(h); + if ( authListenSocket.Bind(bind_ip.c_str(),rmport)) + { + sLog.outError( "Trinity realm can not bind to %s:%d",bind_ip.c_str(), rmport ); + return 1; + } + + h.Add(&authListenSocket); + + ///- Catch termination signals + HookSignals(); + + ///- Handle affinity for multiple processors and process priority on Windows + #ifdef WIN32 + { + HANDLE hProcess = GetCurrentProcess(); + + uint32 Aff = sConfig.GetIntDefault("UseProcessors", 0); + if(Aff > 0) + { + ULONG_PTR appAff; + ULONG_PTR sysAff; + + if(GetProcessAffinityMask(hProcess,&appAff,&sysAff)) + { + ULONG_PTR curAff = Aff & appAff; // remove non accessible processors + + if(!curAff ) + { + sLog.outError("Processors marked in UseProcessors bitmask (hex) %x not accessible for realmd. Accessible processors bitmask (hex): %x",Aff,appAff); + } + else + { + if(SetProcessAffinityMask(hProcess,curAff)) + sLog.outString("Using processors (bitmask, hex): %x", curAff); + else + sLog.outError("Can't set used processors (hex): %x", curAff); + } + } + sLog.outString(); + } + + bool Prio = sConfig.GetBoolDefault("ProcessPriority", false); + + if(Prio) + { + if(SetPriorityClass(hProcess,HIGH_PRIORITY_CLASS)) + sLog.outString("TrinityRealm process priority class set to HIGH"); + else + sLog.outError("ERROR: Can't set realmd process priority class."); + sLog.outString(); + } + } + #endif + + // maximum counter for next ping + uint32 numLoops = (sConfig.GetIntDefault( "MaxPingTime", 30 ) * (MINUTE * 1000000 / 100000)); + uint32 loopCounter = 0; + + ///- Wait for termination signal + while (!stopEvent) + { + + h.Select(0, 100000); + + if( (++loopCounter) == numLoops ) + { + loopCounter = 0; + sLog.outDetail("Ping MySQL to keep connection alive"); + delete dbRealmServer.Query("SELECT 1 FROM realmlist LIMIT 1"); + } +#ifdef WIN32 + if (m_ServiceStatus == 0) stopEvent = true; + while (m_ServiceStatus == 2) Sleep(1000); +#endif + } + + ///- Wait for the delay thread to exit + dbRealmServer.HaltDelayThread(); + + ///- Remove signal handling before leaving + UnhookSignals(); + + sLog.outString( "Halting process..." ); + return 0; +} + +/// Handle termination signals +/** Put the global variable stopEvent to 'true' if a termination signal is caught **/ +void OnSignal(int s) +{ + switch (s) + { + case SIGINT: + case SIGTERM: + stopEvent = true; + break; + #ifdef _WIN32 + case SIGBREAK: + stopEvent = true; + break; + #endif + } + + signal(s, OnSignal); +} + +/// Initialize connection to the database +bool StartDB(std::string &dbstring) +{ + if(!sConfig.GetString("LoginDatabaseInfo", &dbstring)) + { + sLog.outError("Database not specified"); + return false; + } + + sLog.outString("Database: %s", dbstring.c_str() ); + if(!dbRealmServer.Initialize(dbstring.c_str())) + { + sLog.outError("Cannot connect to database"); + return false; + } + + return true; +} + +/// Define hook 'OnSignal' for all termination signals +void HookSignals() +{ + signal(SIGINT, OnSignal); + signal(SIGTERM, OnSignal); + #ifdef _WIN32 + signal(SIGBREAK, OnSignal); + #endif +} + +/// Unhook the signals before leaving +void UnhookSignals() +{ + signal(SIGINT, 0); + signal(SIGTERM, 0); + #ifdef _WIN32 + signal(SIGBREAK, 0); + #endif +} + +/// @} diff --git a/src/realmd/Makefile.am b/src/realmd/Makefile.am new file mode 100644 index 00000000000..9baeed6c2b1 --- /dev/null +++ b/src/realmd/Makefile.am @@ -0,0 +1,72 @@ +# Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/> +# +# Copyright (C) 2008 Trinity <http://www.trinitycore.org/> +# +# This program 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 2 of the License, or +# (at your option) any later version. +# +# This program 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 this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +## Process this file with automake to produce Makefile.in + +## Build realm list daemon as standalone program +bin_PROGRAMS = trinity-realm + +## Preprocessor flags +trinity_realm_CPPFLAGS = \ +$(MYSQL_INCLUDES) \ +$(POSTGRE_INCLUDES) \ +$(TRINI_INCLUDES) \ +-I$(top_srcdir)/dep/include \ +-I$(top_srcdir)/src/framework \ +-I$(top_srcdir)/src/shared \ +-D_TRINITY_REALM_CONFIG='"$(sysconfdir)/trinityrealm.conf"' + +## Sources +trinity_realm_SOURCES = \ +$(srcdir)/AuthCodes.h \ +$(srcdir)/AuthSocket.cpp \ +$(srcdir)/AuthSocket.h \ +$(srcdir)/Main.cpp \ +$(srcdir)/RealmList.cpp \ +$(srcdir)/RealmList.h + +## Convenience libs to add +trinity_realm_LDADD = \ +$(top_builddir)/src/shared/libshared.a \ +$(top_builddir)/src/framework/libmangosframework.a \ +$(top_builddir)/dep/src/sockets/libmangossockets.a \ +$(top_builddir)/dep/src/zthread/libZThread.la + +## Linker flags +trinity_realm_LDFLAGS = $(MYSQL_LIBS) $(POSTGRE_LIBS) $(ZLIB) $(COMPATLIB) $(SSLLIB) $(TRINI_LIBS) + +## Additional files to install +sysconf_DATA = \ + trinityrealm.conf.dist + +## Prevend overwrite of the config file, if its already installed +install-data-hook: + @list='$(sysconf_DATA)'; for p in $$list; do \ + dest=`echo $$p | sed -e s/.dist//`; \ + if test -f $(DESTDIR)$(sysconfdir)/$$dest; then \ + echo "$@ will not overwrite existing $(DESTDIR)$(sysconfdir)/$$dest"; \ + else \ + echo " $(INSTALL_DATA) $(srcdir)/$$p $(DESTDIR)$(sysconfdir)/$$dest"; \ + $(INSTALL_DATA) $(srcdir)/$$p $(DESTDIR)$(sysconfdir)/$$dest; \ + fi; \ + done + +## Additional files to include when running 'make dist' +EXTRA_DIST = trinityrealm.conf.dist + + diff --git a/src/realmd/RealmList.cpp b/src/realmd/RealmList.cpp new file mode 100644 index 00000000000..97fdfbdd91f --- /dev/null +++ b/src/realmd/RealmList.cpp @@ -0,0 +1,102 @@ +/* + * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/> + * + * Copyright (C) 2008 Trinity <http://www.trinitycore.org/> + * + * This program 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 2 of the License, or + * (at your option) any later version. + * + * This program 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 this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/** \file + \ingroup realmd +*/ + +#include "Common.h" +#include "RealmList.h" +#include "Policies/SingletonImp.h" +#include "Database/DatabaseEnv.h" + +INSTANTIATE_SINGLETON_1( RealmList ); + +extern DatabaseType dbRealmServer; + +RealmList::RealmList( ) : m_UpdateInterval(0), m_NextUpdateTime(time(NULL)) +{ +} + +/// Load the realm list from the database +void RealmList::Initialize(uint32 updateInterval) +{ + m_UpdateInterval = updateInterval; + + ///- Get the content of the realmlist table in the database + UpdateRealms(true); +} + +void RealmList::UpdateRealm( uint32 ID, const std::string& name, const std::string& address, uint32 port, uint8 icon, uint8 color, uint8 timezone, AccountTypes allowedSecurityLevel, float popu) +{ + ///- Create new if not exist or update existed + Realm& realm = m_realms[name]; + + realm.m_ID = ID; + realm.name = name; + realm.icon = icon; + realm.color = color; + realm.timezone = timezone; + realm.allowedSecurityLevel = allowedSecurityLevel; + realm.populationLevel = popu; + + ///- Append port to IP address. + std::ostringstream ss; + ss << address << ":" << port; + realm.address = ss.str(); +} + +void RealmList::UpdateIfNeed() +{ + // maybe disabled or updated recently + if(!m_UpdateInterval || m_NextUpdateTime > time(NULL)) + return; + + m_NextUpdateTime = time(NULL) + m_UpdateInterval; + + // Clears Realm list + m_realms.clear(); + + // Get the content of the realmlist table in the database + UpdateRealms(false); +} + +void RealmList::UpdateRealms(bool init) +{ + sLog.outDetail("Updating Realm List..."); + + QueryResult *result = dbRealmServer.Query( "SELECT id, name, address, port, icon, color, timezone, allowedSecurityLevel, population FROM realmlist WHERE color <> 3 ORDER BY name" ); + + ///- Circle through results and add them to the realm map + if(result) + { + do + { + Field *fields = result->Fetch(); + + uint8 allowedSecurityLevel = fields[7].GetUInt8(); + + UpdateRealm(fields[0].GetUInt32(), fields[1].GetCppString(),fields[2].GetCppString(),fields[3].GetUInt32(),fields[4].GetUInt8(), fields[5].GetUInt8(), fields[6].GetUInt8(), (allowedSecurityLevel <= SEC_ADMINISTRATOR ? AccountTypes(allowedSecurityLevel) : SEC_ADMINISTRATOR), fields[8].GetFloat() ); + if(init) + sLog.outString("Added realm \"%s\".", fields[1].GetString()); + } while( result->NextRow() ); + delete result; + } +} diff --git a/src/realmd/RealmList.h b/src/realmd/RealmList.h new file mode 100644 index 00000000000..9cb5380bd25 --- /dev/null +++ b/src/realmd/RealmList.h @@ -0,0 +1,68 @@ +/* + * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/> + * + * Copyright (C) 2008 Trinity <http://www.trinitycore.org/> + * + * This program 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 2 of the License, or + * (at your option) any later version. + * + * This program 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 this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/// \addtogroup realmd +/// @{ +/// \file + +#ifndef _REALMLIST_H +#define _REALMLIST_H + +#include "Common.h" + +/// Storage object for a realm +struct Realm +{ + std::string name; + std::string address; + uint8 icon; + uint8 color; + uint8 timezone; + uint32 m_ID; + AccountTypes allowedSecurityLevel; + float populationLevel; +}; + +/// Storage object for the list of realms on the server +class RealmList +{ + public: + typedef std::map<std::string, Realm> RealmMap; + + RealmList(); + ~RealmList() {} + + void Initialize(uint32 updateInterval); + + void UpdateIfNeed(); + + RealmMap::const_iterator begin() const { return m_realms.begin(); } + RealmMap::const_iterator end() const { return m_realms.end(); } + uint32 size() const { return m_realms.size(); } + private: + void UpdateRealms(bool init); + void UpdateRealm( uint32 ID, const std::string& name, const std::string& address, uint32 port, uint8 icon, uint8 color, uint8 timezone, AccountTypes allowedSecurityLevel, float popu); + private: + RealmMap m_realms; ///< Internal map of realms + uint32 m_UpdateInterval; + time_t m_NextUpdateTime; +}; +#endif +/// @} diff --git a/src/realmd/Realmd.rc b/src/realmd/Realmd.rc new file mode 100644 index 00000000000..33c7eef719a --- /dev/null +++ b/src/realmd/Realmd.rc @@ -0,0 +1,85 @@ +/* + * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/> + * + * This program 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 2 of the License, or + * (at your option) any later version. + * + * This program 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 this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "resource.h" + +#define APSTUDIO_READONLY_SYMBOLS +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 2 resource. +// +#include "windows.h" //"afxres.h" + +///////////////////////////////////////////////////////////////////////////// +#undef APSTUDIO_READONLY_SYMBOLS + +///////////////////////////////////////////////////////////////////////////// +// +// Icon +// + +// Icon with lowest ID value placed first to ensure application icon +// remains consistent on all systems. +IDI_APPICON ICON "TrinityRealm.ico" + +///////////////////////////////////////////////////////////////////////////// +// Neutre (Par défaut système) resources + +#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_NEUSD) +#ifdef _WIN32 +LANGUAGE LANG_NEUTRAL, SUBLANG_SYS_DEFAULT +#pragma code_page(1252) +#endif //_WIN32 + +///////////////////////////////////////////////////////////////////////////// +// +// Version +// + +VS_VERSION_INFO VERSIONINFO + FILEVERSION 0,4,6743,685 + PRODUCTVERSION 0,4,6743,685 + FILEFLAGSMASK 0x17L +#ifdef _DEBUG + FILEFLAGS 0x1L +#else + FILEFLAGS 0x0L +#endif + FILEOS 0x0L + FILETYPE 0x0L + FILESUBTYPE 0x0L +BEGIN + BLOCK "StringFileInfo" + BEGIN + BLOCK "080004b0" + BEGIN + VALUE "FileDescription", "TrinityRealm" + VALUE "FileVersion", "0, 4, 6743, 685" + VALUE "InternalName", "TrinityRealm" + VALUE "LegalCopyright", "Copyright (C) 2008" + VALUE "OriginalFilename", "TrinityRealm.exe" + VALUE "ProductName", "TrinityRealm" + VALUE "ProductVersion", "0, 4, 6743, 685" + END + END + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0x800, 1200 + END +END +#endif diff --git a/src/realmd/TrinityRealm.ico b/src/realmd/TrinityRealm.ico Binary files differnew file mode 100644 index 00000000000..da318f48a8c --- /dev/null +++ b/src/realmd/TrinityRealm.ico diff --git a/src/realmd/realmd.conf.dist.in b/src/realmd/realmd.conf.dist.in new file mode 100644 index 00000000000..72ef1c9012e --- /dev/null +++ b/src/realmd/realmd.conf.dist.in @@ -0,0 +1,116 @@ +########################################## +# Trinity Core realmd configuration file # +########################################## +ConfVersion=2007062001 + +################################################################################################################### +# REALMD SETTINGS +# +# LoginDatabaseInfo +# Database connection settings for the realm server. +# Default: hostname;port;username;password;database +# .;somenumber;username;password;database - use named pipes at Windows +# Named pipes: mySQL required adding "enable-named-pipe" to [mysqld] section my.ini +# .;/path/to/unix_socket;username;password;database - use Unix sockets at Unix/Linux +# Unix sockets: experimental, not tested +# +# LogsDir +# Logs directory setting. +# Important: Logs dir must exists, or all logs be disable +# Default: "" - no log directory prefix, if used log names isn't absolute path then logs will be +# stored in current directory for run program. +# +# MaxPingTime +# Settings for maximum database-ping interval (minutes between pings) +# +# RealmServerPort +# Default RealmServerPort +# +# BindIP +# Bind Realm Server to IP/hostname +# +# PidFile +# Realmd daemon PID file +# Default: "" - do not create PID file +# "./realmd.pid" - create PID file (recommended name) +# +# LogLevel +# Server console level of logging +# 0 = Minimum; 1 = Error; 2 = Detail; 3 = Full/Debug +# Default: 0 +# +# LogTime +# Include time in server console output [hh:mm:ss] +# Default: 0 (no time) +# 1 (print time) +# +# LogFile +# Logfile name +# Default: "realmd.log" +# "" - empty name disable creating log file +# +# LogTimestamp +# Logfile with timestamp of server start in name +# Default: 0 - no timestamp in name +# 1 - add timestamp in name in form Logname_YYYY-MM-DD_HH-MM-SS.Ext for Logname.Ext +# +# LogFileLevel +# Server file level of logging +# 0 = Minimum; 1 = Error; 2 = Detail; 3 = Full/Debug +# Default: 0 +# +# LogColors +# Color for messages (format "normal_color details_color debug_color error_color) +# Colors: 0 - BLACK, 1 - RED, 2 - GREEN, 3 - BROWN, 4 - BLUE, 5 - MAGENTA, 6 - CYAN, 7 - GREY, +# 8 - YELLOW, 9 - LRED, 10 - LGREEN, 11 - LBLUE, 12 - LMAGENTA, 13 - LCYAN, 14 - WHITE +# Default: "" - none colors +# "13 7 11 9" - for example :) +# +# UseProcessors +# Used processors mask for multi-processors system (Used only at Windows) +# Default: 0 (selected by OS) +# number (bitmask value of selected processors) +# +# ProcessPriority +# Process proirity setting (Used only at Windows) +# Default: 1 (HIGH) +# 0 (Normal) +# +# RealmsStateUpdateDelay +# Realm list Update up delay (updated at realm list request if delay expired). +# Default: 20 +# 0 (Disabled) +# +# WrongPass.MaxCount +# Number of login attemps with wrong password before the account or IP is banned +# Default: 0 (Never ban) +# +# WrongPass.BanTime +# Duration of the ban in seconds (0 means permanent ban) +# Default: 600 +# +# WrongPass.BanType +# Ban the IP or account on which login is attempted +# Default: 0 (Ban IP) +# 1 (Ban Account) +# +################################################################################################################### + +LoginDatabaseInfo = "127.0.0.1;3306;trinity;trinity;realmd" +LogsDir = "" +MaxPingTime = 30 +RealmServerPort = 3724 +BindIP = "0.0.0.0" +PidFile = "" +LogLevel = 0 +LogTime = 0 +LogFile = "realmd.log" +LogTimestamp = 0 +LogFileLevel = 0 +LogColors = "" +UseProcessors = 0 +ProcessPriority = 1 +RealmsStateUpdateDelay = 20 +WrongPass.MaxCount = 0 +WrongPass.BanTime = 600 +WrongPass.BanType = 0 diff --git a/src/trinityrealm/resource.h b/src/realmd/resource.h index 7e7d8e4b76f..7e7d8e4b76f 100644 --- a/src/trinityrealm/resource.h +++ b/src/realmd/resource.h diff --git a/src/shared/MemoryLeaks.h b/src/shared/MemoryLeaks.h new file mode 100644 index 00000000000..fceb9d6f444 --- /dev/null +++ b/src/shared/MemoryLeaks.h @@ -0,0 +1,48 @@ +/* + * Copyright (C) 2005-2008 MaNGOS <http://getmangos.com/> + * + * This program 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 2 of the License, or + * (at your option) any later version. + * + * This program 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 this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef MANGOSSERVER_MEMORY_H +#define MANGOSSERVER_MEMORY_H + +#include "Platform/CompilerDefs.h" + +#if COMPILER == COMPILER_MICROSOFT + +#ifndef _WIN64 +// Visual Leak Detector support enabled +//#include <vld/vld.h> +// standard Visual Studio leak check disabled, +//# define _CRTDBG_MAP_ALLOC +//# include <stdlib.h> +//# include <crtdbg.h> +#else +# define _CRTDBG_MAP_ALLOC +# include <stdlib.h> +# include <crtdbg.h> +#endif + +#endif + + +#include "Policies/Singleton.h" + +struct MemoryManager : public MaNGOS::Singleton < MemoryManager > +{ + MemoryManager(); +}; +#endif diff --git a/src/shared/revision_nr.h b/src/shared/revision_nr.h new file mode 100644 index 00000000000..5d6f980e5f4 --- /dev/null +++ b/src/shared/revision_nr.h @@ -0,0 +1,4 @@ +#ifndef __REVISION_NR_H__ +#define __REVISION_NR_H__ + #define REVISION_NR "6940" +#endif // __REVISION_NR_H__ |