diff options
author | megamage <none@none> | 2009-01-04 16:17:46 -0600 |
---|---|---|
committer | megamage <none@none> | 2009-01-04 16:17:46 -0600 |
commit | eb5a7b02eef6fe13684dfe14faf048bbce1d0f83 (patch) | |
tree | 395bd3da004c0628956d64335ee0e2c404d4f772 /src | |
parent | 820e0214faf4645ef1bc411aa5cc581392db62db (diff) | |
parent | 15d25f45ae920a4418f3fcb1e6828c923a3aedc1 (diff) |
*Merge with Trinity 783.
--HG--
branch : trunk
Diffstat (limited to 'src')
36 files changed, 3079 insertions, 415 deletions
diff --git a/src/bindings/scripts/Makefile.am b/src/bindings/scripts/Makefile.am index 34ef03bb3e0..74e1674619a 100644 --- a/src/bindings/scripts/Makefile.am +++ b/src/bindings/scripts/Makefile.am @@ -375,6 +375,7 @@ scripts/zone/temple_of_ahnqiraj/mob_anubisath_sentinel.cpp \ scripts/zone/terokkar_forest/terokkar_forest.cpp \ scripts/zone/thunder_bluff/thunder_bluff.cpp \ scripts/zone/tirisfal_glades/tirisfal_glades.cpp \ +scripts/zone/thousand_needles/thousand_needles.cpp \ scripts/zone/uldaman/boss_archaedas.cpp \ scripts/zone/uldaman/instance_uldaman.cpp \ scripts/zone/uldaman/boss_ironaya.cpp \ diff --git a/src/bindings/scripts/ScriptMgr.cpp b/src/bindings/scripts/ScriptMgr.cpp index 9024498d00b..d28a17b9104 100644 --- a/src/bindings/scripts/ScriptMgr.cpp +++ b/src/bindings/scripts/ScriptMgr.cpp @@ -545,6 +545,8 @@ extern void AddSC_instance_temple_of_ahnqiraj(); extern void AddSC_terokkar_forest(); //Thousand Needles +extern void AddSC_thousand_needles(); + //Thunder Bluff extern void AddSC_thunder_bluff(); @@ -1749,6 +1751,8 @@ void ScriptsInit() AddSC_terokkar_forest(); //Thousand Needles + AddSC_thousand_needles(); + //Thunder Bluff AddSC_thunder_bluff(); diff --git a/src/bindings/scripts/VC71/71ScriptDev2.vcproj b/src/bindings/scripts/VC71/71ScriptDev2.vcproj index bb84b59dad5..a9ed124ebec 100644 --- a/src/bindings/scripts/VC71/71ScriptDev2.vcproj +++ b/src/bindings/scripts/VC71/71ScriptDev2.vcproj @@ -1072,6 +1072,10 @@ <Filter Name="Thousand Needles" > + <File + RelativePath="..\scripts\zone\thousand_needles\thousand_needles.cpp" + > + </File> </Filter> <Filter Name="Silithus" diff --git a/src/bindings/scripts/VC80/80ScriptDev2.vcproj b/src/bindings/scripts/VC80/80ScriptDev2.vcproj index d6123c0ef43..5d84995e2ed 100644 --- a/src/bindings/scripts/VC80/80ScriptDev2.vcproj +++ b/src/bindings/scripts/VC80/80ScriptDev2.vcproj @@ -1334,6 +1334,10 @@ <Filter Name="Thousand Needles" > + <File + RelativePath="..\scripts\zone\thousand_needles\thousand_needles.cpp" + > + </File> </Filter> <Filter Name="Silithus" diff --git a/src/bindings/scripts/VC90/90ScriptDev2.vcproj b/src/bindings/scripts/VC90/90ScriptDev2.vcproj index f325a567d13..635c7c67bf9 100644 --- a/src/bindings/scripts/VC90/90ScriptDev2.vcproj +++ b/src/bindings/scripts/VC90/90ScriptDev2.vcproj @@ -1354,6 +1354,10 @@ <Filter Name="Thousand Needles" > + <File + RelativePath="..\scripts\zone\thousand_needles\thousand_needles.cpp" + > + </File> </Filter> <Filter Name="Silithus" diff --git a/src/bindings/scripts/include/sc_creature.cpp b/src/bindings/scripts/include/sc_creature.cpp index b54f91a113d..bb7f98c9f7b 100644 --- a/src/bindings/scripts/include/sc_creature.cpp +++ b/src/bindings/scripts/include/sc_creature.cpp @@ -432,7 +432,7 @@ void ScriptedAI::SelectUnitList(std::list<Unit*> &targetList, uint32 num, Select std::list<HostilReference*> m_threatlist = m_creature->getThreatManager().getThreatList(); std::list<HostilReference*>::iterator i; Unit *target; - while(m_threatlist.size()) + while(m_threatlist.size() && num) { if(targetType == SELECT_TARGET_BOTTOMAGGRO) { @@ -455,6 +455,7 @@ void ScriptedAI::SelectUnitList(std::list<Unit*> &targetList, uint32 num, Select continue; } targetList.push_back(target); + --num; } } } diff --git a/src/bindings/scripts/scripts/zone/aunchindoun/shadow_labyrinth/boss_grandmaster_vorpil.cpp b/src/bindings/scripts/scripts/zone/aunchindoun/shadow_labyrinth/boss_grandmaster_vorpil.cpp index 06947376619..35f1cffa8e8 100644 --- a/src/bindings/scripts/scripts/zone/aunchindoun/shadow_labyrinth/boss_grandmaster_vorpil.cpp +++ b/src/bindings/scripts/scripts/zone/aunchindoun/shadow_labyrinth/boss_grandmaster_vorpil.cpp @@ -1,4 +1,4 @@ -/* Copyright (C) 2006 - 2008 ScriptDev2 <https://scriptdev2.svn.sourceforge.net/> +/* Copyright (C) 2006 - 2009 ScriptDev2 <https://scriptdev2.svn.sourceforge.net/> * 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 @@ -36,22 +36,20 @@ EndScriptData */ #define SPELL_RAIN_OF_FIRE 33617 #define H_SPELL_RAIN_OF_FIRE 39363 -#define SPELL_DRAWN_SHADOWS 33563 +#define SPELL_DRAW_SHADOWS 33563 #define SPELL_SHADOWBOLT_VOLLEY 33841 +#define SPELL_BANISH 38791 #define MOB_VOID_TRAVELER 19226 #define SPELL_SACRIFICE 33587 #define SPELL_SHADOW_NOVA 33846 -#define SPELL_HEALVORPIL 33783 -#define H_SPELL_HEALVORPIL 39364 +#define SPELL_EMPOWERING_SHADOWS 33783 +#define H_SPELL_EMPOWERING_SHADOWS 39364 #define MOB_VOID_PORTAL 19224 #define SPELL_VOID_PORTAL_VISUAL 33569 -float VorpilPosition[1][3] = -{ - {-252.8820,-264.3030,17.1} -}; +float VorpilPosition[3] = {-252.8820,-264.3030,17.1}; float VoidPortalCoords[5][3] = { @@ -62,76 +60,154 @@ float VoidPortalCoords[5][3] = {-261.4533, -297.3298, 17.1} }; +class EmpoweringShadowsAura: public Aura +{ + public: + EmpoweringShadowsAura(SpellEntry *spell, uint32 eff, int32 *bp, Unit *target, Unit *caster) : Aura(spell, eff, bp, target, caster, NULL) {} +}; + +struct TRINITY_DLL_DECL mob_voidtravelerAI : public ScriptedAI +{ + mob_voidtravelerAI(Creature *c) : ScriptedAI(c) + { + HeroicMode = m_creature->GetMap()->IsHeroic(); + Reset(); + } + + bool HeroicMode; + Unit *Vorpil; + uint32 move; + bool sacrificed; + + void Reset() + { + Vorpil = NULL; + move = 0; + sacrificed = false; + } + + void Aggro(Unit *who){} + + void UpdateAI(const uint32 diff) + { + if(!Vorpil) + { + m_creature->DealDamage(m_creature, m_creature->GetMaxHealth(), NULL, DIRECT_DAMAGE, SPELL_SCHOOL_MASK_NORMAL, NULL, false); + return; + } + if(move < diff) + { + if(sacrificed) + { + SpellEntry *spell = (SpellEntry *)GetSpellStore()->LookupEntry(HeroicMode?H_SPELL_EMPOWERING_SHADOWS:SPELL_EMPOWERING_SHADOWS); + if( spell ) + Vorpil->AddAura(new EmpoweringShadowsAura(spell, 0, NULL, Vorpil, m_creature)); + Vorpil->SetHealth(Vorpil->GetHealth()+Vorpil->GetMaxHealth()/25); + DoCast(m_creature, SPELL_SHADOW_NOVA, true); + m_creature->DealDamage(m_creature, m_creature->GetMaxHealth(), NULL, DIRECT_DAMAGE, SPELL_SCHOOL_MASK_NORMAL, NULL, false); + return; + } + m_creature->GetMotionMaster()->MoveFollow(Vorpil,0,0); + if(m_creature->GetDistance(Vorpil) < 3) + { + DoCast(m_creature, SPELL_SACRIFICE, false); + sacrificed = true; + move = 500; + return; + } + if(!Vorpil->isInCombat() || Vorpil->isDead()) + { + m_creature->DealDamage(m_creature, m_creature->GetMaxHealth(), NULL, DIRECT_DAMAGE, SPELL_SCHOOL_MASK_NORMAL, NULL, false); + return; + } + move = 1000; + }else move -= diff; + } +}; +CreatureAI* GetAI_mob_voidtraveler(Creature *_Creature) +{ + return new mob_voidtravelerAI (_Creature); +} + struct TRINITY_DLL_DECL boss_grandmaster_vorpilAI : public ScriptedAI { boss_grandmaster_vorpilAI(Creature *c) : ScriptedAI(c) { pInstance = ((ScriptedInstance*)c->GetInstanceData()); + HeroicMode = m_creature->GetMap()->IsHeroic(); Intro = false; Reset(); } ScriptedInstance *pInstance; - bool Intro; + bool Intro, HelpYell; bool sumportals; bool HeroicMode; uint32 ShadowBoltVolley_Timer; - uint32 DrawnShadows_Timer; - uint32 sumportals_Timer; + uint32 DrawShadows_Timer; uint32 summonTraveler_Timer; - uint64 PortalsGuid[5]; + uint32 banish_Timer; + uint64 PortalsGuid[5]; + + void Reset() + { + ShadowBoltVolley_Timer = 15000; + DrawShadows_Timer = 45000; + summonTraveler_Timer = 90000; + banish_Timer = 17000; + HelpYell = false; + destroyPortals(); + if(pInstance) + pInstance->SetData(DATA_GRANDMASTERVORPILEVENT, NOT_STARTED); + } void summonPortals() { - for (int i = 0;i<5;i++) + if(!sumportals) { - Creature *Portal = NULL; - Portal = m_creature->SummonCreature(MOB_VOID_PORTAL,VoidPortalCoords[i][0],VoidPortalCoords[i][1],VoidPortalCoords[i][2],0,TEMPSUMMON_CORPSE_DESPAWN,3000000); - PortalsGuid[i] = Portal->GetGUID(); - Portal->CastSpell(Portal,SPELL_VOID_PORTAL_VISUAL,false); + for (int i = 0;i<5;i++) + { + Creature *Portal = NULL; + Portal = m_creature->SummonCreature(MOB_VOID_PORTAL,VoidPortalCoords[i][0],VoidPortalCoords[i][1],VoidPortalCoords[i][2],0,TEMPSUMMON_CORPSE_DESPAWN,3000000); + PortalsGuid[i] = Portal->GetGUID(); + Portal->CastSpell(Portal,SPELL_VOID_PORTAL_VISUAL,false); + } + sumportals = true; + summonTraveler_Timer = 5000; } - sumportals = true; - summonTraveler_Timer = 5000; } void destroyPortals() { - for (int i = 0;i < 5; i ++) + if(sumportals) { - Unit *Portal = Unit::GetUnit((*m_creature), PortalsGuid[i]); - if (Portal) - if (Portal->isAlive()) + for (int i = 0;i < 5; i ++) + { + Unit *Portal = Unit::GetUnit((*m_creature), PortalsGuid[i]); + if (Portal && Portal->isAlive()) Portal->DealDamage(Portal, Portal->GetHealth(), NULL, DIRECT_DAMAGE, SPELL_SCHOOL_MASK_NORMAL, NULL, false); - PortalsGuid[i] = 0; - } + PortalsGuid[i] = 0; + } + sumportals = false; + } } void spawnVoidTraveler() { - srand( (unsigned) time(NULL) ) ; int pos = rand()%5; - Creature *traveler; - traveler = m_creature->SummonCreature(MOB_VOID_TRAVELER,VoidPortalCoords[pos][0],VoidPortalCoords[pos][1],VoidPortalCoords[pos][2],0,TEMPSUMMON_CORPSE_TIMED_DESPAWN,10000); + m_creature->SummonCreature(MOB_VOID_TRAVELER,VoidPortalCoords[pos][0],VoidPortalCoords[pos][1],VoidPortalCoords[pos][2],0,TEMPSUMMON_CORPSE_TIMED_DESPAWN,5000); + if(!HelpYell) + { + DoScriptText(SAY_HELP, m_creature); + HelpYell = true; + } } - void Reset() + void JustSummoned(Creature *summoned) { - HeroicMode = m_creature->GetMap()->IsHeroic(); - if( HeroicMode ) - debug_log("SD2: creature %u is in heroic mode",m_creature->GetEntry()); - - ShadowBoltVolley_Timer = 15000; - DrawnShadows_Timer = 45000; - sumportals_Timer = 10000; - summonTraveler_Timer = 90000; - - InCombat = false; - sumportals = false; - destroyPortals(); - - if(pInstance) - pInstance->SetData(DATA_GRANDMASTERVORPILEVENT, NOT_STARTED); + if (summoned && summoned->GetEntry() == MOB_VOID_TRAVELER) + ((mob_voidtravelerAI*)summoned->AI())->Vorpil = m_creature; } void KilledUnit(Unit *victim) @@ -151,7 +227,7 @@ struct TRINITY_DLL_DECL boss_grandmaster_vorpilAI : public ScriptedAI pInstance->SetData(DATA_GRANDMASTERVORPILEVENT, DONE); } - void StartEvent() + void Aggro(Unit *who) { switch(rand()%3) { @@ -159,55 +235,27 @@ struct TRINITY_DLL_DECL boss_grandmaster_vorpilAI : public ScriptedAI case 1: DoScriptText(SAY_AGGRO2, m_creature); break; case 2: DoScriptText(SAY_AGGRO3, m_creature); break; } - + summonPortals(); if(pInstance) pInstance->SetData(DATA_GRANDMASTERVORPILEVENT, IN_PROGRESS); } - void Aggro(Unit *who) - { - if(!InCombat) - { - InCombat = true; - StartEvent(); - } - } - void MoveInLineOfSight(Unit *who) { - if (!who || m_creature->getVictim()) - return; - - if (who->isTargetableForAttack() && who->isInAccessiblePlaceFor(m_creature) && m_creature->IsHostileTo(who)) - { - float attackRadius = m_creature->GetAttackDistance(who); - if (m_creature->IsWithinDistInMap(who, attackRadius) && m_creature->GetDistanceZ(who) <= CREATURE_Z_ATTACK_RANGE && m_creature->IsWithinLOSInMap(who)) - { - AttackStart(who); - } - } - else if (!Intro && m_creature->IsWithinLOSInMap(who)&& m_creature->IsWithinDistInMap(who, 100) ) //not sure about right radius + if(who && !m_creature->getVictim() && m_creature->canStartAttack(who)) + AttackStart(who); + if (!Intro && who && m_creature->IsWithinLOSInMap(who)&& m_creature->IsWithinDistInMap(who, 100) && m_creature->IsHostileTo(who)) { DoScriptText(SAY_INTRO, m_creature); Intro = true; } - } - + void UpdateAI(const uint32 diff) { if (!m_creature->SelectHostilTarget() || !m_creature->getVictim()) return; - - if (!sumportals) - if (sumportals_Timer < diff) - { - DoScriptText(SAY_HELP, m_creature); - summonPortals(); - sumportals_Timer = 1000000; - - }else sumportals_Timer -= diff; if (ShadowBoltVolley_Timer < diff) { @@ -215,23 +263,41 @@ struct TRINITY_DLL_DECL boss_grandmaster_vorpilAI : public ScriptedAI ShadowBoltVolley_Timer = 15000; }else ShadowBoltVolley_Timer -= diff; - if ( DrawnShadows_Timer < diff) + if (HeroicMode && banish_Timer < diff) + { + Unit *target = SelectUnit(SELECT_TARGET_RANDOM,0,30,false); + if (target) + { + DoCast(target,SPELL_BANISH); + banish_Timer = 16000; + } + }else banish_Timer -= diff; + + if ( DrawShadows_Timer < diff) { - DoTeleportAll(VorpilPosition[0][0],VorpilPosition[0][1],VorpilPosition[0][2],0); - m_creature->Relocate(VorpilPosition[0][0],VorpilPosition[0][1],VorpilPosition[0][2],0); - DoCast(m_creature,SPELL_DRAWN_SHADOWS,true); + Map *map = m_creature->GetMap(); + Map::PlayerList const &PlayerList = map->GetPlayers(); + for(Map::PlayerList::const_iterator i = PlayerList.begin(); i != PlayerList.end(); ++i) + if (Player* i_pl = i->getSource()) + if (i_pl->isAlive() && !i_pl->HasAura(SPELL_BANISH,0)) + i_pl->TeleportTo(m_creature->GetMapId(), VorpilPosition[0],VorpilPosition[1],VorpilPosition[2], 0, TELE_TO_NOT_LEAVE_COMBAT); - if(!HeroicMode) DoCast(m_creature,SPELL_RAIN_OF_FIRE); - else DoCast(m_creature,H_SPELL_RAIN_OF_FIRE); + m_creature->Relocate(VorpilPosition[0],VorpilPosition[1],VorpilPosition[2]); + DoCast(m_creature,SPELL_DRAW_SHADOWS,true); + + DoCast(m_creature,HeroicMode?H_SPELL_RAIN_OF_FIRE:SPELL_RAIN_OF_FIRE); ShadowBoltVolley_Timer = 6000; - DrawnShadows_Timer = 45000; - }else DrawnShadows_Timer -= diff; + DrawShadows_Timer = 30000; + }else DrawShadows_Timer -= diff; if ( summonTraveler_Timer < diff) { spawnVoidTraveler(); summonTraveler_Timer = 10000; + //enrage at 20% + if((m_creature->GetHealth()*5) < m_creature->GetMaxHealth()) + summonTraveler_Timer = 5000; }else summonTraveler_Timer -=diff; DoMeleeAttackIfReady(); @@ -242,118 +308,6 @@ CreatureAI* GetAI_boss_grandmaster_vorpil(Creature *_Creature) return new boss_grandmaster_vorpilAI (_Creature); } -struct TRINITY_DLL_DECL mob_voidtravelerAI : public ScriptedAI -{ - mob_voidtravelerAI(Creature *c) : ScriptedAI(c) - { - pInstance = ((ScriptedInstance*)c->GetInstanceData()); - Reset(); - } - - ScriptedInstance *pInstance; - uint32 VorpilCheck_Timer; - uint32 eventCheck_Timer; - bool sacrifice; - bool sacrificed; - bool oneTarget; - bool HeroicMode; - - uint32 target_timer; - - void Reset() - { - HeroicMode = m_creature->GetMap()->IsHeroic(); - if( HeroicMode ) - debug_log("SD2: creature %u is in heroic mode",m_creature->GetEntry()); - - VorpilCheck_Timer = 5000; - eventCheck_Timer = 1000; - target_timer = 2000; - oneTarget = false; - sacrificed = false; - sacrifice = false; - } - void EnterEvadeMode(){} - void Aggro(Unit *who) {} - void AttackStart(Unit *who){} - void MoveInLineOfSight(Unit *who){} - - void UpdateAI(const uint32 diff) - { - if (eventCheck_Timer < diff) - { - if(pInstance) - { - Unit *Vorpil = Unit::GetUnit((*m_creature),pInstance->GetData64(DATA_GRANDMASTERVORPIL)); - if (Vorpil) - { - if (Vorpil->isDead()) - { - m_creature->DealDamage(m_creature, m_creature->GetHealth(), NULL, DIRECT_DAMAGE, SPELL_SCHOOL_MASK_NORMAL, NULL, false); - } - - if (Vorpil->getVictim()) - { - if((*m_creature).GetMotionMaster()->GetCurrentMovementGeneratorType() != TARGETED_MOTION_TYPE) - (*m_creature).GetMotionMaster()->MoveFollow(Vorpil,1,0); - } - } - if(pInstance->GetData(DATA_GRANDMASTERVORPILEVENT) != IN_PROGRESS) - { - m_creature->DealDamage(m_creature, m_creature->GetHealth(), NULL, DIRECT_DAMAGE, SPELL_SCHOOL_MASK_NORMAL, NULL, false); - } - } - eventCheck_Timer = 5000; - }else eventCheck_Timer -=diff; - - if (VorpilCheck_Timer < diff) - { - if (pInstance) - { - if (!sacrificed) - { - if (!sacrifice) - { - Unit *Vorpil = Unit::GetUnit((*m_creature), pInstance->GetData64(DATA_GRANDMASTERVORPIL)); - if (Vorpil) - if (Vorpil->isAlive()) - { - if (m_creature->IsWithinDistInMap(Vorpil, 2)) - { - sacrifice = true; - DoCast(m_creature,SPELL_SACRIFICE); - VorpilCheck_Timer = 2000; - } - } - - if (!sacrifice) - VorpilCheck_Timer = 3000; - } - else - { - Unit *Vorpil = Unit::GetUnit((*m_creature),pInstance->GetData64(DATA_GRANDMASTERVORPIL)); - if (Vorpil) - if (Vorpil->isAlive()) - { - if(!HeroicMode) Vorpil->CastSpell(Vorpil,SPELL_HEALVORPIL,true); - else Vorpil->CastSpell(Vorpil,H_SPELL_HEALVORPIL,true); - }; - DoCast(m_creature,SPELL_SHADOW_NOVA); - sacrificed = true; - m_creature->DealDamage(m_creature, m_creature->GetHealth(), NULL, DIRECT_DAMAGE, SPELL_SCHOOL_MASK_NORMAL, NULL, false); - VorpilCheck_Timer = 100000; - } - } - } - }else VorpilCheck_Timer -= diff; - } -}; - -CreatureAI* GetAI_mob_voidtraveler(Creature *_Creature) -{ - return new mob_voidtravelerAI (_Creature); -} - void AddSC_boss_grandmaster_vorpil() { Script *newscript; diff --git a/src/bindings/scripts/scripts/zone/thousand_needles/thousand_needles.cpp b/src/bindings/scripts/scripts/zone/thousand_needles/thousand_needles.cpp new file mode 100644 index 00000000000..6d230a49e3b --- /dev/null +++ b/src/bindings/scripts/scripts/zone/thousand_needles/thousand_needles.cpp @@ -0,0 +1,200 @@ +/* Copyright (C) 2006 - 2008 ScriptDev2 <https://scriptdev2.svn.sourceforge.net/> + * 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 + */ + +/* ScriptData +SDName: Thousand Needles +SD%Complete: 100 +SDComment: Support for Quest: 4770 +SDCategory: Thousand Needles +EndScriptData */ + +/* ContentData +npc_swiftmountain +EndContentData */ + +#include "precompiled.h" +#include "../../npc/npc_escortAI.h" + +#define SAY_READY -1000147 +#define SAY_AGGRO -1000148 +#define SAY_FINISH -1000149 + +#define QUEST_HOMEWARD_BOUND 4770 +#define ENTRY_WYVERN 4107 + + + +struct TRINITY_DLL_DECL npc_swiftmountainAI : public npc_escortAI +{ +npc_swiftmountainAI(Creature *c) : npc_escortAI(c) {Reset();} + + void WaypointReached(uint32 i) + { + Unit* player = Unit::GetUnit((*m_creature), PlayerGUID); + + if (!player) + return; + + switch (i) + { + case 46: + DoScriptText(SAY_AGGRO, m_creature, player); + break; + case 47: + m_creature->SummonCreature(ENTRY_WYVERN, -5016.45, -935.01, -5.46, 5.36,TEMPSUMMON_TIMED_DESPAWN_OUT_OF_COMBAT, 25000); + m_creature->SummonCreature(ENTRY_WYVERN, -5001.98, -934.96, -5.55, 3.18,TEMPSUMMON_TIMED_DESPAWN_OUT_OF_COMBAT, 25000); + m_creature->SummonCreature(ENTRY_WYVERN, -4999.06, -949.61, -5.42, 2.04,TEMPSUMMON_TIMED_DESPAWN_OUT_OF_COMBAT, 25000); + break; + case 70: + DoScriptText(SAY_FINISH, m_creature, player); + if (player && player->GetTypeId() == TYPEID_PLAYER) + ((Player*)player)->GroupEventHappens(QUEST_HOMEWARD_BOUND,m_creature); + break; + + } + } + + void Reset() + { + + } + + void Aggro(Unit* who) + {} + + void JustSummoned(Creature* summoned) + { + summoned->AI()->AttackStart(m_creature); + } + + void JustDied(Unit* killer) + { + if (PlayerGUID) + { + if (Unit* player = Unit::GetUnit((*m_creature), PlayerGUID)) + ((Player*)player)->FailQuest(QUEST_HOMEWARD_BOUND); + } + } + + void UpdateAI(const uint32 diff) + { + npc_escortAI::UpdateAI(diff); + } +}; + +bool QuestAccept_npc_swiftmountain(Player* player, Creature* creature, Quest const* quest) +{ + if (quest->GetQuestId() == QUEST_HOMEWARD_BOUND) + { + ((npc_escortAI*)(creature->AI()))->Start(true, true, false, player->GetGUID()); + DoScriptText(SAY_READY, creature, player); + // Change faction so mobs attack + creature->setFaction(775); + } + + return true; +} + +CreatureAI* GetAI_npc_swiftmountain(Creature *_Creature) +{ + npc_swiftmountainAI* thisAI = new npc_swiftmountainAI(_Creature); + + thisAI->AddWaypoint(0, -5156.69, -1220.49, 48.78, 5000); + thisAI->AddWaypoint(1, -5157.12, -1220.13, 48.67); + thisAI->AddWaypoint(2, -5171.7, -1205.36, 47.43); + thisAI->AddWaypoint(3, -5174.08, -1197.7, 46.90); + thisAI->AddWaypoint(4, -5178.24, -1183.78, 45.97); + thisAI->AddWaypoint(5, -5181.52, -1171.03, 45.29); + thisAI->AddWaypoint(6, -5184.29, -1159.21, 44.62); + thisAI->AddWaypoint(7, -5184.84, -1152.95, 44.84); + thisAI->AddWaypoint(8, -5182.04, -1142.83, 44.07); + thisAI->AddWaypoint(9, -5178.44, -1133.57, 43.91); + thisAI->AddWaypoint(10, -5176.68, -1129.48, 43.81); + thisAI->AddWaypoint(11, -5164.85, -1123.33, 43.99); + thisAI->AddWaypoint(12, -5153.07, -1117.7, 43.66); + thisAI->AddWaypoint(13, -5143.52, -1113.14, 43.78); + thisAI->AddWaypoint(14, -5135.86, -1104.42, 47.23); + thisAI->AddWaypoint(15, -5129.86, -1097.22, 49.52); + thisAI->AddWaypoint(16, -5127.48, -1087.29, 49.03); + thisAI->AddWaypoint(17, -5127.57, -1080.4, 46.64); + thisAI->AddWaypoint(18, -5129.65, -1077.58, 45.29); + thisAI->AddWaypoint(19, -5135.86, -1069.12, 39.53); + thisAI->AddWaypoint(20, -5141.97, -1060.58, 32.70); + thisAI->AddWaypoint(21, -5145.99, -1054.85, 28.98); + thisAI->AddWaypoint(22, -5147.08, -1050.35, 26.36); + thisAI->AddWaypoint(23, -5147.5, -1043.37, 21.84); + thisAI->AddWaypoint(24, -5147.68, -1036.37, 17.05); + thisAI->AddWaypoint(25, -5147.68, -1029.37, 14.59); + thisAI->AddWaypoint(26, -5144.62, -1023.9, 11.67); + thisAI->AddWaypoint(27, -5138.67, -1020.23, 7.81); + thisAI->AddWaypoint(28, -5135.6, -1018.55, 6.19); + thisAI->AddWaypoint(29, -5126.25, -1014.76, 1.08); + thisAI->AddWaypoint(30, -5120.03, -1013.12, -1.11); + thisAI->AddWaypoint(31, -5112.3, -1027.65, -5.39); + thisAI->AddWaypoint(32, -5106.99, -1023.09, -5.10); + thisAI->AddWaypoint(33, -5099.07, -1016.19, -4.92); + thisAI->AddWaypoint(34, -5091.23, -1009.21, -5.22); + thisAI->AddWaypoint(35, -5083.62, -1001.97, -5.22); + thisAI->AddWaypoint(36, -5076.1, -994.652, -4.92); + thisAI->AddWaypoint(37, -5066.71, -985.507, -4.97); + thisAI->AddWaypoint(38, -5065.13, -978.689, -5.02); + thisAI->AddWaypoint(39, -5062.33, -968.57, -5.08); + thisAI->AddWaypoint(40, -5059.46, -958.469, -5.16); + thisAI->AddWaypoint(41, -5056.59, -948.375, -5.10); + thisAI->AddWaypoint(42, -5053.73, -938.274, -5.69); + thisAI->AddWaypoint(43, -5043.06, -934.822, -5.35); + thisAI->AddWaypoint(44, -5029.35, -932.007, -5.27); + thisAI->AddWaypoint(45, -5024.58, -933.781, -5.40); + thisAI->AddWaypoint(46, -5019.13, -938.172, -5.54); + thisAI->AddWaypoint(47, -5011, -944.812, -5.47); + thisAI->AddWaypoint(48, -5002.86, -951.455, -5.44); + thisAI->AddWaypoint(49, -4994.73, -958.099, -5.41); + thisAI->AddWaypoint(50, -4990.57, -963.782, -5.33); + thisAI->AddWaypoint(51, -4987.43, -970.041, -5.17); + thisAI->AddWaypoint(52, -4981.41, -982.678, -5.10); + thisAI->AddWaypoint(53, -4977.03, -992.221, -4.97); + thisAI->AddWaypoint(54, -4972.6, -1001.74, -5.24); + thisAI->AddWaypoint(55, -4968.15, -1011.25, -5.49); + thisAI->AddWaypoint(56, -4963.75, -1020.78, -5.07); + thisAI->AddWaypoint(57, -4959.3, -1030.3, -5.28); + thisAI->AddWaypoint(58, -4956.28, -1036.61, -5.84); + thisAI->AddWaypoint(59, -4952.05, -1043.75, -9.04); + thisAI->AddWaypoint(60, -4943.8, -1055.06, -17.91); + thisAI->AddWaypoint(61, -4939.47, -1055.61, -20.73); + thisAI->AddWaypoint(62, -4929.16, -1053.64, -25.65); + thisAI->AddWaypoint(63, -4922.28, -1052.37, -28.72); + thisAI->AddWaypoint(64, -4913.26, -1051.3, -31.80); + thisAI->AddWaypoint(65, -4903.54, -1054.17, -33.48); + thisAI->AddWaypoint(66, -4905.95, -1056.71, -33.68); + thisAI->AddWaypoint(67, -4913.18, -1064.32, -39.46); + thisAI->AddWaypoint(68, -4925.27, -1076.98, -47.39); + thisAI->AddWaypoint(69, -4932.68, -1084.42, -51.00); + thisAI->AddWaypoint(70, -4938.3, -1100.41, -50.71, 5000); + thisAI->AddWaypoint(71, -4937.34, -1102.87, -49.82); + + return (CreatureAI*)thisAI; +} + +void AddSC_thousand_needles() +{ + Script *newscript; + + newscript = new Script; + newscript->Name = "npc_swiftmountain"; + newscript->GetAI = &GetAI_npc_swiftmountain; + newscript->pQuestAccept = &QuestAccept_npc_swiftmountain; + newscript->RegisterSelf(); +}
\ No newline at end of file diff --git a/src/game/AccountMgr.cpp b/src/game/AccountMgr.cpp index dedc1150ba4..adf47f8acab 100644 --- a/src/game/AccountMgr.cpp +++ b/src/game/AccountMgr.cpp @@ -26,9 +26,9 @@ #include "Util.h" #ifdef DO_POSTGRESQL -extern DatabasePostgre loginDatabase; +extern DatabasePostgre LoginDatabase; #else -extern DatabaseMysql loginDatabase; +extern DatabaseMysql LoginDatabase; #endif INSTANTIATE_SINGLETON_1(AccountMgr); @@ -47,26 +47,26 @@ AccountOpResult AccountMgr::CreateAccount(std::string username, std::string pass normilizeString(username); normilizeString(password); - loginDatabase.escape_string(username); - loginDatabase.escape_string(password); + LoginDatabase.escape_string(username); + LoginDatabase.escape_string(password); - QueryResult *result = loginDatabase.PQuery("SELECT 1 FROM account WHERE username = '%s'", username.c_str()); + QueryResult *result = LoginDatabase.PQuery("SELECT 1 FROM account WHERE username = '%s'", username.c_str()); if(result) { delete result; return AOR_NAME_ALREDY_EXIST; // username does already exist } - if(!loginDatabase.PExecute("INSERT INTO account(username,sha_pass_hash,joindate) VALUES('%s',SHA1(CONCAT('%s',':','%s')),NOW())", username.c_str(), username.c_str(), password.c_str())) + if(!LoginDatabase.PExecute("INSERT INTO account(username,sha_pass_hash,joindate) VALUES('%s',SHA1(CONCAT('%s',':','%s')),NOW())", username.c_str(), username.c_str(), password.c_str())) return AOR_DB_INTERNAL_ERROR; // unexpected error - loginDatabase.Execute("INSERT INTO realmcharacters (realmid, acctid, numchars) SELECT realmlist.id, account.id, 0 FROM realmlist,account LEFT JOIN realmcharacters ON acctid=account.id WHERE acctid IS NULL"); + LoginDatabase.Execute("INSERT INTO realmcharacters (realmid, acctid, numchars) SELECT realmlist.id, account.id, 0 FROM realmlist,account LEFT JOIN realmcharacters ON acctid=account.id WHERE acctid IS NULL"); return AOR_OK; // everything's fine } AccountOpResult AccountMgr::DeleteAccount(uint32 accid) { - QueryResult *result = loginDatabase.PQuery("SELECT 1 FROM account WHERE id='%d'", accid); + QueryResult *result = LoginDatabase.PQuery("SELECT 1 FROM account WHERE id='%d'", accid); if(!result) return AOR_NAME_NOT_EXIST; // account doesn't exist delete result; @@ -97,13 +97,13 @@ AccountOpResult AccountMgr::DeleteAccount(uint32 accid) // table realm specific but common for all characters of account for realm CharacterDatabase.PExecute("DELETE FROM character_tutorial WHERE account = '%u'",accid); - loginDatabase.BeginTransaction(); + LoginDatabase.BeginTransaction(); bool res = - loginDatabase.PExecute("DELETE FROM account WHERE id='%d'", accid) && - loginDatabase.PExecute("DELETE FROM realmcharacters WHERE acctid='%d'", accid); + LoginDatabase.PExecute("DELETE FROM account WHERE id='%d'", accid) && + LoginDatabase.PExecute("DELETE FROM realmcharacters WHERE acctid='%d'", accid); - loginDatabase.CommitTransaction(); + LoginDatabase.CommitTransaction(); if(!res) return AOR_DB_INTERNAL_ERROR; // unexpected error; @@ -113,7 +113,7 @@ AccountOpResult AccountMgr::DeleteAccount(uint32 accid) AccountOpResult AccountMgr::ChangeUsername(uint32 accid, std::string new_uname, std::string new_passwd) { - QueryResult *result = loginDatabase.PQuery("SELECT 1 FROM account WHERE id='%d'", accid); + QueryResult *result = LoginDatabase.PQuery("SELECT 1 FROM account WHERE id='%d'", accid); if(!result) return AOR_NAME_NOT_EXIST; // account doesn't exist delete result; @@ -127,9 +127,9 @@ AccountOpResult AccountMgr::ChangeUsername(uint32 accid, std::string new_uname, normilizeString(new_uname); normilizeString(new_passwd); - loginDatabase.escape_string(new_uname); - loginDatabase.escape_string(new_passwd); - if(!loginDatabase.PExecute("UPDATE account SET username='%s',sha_pass_hash=SHA1(CONCAT('%s',':','%s')) WHERE id='%d'", new_uname.c_str(), new_uname.c_str(), new_passwd.c_str(), accid)) + LoginDatabase.escape_string(new_uname); + LoginDatabase.escape_string(new_passwd); + if(!LoginDatabase.PExecute("UPDATE account SET username='%s',sha_pass_hash=SHA1(CONCAT('%s',':','%s')) WHERE id='%d'", new_uname.c_str(), new_uname.c_str(), new_passwd.c_str(), accid)) return AOR_DB_INTERNAL_ERROR; // unexpected error return AOR_OK; @@ -137,7 +137,7 @@ AccountOpResult AccountMgr::ChangeUsername(uint32 accid, std::string new_uname, AccountOpResult AccountMgr::ChangePassword(uint32 accid, std::string new_passwd) { - QueryResult *result = loginDatabase.PQuery("SELECT 1 FROM account WHERE id='%d'", accid); + QueryResult *result = LoginDatabase.PQuery("SELECT 1 FROM account WHERE id='%d'", accid); if(!result) return AOR_NAME_NOT_EXIST; // account doesn't exist delete result; @@ -147,8 +147,8 @@ AccountOpResult AccountMgr::ChangePassword(uint32 accid, std::string new_passwd) normilizeString(new_passwd); - loginDatabase.escape_string(new_passwd); - if(!loginDatabase.PExecute("UPDATE account SET sha_pass_hash=SHA1(CONCAT(username,':','%s')) WHERE id='%d'", new_passwd.c_str(), accid)) + LoginDatabase.escape_string(new_passwd); + if(!LoginDatabase.PExecute("UPDATE account SET sha_pass_hash=SHA1(CONCAT(username,':','%s')) WHERE id='%d'", new_passwd.c_str(), accid)) return AOR_DB_INTERNAL_ERROR; // unexpected error return AOR_OK; @@ -156,8 +156,8 @@ AccountOpResult AccountMgr::ChangePassword(uint32 accid, std::string new_passwd) uint32 AccountMgr::GetId(std::string username) { - loginDatabase.escape_string(username); - QueryResult *result = loginDatabase.PQuery("SELECT id FROM account WHERE username = '%s'", username.c_str()); + LoginDatabase.escape_string(username); + QueryResult *result = LoginDatabase.PQuery("SELECT id FROM account WHERE username = '%s'", username.c_str()); if(!result) return 0; else @@ -170,7 +170,7 @@ uint32 AccountMgr::GetId(std::string username) uint32 AccountMgr::GetSecurity(uint32 acc_id) { - QueryResult *result = loginDatabase.PQuery("SELECT gmlevel FROM account WHERE id = '%u'", acc_id); + QueryResult *result = LoginDatabase.PQuery("SELECT gmlevel FROM account WHERE id = '%u'", acc_id); if(result) { uint32 sec = (*result)[0].GetUInt32(); @@ -183,7 +183,7 @@ uint32 AccountMgr::GetSecurity(uint32 acc_id) bool AccountMgr::GetName(uint32 acc_id, std::string &name) { - QueryResult *result = loginDatabase.PQuery("SELECT username FROM account WHERE id = '%u'", acc_id); + QueryResult *result = LoginDatabase.PQuery("SELECT username FROM account WHERE id = '%u'", acc_id); if(result) { name = (*result)[0].GetCppString(); @@ -197,9 +197,9 @@ bool AccountMgr::GetName(uint32 acc_id, std::string &name) bool AccountMgr::CheckPassword(uint32 accid, std::string passwd) { normilizeString(passwd); - loginDatabase.escape_string(passwd); + LoginDatabase.escape_string(passwd); - QueryResult *result = loginDatabase.PQuery("SELECT 1 FROM account WHERE id='%d' AND sha_pass_hash=SHA1(CONCAT(username,':','%s'))", accid, passwd.c_str()); + QueryResult *result = LoginDatabase.PQuery("SELECT 1 FROM account WHERE id='%d' AND sha_pass_hash=SHA1(CONCAT(username,':','%s'))", accid, passwd.c_str()); if (result) { delete result; diff --git a/src/game/CharacterHandler.cpp b/src/game/CharacterHandler.cpp index 5d091d55b1c..b51f24800ad 100644 --- a/src/game/CharacterHandler.cpp +++ b/src/game/CharacterHandler.cpp @@ -123,7 +123,7 @@ class CharacterHandler void WorldSession::HandleCharEnum(QueryResult * result) { // keys can be non cleared if player open realm list and close it by 'cancel' - loginDatabase.PExecute("UPDATE account SET v = '0', s = '0' WHERE id = '%u'", GetAccountId()); + LoginDatabase.PExecute("UPDATE account SET v = '0', s = '0' WHERE id = '%u'", GetAccountId()); WorldPacket data(SMSG_CHAR_ENUM, 100); // we guess size @@ -281,7 +281,7 @@ void WorldSession::HandleCharCreateOpcode( WorldPacket & recv_data ) return; } - QueryResult *resultacct = loginDatabase.PQuery("SELECT SUM(numchars) FROM realmcharacters WHERE acctid = '%d'", GetAccountId()); + QueryResult *resultacct = LoginDatabase.PQuery("SELECT SUM(numchars) FROM realmcharacters WHERE acctid = '%d'", GetAccountId()); if ( resultacct ) { Field *fields=resultacct->Fetch(); @@ -464,8 +464,8 @@ void WorldSession::HandleCharCreateOpcode( WorldPacket & recv_data ) pNewChar->SaveToDB(); charcount+=1; - loginDatabase.PExecute("DELETE FROM realmcharacters WHERE acctid= '%d' AND realmid = '%d'", GetAccountId(), realmID); - loginDatabase.PExecute("INSERT INTO realmcharacters (numchars, acctid, realmid) VALUES (%u, %u, %u)", charcount, GetAccountId(), realmID); + LoginDatabase.PExecute("DELETE FROM realmcharacters WHERE acctid= '%d' AND realmid = '%d'", GetAccountId(), realmID); + LoginDatabase.PExecute("INSERT INTO realmcharacters (numchars, acctid, realmid) VALUES (%u, %u, %u)", charcount, GetAccountId(), realmID); delete pNewChar; // created only to call SaveToDB() @@ -742,7 +742,7 @@ void WorldSession::HandlePlayerLogin(LoginQueryHolder * holder) pCurrChar->SendInitialPacketsAfterAddToMap(); CharacterDatabase.PExecute("UPDATE characters SET online = 1 WHERE guid = '%u'", pCurrChar->GetGUIDLow()); - loginDatabase.PExecute("UPDATE account SET online = 1 WHERE id = '%u'", GetAccountId()); + LoginDatabase.PExecute("UPDATE account SET online = 1 WHERE id = '%u'", GetAccountId()); pCurrChar->SetInGameTime( getMSTime() ); // announce group about member online (must be after add to player list to receive announce to self) diff --git a/src/game/Chat.cpp b/src/game/Chat.cpp index 1d45afef5ea..d5a59360035 100644 --- a/src/game/Chat.cpp +++ b/src/game/Chat.cpp @@ -304,7 +304,7 @@ ChatCommand * ChatHandler::getCommandTable() { "locales_page_text", SEC_ADMINISTRATOR, true, &ChatHandler::HandleReloadLocalesPageTextCommand, "", NULL }, { "locales_quest", SEC_ADMINISTRATOR, true, &ChatHandler::HandleReloadLocalesQuestCommand, "", NULL }, { "waypoint_scripts", SEC_ADMINISTRATOR, true, &ChatHandler::HandleReloadWpScriptsCommand, "", NULL }, - { "tickets", SEC_ADMINISTRATOR, true, &ChatHandler::HandleGMTicketReloadCommand, "", NULL }, + { "gm_tickets", SEC_ADMINISTRATOR, true, &ChatHandler::HandleGMTicketReloadCommand, "", NULL }, { NULL, 0, false, NULL, "", NULL } }; diff --git a/src/game/Level0.cpp b/src/game/Level0.cpp index 75f7a257caf..63a015f9802 100644 --- a/src/game/Level0.cpp +++ b/src/game/Level0.cpp @@ -197,7 +197,7 @@ bool ChatHandler::HandlePasswordCommand(const char* args) std::string password_new = new_pass; std::string password_new_c = new_pass_c; - if (password_new != password_new_c) + if (strcmp(new_pass, new_pass_c) != 0) { SendSysMessage (LANG_NEW_PASSWORDS_NOT_MATCH); SetSentErrorMessage (true); @@ -243,14 +243,14 @@ bool ChatHandler::HandleLockAccountCommand(const char* args) std::string argstr = (char*)args; if (argstr == "on") { - loginDatabase.PExecute( "UPDATE account SET locked = '1' WHERE id = '%d'",m_session->GetAccountId()); + LoginDatabase.PExecute( "UPDATE account SET locked = '1' WHERE id = '%d'",m_session->GetAccountId()); PSendSysMessage(LANG_COMMAND_ACCLOCKLOCKED); return true; } if (argstr == "off") { - loginDatabase.PExecute( "UPDATE account SET locked = '0' WHERE id = '%d'",m_session->GetAccountId()); + LoginDatabase.PExecute( "UPDATE account SET locked = '0' WHERE id = '%d'",m_session->GetAccountId()); PSendSysMessage(LANG_COMMAND_ACCLOCKUNLOCKED); return true; } diff --git a/src/game/Level1.cpp b/src/game/Level1.cpp index 4489cbf1579..ba68df16f7f 100644 --- a/src/game/Level1.cpp +++ b/src/game/Level1.cpp @@ -275,19 +275,20 @@ bool ChatHandler::HandleGMTicketListCommand(const char* args) SendSysMessage(LANG_COMMAND_TICKETSHOWLIST); for(GmTicketList::iterator itr = ticketmgr.GM_TicketList.begin(); itr != ticketmgr.GM_TicketList.end(); ++itr) { - if((*itr)->closed == 1) + if((*itr)->closed != 0) continue; std::stringstream message; message << "|cff00ff00Ticket|r: |cff00ccff" << (*itr)->guid; message << ".|r |cff00ff00created by:|r |cff00ccff" << (*itr)->name; message << ".|r |cff00ff00Last change:|r |cff00ccff " << secsToTimeString(time(NULL) - (*itr)->timestamp, true, false) << " ago."; - if((*itr)->assignedToGM != 0 && objmgr.GetPlayer((*itr)->assignedToGM)) + if((*itr)->assignedToGM != 0) { - std::string gmname = objmgr.GetPlayer((*itr)->assignedToGM)->GetName(); + std::string gmname; + objmgr.GetPlayerNameByGUID((*itr)->assignedToGM, gmname); message << "|r |cff00ff00Assigned to:|r |cff00ccff " << gmname; } - SendGlobalGMSysMessage(message.str().c_str()); + SendSysMessage(message.str().c_str()); } return true; } @@ -298,7 +299,7 @@ bool ChatHandler::HandleGMTicketListOnlineCommand(const char* args) SendSysMessage(LANG_COMMAND_TICKETSHOWONLINELIST); for(GmTicketList::iterator itr = ticketmgr.GM_TicketList.begin(); itr != ticketmgr.GM_TicketList.end(); ++itr) { - if((*itr)->closed == 1 || !objmgr.GetPlayer((*itr)->playerGuid)) + if((*itr)->closed != 0 || !objmgr.GetPlayer((*itr)->playerGuid)) continue; std::stringstream message; @@ -307,10 +308,11 @@ bool ChatHandler::HandleGMTicketListOnlineCommand(const char* args) message << ".|r |cff00ff00Last change:|r |cff00ccff " << secsToTimeString((time(NULL) - (*itr)->timestamp), true, false) << " ago."; if((*itr)->assignedToGM != 0 && objmgr.GetPlayer((*itr)->assignedToGM)) { - std::string gmname = objmgr.GetPlayer((*itr)->assignedToGM)->GetName(); + std::string gmname; + objmgr.GetPlayerNameByGUID((*itr)->assignedToGM, gmname); message << "|r |cff00ff00Assigned to:|r |cff00ccff " << gmname; } - SendGlobalGMSysMessage(message.str().c_str()); + SendSysMessage(message.str().c_str()); } return true; } @@ -334,7 +336,8 @@ bool ChatHandler::HandleGMTicketGetByIdCommand(const char* args) message << ".|r |cff00ff00Last change:|r |cff00ccff " << secsToTimeString((time(NULL)-ticket->timestamp), true, false) << " ago."; if(ticket->assignedToGM != 0 && objmgr.GetPlayer(ticket->assignedToGM)) { - std::string gmname = objmgr.GetPlayer(ticket->assignedToGM)->GetName(); + std::string gmname; + objmgr.GetPlayerNameByGUID(ticket->assignedToGM, gmname); message << "|r |cff00ff00Assigned to:|r |cff00ccff " << gmname; } message << "|r\n|cff00ff00Message:|r " << ticket->message; @@ -342,7 +345,7 @@ bool ChatHandler::HandleGMTicketGetByIdCommand(const char* args) { message << "|r |cff00ff00Comment:|r |cff00ccff " << ticket->comment; } - PSendSysMessage(message.str().c_str()); + SendSysMessage(message.str().c_str()); return true; } @@ -364,7 +367,8 @@ bool ChatHandler::HandleGMTicketGetByNameCommand(const char* args) message << ".|r |cff00ff00Last change:|r |cff00ccff " << secsToTimeString((time(NULL)-ticket->timestamp), true, false) << " ago."; if(ticket->assignedToGM != 0 && objmgr.GetPlayer(ticket->assignedToGM)) { - std::string gmname = objmgr.GetPlayer(ticket->assignedToGM)->GetName(); + std::string gmname; + objmgr.GetPlayerNameByGUID(ticket->assignedToGM, gmname); message << "|r |cff00ff00Assigned to:|r |cff00ccff " << gmname; } message << "|r\n|cff00ff00Message:|r " << ticket->message; @@ -372,7 +376,7 @@ bool ChatHandler::HandleGMTicketGetByNameCommand(const char* args) { message << "|r |cff00ff00Comment:|r |cff00ccff " << ticket->comment; } - PSendSysMessage(message.str().c_str()); + SendSysMessage(message.str().c_str()); return true; } @@ -383,22 +387,22 @@ bool ChatHandler::HandleGMTicketCloseByIdCommand(const char* args) uint64 tguid = atoi(args); GM_Ticket *ticket = ticketmgr.GetGMTicket(tguid); - if(!ticket || ticket->closed == 1) + if(!ticket || ticket->closed != 0) { SendSysMessage(LANG_COMMAND_TICKETNOTEXIST); - return false; + return true; } if(ticket && ticket->assignedToGM != 0 && ticket->assignedToGM != m_session->GetPlayer()->GetGUID()) { PSendSysMessage(LANG_COMMAND_TICKETCANNOTCLOSE, ticket->guid); - return false; + return true; } - sWorld.SendGMText(LANG_COMMAND_TICKETCLOSED, ticket->name.c_str(), ticket->guid); - ticketmgr.RemoveGMTicket(ticket->guid); + sWorld.SendGMText(LANG_COMMAND_TICKETCLOSED, m_session->GetPlayer()->GetName(), ticket->guid); + ticketmgr.RemoveGMTicket(ticket->guid, m_session->GetPlayer()->GetGUID()); Player *plr = objmgr.GetPlayer(ticket->playerGuid); if(!plr || !plr->IsInWorld()) - return false; + return true; // send abandon ticket WorldPacket data(SMSG_GMTICKET_DELETETICKET, 4); @@ -428,40 +432,38 @@ bool ChatHandler::HandleGMTicketAssignToCommand(const char* args) std::string gmname; GM_Ticket *ticket = ticketmgr.GetGMTicket(ticketGuid); - if(!ticket || ticket->closed == 1) + if(!ticket || ticket->closed != 0) { SendSysMessage(LANG_COMMAND_TICKETNOTEXIST); return true; } - Player *plr = objmgr.GetPlayer(targm.c_str()); - if(!plr || !plr->IsInWorld() || plr->GetSession()->GetSecurity() < SEC_MODERATOR) + uint64 tarGUID = objmgr.GetPlayerGUIDByName(targm.c_str()); + uint64 accid = objmgr.GetPlayerAccountIdByGUID(tarGUID); + QueryResult *result = LoginDatabase.PQuery("SELECT `gmlevel` FROM `account` WHERE `id` = '%u'", accid); + if(!tarGUID|| !result || result->Fetch()->GetUInt32() < SEC_MODERATOR) { SendSysMessage(LANG_COMMAND_TICKETASSIGNERROR_A); return true; } - if(ticket->assignedToGM == plr->GetGUID()) + if(ticket->assignedToGM == tarGUID) { PSendSysMessage(LANG_COMMAND_TICKETASSIGNERROR_B, ticket->guid); return true; } + objmgr.GetPlayerNameByGUID(tarGUID, gmname); if(ticket->assignedToGM != 0 && ticket->assignedToGM != cplr->GetGUID()) { - Player *aplr = objmgr.GetPlayer(ticket->assignedToGM); - if(aplr && aplr->IsInWorld()) - { - gmname = aplr->GetName(); - PSendSysMessage(LANG_COMMAND_TICKETALREADYASSIGNED, ticket->guid, gmname.c_str()); - return true; - } + PSendSysMessage(LANG_COMMAND_TICKETALREADYASSIGNED, ticket->guid, gmname.c_str()); + return true; } - ticket->assignedToGM = plr->GetGUID(); + ticket->assignedToGM = tarGUID; ticketmgr.UpdateGMTicket(ticket); std::stringstream ss; ss << "|cff00ff00Ticket:|r "; ss << "|cffff00ff" << ticket->guid << ". " << cplr->GetName() << "|r"; ss << "|cff00ff00 assigned to:|r "; - ss << "|cffff00ff\"" << targetgm << "\"."; + ss << "|cffff00ff\"" << gmname << "\"."; SendGlobalGMSysMessage(ss.str().c_str()); return true; } @@ -475,19 +477,20 @@ bool ChatHandler::HandleGMTicketUnAssignCommand(const char* args) Player *cplr = m_session->GetPlayer(); GM_Ticket *ticket = ticketmgr.GetGMTicket(ticketGuid); - if(!ticket|| ticket->closed) + if(!ticket|| ticket->closed != 0) { SendSysMessage(LANG_COMMAND_TICKETNOTEXIST); return true; } - if(ticket->assignedToGM = 0) + if(ticket->assignedToGM == 0) { SendSysMessage(LANG_COMMAND_TICKETNOTASSIGNED); return true; } - Player *plr = objmgr.GetPlayer(ticket->assignedToGM); - + std::string gmname; + objmgr.GetPlayerNameByGUID(ticket->assignedToGM, gmname); + Player *plr = objmgr.GetPlayer(ticket->assignedToGM); if(plr && plr->IsInWorld() && plr->GetSession()->GetSecurity() > cplr->GetSession()->GetSecurity()) { SendSysMessage(LANG_COMMAND_TICKETUNASSIGNSECURITY); @@ -519,7 +522,7 @@ bool ChatHandler::HandleGMTicketCommentCommand(const char* args) Player *cplr = m_session->GetPlayer(); GM_Ticket *ticket = ticketmgr.GetGMTicket(ticketGuid); - if(!ticket || ticket->closed == 1) + if(!ticket || ticket->closed != 0) { PSendSysMessage(LANG_COMMAND_TICKETNOTEXIST); return true; @@ -553,14 +556,17 @@ bool ChatHandler::HandleGMTicketDeleteByIdCommand(const char* args) SendSysMessage(LANG_COMMAND_TICKETNOTEXIST); return true; } - if(!ticket->closed == 1) + if(ticket->closed == 0) { SendSysMessage(LANG_COMMAND_TICKETCLOSEFIRST); return true; } - std::string gmname = m_session->GetPlayer()->GetName(); - sWorld.SendGMText(LANG_COMMAND_TICKETDELETED, ticket->guid, gmname.c_str()); + std::stringstream ss; + ss << "|cff00ff00Ticket:|r "; + ss << "|cffff00ff" << m_session->GetPlayer()->GetName() << "|r"; + ss << "|cff00ff00 deleted.|r"; + SendGlobalGMSysMessage(ss.str().c_str()); Player *plr = objmgr.GetPlayer(ticket->playerGuid); ticketmgr.DeleteGMTicketPermanently(ticket->guid); if(plr && plr->IsInWorld()) diff --git a/src/game/Level2.cpp b/src/game/Level2.cpp index afe22846cdd..c5db2cc2869 100644 --- a/src/game/Level2.cpp +++ b/src/game/Level2.cpp @@ -115,7 +115,7 @@ bool ChatHandler::HandleMuteCommand(const char* args) if (chr) chr->GetSession()->m_muteTime = mutetime; - loginDatabase.PExecute("UPDATE account SET mutetime = " I64FMTD " WHERE id = '%u'",uint64(mutetime), account_id ); + LoginDatabase.PExecute("UPDATE account SET mutetime = " I64FMTD " WHERE id = '%u'",uint64(mutetime), account_id ); if(chr) ChatHandler(chr).PSendSysMessage(LANG_YOUR_CHAT_DISABLED, notspeaktime); @@ -188,7 +188,7 @@ bool ChatHandler::HandleUnmuteCommand(const char* args) chr->GetSession()->m_muteTime = 0; } - loginDatabase.PExecute("UPDATE account SET mutetime = '0' WHERE id = '%u'", account_id ); + LoginDatabase.PExecute("UPDATE account SET mutetime = '0' WHERE id = '%u'", account_id ); if(chr) ChatHandler(chr).PSendSysMessage(LANG_YOUR_CHAT_ENABLED); @@ -1896,7 +1896,7 @@ bool ChatHandler::HandlePInfoCommand(const char* args) uint32 security = 0; std::string last_login = GetTrinityString(LANG_ERROR); - QueryResult* result = loginDatabase.PQuery("SELECT username,gmlevel,last_ip,last_login FROM account WHERE id = '%u'",accId); + QueryResult* result = LoginDatabase.PQuery("SELECT username,gmlevel,last_ip,last_login FROM account WHERE id = '%u'",accId); if(result) { Field* fields = result->Fetch(); @@ -3870,9 +3870,9 @@ bool ChatHandler::HandleLookupPlayerIpCommand(const char* args) char* limit_str = strtok (NULL, " "); int32 limit = limit_str ? atoi (limit_str) : -1; - loginDatabase.escape_string (ip); + LoginDatabase.escape_string (ip); - QueryResult* result = loginDatabase.PQuery ("SELECT id,username FROM account WHERE last_ip = '%s'", ip.c_str ()); + QueryResult* result = LoginDatabase.PQuery ("SELECT id,username FROM account WHERE last_ip = '%s'", ip.c_str ()); return LookupPlayerSearchCommand (result,limit); } @@ -3889,9 +3889,9 @@ bool ChatHandler::HandleLookupPlayerAccountCommand(const char* args) if (!AccountMgr::normilizeString (account)) return false; - loginDatabase.escape_string (account); + LoginDatabase.escape_string (account); - QueryResult* result = loginDatabase.PQuery ("SELECT id,username FROM account WHERE username = '%s'", account.c_str ()); + QueryResult* result = LoginDatabase.PQuery ("SELECT id,username FROM account WHERE username = '%s'", account.c_str ()); return LookupPlayerSearchCommand (result,limit); } @@ -3906,9 +3906,9 @@ bool ChatHandler::HandleLookupPlayerEmailCommand(const char* args) char* limit_str = strtok (NULL, " "); int32 limit = limit_str ? atoi (limit_str) : -1; - loginDatabase.escape_string (email); + LoginDatabase.escape_string (email); - QueryResult* result = loginDatabase.PQuery ("SELECT id,username FROM account WHERE email = '%s'", email.c_str ()); + QueryResult* result = LoginDatabase.PQuery ("SELECT id,username FROM account WHERE email = '%s'", email.c_str ()); return LookupPlayerSearchCommand (result,limit); } diff --git a/src/game/Level3.cpp b/src/game/Level3.cpp index a17d9d621b1..9c8c234c91c 100644 --- a/src/game/Level3.cpp +++ b/src/game/Level3.cpp @@ -764,7 +764,7 @@ bool ChatHandler::HandleAccountSetGmLevelCommand(const char* args) PSendSysMessage(LANG_YOURS_SECURITY_CHANGED, m_session->GetPlayer()->GetName(), gm); } - loginDatabase.PExecute("UPDATE account SET gmlevel = '%d' WHERE id = '%u'", gm, targetAccountId); + LoginDatabase.PExecute("UPDATE account SET gmlevel = '%d' WHERE id = '%u'", gm, targetAccountId); return true; }else { @@ -805,7 +805,7 @@ bool ChatHandler::HandleAccountSetGmLevelCommand(const char* args) } PSendSysMessage(LANG_YOU_CHANGE_SECURITY, targetAccountName.c_str(), gm); - loginDatabase.PExecute("UPDATE account SET gmlevel = '%d' WHERE id = '%u'", gm, targetAccountId); + LoginDatabase.PExecute("UPDATE account SET gmlevel = '%d' WHERE id = '%u'", gm, targetAccountId); return true; } } @@ -5213,7 +5213,7 @@ bool ChatHandler::HandleBanInfoCharacterCommand(const char* args) bool ChatHandler::HandleBanInfoHelper(uint32 accountid, char const* accountname) { - QueryResult *result = loginDatabase.PQuery("SELECT FROM_UNIXTIME(bandate), unbandate-bandate, active, unbandate,banreason,bannedby FROM account_banned WHERE id = '%u' ORDER BY bandate ASC",accountid); + QueryResult *result = LoginDatabase.PQuery("SELECT FROM_UNIXTIME(bandate), unbandate-bandate, active, unbandate,banreason,bannedby FROM account_banned WHERE id = '%u' ORDER BY bandate ASC",accountid); if(!result) { PSendSysMessage(LANG_BANINFO_NOACCOUNTBAN, accountname); @@ -5253,8 +5253,8 @@ bool ChatHandler::HandleBanInfoIPCommand(const char* args) std::string IP = cIP; - loginDatabase.escape_string(IP); - QueryResult *result = loginDatabase.PQuery("SELECT ip, FROM_UNIXTIME(bandate), FROM_UNIXTIME(unbandate), unbandate-UNIX_TIMESTAMP(), banreason,bannedby,unbandate-bandate FROM ip_banned WHERE ip = '%s'",IP.c_str()); + LoginDatabase.escape_string(IP); + QueryResult *result = LoginDatabase.PQuery("SELECT ip, FROM_UNIXTIME(bandate), FROM_UNIXTIME(unbandate), unbandate-UNIX_TIMESTAMP(), banreason,bannedby,unbandate-bandate FROM ip_banned WHERE ip = '%s'",IP.c_str()); if(!result) { PSendSysMessage(LANG_BANINFO_NOIP); @@ -5272,14 +5272,14 @@ bool ChatHandler::HandleBanInfoIPCommand(const char* args) bool ChatHandler::HandleBanListCharacterCommand(const char* args) { - loginDatabase.Execute("DELETE FROM ip_banned WHERE unbandate<=UNIX_TIMESTAMP() AND unbandate<>bandate"); + LoginDatabase.Execute("DELETE FROM ip_banned WHERE unbandate<=UNIX_TIMESTAMP() AND unbandate<>bandate"); char* cFilter = strtok ((char*)args, " "); if(!cFilter) return false; std::string filter = cFilter; - loginDatabase.escape_string(filter); + LoginDatabase.escape_string(filter); QueryResult* result = CharacterDatabase.PQuery("SELECT account FROM characters WHERE name "_LIKE_" "_CONCAT3_("'%%'","'%s'","'%%'"),filter.c_str()); if (!result) { @@ -5292,22 +5292,22 @@ bool ChatHandler::HandleBanListCharacterCommand(const char* args) bool ChatHandler::HandleBanListAccountCommand(const char* args) { - loginDatabase.Execute("DELETE FROM ip_banned WHERE unbandate<=UNIX_TIMESTAMP() AND unbandate<>bandate"); + LoginDatabase.Execute("DELETE FROM ip_banned WHERE unbandate<=UNIX_TIMESTAMP() AND unbandate<>bandate"); char* cFilter = strtok((char*)args, " "); std::string filter = cFilter ? cFilter : ""; - loginDatabase.escape_string(filter); + LoginDatabase.escape_string(filter); QueryResult* result; if(filter.empty()) { - result = loginDatabase.Query("SELECT account.id, username FROM account, account_banned" + result = LoginDatabase.Query("SELECT account.id, username FROM account, account_banned" " WHERE account.id = account_banned.id AND active = 1 GROUP BY account.id"); } else { - result = loginDatabase.PQuery("SELECT account.id, username FROM account, account_banned" + result = LoginDatabase.PQuery("SELECT account.id, username FROM account, account_banned" " WHERE account.id = account_banned.id AND active = 1 AND username "_LIKE_" "_CONCAT3_("'%%'","'%s'","'%%'")" GROUP BY account.id", filter.c_str()); } @@ -5333,7 +5333,7 @@ bool ChatHandler::HandleBanListHelper(QueryResult* result) Field* fields = result->Fetch(); uint32 accountid = fields[0].GetUInt32(); - QueryResult* banresult = loginDatabase.PQuery("SELECT account.username FROM account,account_banned WHERE account_banned.id='%u' AND account_banned.id=account.id",accountid); + QueryResult* banresult = LoginDatabase.PQuery("SELECT account.username FROM account,account_banned WHERE account_banned.id='%u' AND account_banned.id=account.id",accountid); if(banresult) { Field* fields2 = banresult->Fetch(); @@ -5364,7 +5364,7 @@ bool ChatHandler::HandleBanListHelper(QueryResult* result) accmgr.GetName (account_id,account_name); // No SQL injection. id is uint32. - QueryResult *banInfo = loginDatabase.PQuery("SELECT bandate,unbandate,bannedby,banreason FROM account_banned WHERE id = %u ORDER BY unbandate", account_id); + QueryResult *banInfo = LoginDatabase.PQuery("SELECT bandate,unbandate,bannedby,banreason FROM account_banned WHERE id = %u ORDER BY unbandate", account_id); if (banInfo) { Field *fields2 = banInfo->Fetch(); @@ -5401,23 +5401,23 @@ bool ChatHandler::HandleBanListHelper(QueryResult* result) bool ChatHandler::HandleBanListIPCommand(const char* args) { - loginDatabase.Execute("DELETE FROM ip_banned WHERE unbandate<=UNIX_TIMESTAMP() AND unbandate<>bandate"); + LoginDatabase.Execute("DELETE FROM ip_banned WHERE unbandate<=UNIX_TIMESTAMP() AND unbandate<>bandate"); char* cFilter = strtok((char*)args, " "); std::string filter = cFilter ? cFilter : ""; - loginDatabase.escape_string(filter); + LoginDatabase.escape_string(filter); QueryResult* result; if(filter.empty()) { - result = loginDatabase.Query ("SELECT ip,bandate,unbandate,bannedby,banreason FROM ip_banned" + result = LoginDatabase.Query ("SELECT ip,bandate,unbandate,bannedby,banreason FROM ip_banned" " WHERE (bandate=unbandate OR unbandate>UNIX_TIMESTAMP())" " ORDER BY unbandate" ); } else { - result = loginDatabase.PQuery( "SELECT ip,bandate,unbandate,bannedby,banreason FROM ip_banned" + result = LoginDatabase.PQuery( "SELECT ip,bandate,unbandate,bannedby,banreason FROM ip_banned" " WHERE (bandate=unbandate OR unbandate>UNIX_TIMESTAMP()) AND ip "_LIKE_" "_CONCAT3_("'%%'","'%s'","'%%'") " ORDER BY unbandate",filter.c_str() ); } @@ -6205,7 +6205,7 @@ bool ChatHandler::HandleInstanceSaveDataCommand(const char * /*args*/) bool ChatHandler::HandleGMListFullCommand(const char* /*args*/) { ///- Get the accounts with GM Level >0 - QueryResult *result = loginDatabase.Query( "SELECT username,gmlevel FROM account WHERE gmlevel > 0" ); + QueryResult *result = LoginDatabase.Query( "SELECT username,gmlevel FROM account WHERE gmlevel > 0" ); if(result) { SendSysMessage(LANG_GMLIST); @@ -6284,7 +6284,7 @@ bool ChatHandler::HandleAccountSetAddonCommand(const char* args) return false; // No SQL injection - loginDatabase.PExecute("UPDATE account SET expansion = '%d' WHERE id = '%u'",lev,account_id); + LoginDatabase.PExecute("UPDATE account SET expansion = '%d' WHERE id = '%u'",lev,account_id); PSendSysMessage(LANG_ACCOUNT_SETADDON,account_name.c_str(),account_id,lev); return true; } diff --git a/src/game/MiscHandler.cpp b/src/game/MiscHandler.cpp index 2107d792c7d..2d8a898132e 100644 --- a/src/game/MiscHandler.cpp +++ b/src/game/MiscHandler.cpp @@ -1367,7 +1367,7 @@ void WorldSession::HandleWhoisOpcode(WorldPacket& recv_data) uint32 accid = plr->GetSession()->GetAccountId(); - QueryResult *result = loginDatabase.PQuery("SELECT username,email,last_ip FROM account WHERE id=%u", accid); + QueryResult *result = LoginDatabase.PQuery("SELECT username,email,last_ip FROM account WHERE id=%u", accid); if(!result) { SendNotification(LANG_ACCOUNT_FOR_PLAYER_NOT_FOUND, charname.c_str()); diff --git a/src/game/PetAI.cpp b/src/game/PetAI.cpp index 6a87d52cfca..82259cbba4d 100644 --- a/src/game/PetAI.cpp +++ b/src/game/PetAI.cpp @@ -73,6 +73,9 @@ void PetAI::AttackStart(Unit *u) if(i_pet.Attack(u,true)) { + i_pet.SetInCombatWith(u); + u->SetInCombatWith(&i_pet); + i_pet.clearUnitState(UNIT_STAT_FOLLOW); // TMGs call CreatureRelocation which via MoveInLineOfSight can call this function // thus with the following clear the original TMG gets invalidated and crash, doh diff --git a/src/game/Player.cpp b/src/game/Player.cpp index 1fca0b32067..cbf5c8dbfc5 100644 --- a/src/game/Player.cpp +++ b/src/game/Player.cpp @@ -3748,7 +3748,7 @@ void Player::DeleteFromDB(uint64 playerguid, uint32 accountId, bool updateRealmC CharacterDatabase.PExecute("DELETE FROM character_achievement_progress WHERE guid = '%u'",guid); CharacterDatabase.CommitTransaction(); - //loginDatabase.PExecute("UPDATE realmcharacters SET numchars = numchars - 1 WHERE acctid = %d AND realmid = %d", accountId, realmID); + //LoginDatabase.PExecute("UPDATE realmcharacters SET numchars = numchars - 1 WHERE acctid = %d AND realmid = %d", accountId, realmID); if(updateRealmChars) sWorld.UpdateRealmCharCount(accountId); } @@ -3815,7 +3815,7 @@ void Player::BuildPlayerRepop() // BG - remove insignia related RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_SKINNABLE); - SendCorpseReclaimDelay(); +// SendCorpseReclaimDelay(); // to prevent cheating corpse->ResetGhostTime(); @@ -3923,6 +3923,7 @@ void Player::KillPlayer() m_deathTimer = 6*MINUTE*1000; UpdateCorpseReclaimDelay(); // dependent at use SetDeathPvP() call before kill + SendCorpseReclaimDelay(); // don't create corpse at this moment, player might be falling @@ -19283,7 +19284,8 @@ uint32 Player::GetCorpseReclaimDelay(bool pvp) const time_t now = time(NULL); // 0..2 full period - uint32 count = (now < m_deathExpireTime) ? (m_deathExpireTime - now)/DEATH_EXPIRE_STEP : 0; + // should be ceil(x)-1 but not floor(x) + uint32 count = (now < m_deathExpireTime - 1) ? (m_deathExpireTime - 1 - now)/DEATH_EXPIRE_STEP : 0; return copseReclaimDelay[count]; } @@ -19312,17 +19314,21 @@ void Player::UpdateCorpseReclaimDelay() void Player::SendCorpseReclaimDelay(bool load) { Corpse* corpse = GetCorpse(); - if(!corpse) + if(load && !corpse) return; + bool pvp; + if(corpse) + pvp = (corpse->GetType() == CORPSE_RESURRECTABLE_PVP); + else + pvp = (m_ExtraFlags & PLAYER_EXTRA_PVP_DEATH); + uint32 delay; if(load) { if(corpse->GetGhostTime() > m_deathExpireTime) return; - bool pvp = corpse->GetType()==CORPSE_RESURRECTABLE_PVP; - uint32 count; if( pvp && sWorld.getConfig(CONFIG_DEATH_CORPSE_RECLAIM_DELAY_PVP) || !pvp && sWorld.getConfig(CONFIG_DEATH_CORPSE_RECLAIM_DELAY_PVE) ) @@ -19343,7 +19349,7 @@ void Player::SendCorpseReclaimDelay(bool load) delay = expected_time-now; } else - delay = GetCorpseReclaimDelay(corpse->GetType()==CORPSE_RESURRECTABLE_PVP); + delay = GetCorpseReclaimDelay(pvp); //! corpse reclaim delay 30 * 1000ms or longer at often deaths WorldPacket data(SMSG_CORPSE_RECLAIM_DELAY, 4); diff --git a/src/game/Spell.cpp b/src/game/Spell.cpp index d48294a77a1..20e10384a49 100644 --- a/src/game/Spell.cpp +++ b/src/game/Spell.cpp @@ -938,7 +938,7 @@ void Spell::DoAllEffectOnTarget(TargetInfo *target) SpellNonMeleeDamage damageInfo(caster, unitTarget, m_spellInfo->Id, m_spellSchoolMask); // Add bonuses and fill damageInfo struct - caster->CalculateSpellDamage(&damageInfo, m_damage, m_spellInfo); + caster->CalculateSpellDamageTaken(&damageInfo, m_damage, m_spellInfo); // Send log damage message to client caster->SendSpellNonMeleeDamageLog(&damageInfo); @@ -5536,19 +5536,14 @@ void Spell::CalculateDamageDoneForAllTargets() int32 Spell::CalculateDamageDone(Unit *unit, const uint32 effectMask, float *multiplier) { - m_damage = 0; + int32 damageDone = 0; unitTarget = unit; for(uint32 i = 0; i < 3; ++i) { if (effectMask & (1<<i)) { - if(m_applyMultiplierMask & (1 << i)) - { - damage = CalculateDamage(i, NULL) * m_damageMultipliers[i]; - m_damageMultipliers[i] *= multiplier[i]; - } - else - damage = CalculateDamage(i, NULL); + m_damage = 0; + damage = CalculateDamage(i, NULL); switch(m_spellInfo->Effect[i]) { @@ -5565,7 +5560,18 @@ int32 Spell::CalculateDamageDone(Unit *unit, const uint32 effectMask, float *mul SpellDamageHeal(i); break; } + + if(m_damage > 0 && m_originalCaster) + m_damage = m_originalCaster->SpellDamageBonus(unit, m_spellInfo, m_damage, SPELL_DIRECT_DAMAGE); + if(m_applyMultiplierMask & (1 << i)) + { + m_damage *= m_damageMultipliers[i]; + m_damageMultipliers[i] *= multiplier[i]; + } + + damageDone += m_damage; } } - return m_damage; + + return damageDone; } diff --git a/src/game/SpellAuras.cpp b/src/game/SpellAuras.cpp index 66a91fbc19b..8ae85c89ff0 100644 --- a/src/game/SpellAuras.cpp +++ b/src/game/SpellAuras.cpp @@ -6033,7 +6033,8 @@ void Aura::PeriodicTick() SpellEntry const* spellProto = GetSpellProto(); //maybe has to be sent different to client, but not by SMSG_PERIODICAURALOG SpellNonMeleeDamage damageInfo(pCaster, m_target, spellProto->Id, spellProto->SchoolMask); - pCaster->CalculateSpellDamage(&damageInfo, gain, spellProto); + //no SpellDamageBonus for burn mana + pCaster->CalculateSpellDamageTaken(&damageInfo, gain, spellProto); pCaster->SendSpellNonMeleeDamageLog(&damageInfo); // Set trigger flag diff --git a/src/game/TicketHandler.cpp b/src/game/TicketHandler.cpp index f3374332f79..1e1d97fd4ce 100644 --- a/src/game/TicketHandler.cpp +++ b/src/game/TicketHandler.cpp @@ -36,8 +36,6 @@ void WorldSession::HandleGMTicketCreateOpcode( WorldPacket & recv_data ) // always do a packet check CHECK_PACKET_SIZE(recv_data, 4*4+1+2*4); - uint32 map; - float x, y, z; std::string ticketText = ""; std::string ticketText2 = ""; GM_Ticket *ticket = new GM_Ticket; @@ -45,13 +43,9 @@ void WorldSession::HandleGMTicketCreateOpcode( WorldPacket & recv_data ) WorldPacket data(SMSG_GMTICKET_CREATE, 4); // recv Data - recv_data >> map; - recv_data >> x; - recv_data >> y; - recv_data >> z; recv_data >> ticketText; - // get additional data + // get additional data, rarely used recv_data >> ticketText2; // assign values @@ -65,7 +59,7 @@ void WorldSession::HandleGMTicketCreateOpcode( WorldPacket & recv_data ) ticket->comment = ""; // remove ticket by player, shouldn't happen - ticketmgr.RemoveGMTicketByPlayer(GetPlayer()->GetGUID()); + ticketmgr.RemoveGMTicketByPlayer(GetPlayer()->GetGUID(), GetPlayer()->GetGUID()); // add ticket ticketmgr.AddGMTicket(ticket, false); @@ -140,7 +134,7 @@ void WorldSession::HandleGMTicketDeleteOpcode( WorldPacket & /*recv_data*/) SendPacket(&data); sWorld.SendGMText(LANG_COMMAND_TICKETPLAYERABANDON, GetPlayer()->GetName(), ticket->guid ); - ticketmgr.RemoveGMTicketByPlayer(GetPlayer()->GetGUID()); + ticketmgr.RemoveGMTicketByPlayer(GetPlayer()->GetGUID(), GetPlayer()->GetGUID()); } } diff --git a/src/game/TicketMgr.cpp b/src/game/TicketMgr.cpp index 0f1f7501e67..ffa8c522672 100644 --- a/src/game/TicketMgr.cpp +++ b/src/game/TicketMgr.cpp @@ -24,13 +24,9 @@ #include "ObjectMgr.h" #include "Language.h" #include "Player.h" -INSTANTIATE_SINGLETON_1( TicketMgr ); - #include "Common.h" -//#include "Log.h" #include "ObjectAccessor.h" - - +INSTANTIATE_SINGLETON_1( TicketMgr ); GM_Ticket* TicketMgr::GetGMTicket(uint64 ticketGuid) { @@ -106,7 +102,7 @@ void TicketMgr::DeleteGMTicketPermanently(uint64 ticketGuid) } // delete database record - CharacterDatabase.PExecute("DELETE FROM gm_tickets WHERE guid=%u", ticketGuid); + CharacterDatabase.PExecute("DELETE FROM `gm_tickets` WHERE guid= '%u'", ticketGuid); } @@ -114,13 +110,13 @@ void TicketMgr::LoadGMTickets() { // Delete all out of object holder GM_TicketList.clear(); - QueryResult *result = CharacterDatabase.Query( "SELECT `guid`, `playerGuid`, `name`, `message`, `timestamp`, `closed`, `assignedto`, `comment` FROM gm_tickets WHERE closed = '0'" ); + QueryResult *result = CharacterDatabase.Query( "SELECT `guid`, `playerGuid`, `name`, `message`, `timestamp`, `closed`, `assignedto`, `comment` FROM `gm_tickets` WHERE `closed` = '0'" ); GM_Ticket *ticket; - //ticket = NULL; if(!result) return; + // Assign values from SQL to the object holder do { Field *fields = result->Fetch(); @@ -143,13 +139,13 @@ void TicketMgr::LoadGMTickets() delete result; } -void TicketMgr::RemoveGMTicket(uint64 ticketGuid) +void TicketMgr::RemoveGMTicket(uint64 ticketGuid, uint64 GMguid) { for(GmTicketList::iterator i = GM_TicketList.begin(); i != GM_TicketList.end();) { if((*i)->guid == ticketGuid && (*i)->closed == 0) { - (*i)->closed = 1; + (*i)->closed = GMguid; SaveGMTicket((*i)); } ++i; @@ -157,13 +153,13 @@ void TicketMgr::RemoveGMTicket(uint64 ticketGuid) } -void TicketMgr::RemoveGMTicketByPlayer(uint64 playerGuid) +void TicketMgr::RemoveGMTicketByPlayer(uint64 playerGuid, uint64 GMguid) { for(GmTicketList::iterator i = GM_TicketList.begin(); i != GM_TicketList.end();) { if((*i)->playerGuid == playerGuid && (*i)->closed == 0) { - (*i)->closed = true; + (*i)->closed = GMguid; SaveGMTicket((*i)); } ++i; @@ -173,15 +169,15 @@ void TicketMgr::RemoveGMTicketByPlayer(uint64 playerGuid) void TicketMgr::SaveGMTicket(GM_Ticket* ticket) { std::stringstream ss; - ss << "REPLACE INTO gm_tickets (`guid`, `playerGuid`, `name`, `message`, `timestamp`, `closed`, `assignedto`, `comment`) VALUES("; - ss << ticket->guid << ", "; - ss << ticket->playerGuid << ", '"; - ss << ticket->name << "', '"; - ss << ticket->message << "', " ; - ss << ticket->timestamp << ", "; - ss << ticket->closed << ", '"; - ss << ticket->assignedToGM << "', '"; - ss << ticket->comment << "');"; + ss << "REPLACE INTO `gm_tickets` (`guid`, `playerGuid`, `name`, `message`, `timestamp`, `closed`, `assignedto`, `comment`) VALUES(\""; + ss << ticket->guid << "\", \""; + ss << ticket->playerGuid << "\", \""; + ss << ticket->name << "\", \""; + ss << ticket->message << "\", \"" ; + ss << ticket->timestamp << "\", \""; + ss << ticket->closed << "\", \""; + ss << ticket->assignedToGM << "\", \""; + ss << ticket->comment << "\");"; CharacterDatabase.BeginTransaction(); CharacterDatabase.Execute(ss.str().c_str()); @@ -203,5 +199,5 @@ uint64 TicketMgr::GenerateTicketID() delete result; } - return m_ticketid; + return ++m_ticketid; }
\ No newline at end of file diff --git a/src/game/TicketMgr.h b/src/game/TicketMgr.h index e41be9db5c3..33a9598e7cf 100644 --- a/src/game/TicketMgr.h +++ b/src/game/TicketMgr.h @@ -39,27 +39,13 @@ struct GM_Ticket std::string comment; }; -enum GMticketType -{ - GM_TICKET_TYPE_STUCK = 1, - GM_TICKET_TYPE_BEHAVIOR_HARASSMENT = 2, - GM_TICKET_TYPE_GUILD = 3, - GM_TICKET_TYPE_ITEM = 4, - GM_TICKET_TYPE_ENVIRONMENTAL = 5, - GM_TICKET_TYPE_NON_QUEST_CREEP = 6, - GM_TICKET_TYPE_QUEST_QUEST_NPC = 7, - GM_TICKET_TYPE_TECHNICAL = 8, - GM_TICKET_TYPE_ACCOUNT_BILLING = 9, - GM_TICKET_TYPE_CHARACTER = 10 -}; - // Map Typedef typedef std::list<GM_Ticket*> GmTicketList; class TicketMgr { public: - TicketMgr(){} //constructor + TicketMgr(){m_ticketid = 1;} //constructor ~TicketMgr(){} //destructor // Object Holder @@ -69,8 +55,8 @@ class TicketMgr void DeleteAllRemovedGMTickets(); void DeleteGMTicketPermanently(uint64 ticketGuid); void LoadGMTickets(); - void RemoveGMTicketByPlayer(uint64 playerGuid); - void RemoveGMTicket(uint64 ticketGuid); + void RemoveGMTicketByPlayer(uint64 playerGuid, uint64 GMguid); + void RemoveGMTicket(uint64 ticketGuid, uint64 GMguid); void UpdateGMTicket(GM_Ticket *ticket); void SaveGMTicket(GM_Ticket* ticket); diff --git a/src/game/Unit.cpp b/src/game/Unit.cpp index 766115742da..65689a71e84 100644 --- a/src/game/Unit.cpp +++ b/src/game/Unit.cpp @@ -1150,13 +1150,14 @@ uint32 Unit::SpellNonMeleeDamageLog(Unit *pVictim, uint32 spellID, uint32 damage { SpellEntry const *spellInfo = sSpellStore.LookupEntry(spellID); SpellNonMeleeDamage damageInfo(this, pVictim, spellInfo->Id, spellInfo->SchoolMask); - CalculateSpellDamage(&damageInfo, damage, spellInfo); + damage = SpellDamageBonus(pVictim, spellInfo, damage, SPELL_DIRECT_DAMAGE); + CalculateSpellDamageTaken(&damageInfo, damage, spellInfo); SendSpellNonMeleeDamageLog(&damageInfo); DealSpellDamage(&damageInfo, true); return damageInfo.damage; } -void Unit::CalculateSpellDamage(SpellNonMeleeDamage *damageInfo, int32 damage, SpellEntry const *spellInfo, WeaponAttackType attackType) +void Unit::CalculateSpellDamageTaken(SpellNonMeleeDamage *damageInfo, int32 damage, SpellEntry const *spellInfo, WeaponAttackType attackType) { SpellSchoolMask damageSchoolMask = SpellSchoolMask(damageInfo->schoolMask); Unit *pVictim = damageInfo->target; @@ -1184,16 +1185,16 @@ void Unit::CalculateSpellDamage(SpellNonMeleeDamage *damageInfo, int32 damage, S if ( damageSchoolMask & SPELL_SCHOOL_MASK_NORMAL ) { //Calculate armor mitigation - damage = CalcArmorReducedDamage(pVictim, damage); + //damage = CalcArmorReducedDamage(pVictim, damage); // Get blocked status blocked = isSpellBlocked(pVictim, spellInfo, attackType); } // Magical Damage - else + /*else { // Calculate damage bonus damage = SpellDamageBonus(pVictim, spellInfo, damage, SPELL_DIRECT_DAMAGE); - } + }*/ if (crit) { damageInfo->HitInfo|= SPELL_HIT_TYPE_CRIT; @@ -1239,7 +1240,7 @@ void Unit::CalculateSpellDamage(SpellNonMeleeDamage *damageInfo, int32 damage, S case SPELL_DAMAGE_CLASS_MAGIC: { // Calculate damage bonus - damage = SpellDamageBonus(pVictim, spellInfo, damage, SPELL_DIRECT_DAMAGE); + //damage = SpellDamageBonus(pVictim, spellInfo, damage, SPELL_DIRECT_DAMAGE); // If crit add critical bonus if (crit) { @@ -1253,6 +1254,9 @@ void Unit::CalculateSpellDamage(SpellNonMeleeDamage *damageInfo, int32 damage, S break; } + if( damageSchoolMask & SPELL_SCHOOL_MASK_NORMAL ) + damage = CalcArmorReducedDamage(pVictim, damage); + // Calculate absorb resist if(damage > 0) { @@ -6713,7 +6717,6 @@ bool Unit::Attack(Unit *victim, bool meleeAttack) ((WorldObject*)this)->SendMessageToSet(&data, true); ((Creature*)this)->CallAssistance(); - ((Creature*)this)->SetHomePosition(GetPositionX(), GetPositionY(), GetPositionZ(), GetOrientation()); } // delay offhand weapon attack to next attack time @@ -7038,6 +7041,10 @@ uint32 Unit::SpellDamageBonus(Unit *pVictim, SpellEntry const *spellProto, uint3 if(!spellProto || !pVictim || damagetype==DIRECT_DAMAGE ) return pdamage; + if(spellProto->SchoolMask == SPELL_SCHOOL_MASK_NORMAL) + return pdamage; + //damage = CalcArmorReducedDamage(pVictim, damage); + int32 BonusDamage = 0; if( GetTypeId()==TYPEID_UNIT ) { @@ -8299,18 +8306,19 @@ void Unit::SetInCombatState(bool PvP) SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_IN_COMBAT); - if(isCharmed() || GetTypeId()!=TYPEID_PLAYER && ((Creature*)this)->isPet()) - SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_PET_IN_COMBAT); + if(GetTypeId() != TYPEID_PLAYER) + ((Creature*)this)->SetHomePosition(GetPositionX(), GetPositionY(), GetPositionZ(), GetOrientation()); - if(GetTypeId() == TYPEID_PLAYER && GetPetGUID()) + if(GetTypeId() != TYPEID_PLAYER && ((Creature*)this)->isPet()) { - if(Pet *pet = GetPet()) - { - pet->UpdateSpeed(MOVE_RUN, true); - pet->UpdateSpeed(MOVE_SWIM, true); - pet->UpdateSpeed(MOVE_FLIGHT, true); - } + UpdateSpeed(MOVE_RUN, true); + UpdateSpeed(MOVE_SWIM, true); + UpdateSpeed(MOVE_FLIGHT, true); } + else if(!isCharmed()) + return; + + SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_PET_IN_COMBAT); } void Unit::ClearInCombat() @@ -8318,19 +8326,22 @@ void Unit::ClearInCombat() m_CombatTimer = 0; RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_IN_COMBAT); - if(isCharmed() || GetTypeId()!=TYPEID_PLAYER && ((Creature*)this)->isPet()) - RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_PET_IN_COMBAT); - // Player's state will be cleared in Player::UpdateContestedPvP if(GetTypeId()!=TYPEID_PLAYER) clearUnitState(UNIT_STAT_ATTACK_PLAYER); - if(GetTypeId() == TYPEID_PLAYER && GetPetGUID()) + if(GetTypeId() != TYPEID_PLAYER && ((Creature*)this)->isPet()) { - if(Pet *pet = GetPet()) + if(Unit *owner = GetOwner()) + { for(int i = 0; i < MAX_MOVE_TYPE; ++i) - pet->SetSpeed(UnitMoveType(i), m_speed_rate[i], true); + SetSpeed(UnitMoveType(i), owner->GetSpeedRate(UnitMoveType(i)), true); + } } + else if(!isCharmed()) + return; + + RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_PET_IN_COMBAT); } //TODO: remove this function @@ -10086,7 +10097,8 @@ void Unit::ProcDamageAndSpellFor( bool isVictim, Unit * pTarget, uint32 procFlag { sLog.outDebug("ProcDamageAndSpell: doing %u damage from spell id %u (triggered by %s aura of spell %u)", auraModifier->m_amount, spellInfo->Id, (isVictim?"a victim's":"an attacker's"), triggeredByAura->GetId()); SpellNonMeleeDamage damageInfo(this, pTarget, spellInfo->Id, spellInfo->SchoolMask); - CalculateSpellDamage(&damageInfo, auraModifier->m_amount, spellInfo); + uint32 damage = SpellDamageBonus(pTarget, spellInfo, auraModifier->m_amount, SPELL_DIRECT_DAMAGE); + CalculateSpellDamageTaken(&damageInfo, damage, spellInfo); SendSpellNonMeleeDamageLog(&damageInfo); DealSpellDamage(&damageInfo, true); break; diff --git a/src/game/Unit.h b/src/game/Unit.h index 98a49e519ac..238490d9428 100644 --- a/src/game/Unit.h +++ b/src/game/Unit.h @@ -951,7 +951,7 @@ class TRINITY_DLL_SPEC Unit : public WorldObject void CalculateMeleeDamage(Unit *pVictim, uint32 damage, CalcDamageInfo *damageInfo, WeaponAttackType attackType = BASE_ATTACK); void DealMeleeDamage(CalcDamageInfo *damageInfo, bool durabilityLoss); - void CalculateSpellDamage(SpellNonMeleeDamage *damageInfo, int32 damage, SpellEntry const *spellInfo, WeaponAttackType attackType = BASE_ATTACK); + void CalculateSpellDamageTaken(SpellNonMeleeDamage *damageInfo, int32 damage, SpellEntry const *spellInfo, WeaponAttackType attackType = BASE_ATTACK); void DealSpellDamage(SpellNonMeleeDamage *damageInfo, bool durabilityLoss); float MeleeSpellMissChance(const Unit *pVictim, WeaponAttackType attType, int32 skillDiff, uint32 spellId) const; diff --git a/src/game/World.cpp b/src/game/World.cpp index 9bb936be6af..83543aa4f74 100644 --- a/src/game/World.cpp +++ b/src/game/World.cpp @@ -267,7 +267,7 @@ World::AddSession_ (WorldSession* s) float popu = GetActiveSessionCount (); //updated number of users on the server popu /= pLimit; popu *= 2; - loginDatabase.PExecute ("UPDATE realmlist SET population = '%f' WHERE id = '%d'", popu, realmID); + LoginDatabase.PExecute ("UPDATE realmlist SET population = '%f' WHERE id = '%d'", popu, realmID); sLog.outDetail ("Server Population (%f).", popu); } } @@ -1085,7 +1085,7 @@ void World::SetInitialWorldSettings() // not send custom type REALM_FFA_PVP to realm list uint32 server_type = IsFFAPvPRealm() ? REALM_TYPE_PVP : getConfig(CONFIG_GAME_TYPE); uint32 realm_zone = getConfig(CONFIG_REALM_ZONE); - loginDatabase.PExecute("UPDATE realmlist SET icon = %u, timezone = %u WHERE id = '%d'", server_type, realm_zone, realmID); + LoginDatabase.PExecute("UPDATE realmlist SET icon = %u, timezone = %u WHERE id = '%d'", server_type, realm_zone, realmID); ///- Remove the bones after a restart CharacterDatabase.PExecute("DELETE FROM corpse WHERE corpse_type = '0'"); @@ -1404,7 +1404,7 @@ void World::SetInitialWorldSettings() objmgr.LoadTransportEvents(); sLog.outString("Deleting expired bans..." ); - loginDatabase.Execute("DELETE FROM ip_banned WHERE unbandate<=UNIX_TIMESTAMP() AND unbandate<>bandate"); + LoginDatabase.Execute("DELETE FROM ip_banned WHERE unbandate<=UNIX_TIMESTAMP() AND unbandate<>bandate"); sLog.outString("Calculate next daily quest reset time..." ); InitDailyQuestResetTime(); @@ -2649,10 +2649,10 @@ bool World::KickPlayer(const std::string& playerName) /// Ban an account or ban an IP address, duration will be parsed using TimeStringToSecs if it is positive, otherwise permban BanReturn World::BanAccount(BanMode mode, std::string nameOrIP, std::string duration, std::string reason, std::string author) { - loginDatabase.escape_string(nameOrIP); - loginDatabase.escape_string(reason); + LoginDatabase.escape_string(nameOrIP); + LoginDatabase.escape_string(reason); std::string safe_author=author; - loginDatabase.escape_string(safe_author); + LoginDatabase.escape_string(safe_author); uint32 duration_secs = TimeStringToSecs(duration); QueryResult *resultAccounts = NULL; //used for kicking @@ -2662,12 +2662,12 @@ BanReturn World::BanAccount(BanMode mode, std::string nameOrIP, std::string dura { case BAN_IP: //No SQL injection as strings are escaped - resultAccounts = loginDatabase.PQuery("SELECT id FROM account WHERE last_ip = '%s'",nameOrIP.c_str()); - loginDatabase.PExecute("INSERT INTO ip_banned VALUES ('%s',UNIX_TIMESTAMP(),UNIX_TIMESTAMP()+%u,'%s','%s')",nameOrIP.c_str(),duration_secs,safe_author.c_str(),reason.c_str()); + resultAccounts = LoginDatabase.PQuery("SELECT id FROM account WHERE last_ip = '%s'",nameOrIP.c_str()); + LoginDatabase.PExecute("INSERT INTO ip_banned VALUES ('%s',UNIX_TIMESTAMP(),UNIX_TIMESTAMP()+%u,'%s','%s')",nameOrIP.c_str(),duration_secs,safe_author.c_str(),reason.c_str()); break; case BAN_ACCOUNT: //No SQL injection as string is escaped - resultAccounts = loginDatabase.PQuery("SELECT id FROM account WHERE username = '%s'",nameOrIP.c_str()); + resultAccounts = LoginDatabase.PQuery("SELECT id FROM account WHERE username = '%s'",nameOrIP.c_str()); break; case BAN_CHARACTER: //No SQL injection as string is escaped @@ -2694,7 +2694,7 @@ BanReturn World::BanAccount(BanMode mode, std::string nameOrIP, std::string dura if(mode!=BAN_IP) { //No SQL injection as strings are escaped - loginDatabase.PExecute("INSERT INTO account_banned VALUES ('%u', UNIX_TIMESTAMP(), UNIX_TIMESTAMP()+%u, '%s', '%s', '1')", + LoginDatabase.PExecute("INSERT INTO account_banned VALUES ('%u', UNIX_TIMESTAMP(), UNIX_TIMESTAMP()+%u, '%s', '%s', '1')", account,duration_secs,safe_author.c_str(),reason.c_str()); } @@ -2713,8 +2713,8 @@ bool World::RemoveBanAccount(BanMode mode, std::string nameOrIP) { if (mode == BAN_IP) { - loginDatabase.escape_string(nameOrIP); - loginDatabase.PExecute("DELETE FROM ip_banned WHERE ip = '%s'",nameOrIP.c_str()); + LoginDatabase.escape_string(nameOrIP); + LoginDatabase.PExecute("DELETE FROM ip_banned WHERE ip = '%s'",nameOrIP.c_str()); } else { @@ -2728,7 +2728,7 @@ bool World::RemoveBanAccount(BanMode mode, std::string nameOrIP) return false; //NO SQL injection as account is uint32 - loginDatabase.PExecute("UPDATE account_banned SET active = '0' WHERE id = '%u'",account); + LoginDatabase.PExecute("UPDATE account_banned SET active = '0' WHERE id = '%u'",account); } return true; } @@ -2925,8 +2925,8 @@ void World::_UpdateRealmCharCount(QueryResult *resultCharCount, uint32 accountId Field *fields = resultCharCount->Fetch(); uint32 charCount = fields[0].GetUInt32(); delete resultCharCount; - loginDatabase.PExecute("DELETE FROM realmcharacters WHERE acctid= '%d' AND realmid = '%d'", accountId, realmID); - loginDatabase.PExecute("INSERT INTO realmcharacters (numchars, acctid, realmid) VALUES (%u, %u, %u)", charCount, accountId, realmID); + LoginDatabase.PExecute("DELETE FROM realmcharacters WHERE acctid= '%d' AND realmid = '%d'", accountId, realmID); + LoginDatabase.PExecute("INSERT INTO realmcharacters (numchars, acctid, realmid) VALUES (%u, %u, %u)", charCount, accountId, realmID); } } @@ -2989,7 +2989,7 @@ void World::SetPlayerLimit( int32 limit, bool needUpdate ) m_playerLimit = limit; if(db_update_need) - loginDatabase.PExecute("UPDATE realmlist SET allowedSecurityLevel = '%u' WHERE id = '%d'",uint8(GetPlayerSecurityLimit()),realmID); + LoginDatabase.PExecute("UPDATE realmlist SET allowedSecurityLevel = '%u' WHERE id = '%d'",uint8(GetPlayerSecurityLimit()),realmID); } void World::UpdateMaxSessionCounters() diff --git a/src/game/WorldSession.cpp b/src/game/WorldSession.cpp index 581b44b9e95..231d7783d9e 100644 --- a/src/game/WorldSession.cpp +++ b/src/game/WorldSession.cpp @@ -328,7 +328,7 @@ void WorldSession::LogoutPlayer(bool Save) ///- Reset the online field in the account table // no point resetting online in character table here as Player::SaveToDB() will set it to 1 since player has not been removed from world at this stage //No SQL injection as AccountID is uint32 - loginDatabase.PExecute("UPDATE account SET online = 0 WHERE id = '%u'", GetAccountId()); + LoginDatabase.PExecute("UPDATE account SET online = 0 WHERE id = '%u'", GetAccountId()); ///- If the player is in a guild, update the guild roster and broadcast a logout message to other guild members Guild *guild = objmgr.GetGuildById(_player->GetGuildId()); diff --git a/src/game/WorldSocket.cpp b/src/game/WorldSocket.cpp index 2ae97819116..1f77e1111e5 100644 --- a/src/game/WorldSocket.cpp +++ b/src/game/WorldSocket.cpp @@ -720,11 +720,11 @@ int WorldSocket::HandleAuthSession (WorldPacket& recvPacket) // Get the account information from the realmd database std::string safe_account = account; // Duplicate, else will screw the SHA hash verification below - loginDatabase.escape_string (safe_account); + LoginDatabase.escape_string (safe_account); // No SQL injection, username escaped. QueryResult *result = - loginDatabase.PQuery ("SELECT " + LoginDatabase.PQuery ("SELECT " "id, " //0 "gmlevel, " //1 "sessionkey, " //2 @@ -789,7 +789,7 @@ int WorldSocket::HandleAuthSession (WorldPacket& recvPacket) vold, vStr); - loginDatabase.PExecute ("UPDATE account " + LoginDatabase.PExecute ("UPDATE account " "SET " "v = '0', " "s = '0' " @@ -841,7 +841,7 @@ int WorldSocket::HandleAuthSession (WorldPacket& recvPacket) // Re-check account ban (same check as in realmd) QueryResult *banresult = - loginDatabase.PQuery ("SELECT " + LoginDatabase.PQuery ("SELECT " "bandate, " "unbandate " "FROM account_banned " @@ -907,9 +907,9 @@ int WorldSocket::HandleAuthSession (WorldPacket& recvPacket) // Update the last_ip in the database // No SQL injection, username escaped. - loginDatabase.escape_string (address); + LoginDatabase.escape_string (address); - loginDatabase.PExecute ("UPDATE account " + LoginDatabase.PExecute ("UPDATE account " "SET last_ip = '%s' " "WHERE username = '%s'", address.c_str (), diff --git a/src/shared/Database/DatabaseEnv.h b/src/shared/Database/DatabaseEnv.h index cc09b7611db..d984a838c90 100644 --- a/src/shared/Database/DatabaseEnv.h +++ b/src/shared/Database/DatabaseEnv.h @@ -53,6 +53,6 @@ typedef DatabaseMysql DatabaseType; extern DatabaseType WorldDatabase; extern DatabaseType CharacterDatabase; -extern DatabaseType loginDatabase; +extern DatabaseType LoginDatabase; #endif diff --git a/src/trinitycore/CliRunnable.cpp b/src/trinitycore/CliRunnable.cpp index 78c6e0dbb85..267a8d41a27 100644 --- a/src/trinitycore/CliRunnable.cpp +++ b/src/trinitycore/CliRunnable.cpp @@ -194,7 +194,7 @@ bool ChatHandler::HandleAccountOnlineListCommand(const char* args) ///- 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); + QueryResult *resultLogin = LoginDatabase.PQuery("SELECT username, last_ip, gmlevel, expansion FROM account WHERE id = '%u'",account); if(resultLogin) { diff --git a/src/trinitycore/Main.cpp b/src/trinitycore/Main.cpp new file mode 100644 index 00000000000..a4bcf717d62 --- /dev/null +++ b/src/trinitycore/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/trinitycore/Master.cpp b/src/trinitycore/Master.cpp new file mode 100644 index 00000000000..c1484fede8e --- /dev/null +++ b/src/trinitycore/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/trinitycore/RASocket.cpp b/src/trinitycore/RASocket.cpp new file mode 100644 index 00000000000..cfc50d95f89 --- /dev/null +++ b/src/trinitycore/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/trinityrealm/AuthSocket.cpp b/src/trinityrealm/AuthSocket.cpp new file mode 100644 index 00000000000..6f8363a9724 --- /dev/null +++ b/src/trinityrealm/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 LoginDatabase; + +#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(); + LoginDatabase.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; + LoginDatabase.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) + LoginDatabase.Execute("DELETE FROM ip_banned WHERE unbandate<=UNIX_TIMESTAMP() AND unbandate<>bandate"); + + std::string address = GetRemoteAddress(); + LoginDatabase.escape_string(address); + QueryResult *result = LoginDatabase.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 = LoginDatabase.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 + LoginDatabase.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 = LoginDatabase.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(); + LoginDatabase.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 + LoginDatabase.PExecute("UPDATE account SET failed_logins = failed_logins + 1 WHERE username = '%s'",_safelogin.c_str()); + + if(QueryResult *loginfail = LoginDatabase.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(); + LoginDatabase.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(); + LoginDatabase.escape_string(current_ip); + LoginDatabase.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 = LoginDatabase.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 = LoginDatabase.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 = LoginDatabase.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/trinityrealm/Main.cpp b/src/trinityrealm/Main.cpp new file mode 100644 index 00000000000..e74cd5144d3 --- /dev/null +++ b/src/trinityrealm/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 "trinityrealm.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 LoginDatabase; ///< 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 LoginDatabase.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 + LoginDatabase.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(!LoginDatabase.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/trinityrealm/RealmList.cpp b/src/trinityrealm/RealmList.cpp new file mode 100644 index 00000000000..32c0cbc12c1 --- /dev/null +++ b/src/trinityrealm/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 LoginDatabase; + +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 = LoginDatabase.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; + } +} |