diff options
38 files changed, 3090 insertions, 415 deletions
diff --git a/sql/updates/773_world_scripts.sql b/sql/updates/773_world_scripts.sql new file mode 100644 index 00000000000..e07beb784a4 --- /dev/null +++ b/sql/updates/773_world_scripts.sql @@ -0,0 +1,9 @@ +Update quest_template set SpecialFlags=2 where entry=4770; +UPDATE `creature_template` SET `ScriptName` = 'npc_swiftmountain' WHERE `entry` = 10427; +DELETE FROM `script_texts` WHERE `entry` BETWEEN -1000149 AND -1000147; +INSERT INTO script_texts + (`entry`, `content_default`, `content_loc1`, `content_loc2`, `content_loc3`, `content_loc4`, `content_loc5`, `content_loc6`, `content_loc7`, `content_loc8`, `sound`, `type`, `language`, `comment`) +VALUES + (-1000147, 'Lets go $N. I am ready to start to Whitereach Post.', NULL, NULL, 'Gehen wir $N.Ich bin bereit,mich zum Weissgipfelposten aufzumachen.', NULL, NULL, NULL, NULL, NULL, 0, 0, 0, NULL), + (-1000148, 'This seems to me trusted. If we move to the east, we are probably... Aah! Wyvern attack !', NULL, NULL, 'Das kommt mir vertraut vor.Wenn wir nach Osten ziehen,können wir wahrscheinlich...Aah!Flügeldrachen im Angriff!', NULL, NULL, NULL, NULL, NULL, 0, 0, 0, NULL), + (-1000149, 'Thanks so much... From here I find the way back to the Whitereach Post. Speak with Motega Firemane, maybe you can persuade him to send me home.', NULL, NULL, 'Tausend Dank...Von hier aus finde ich den Rückweg zum Weissgipfelposten.Sprecht unbedingt mit Motega Feuermähne,vielleicht könnt ihr ihn überreden,mich nach Hause zu schicken.', NULL, NULL, NULL, NULL, NULL, 0, 0, 0, NULL);
\ No newline at end of file diff --git a/sql/updates/780_characters.sql b/sql/updates/780_characters.sql new file mode 100644 index 00000000000..cbcb193c276 --- /dev/null +++ b/sql/updates/780_characters.sql @@ -0,0 +1,2 @@ +ALTER TABLE `gm_tickets` CHANGE `guid` `guid` int(10) NOT NULL AUTO_INCREMENT; +ALTER TABLE `gm_tickets` CHANGE `closed` `closed` int(10) NOT NULL;
\ No newline at end of file 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; + } +} |