diff options
author | Shauren <shauren.trinity@gmail.com> | 2016-04-05 20:35:59 +0200 |
---|---|---|
committer | Shauren <shauren.trinity@gmail.com> | 2016-04-05 20:35:59 +0200 |
commit | 4e673836212424cef55503b852f56aab6b1c4354 (patch) | |
tree | 0d9910abe23d14d166e28e57be7af8c84e292f51 | |
parent | 6d2dd48183790da0ee36956952c4a39daf7b551f (diff) |
Merge branch '3.3.5-instanceextend' into 3.3.5 (PR #16392)
(cherry picked from commit 62aff401f687b56d720a320778950e82b65fbd8b)
-rw-r--r-- | sql/base/characters_database.sql | 3 | ||||
-rw-r--r-- | sql/updates/characters/2016_04_05_00_characters_2016_02_10_00_characters.sql | 2 | ||||
-rw-r--r-- | sql/updates/world/2016_04_05_07_world_2016_02_10_07_world.sql | 6 | ||||
-rw-r--r-- | src/server/database/Database/Implementation/CharacterDatabase.cpp | 12 | ||||
-rw-r--r-- | src/server/database/Database/Implementation/CharacterDatabase.h | 6 | ||||
-rw-r--r-- | src/server/game/Entities/Player/Player.cpp | 48 | ||||
-rw-r--r-- | src/server/game/Entities/Player/Player.h | 25 | ||||
-rw-r--r-- | src/server/game/Groups/Group.cpp | 3 | ||||
-rw-r--r-- | src/server/game/Handlers/CalendarHandler.cpp | 15 | ||||
-rw-r--r-- | src/server/game/Instances/InstanceSaveMgr.cpp | 102 | ||||
-rw-r--r-- | src/server/game/Instances/InstanceSaveMgr.h | 2 | ||||
-rw-r--r-- | src/server/game/Maps/Map.cpp | 34 | ||||
-rw-r--r-- | src/server/game/Maps/Map.h | 3 | ||||
-rw-r--r-- | src/server/game/Scripting/ScriptMgr.cpp | 4 | ||||
-rw-r--r-- | src/server/game/Scripting/ScriptMgr.h | 4 | ||||
-rw-r--r-- | src/server/scripts/Commands/cs_debug.cpp | 31 | ||||
-rw-r--r-- | src/server/scripts/Commands/cs_instance.cpp | 4 |
17 files changed, 229 insertions, 75 deletions
diff --git a/sql/base/characters_database.sql b/sql/base/characters_database.sql index d5d706a2e7c..4ab419be76f 100644 --- a/sql/base/characters_database.sql +++ b/sql/base/characters_database.sql @@ -979,6 +979,7 @@ CREATE TABLE `character_instance` ( `guid` bigint(20) unsigned NOT NULL DEFAULT '0', `instance` int(10) unsigned NOT NULL DEFAULT '0', `permanent` tinyint(3) unsigned NOT NULL DEFAULT '0', + `extendState` TINYINT(2) UNSIGNED NOT NULL DEFAULT '1', PRIMARY KEY (`guid`,`instance`), KEY `instance` (`instance`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8; @@ -3082,7 +3083,7 @@ CREATE TABLE `updates` ( LOCK TABLES `updates` WRITE; /*!40000 ALTER TABLE `updates` DISABLE KEYS */; -INSERT INTO `updates` VALUES ('2014_10_20_00_characters.sql','A5882DA0979CF4DAE33DA011EBAA006C24BE7230','ARCHIVED','2015-03-21 15:55:55',0),('2014_10_23_00_characters.sql','E2AC4758133EE19B7F08464A445802154D1261C8','ARCHIVED','2015-03-21 15:55:55',0),('2014_10_23_01_characters.sql','20029E6323D9773B32C34D84FFED1711CC60F09F','ARCHIVED','2015-03-21 15:55:55',0),('2014_10_23_02_characters.sql','8A7A16886EE71E7ACDDB3DDA6D0ECAC2FD2FDCA8','ARCHIVED','2015-03-21 15:55:55',0),('2014_10_24_00_characters.sql','D008FE81AE844FCA686439D6ECC5108FB0DD1EB9','ARCHIVED','2015-03-21 15:55:55',0),('2014_10_25_00_characters.sql','A39C7BE46686B54776BDAB9D7A882D91EDEC51A4','ARCHIVED','2015-03-21 15:55:55',0),('2014_10_26_00_characters.sql','C787954CC35FE34B4101FDE6527F14C027F4947C','ARCHIVED','2015-03-21 15:55:55',0),('2014_11_12_00_characters.sql','B160BB2313F1BD5F3B076A5A9279DC10D4796E34','ARCHIVED','2015-03-21 15:55:55',0),('2014_12_23_00_characters.sql','3D9D648B2387B357F4BD090B33F80682F7924882','ARCHIVED','2015-03-21 15:55:55',0),('2014_12_28_00_characters.sql','5362922FF4483A336311D73082A5727309CD9219','ARCHIVED','2015-03-21 15:55:55',0),('2014_12_31_00_characters.sql','498DDF2DD936CF156D74A8208DC93DCE9FCAB5AA','ARCHIVED','2015-03-21 15:55:55',0),('2015_01_02_00_characters.sql','E5940BE836F253982E07930120422E598D08BDE1','ARCHIVED','2015-03-21 15:55:55',0),('2015_01_10_00_characters.sql','30796056C8623699B2FE1BF626A19D38262E9284','ARCHIVED','2015-03-21 15:55:55',0),('2015_01_16_00_characters.sql','96642760A54C8D799AAFE438049A63AA521656F2','ARCHIVED','2015-03-21 15:55:55',0),('2015_01_27_00_characters.sql','EB710E3EB9F2CAFD84AB62CDC84E898403A80A4F','ARCHIVED','2015-03-21 15:55:55',0),('2015_02_13_00_characters.sql','405BEB4ED207DC6076442A37EE2AFB1F21E274A0','ARCHIVED','2015-03-21 15:55:55',0),('2015_02_13_01_characters.sql','35F582D4F33BF55D1685A1BA89273ED895FD09C5','ARCHIVED','2015-03-21 15:55:55',0),('2015_02_17_00_characters.sql','8D21FC5A55BF8B55D6DCDCE5F02CF2B640230E94','ARCHIVED','2015-03-21 15:55:55',0),('2015_03_10_00_characters.sql','E565B89B145C340067742DFF2DEF1B74F5F1BD4E','ARCHIVED','2015-03-21 15:55:55',0),('2015_03_20_00_characters.sql','B761760804EA73BD297F296C5C1919687DF7191C','ARCHIVED','2015-03-21 15:55:55',0),('2015_03_20_01_characters.sql','20BD68468C57FCF7E665B4DA185DCD52FACE8B3F','ARCHIVED','2015-03-21 15:55:55',0),('2015_03_20_02_characters.sql','0296995DCD3676BA9AE6024CA7C91C5F39D927A3','ARCHIVED','2015-03-21 15:56:46',0),('2015_03_29_00_characters.sql','95D6A46BB746A8BD3EE3FE2086DF1A07F7C33B92','ARCHIVED','2015-05-02 15:43:06',0),('2015_04_21_00_characters.sql','F2032B9BF4EDA7EDE5065554724ED392FD91657D','ARCHIVED','2015-05-02 15:43:06',0),('2015_04_28_00_characters.sql','949F62DB3A3461D420A1230ECF7A6A3ED6435703','ARCHIVED','2015-05-02 15:43:06',0),('2015_05_08_00_characters.sql','0F14B7821618D1C872625B6EDDAA9A667B211167','ARCHIVED','2015-07-10 19:32:17',0),('2015_05_22_00_characters.sql','65B82152413FAB23BE413656E59A486A74447FF7','ARCHIVED','2015-07-10 19:32:17',0),('2015_07_08_00_characters.sql','DAB25360ACB5244C8F8E6214CF6BD97160588A5B','ARCHIVED','2015-07-10 19:32:17',0),('2015_07_11_00_characters.sql','B421B6C0E57BD0FD587071358863D9DABF4BA849','ARCHIVED','2015-07-13 21:50:02',0),('2015_07_12_00_characters.sql','E98E7FD61EF6426E7EDE8ED9AD8C15D8D7132589','ARCHIVED','2015-07-13 21:50:02',0),('2015_07_28_00_characters.sql','0711BC3A658D189EF71B0CB68DCFF2E9B781C4A0','ARCHIVED','2015-07-29 16:23:56',0),('2015_08_08_00_characters.sql','EA12BB2DC24FAF2300A96D0888A45BBEA158D5DC','ARCHIVED','2015-08-08 16:34:07',0),('2015_08_12_00_characters.sql','4FD7F89FE5DA51D4E0C33E520719986AA3EBD31B','ARCHIVED','2015-08-12 12:35:20',0),('2015_09_05_00_characters.sql','4C22BB29365BE4B6B95E64DAD84B63CA002304EA','ARCHIVED','2015-09-05 12:35:20',0),('2015_09_09_00_characters.sql','AFC32E693BC17CFD9A17919FE5317B8FE337ACAD','ARCHIVED','2015-09-09 12:35:20',0),('2015_09_10_00_characters.sql','4555A7F35C107E54C13D74D20F141039ED42943E','ARCHIVED','2015-09-10 22:50:42',0),('2015_10_16_00_characters.sql','E3A3FFF0CB42F04A8DCF0CE4362143C16E2083AF','ARCHIVED','2015-10-15 21:54:11',0),('2015_11_06_00_characters_2015_10_12_00.sql','D6F9927BDED72AD0A81D6EC2C6500CBC34A39FA2','ARCHIVED','2015-11-06 23:43:27',0),('2015_11_08_00_characters.sql','0ACDD35EC9745231BCFA701B78056DEF94D0CC53','RELEASED','2015-11-08 00:51:45',15),('2015_11_23_00_characters.sql','9FC828E9E48E8E2E9B99A5A0073D6614C5BFC6B5','RELEASED','2015-11-22 23:27:34',0),('2016_01_05_00_characters.sql','0EAD24977F40DE2476B4567DA2B477867CC0DA1A','RELEASED','2016-01-04 23:07:40',0); +INSERT INTO `updates` VALUES ('2014_10_20_00_characters.sql','A5882DA0979CF4DAE33DA011EBAA006C24BE7230','ARCHIVED','2015-03-21 15:55:55',0),('2014_10_23_00_characters.sql','E2AC4758133EE19B7F08464A445802154D1261C8','ARCHIVED','2015-03-21 15:55:55',0),('2014_10_23_01_characters.sql','20029E6323D9773B32C34D84FFED1711CC60F09F','ARCHIVED','2015-03-21 15:55:55',0),('2014_10_23_02_characters.sql','8A7A16886EE71E7ACDDB3DDA6D0ECAC2FD2FDCA8','ARCHIVED','2015-03-21 15:55:55',0),('2014_10_24_00_characters.sql','D008FE81AE844FCA686439D6ECC5108FB0DD1EB9','ARCHIVED','2015-03-21 15:55:55',0),('2014_10_25_00_characters.sql','A39C7BE46686B54776BDAB9D7A882D91EDEC51A4','ARCHIVED','2015-03-21 15:55:55',0),('2014_10_26_00_characters.sql','C787954CC35FE34B4101FDE6527F14C027F4947C','ARCHIVED','2015-03-21 15:55:55',0),('2014_11_12_00_characters.sql','B160BB2313F1BD5F3B076A5A9279DC10D4796E34','ARCHIVED','2015-03-21 15:55:55',0),('2014_12_23_00_characters.sql','3D9D648B2387B357F4BD090B33F80682F7924882','ARCHIVED','2015-03-21 15:55:55',0),('2014_12_28_00_characters.sql','5362922FF4483A336311D73082A5727309CD9219','ARCHIVED','2015-03-21 15:55:55',0),('2014_12_31_00_characters.sql','498DDF2DD936CF156D74A8208DC93DCE9FCAB5AA','ARCHIVED','2015-03-21 15:55:55',0),('2015_01_02_00_characters.sql','E5940BE836F253982E07930120422E598D08BDE1','ARCHIVED','2015-03-21 15:55:55',0),('2015_01_10_00_characters.sql','30796056C8623699B2FE1BF626A19D38262E9284','ARCHIVED','2015-03-21 15:55:55',0),('2015_01_16_00_characters.sql','96642760A54C8D799AAFE438049A63AA521656F2','ARCHIVED','2015-03-21 15:55:55',0),('2015_01_27_00_characters.sql','EB710E3EB9F2CAFD84AB62CDC84E898403A80A4F','ARCHIVED','2015-03-21 15:55:55',0),('2015_02_13_00_characters.sql','405BEB4ED207DC6076442A37EE2AFB1F21E274A0','ARCHIVED','2015-03-21 15:55:55',0),('2015_02_13_01_characters.sql','35F582D4F33BF55D1685A1BA89273ED895FD09C5','ARCHIVED','2015-03-21 15:55:55',0),('2015_02_17_00_characters.sql','8D21FC5A55BF8B55D6DCDCE5F02CF2B640230E94','ARCHIVED','2015-03-21 15:55:55',0),('2015_03_10_00_characters.sql','E565B89B145C340067742DFF2DEF1B74F5F1BD4E','ARCHIVED','2015-03-21 15:55:55',0),('2015_03_20_00_characters.sql','B761760804EA73BD297F296C5C1919687DF7191C','ARCHIVED','2015-03-21 15:55:55',0),('2015_03_20_01_characters.sql','20BD68468C57FCF7E665B4DA185DCD52FACE8B3F','ARCHIVED','2015-03-21 15:55:55',0),('2015_03_20_02_characters.sql','0296995DCD3676BA9AE6024CA7C91C5F39D927A3','ARCHIVED','2015-03-21 15:56:46',0),('2015_03_29_00_characters.sql','95D6A46BB746A8BD3EE3FE2086DF1A07F7C33B92','ARCHIVED','2015-05-02 15:43:06',0),('2015_04_21_00_characters.sql','F2032B9BF4EDA7EDE5065554724ED392FD91657D','ARCHIVED','2015-05-02 15:43:06',0),('2015_04_28_00_characters.sql','949F62DB3A3461D420A1230ECF7A6A3ED6435703','ARCHIVED','2015-05-02 15:43:06',0),('2015_05_08_00_characters.sql','0F14B7821618D1C872625B6EDDAA9A667B211167','ARCHIVED','2015-07-10 19:32:17',0),('2015_05_22_00_characters.sql','65B82152413FAB23BE413656E59A486A74447FF7','ARCHIVED','2015-07-10 19:32:17',0),('2015_07_08_00_characters.sql','DAB25360ACB5244C8F8E6214CF6BD97160588A5B','ARCHIVED','2015-07-10 19:32:17',0),('2015_07_11_00_characters.sql','B421B6C0E57BD0FD587071358863D9DABF4BA849','ARCHIVED','2015-07-13 21:50:02',0),('2015_07_12_00_characters.sql','E98E7FD61EF6426E7EDE8ED9AD8C15D8D7132589','ARCHIVED','2015-07-13 21:50:02',0),('2015_07_28_00_characters.sql','0711BC3A658D189EF71B0CB68DCFF2E9B781C4A0','ARCHIVED','2015-07-29 16:23:56',0),('2015_08_08_00_characters.sql','EA12BB2DC24FAF2300A96D0888A45BBEA158D5DC','ARCHIVED','2015-08-08 16:34:07',0),('2015_08_12_00_characters.sql','4FD7F89FE5DA51D4E0C33E520719986AA3EBD31B','ARCHIVED','2015-08-12 12:35:20',0),('2015_09_05_00_characters.sql','4C22BB29365BE4B6B95E64DAD84B63CA002304EA','ARCHIVED','2015-09-05 12:35:20',0),('2015_09_09_00_characters.sql','AFC32E693BC17CFD9A17919FE5317B8FE337ACAD','ARCHIVED','2015-09-09 12:35:20',0),('2015_09_10_00_characters.sql','4555A7F35C107E54C13D74D20F141039ED42943E','ARCHIVED','2015-09-10 22:50:42',0),('2015_10_16_00_characters.sql','E3A3FFF0CB42F04A8DCF0CE4362143C16E2083AF','ARCHIVED','2015-10-15 21:54:11',0),('2015_11_06_00_characters_2015_10_12_00.sql','D6F9927BDED72AD0A81D6EC2C6500CBC34A39FA2','ARCHIVED','2015-11-06 23:43:27',0),('2015_11_08_00_characters.sql','0ACDD35EC9745231BCFA701B78056DEF94D0CC53','RELEASED','2015-11-08 00:51:45',15),('2015_11_23_00_characters.sql','9FC828E9E48E8E2E9B99A5A0073D6614C5BFC6B5','RELEASED','2015-11-22 23:27:34',0),('2016_01_05_00_characters.sql','0EAD24977F40DE2476B4567DA2B477867CC0DA1A','RELEASED','2016-01-04 23:07:40',0),('2016_04_05_00_characters_2016_02_10_00_characters.sql','F1B4DA202819CABC7319A4470A2D224A34609E97','RELEASED','2016-04-05 20:34:41',0); /*!40000 ALTER TABLE `updates` ENABLE KEYS */; UNLOCK TABLES; diff --git a/sql/updates/characters/2016_04_05_00_characters_2016_02_10_00_characters.sql b/sql/updates/characters/2016_04_05_00_characters_2016_02_10_00_characters.sql new file mode 100644 index 00000000000..d49ed155bc8 --- /dev/null +++ b/sql/updates/characters/2016_04_05_00_characters_2016_02_10_00_characters.sql @@ -0,0 +1,2 @@ +-- extend=0 is expired, extend=1 is normal, extend=2 is extended +ALTER TABLE `character_instance` ADD COLUMN `extendState` TINYINT(2) UNSIGNED NOT NULL DEFAULT '1'; diff --git a/sql/updates/world/2016_04_05_07_world_2016_02_10_07_world.sql b/sql/updates/world/2016_04_05_07_world_2016_02_10_07_world.sql new file mode 100644 index 00000000000..5feb4f44163 --- /dev/null +++ b/sql/updates/world/2016_04_05_07_world_2016_02_10_07_world.sql @@ -0,0 +1,6 @@ +-- +DELETE FROM `command` WHERE `name`="debug raidreset"; +INSERT INTO `command` (`name`,`permission`,`help`) VALUES ("debug raidreset",414,"Syntax: .debug raidreset mapid [difficulty] +Forces a global reset of the specified map on all difficulties (or only the specific difficulty if specified). Effectively the same as setting the specified map's reset timer to now."); + +UPDATE `trinity_string` SET `content_default`="Map: %d | ID: %d | perm: %s | extended: %s | diff: %d | canReset: %s | TTR: %s" WHERE `entry`=5045; diff --git a/src/server/database/Database/Implementation/CharacterDatabase.cpp b/src/server/database/Database/Implementation/CharacterDatabase.cpp index 8b7baf385ce..c519dfc02b7 100644 --- a/src/server/database/Database/Implementation/CharacterDatabase.cpp +++ b/src/server/database/Database/Implementation/CharacterDatabase.cpp @@ -83,7 +83,7 @@ void CharacterDatabaseConnection::DoPrepareStatements() "FROM characters WHERE guid = ?", CONNECTION_ASYNC); PrepareStatement(CHAR_SEL_GROUP_MEMBER, "SELECT guid FROM group_member WHERE memberGuid = ?", CONNECTION_BOTH); - PrepareStatement(CHAR_SEL_CHARACTER_INSTANCE, "SELECT id, permanent, map, difficulty, resettime FROM character_instance LEFT JOIN instance ON instance = id WHERE guid = ?", CONNECTION_ASYNC); + PrepareStatement(CHAR_SEL_CHARACTER_INSTANCE, "SELECT id, permanent, map, difficulty, extendState, resettime FROM character_instance LEFT JOIN instance ON instance = id WHERE guid = ?", CONNECTION_ASYNC); PrepareStatement(CHAR_SEL_CHARACTER_AURAS, "SELECT casterGuid, itemGuid, spell, effectMask, recalculateMask, stackCount, maxDuration, remainTime, remainCharges, castItemLevel FROM character_aura WHERE guid = ?", CONNECTION_ASYNC); PrepareStatement(CHAR_SEL_CHARACTER_AURA_EFFECTS, "SELECT casterGuid, itemGuid, spell, effectMask, effectIndex, amount, baseAmount FROM character_aura_effect WHERE guid = ?", CONNECTION_ASYNC); PrepareStatement(CHAR_SEL_CHARACTER_SPELL, "SELECT spell, active, disabled FROM character_spell WHERE guid = ?", CONNECTION_ASYNC); @@ -441,8 +441,8 @@ void CharacterDatabaseConnection::DoPrepareStatements() PrepareStatement(CHAR_UPD_WORLDSTATE, "UPDATE worldstates SET value = ? WHERE entry = ?", CONNECTION_ASYNC); PrepareStatement(CHAR_INS_WORLDSTATE, "INSERT INTO worldstates (entry, value) VALUES (?, ?)", CONNECTION_ASYNC); PrepareStatement(CHAR_DEL_CHAR_INSTANCE_BY_INSTANCE_GUID, "DELETE FROM character_instance WHERE guid = ? AND instance = ?", CONNECTION_ASYNC); - PrepareStatement(CHAR_UPD_CHAR_INSTANCE, "UPDATE character_instance SET instance = ?, permanent = ? WHERE guid = ? AND instance = ?", CONNECTION_ASYNC); - PrepareStatement(CHAR_INS_CHAR_INSTANCE, "INSERT INTO character_instance (guid, instance, permanent) VALUES (?, ?, ?)", CONNECTION_ASYNC); + PrepareStatement(CHAR_UPD_CHAR_INSTANCE, "UPDATE character_instance SET instance = ?, permanent = ?, extendState = ? WHERE guid = ? AND instance = ?", CONNECTION_ASYNC); + PrepareStatement(CHAR_INS_CHAR_INSTANCE, "INSERT INTO character_instance (guid, instance, permanent, extendState) VALUES (?, ?, ?, ?)", CONNECTION_ASYNC); PrepareStatement(CHAR_UPD_GENDER_AND_APPEARANCE, "UPDATE characters SET gender = ?, skin = ?, face = ?, hairStyle = ?, hairColor = ?, facialStyle = ? WHERE guid = ?", CONNECTION_ASYNC); PrepareStatement(CHAR_DEL_CHARACTER_SKILL, "DELETE FROM character_skills WHERE guid = ? AND skill = ?", CONNECTION_ASYNC); PrepareStatement(CHAR_UPD_CHARACTER_SOCIAL_FLAGS, "UPDATE character_social SET flags = ? WHERE guid = ? AND friend = ?", CONNECTION_ASYNC); @@ -468,6 +468,7 @@ void CharacterDatabaseConnection::DoPrepareStatements() PrepareStatement(CHAR_SEL_CHAR_CUSTOMIZE_INFO, "SELECT name, race, class, gender, at_login FROM characters WHERE guid = ?", CONNECTION_ASYNC); PrepareStatement(CHAR_SEL_CHAR_RACE_OR_FACTION_CHANGE_INFOS, "SELECT at_login, knownTitles, skin, face, hairStyle, hairColor, facialStyle FROM characters WHERE guid = ?", CONNECTION_ASYNC); PrepareStatement(CHAR_SEL_INSTANCE, "SELECT data, completedEncounters FROM instance WHERE map = ? AND id = ?", CONNECTION_SYNCH); + PrepareStatement(CHAR_SEL_PERM_BIND_BY_INSTANCE, "SELECT guid FROM character_instance WHERE instance = ? and permanent = 1", CONNECTION_SYNCH); PrepareStatement(CHAR_SEL_CHAR_COD_ITEM_MAIL, "SELECT id, messageType, mailTemplateId, sender, subject, body, money, has_items FROM mail WHERE receiver = ? AND has_items <> 0 AND cod <> 0", CONNECTION_SYNCH); PrepareStatement(CHAR_SEL_CHAR_SOCIAL, "SELECT DISTINCT guid FROM character_social WHERE friend = ?", CONNECTION_SYNCH); PrepareStatement(CHAR_SEL_CHAR_OLD_CHARS, "SELECT guid, deleteInfos_Account FROM characters WHERE deleteDate IS NOT NULL AND deleteDate < ?", CONNECTION_SYNCH); @@ -501,9 +502,10 @@ void CharacterDatabaseConnection::DoPrepareStatements() PrepareStatement(CHAR_INS_CHAR_GIFT, "INSERT INTO character_gifts (guid, item_guid, entry, flags) VALUES (?, ?, ?, ?)", CONNECTION_ASYNC); PrepareStatement(CHAR_DEL_INSTANCE_BY_INSTANCE, "DELETE FROM instance WHERE id = ?", CONNECTION_ASYNC); PrepareStatement(CHAR_DEL_CHAR_INSTANCE_BY_INSTANCE, "DELETE FROM character_instance WHERE instance = ?", CONNECTION_ASYNC); - PrepareStatement(CHAR_DEL_CHAR_INSTANCE_BY_MAP_DIFF, "DELETE FROM character_instance USING character_instance LEFT JOIN instance ON character_instance.instance = id WHERE map = ? and difficulty = ?", CONNECTION_ASYNC); + PrepareStatement(CHAR_DEL_EXPIRED_CHAR_INSTANCE_BY_MAP_DIFF, "DELETE FROM character_instance USING character_instance LEFT JOIN instance ON character_instance.instance = id WHERE (extendState = 0 or permanent = 0) and map = ? and difficulty = ?", CONNECTION_ASYNC); PrepareStatement(CHAR_DEL_GROUP_INSTANCE_BY_MAP_DIFF, "DELETE FROM group_instance USING group_instance LEFT JOIN instance ON group_instance.instance = id WHERE map = ? and difficulty = ?", CONNECTION_ASYNC); - PrepareStatement(CHAR_DEL_INSTANCE_BY_MAP_DIFF, "DELETE FROM instance WHERE map = ? and difficulty = ?", CONNECTION_ASYNC); + PrepareStatement(CHAR_DEL_EXPIRED_INSTANCE_BY_MAP_DIFF, "DELETE FROM instance WHERE map = ? and difficulty = ? and (SELECT guid FROM character_instance WHERE extendState != 0 AND instance = id LIMIT 1) IS NULL", CONNECTION_ASYNC); + PrepareStatement(CHAR_UPD_EXPIRE_CHAR_INSTANCE_BY_MAP_DIFF, "UPDATE character_instance LEFT JOIN instance ON character_instance.instance = id SET extendState = extendState-1 WHERE map = ? and difficulty = ?", CONNECTION_ASYNC); PrepareStatement(CHAR_DEL_MAIL_ITEM_BY_ID, "DELETE FROM mail_items WHERE mail_id = ?", CONNECTION_ASYNC); PrepareStatement(CHAR_INS_PETITION, "INSERT INTO petition (ownerguid, petitionguid, name) VALUES (?, ?, ?)", CONNECTION_ASYNC); PrepareStatement(CHAR_DEL_PETITION_BY_GUID, "DELETE FROM petition WHERE petitionguid = ?", CONNECTION_ASYNC); diff --git a/src/server/database/Database/Implementation/CharacterDatabase.h b/src/server/database/Database/Implementation/CharacterDatabase.h index 192a7e5f8dd..f3cb4a7c0c1 100644 --- a/src/server/database/Database/Implementation/CharacterDatabase.h +++ b/src/server/database/Database/Implementation/CharacterDatabase.h @@ -389,6 +389,7 @@ enum CharacterDatabaseStatements CHAR_SEL_CHAR_CUSTOMIZE_INFO, CHAR_SEL_CHAR_RACE_OR_FACTION_CHANGE_INFOS, CHAR_SEL_INSTANCE, + CHAR_SEL_PERM_BIND_BY_INSTANCE, CHAR_SEL_CHAR_COD_ITEM_MAIL, CHAR_SEL_CHAR_SOCIAL, CHAR_SEL_CHAR_OLD_CHARS, @@ -417,9 +418,10 @@ enum CharacterDatabaseStatements CHAR_INS_CHAR_GIFT, CHAR_DEL_INSTANCE_BY_INSTANCE, CHAR_DEL_CHAR_INSTANCE_BY_INSTANCE, - CHAR_DEL_CHAR_INSTANCE_BY_MAP_DIFF, + CHAR_DEL_EXPIRED_CHAR_INSTANCE_BY_MAP_DIFF, CHAR_DEL_GROUP_INSTANCE_BY_MAP_DIFF, - CHAR_DEL_INSTANCE_BY_MAP_DIFF, + CHAR_DEL_EXPIRED_INSTANCE_BY_MAP_DIFF, + CHAR_UPD_EXPIRE_CHAR_INSTANCE_BY_MAP_DIFF, CHAR_DEL_MAIL_ITEM_BY_ID, CHAR_INS_PETITION, CHAR_DEL_PETITION_BY_GUID, diff --git a/src/server/game/Entities/Player/Player.cpp b/src/server/game/Entities/Player/Player.cpp index a4db0048aa1..43d9c5292bc 100644 --- a/src/server/game/Entities/Player/Player.cpp +++ b/src/server/game/Entities/Player/Player.cpp @@ -18062,8 +18062,9 @@ void Player::_LoadBoundInstances(PreparedQueryResult result) uint32 mapId = fields[2].GetUInt16(); uint32 instanceId = fields[0].GetUInt32(); uint8 difficulty = fields[3].GetUInt8(); + BindExtensionState extendState = BindExtensionState(fields[4].GetUInt8()); - time_t resetTime = time_t(fields[4].GetUInt32()); + time_t resetTime = time_t(fields[5].GetUInt32()); // the resettime for normal instances is only saved when the InstanceSave is unloaded // so the value read from the DB may be wrong here but only if the InstanceSave is loaded // and in that case it is not used @@ -18116,13 +18117,13 @@ void Player::_LoadBoundInstances(PreparedQueryResult result) // since non permanent binds are always solo bind, they can always be reset if (InstanceSave* save = sInstanceSaveMgr->AddInstanceSave(mapId, instanceId, Difficulty(difficulty), resetTime, !perm, true)) - BindToInstance(save, perm, true); + BindToInstance(save, perm, extendState, true); } while (result->NextRow()); } } -InstancePlayerBind* Player::GetBoundInstance(uint32 mapid, Difficulty difficulty) +InstancePlayerBind* Player::GetBoundInstance(uint32 mapid, Difficulty difficulty, bool withExpired) { // some instances only have one difficulty MapDifficultyEntry const* mapDiff = GetDownscaledMapDifficultyData(mapid, difficulty); @@ -18131,8 +18132,8 @@ InstancePlayerBind* Player::GetBoundInstance(uint32 mapid, Difficulty difficulty BoundInstancesMap::iterator itr = m_boundInstances[difficulty].find(mapid); if (itr != m_boundInstances[difficulty].end()) - return &itr->second; - + if (itr->second.extendState || withExpired) + return &itr->second; return nullptr; } @@ -18191,24 +18192,32 @@ void Player::UnbindInstance(BoundInstancesMap::iterator &itr, Difficulty difficu } } -InstancePlayerBind* Player::BindToInstance(InstanceSave* save, bool permanent, bool load) +InstancePlayerBind* Player::BindToInstance(InstanceSave* save, bool permanent, BindExtensionState extendState, bool load) { if (save) { InstancePlayerBind& bind = m_boundInstances[save->GetDifficultyID()][save->GetMapId()]; + if (extendState == EXTEND_STATE_KEEP) // special flag, keep the player's current extend state when updating for new boss down + { + if (save == bind.save) + extendState = bind.extendState; + else + extendState = EXTEND_STATE_NORMAL; + } if (!load) { if (bind.save) { // update the save when the group kills a boss - if (permanent != bind.perm || save != bind.save) + if (permanent != bind.perm || save != bind.save || extendState != bind.extendState) { PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_UPD_CHAR_INSTANCE); stmt->setUInt32(0, save->GetInstanceId()); stmt->setBool(1, permanent); - stmt->setUInt64(2, GetGUID().GetCounter()); - stmt->setUInt32(3, bind.save->GetInstanceId()); + stmt->setUInt8(2, extendState); + stmt->setUInt64(3, GetGUID().GetCounter()); + stmt->setUInt32(4, bind.save->GetInstanceId()); CharacterDatabase.Execute(stmt); } @@ -18220,6 +18229,7 @@ InstancePlayerBind* Player::BindToInstance(InstanceSave* save, bool permanent, b stmt->setUInt64(0, GetGUID().GetCounter()); stmt->setUInt32(1, save->GetInstanceId()); stmt->setBool(2, permanent); + stmt->setUInt8(3, extendState); CharacterDatabase.Execute(stmt); } @@ -18237,10 +18247,10 @@ InstancePlayerBind* Player::BindToInstance(InstanceSave* save, bool permanent, b bind.save = save; bind.perm = permanent; + bind.extendState = extendState; if (!load) - TC_LOG_DEBUG("maps", "Player::BindToInstance: Player '%s' (%s) is now bound to map (ID: %d, Instance: %d, Difficulty: %d)", - GetName().c_str(), GetGUID().ToString().c_str(), save->GetMapId(), save->GetInstanceId(), save->GetDifficultyID()); - sScriptMgr->OnPlayerBindToInstance(this, save->GetDifficultyID(), save->GetMapId(), permanent); + TC_LOG_DEBUG("maps", "Player::BindToInstance: Player '%s' (%s) is now bound to map (ID: %d, Instance %d, Difficulty %d)", GetName().c_str(), GetGUID().ToString().c_str(), save->GetMapId(), save->GetInstanceId(), save->GetDifficultyID()); + sScriptMgr->OnPlayerBindToInstance(this, save->GetDifficultyID(), save->GetMapId(), permanent, uint8(extendState)); return &bind; } @@ -18258,7 +18268,7 @@ void Player::BindToInstance() GetSession()->SendPacket(data.Write()); if (!IsGameMaster()) { - BindToInstance(mapSave, true); + BindToInstance(mapSave, true, EXTEND_STATE_KEEP); GetSession()->SendCalendarRaidLockout(mapSave, true); } } @@ -18279,7 +18289,8 @@ void Player::SendRaidInfo() { for (BoundInstancesMap::iterator itr = m_boundInstances[i].begin(); itr != m_boundInstances[i].end(); ++itr) { - if (itr->second.perm) + InstancePlayerBind const& bind = itr->second; + if (bind.perm) { InstanceSave* save = itr->second.save; @@ -18288,15 +18299,18 @@ void Player::SendRaidInfo() lockInfos.InstanceID = save->GetInstanceId(); lockInfos.MapID = save->GetMapId(); lockInfos.DifficultyID = save->GetDifficultyID(); - lockInfos.TimeRemaining = save->GetResetTime() - now; + if (bind.extendState != EXTEND_STATE_EXTENDED) + lockInfos.TimeRemaining = save->GetResetTime() - now; + else + lockInfos.TimeRemaining = sInstanceSaveMgr->GetSubsequentResetTime(save->GetMapId(), save->GetDifficultyID(), save->GetResetTime()) - now; lockInfos.CompletedMask = 0; if (Map* map = sMapMgr->FindMap(save->GetMapId(), save->GetInstanceId())) if (InstanceScript* instanceScript = ((InstanceMap*)map)->GetInstanceScript()) lockInfos.CompletedMask = instanceScript->GetCompletedEncounterMask(); - lockInfos.Locked = lockInfos.TimeRemaining <= 0; - lockInfos.Extended = false; + lockInfos.Locked = bind.extendState != EXTEND_STATE_EXPIRED; + lockInfos.Extended = bind.extendState == EXTEND_STATE_EXTENDED; instanceInfo.LockList.push_back(lockInfos); } diff --git a/src/server/game/Entities/Player/Player.h b/src/server/game/Entities/Player/Player.h index 1326a60beda..c87e5f6cbee 100644 --- a/src/server/game/Entities/Player/Player.h +++ b/src/server/game/Entities/Player/Player.h @@ -1001,14 +1001,27 @@ enum PlayerDelayedOperations // Maximum money amount : 2^31 - 1 TC_GAME_API extern uint64 const MAX_MONEY_AMOUNT; +enum BindExtensionState +{ + EXTEND_STATE_EXPIRED = 0, + EXTEND_STATE_NORMAL = 1, + EXTEND_STATE_EXTENDED = 2, + EXTEND_STATE_KEEP = 255 // special state: keep current save type +}; struct InstancePlayerBind { InstanceSave* save; - bool perm; /* permanent PlayerInstanceBinds are created in Raid/Heroic instances for players - that aren't already permanently bound when they are inside when a boss is killed - or when they enter an instance that the group leader is permanently bound to. */ - InstancePlayerBind() : save(nullptr), perm(false) { } + that aren't already permanently bound when they are inside when a boss is killed + or when they enter an instance that the group leader is permanently bound to. */ + bool perm; + /* extend state listing: + EXPIRED - doesn't affect anything unless manually re-extended by player + NORMAL - standard state + EXTENDED - won't be promoted to EXPIRED at next reset period, will instead be promoted to NORMAL */ + BindExtensionState extendState; + + InstancePlayerBind() : save(NULL), perm(false), extendState(EXTEND_STATE_NORMAL) { } }; struct AccessRequirement @@ -2329,13 +2342,13 @@ class TC_GAME_API Player : public Unit, public GridObject<Player> bool m_InstanceValid; // permanent binds and solo binds by difficulty BoundInstancesMap m_boundInstances[MAX_DIFFICULTY]; - InstancePlayerBind* GetBoundInstance(uint32 mapid, Difficulty difficulty); + InstancePlayerBind* GetBoundInstance(uint32 mapid, Difficulty difficulty, bool withExpired = false); InstancePlayerBind const* GetBoundInstance(uint32 mapid, Difficulty difficulty) const; BoundInstancesMap& GetBoundInstances(Difficulty difficulty) { return m_boundInstances[difficulty]; } InstanceSave* GetInstanceSave(uint32 mapid); void UnbindInstance(uint32 mapid, Difficulty difficulty, bool unload = false); void UnbindInstance(BoundInstancesMap::iterator &itr, Difficulty difficulty, bool unload = false); - InstancePlayerBind* BindToInstance(InstanceSave* save, bool permanent, bool load = false); + InstancePlayerBind* BindToInstance(InstanceSave* save, bool permanent, BindExtensionState extendState = EXTEND_STATE_NORMAL, bool load = false); void BindToInstance(); void SetPendingBind(uint32 instanceId, uint32 bindTimer); bool HasPendingBind() const { return _pendingBindId > 0; } diff --git a/src/server/game/Groups/Group.cpp b/src/server/game/Groups/Group.cpp index 494b3e4582e..b95a235bcb6 100644 --- a/src/server/game/Groups/Group.cpp +++ b/src/server/game/Groups/Group.cpp @@ -727,7 +727,8 @@ void Group::ConvertLeaderInstancesToGroup(Player* player, Group* group, bool swi for (Player::BoundInstancesMap::iterator itr = player->m_boundInstances[i].begin(); itr != player->m_boundInstances[i].end();) { if (!switchLeader || !group->GetBoundInstance(itr->second.save->GetDifficultyID(), itr->first)) - group->BindToInstance(itr->second.save, itr->second.perm, false); + if (itr->second.extendState) // not expired + group->BindToInstance(itr->second.save, itr->second.perm, false); // permanent binds are not removed if (switchLeader && !itr->second.perm) diff --git a/src/server/game/Handlers/CalendarHandler.cpp b/src/server/game/Handlers/CalendarHandler.cpp index 22927a93ef9..71e469b23c3 100644 --- a/src/server/game/Handlers/CalendarHandler.cpp +++ b/src/server/game/Handlers/CalendarHandler.cpp @@ -484,6 +484,21 @@ void WorldSession::HandleSetSavedInstanceExtend(WorldPackets::Calendar::SetSaved { TC_LOG_DEBUG("network", "CMSG_SET_SAVED_INSTANCE_EXTEND - MapId: %u, Difficulty: %u, ToggleExtend: %s", setSavedInstanceExtend.MapID, setSavedInstanceExtend.DifficultyID, setSavedInstanceExtend.Extend ? "On" : "Off"); + if (Player* player = GetPlayer()) + { + InstancePlayerBind* instanceBind = player->GetBoundInstance(setSavedInstanceExtend.MapID, Difficulty(setSavedInstanceExtend.DifficultyID), setSavedInstanceExtend.Extend); // include expired instances if we are toggling extend on + if (!instanceBind || !instanceBind->save || !instanceBind->perm) + return; + + BindExtensionState newState; + if (!setSavedInstanceExtend.Extend || instanceBind->extendState == EXTEND_STATE_EXPIRED) + newState = EXTEND_STATE_NORMAL; + else + newState = EXTEND_STATE_EXTENDED; + + player->BindToInstance(instanceBind->save, true, newState, false); + } + /* InstancePlayerBind* instanceBind = _player->GetBoundInstance(setSavedInstanceExtend.MapID, Difficulty(setSavedInstanceExtend.DifficultyID)); if (!instanceBind || !instanceBind->save) diff --git a/src/server/game/Instances/InstanceSaveMgr.cpp b/src/server/game/Instances/InstanceSaveMgr.cpp index fc65a1da82f..4d979ecb9b3 100644 --- a/src/server/game/Instances/InstanceSaveMgr.cpp +++ b/src/server/game/Instances/InstanceSaveMgr.cpp @@ -445,6 +445,23 @@ void InstanceSaveManager::LoadResetTimes() } } +time_t InstanceSaveManager::GetSubsequentResetTime(uint32 mapid, Difficulty difficulty, time_t resetTime) const +{ + MapDifficultyEntry const* mapDiff = GetMapDifficultyData(mapid, difficulty); + if (!mapDiff || !mapDiff->RaidDuration) + { + TC_LOG_ERROR("misc", "InstanceSaveManager::GetSubsequentResetTime: not valid difficulty or no reset delay for map %u", mapid); + return 0; + } + + time_t diff = sWorld->getIntConfig(CONFIG_INSTANCE_RESET_TIME_HOUR) * HOUR; + time_t period = uint32(((mapDiff->RaidDuration * sWorld->getRate(RATE_INSTANCE_RESET_TIME)) / DAY) * DAY); + if (period < DAY) + period = DAY; + + return ((resetTime + MINUTE) / DAY * DAY) + period + diff; +} + void InstanceSaveManager::ScheduleReset(bool add, time_t time, InstResetEvent event) { if (!add) @@ -482,6 +499,17 @@ void InstanceSaveManager::ScheduleReset(bool add, time_t time, InstResetEvent ev m_resetTimeQueue.insert(std::pair<time_t, InstResetEvent>(time, event)); } +void InstanceSaveManager::ForceGlobalReset(uint32 mapId, Difficulty difficulty) +{ + if (!GetDownscaledMapDifficultyData(mapId, difficulty)) + return; + // remove currently scheduled reset times + ScheduleReset(false, 0, InstResetEvent(1, mapId, difficulty, 0)); + ScheduleReset(false, 0, InstResetEvent(4, mapId, difficulty, 0)); + // force global reset on the instance + _ResetOrWarnAll(mapId, difficulty, false, time(nullptr)); +} + void InstanceSaveManager::Update() { time_t now = time(NULL); @@ -522,10 +550,26 @@ void InstanceSaveManager::_ResetSave(InstanceSaveHashMap::iterator &itr) // do not allow UnbindInstance to automatically unload the InstanceSaves lock_instLists = true; + bool shouldDelete = true; InstanceSave::PlayerListType &pList = itr->second->m_playerList; - while (!pList.empty()) + std::vector<Player*> temp; // list of expired binds that should be unbound + for (Player* player : pList) + { + if (InstancePlayerBind* bind = player->GetBoundInstance(itr->second->GetMapId(), itr->second->GetDifficultyID())) + { + ASSERT(bind->save == itr->second); + if (bind->perm && bind->extendState) // permanent and not already expired + { + // actual promotion in DB already happened in caller + bind->extendState = bind->extendState == EXTEND_STATE_EXTENDED ? EXTEND_STATE_NORMAL : EXTEND_STATE_EXPIRED; + shouldDelete = false; + continue; + } + } + temp.push_back(player); + } + for (Player* player : temp) { - Player* player = *(pList.begin()); player->UnbindInstance(itr->second->GetMapId(), itr->second->GetDifficultyID(), true); } @@ -536,8 +580,13 @@ void InstanceSaveManager::_ResetSave(InstanceSaveHashMap::iterator &itr) group->UnbindInstance(itr->second->GetMapId(), itr->second->GetDifficultyID(), true); } - delete itr->second; - m_instanceSaveById.erase(itr++); + if (shouldDelete) + { + delete itr->second; + itr = m_instanceSaveById.erase(itr); + } + else + ++itr; lock_instLists = false; } @@ -578,31 +627,21 @@ void InstanceSaveManager::_ResetOrWarnAll(uint32 mapid, Difficulty difficulty, b MapEntry const* mapEntry = sMapStore.LookupEntry(mapid); if (!mapEntry->Instanceable()) return; + TC_LOG_DEBUG("misc", "InstanceSaveManager::ResetOrWarnAll: Processing map %s (%u) on difficulty %u (warn? %u)", mapEntry->MapName_lang, mapid, uint8(difficulty), warn); time_t now = time(NULL); if (!warn) { - MapDifficultyEntry const* mapDiff = GetMapDifficultyData(mapid, difficulty); - if (!mapDiff || !mapDiff->RaidDuration) - { - TC_LOG_ERROR("misc", "InstanceSaveManager::ResetOrWarnAll: not valid difficulty or no reset delay for map %d", mapid); + // calculate the next reset time + time_t next_reset = GetSubsequentResetTime(mapid, difficulty, resetTime); + if (!next_reset) return; - } - - // remove all binds to instances of the given map - for (InstanceSaveHashMap::iterator itr = m_instanceSaveById.begin(); itr != m_instanceSaveById.end();) - { - if (itr->second->GetMapId() == mapid && itr->second->GetDifficultyID() == difficulty) - _ResetSave(itr); - else - ++itr; - } - // delete them from the DB, even if not loaded + // delete/promote instance binds from the DB, even if not loaded SQLTransaction trans = CharacterDatabase.BeginTransaction(); - PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_CHAR_INSTANCE_BY_MAP_DIFF); + PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_EXPIRED_CHAR_INSTANCE_BY_MAP_DIFF); stmt->setUInt16(0, uint16(mapid)); stmt->setUInt8(1, uint8(difficulty)); trans->Append(stmt); @@ -612,21 +651,26 @@ void InstanceSaveManager::_ResetOrWarnAll(uint32 mapid, Difficulty difficulty, b stmt->setUInt8(1, uint8(difficulty)); trans->Append(stmt); - stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_INSTANCE_BY_MAP_DIFF); + stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_EXPIRED_INSTANCE_BY_MAP_DIFF); stmt->setUInt16(0, uint16(mapid)); stmt->setUInt8(1, uint8(difficulty)); trans->Append(stmt); - CharacterDatabase.CommitTransaction(trans); - - // calculate the next reset time - uint32 diff = sWorld->getIntConfig(CONFIG_INSTANCE_RESET_TIME_HOUR) * HOUR; + stmt = CharacterDatabase.GetPreparedStatement(CHAR_UPD_EXPIRE_CHAR_INSTANCE_BY_MAP_DIFF); + stmt->setUInt16(0, uint16(mapid)); + stmt->setUInt8(1, uint8(difficulty)); + trans->Append(stmt); - uint32 period = uint32(((mapDiff->RaidDuration * sWorld->getRate(RATE_INSTANCE_RESET_TIME))/DAY) * DAY); - if (period < DAY) - period = DAY; + CharacterDatabase.CommitTransaction(trans); - uint32 next_reset = uint32(((resetTime + MINUTE) / DAY * DAY) + period + diff); + // promote loaded binds to instances of the given map + for (InstanceSaveHashMap::iterator itr = m_instanceSaveById.begin(); itr != m_instanceSaveById.end();) + { + if (itr->second->GetMapId() == mapid && itr->second->GetDifficultyID() == difficulty) + _ResetSave(itr); + else + ++itr; + } SetResetTimeFor(mapid, difficulty, next_reset); ScheduleReset(true, time_t(next_reset-3600), InstResetEvent(1, mapid, difficulty, 0)); diff --git a/src/server/game/Instances/InstanceSaveMgr.h b/src/server/game/Instances/InstanceSaveMgr.h index 21b5c3ca5c3..ff99736f2b7 100644 --- a/src/server/game/Instances/InstanceSaveMgr.h +++ b/src/server/game/Instances/InstanceSaveMgr.h @@ -186,6 +186,7 @@ class TC_GAME_API InstanceSaveManager ResetTimeByMapDifficultyMap::const_iterator itr = m_resetTimeByMapDifficulty.find(MAKE_PAIR64(mapid, d)); return itr != m_resetTimeByMapDifficulty.end() ? itr->second : 0; } + time_t GetSubsequentResetTime(uint32 mapid, Difficulty difficulty, time_t resetTime) const; // Use this on startup when initializing reset times void InitializeResetTimeFor(uint32 mapid, Difficulty d, time_t t) @@ -206,6 +207,7 @@ class TC_GAME_API InstanceSaveManager return m_resetTimeByMapDifficulty; } void ScheduleReset(bool add, time_t time, InstResetEvent event); + void ForceGlobalReset(uint32 mapId, Difficulty difficulty); void Update(); diff --git a/src/server/game/Maps/Map.cpp b/src/server/game/Maps/Map.cpp index d1d13e428f9..a2614fc0766 100644 --- a/src/server/game/Maps/Map.cpp +++ b/src/server/game/Maps/Map.cpp @@ -3294,22 +3294,37 @@ bool InstanceMap::Reset(uint8 method) } else { + bool doUnload = true; if (method == INSTANCE_RESET_GLOBAL) + { // set the homebind timer for players inside (1 minute) for (MapRefManager::iterator itr = m_mapRefManager.begin(); itr != m_mapRefManager.end(); ++itr) - itr->GetSource()->m_InstanceValid = false; + { + InstancePlayerBind* bind = itr->GetSource()->GetBoundInstance(GetId(), GetDifficultyID()); + if (bind && bind->extendState && bind->save->GetInstanceId() == GetInstanceId()) + doUnload = false; + else + itr->GetSource()->m_InstanceValid = false; + } + + if (doUnload && HasPermBoundPlayers()) // check if any unloaded players have a nonexpired save to this + doUnload = false; + } - // the unload timer is not started - // instead the map will unload immediately after the players have left - m_unloadWhenEmpty = true; - m_resetAfterUnload = true; + if (doUnload) + { + // the unload timer is not started + // instead the map will unload immediately after the players have left + m_unloadWhenEmpty = true; + m_resetAfterUnload = true; + } } } else { // unloaded at next update m_unloadTimer = MIN_UNLOAD_DELAY; - m_resetAfterUnload = true; + m_resetAfterUnload = !(method == INSTANCE_RESET_GLOBAL && HasPermBoundPlayers()); } return m_mapRefManager.isEmpty(); @@ -3409,6 +3424,13 @@ bool Map::IsHeroic() const return false; } +bool InstanceMap::HasPermBoundPlayers() const +{ + PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_PERM_BIND_BY_INSTANCE); + stmt->setUInt16(0,GetInstanceId()); + return !!CharacterDatabase.Query(stmt); +} + uint32 InstanceMap::GetMaxPlayers() const { MapDifficultyEntry const* mapDiff = GetMapDifficulty(); diff --git a/src/server/game/Maps/Map.h b/src/server/game/Maps/Map.h index 75d184b19a6..6a917555fb0 100644 --- a/src/server/game/Maps/Map.h +++ b/src/server/game/Maps/Map.h @@ -773,6 +773,9 @@ class TC_GAME_API InstanceMap : public Map void SendResetWarnings(uint32 timeLeft) const; void SetResetSchedule(bool on); + /* this checks if any players have a permanent bind (included reactivatable expired binds) to the instance ID + it needs a DB query, so use sparingly */ + bool HasPermBoundPlayers() const; uint32 GetMaxPlayers() const; uint32 GetMaxResetDelay() const; diff --git a/src/server/game/Scripting/ScriptMgr.cpp b/src/server/game/Scripting/ScriptMgr.cpp index 9b403e608c1..77d747898f9 100644 --- a/src/server/game/Scripting/ScriptMgr.cpp +++ b/src/server/game/Scripting/ScriptMgr.cpp @@ -1360,9 +1360,9 @@ void ScriptMgr::OnPlayerSave(Player* player) FOREACH_SCRIPT(PlayerScript)->OnSave(player); } -void ScriptMgr::OnPlayerBindToInstance(Player* player, Difficulty difficulty, uint32 mapid, bool permanent) +void ScriptMgr::OnPlayerBindToInstance(Player* player, Difficulty difficulty, uint32 mapid, bool permanent, uint8 extendState) { - FOREACH_SCRIPT(PlayerScript)->OnBindToInstance(player, difficulty, mapid, permanent); + FOREACH_SCRIPT(PlayerScript)->OnBindToInstance(player, difficulty, mapid, permanent, extendState); } void ScriptMgr::OnPlayerUpdateZone(Player* player, uint32 newZone, uint32 newArea) diff --git a/src/server/game/Scripting/ScriptMgr.h b/src/server/game/Scripting/ScriptMgr.h index 368d5c4f105..a94efc24e0d 100644 --- a/src/server/game/Scripting/ScriptMgr.h +++ b/src/server/game/Scripting/ScriptMgr.h @@ -729,7 +729,7 @@ class TC_GAME_API PlayerScript : public UnitScript virtual void OnSave(Player* /*player*/) { } // Called when a player is bound to an instance - virtual void OnBindToInstance(Player* /*player*/, Difficulty /*difficulty*/, uint32 /*mapId*/, bool /*permanent*/) { } + virtual void OnBindToInstance(Player* /*player*/, Difficulty /*difficulty*/, uint32 /*mapId*/, bool /*permanent*/, uint8 /*extendState*/) { } // Called when a player switches to a new zone virtual void OnUpdateZone(Player* /*player*/, uint32 /*newZone*/, uint32 /*newArea*/) { } @@ -1049,7 +1049,7 @@ class TC_GAME_API ScriptMgr void OnPlayerDelete(ObjectGuid guid, uint32 accountId); void OnPlayerFailedDelete(ObjectGuid guid, uint32 accountId); void OnPlayerSave(Player* player); - void OnPlayerBindToInstance(Player* player, Difficulty difficulty, uint32 mapid, bool permanent); + void OnPlayerBindToInstance(Player* player, Difficulty difficulty, uint32 mapid, bool permanent, uint8 extendState); void OnPlayerUpdateZone(Player* player, uint32 newZone, uint32 newArea); void OnQuestStatusChange(Player* player, uint32 questId, QuestStatus status); diff --git a/src/server/scripts/Commands/cs_debug.cpp b/src/server/scripts/Commands/cs_debug.cpp index 8be6e029cf0..19af35fa1f2 100644 --- a/src/server/scripts/Commands/cs_debug.cpp +++ b/src/server/scripts/Commands/cs_debug.cpp @@ -98,7 +98,8 @@ public: { "transport", rbac::RBAC_PERM_COMMAND_DEBUG_TRANSPORT, false, &HandleDebugTransportCommand, "" }, { "loadcells", rbac::RBAC_PERM_COMMAND_DEBUG_LOADCELLS, false, &HandleDebugLoadCellsCommand, "",}, { "phase", rbac::RBAC_PERM_COMMAND_DEBUG_PHASE, false, &HandleDebugPhaseCommand, "" }, - { "boundary", rbac::RBAC_PERM_COMMAND_DEBUG_BOUNDARY, false, &HandleDebugBoundaryCommand, "" } + { "boundary", rbac::RBAC_PERM_COMMAND_DEBUG_BOUNDARY, false, &HandleDebugBoundaryCommand, "" }, + { "raidreset", rbac::RBAC_PERM_COMMAND_INSTANCE_UNBIND, false, &HandleDebugRaidResetCommand, "" } }; static std::vector<ChatCommand> commandTable = { @@ -1439,7 +1440,7 @@ public: duration = 3 * MINUTE; bool doFill = fill_str ? (stricmp(fill_str, "FILL") == 0) : false; - + int32 errMsg = target->AI()->VisualizeBoundary(duration, player, doFill); if (errMsg > 0) handler->PSendSysMessage(errMsg); @@ -1522,6 +1523,32 @@ public: return true; } + + static bool HandleDebugRaidResetCommand(ChatHandler* /*handler*/, char const* args) + { + char* map_str = args ? strtok((char*)args, " ") : nullptr; + char* difficulty_str = args ? strtok(nullptr, " ") : nullptr; + + int32 map = map_str ? atoi(map_str) : -1; + if (map <= 0) + return false; + MapEntry const* mEntry = sMapStore.LookupEntry(map); + if (!mEntry || !mEntry->IsRaid()) + return false; + int32 difficulty = difficulty_str ? atoi(difficulty_str) : -1; + if (difficulty >= MAX_DIFFICULTY || difficulty < -1) + return false; + + if (difficulty == -1) + for (uint8 diff = 0; diff < MAX_DIFFICULTY; ++diff) + { + if (GetMapDifficultyData(mEntry->ID, Difficulty(diff))) + sInstanceSaveMgr->ForceGlobalReset(mEntry->ID, Difficulty(diff)); + } + else + sInstanceSaveMgr->ForceGlobalReset(mEntry->ID, Difficulty(difficulty)); + return true; + } }; void AddSC_debug_commandscript() diff --git a/src/server/scripts/Commands/cs_instance.cpp b/src/server/scripts/Commands/cs_instance.cpp index 074a322e845..b94c71f6713 100644 --- a/src/server/scripts/Commands/cs_instance.cpp +++ b/src/server/scripts/Commands/cs_instance.cpp @@ -82,7 +82,7 @@ public: { InstanceSave* save = itr->second.save; std::string timeleft = GetTimeString(save->GetResetTime() - time(NULL)); - handler->PSendSysMessage(LANG_COMMAND_LIST_BIND_INFO, itr->first, save->GetInstanceId(), itr->second.perm ? "yes" : "no", save->GetDifficultyID(), save->CanReset() ? "yes" : "no", timeleft.c_str()); + handler->PSendSysMessage(LANG_COMMAND_LIST_BIND_INFO, itr->first, save->GetInstanceId(), itr->second.perm ? "yes" : "no", itr->second.extendState == EXTEND_STATE_EXPIRED ? "expired" : itr->second.extendState == EXTEND_STATE_EXTENDED ? "yes" : "no", save->GetDifficultyID(), save->CanReset() ? "yes" : "no", timeleft.c_str()); counter++; } } @@ -98,7 +98,7 @@ public: { InstanceSave* save = itr->second.save; std::string timeleft = GetTimeString(save->GetResetTime() - time(NULL)); - handler->PSendSysMessage(LANG_COMMAND_LIST_BIND_INFO, itr->first, save->GetInstanceId(), itr->second.perm ? "yes" : "no", save->GetDifficultyID(), save->CanReset() ? "yes" : "no", timeleft.c_str()); + handler->PSendSysMessage(LANG_COMMAND_LIST_BIND_INFO, itr->first, save->GetInstanceId(), itr->second.perm ? "yes" : "no", "-", save->GetDifficultyID(), save->CanReset() ? "yes" : "no", timeleft.c_str()); counter++; } } |