diff --git a/common/database/database_update_manifest.cpp b/common/database/database_update_manifest.cpp index 3ed90b625e..f99b42f44d 100644 --- a/common/database/database_update_manifest.cpp +++ b/common/database/database_update_manifest.cpp @@ -5186,7 +5186,19 @@ ALTER TABLE `instance_list` ADD COLUMN `notes` varchar(50) NOT NULL DEFAULT '' AFTER `never_expires`; )", }, - + ManifestEntry{ + .version = 9252, + .description = "2024_01_07_zone_idle_when_empty.sql", + .check = "SHOW COLUMNS FROM `zone` LIKE 'idle_when_empty'", + .condition = "empty", + .match = "", + .sql = R"( +ALTER TABLE `zone` +ADD COLUMN `idle_when_empty` tinyint(1) UNSIGNED NOT NULL DEFAULT 1 AFTER `min_lava_damage` +ADD COLUMN `seconds_before_idle` int(11) UNSIGNED NOT NULL DEFAULT 60 AFTER `idle_when_empty` +)", + .content_schema_update = true + } // -- template; copy/paste this when you need to create a new entry // ManifestEntry{ // .version = 9228, diff --git a/common/features.h b/common/features.h index 195d825dce..0e157a6529 100644 --- a/common/features.h +++ b/common/features.h @@ -35,11 +35,6 @@ Core Zone features */ - -//Uncomment this to cause a zone to basically idle -//when there are no players in it, mobs stop wandering, etc.. -#define IDLE_WHEN_EMPTY - #ifdef EMBPERL //Enable the new XS based perl parser #define EMBPERL_XS diff --git a/common/repositories/base/base_zone_repository.h b/common/repositories/base/base_zone_repository.h index 3e875169d8..00275a729d 100644 --- a/common/repositories/base/base_zone_repository.h +++ b/common/repositories/base/base_zone_repository.h @@ -115,6 +115,8 @@ class BaseZoneRepository { int32_t underworld_teleport_index; int32_t lava_damage; int32_t min_lava_damage; + uint8_t idle_when_empty; + uint32_t seconds_before_idle; }; static std::string PrimaryKey() @@ -221,6 +223,8 @@ class BaseZoneRepository { "underworld_teleport_index", "lava_damage", "min_lava_damage", + "idle_when_empty", + "seconds_before_idle", }; } @@ -323,6 +327,8 @@ class BaseZoneRepository { "underworld_teleport_index", "lava_damage", "min_lava_damage", + "idle_when_empty", + "seconds_before_idle", }; } @@ -459,6 +465,8 @@ class BaseZoneRepository { e.underworld_teleport_index = 0; e.lava_damage = 50; e.min_lava_damage = 10; + e.idle_when_empty = 1; + e.seconds_before_idle = 60; return e; } @@ -496,7 +504,7 @@ class BaseZoneRepository { Zone e{}; e.short_name = row[0] ? row[0] : ""; - e.id = static_cast(atoi(row[1])); + e.id = row[1] ? static_cast(atoi(row[1])) : 0; e.file_name = row[2] ? row[2] : ""; e.long_name = row[3] ? row[3] : ""; e.map_file_name = row[4] ? row[4] : ""; @@ -508,10 +516,10 @@ class BaseZoneRepository { e.min_level = row[10] ? static_cast(strtoul(row[10], nullptr, 10)) : 0; e.max_level = row[11] ? static_cast(strtoul(row[11], nullptr, 10)) : 255; e.min_status = row[12] ? static_cast(strtoul(row[12], nullptr, 10)) : 0; - e.zoneidnumber = static_cast(atoi(row[13])); + e.zoneidnumber = row[13] ? static_cast(atoi(row[13])) : 0; e.version = row[14] ? static_cast(strtoul(row[14], nullptr, 10)) : 0; - e.timezone = static_cast(atoi(row[15])); - e.maxclients = static_cast(atoi(row[16])); + e.timezone = row[15] ? static_cast(atoi(row[15])) : 0; + e.maxclients = row[16] ? static_cast(atoi(row[16])) : 0; e.ruleset = row[17] ? static_cast(strtoul(row[17], nullptr, 10)) : 0; e.note = row[18] ? row[18] : ""; e.underworld = row[19] ? strtof(row[19], nullptr) : 0; @@ -549,48 +557,50 @@ class BaseZoneRepository { e.fog_maxclip4 = row[51] ? strtof(row[51], nullptr) : 450; e.fog_density = row[52] ? strtof(row[52], nullptr) : 0; e.flag_needed = row[53] ? row[53] : ""; - e.canbind = static_cast(atoi(row[54])); - e.cancombat = static_cast(atoi(row[55])); - e.canlevitate = static_cast(atoi(row[56])); - e.castoutdoor = static_cast(atoi(row[57])); + e.canbind = row[54] ? static_cast(atoi(row[54])) : 1; + e.cancombat = row[55] ? static_cast(atoi(row[55])) : 1; + e.canlevitate = row[56] ? static_cast(atoi(row[56])) : 1; + e.castoutdoor = row[57] ? static_cast(atoi(row[57])) : 1; e.hotzone = row[58] ? static_cast(strtoul(row[58], nullptr, 10)) : 0; e.insttype = row[59] ? static_cast(strtoul(row[59], nullptr, 10)) : 0; e.shutdowndelay = row[60] ? strtoull(row[60], nullptr, 10) : 5000; - e.peqzone = static_cast(atoi(row[61])); - e.expansion = static_cast(atoi(row[62])); - e.bypass_expansion_check = static_cast(atoi(row[63])); + e.peqzone = row[61] ? static_cast(atoi(row[61])) : 1; + e.expansion = row[62] ? static_cast(atoi(row[62])) : 0; + e.bypass_expansion_check = row[63] ? static_cast(atoi(row[63])) : 0; e.suspendbuffs = row[64] ? static_cast(strtoul(row[64], nullptr, 10)) : 0; - e.rain_chance1 = static_cast(atoi(row[65])); - e.rain_chance2 = static_cast(atoi(row[66])); - e.rain_chance3 = static_cast(atoi(row[67])); - e.rain_chance4 = static_cast(atoi(row[68])); - e.rain_duration1 = static_cast(atoi(row[69])); - e.rain_duration2 = static_cast(atoi(row[70])); - e.rain_duration3 = static_cast(atoi(row[71])); - e.rain_duration4 = static_cast(atoi(row[72])); - e.snow_chance1 = static_cast(atoi(row[73])); - e.snow_chance2 = static_cast(atoi(row[74])); - e.snow_chance3 = static_cast(atoi(row[75])); - e.snow_chance4 = static_cast(atoi(row[76])); - e.snow_duration1 = static_cast(atoi(row[77])); - e.snow_duration2 = static_cast(atoi(row[78])); - e.snow_duration3 = static_cast(atoi(row[79])); - e.snow_duration4 = static_cast(atoi(row[80])); + e.rain_chance1 = row[65] ? static_cast(atoi(row[65])) : 0; + e.rain_chance2 = row[66] ? static_cast(atoi(row[66])) : 0; + e.rain_chance3 = row[67] ? static_cast(atoi(row[67])) : 0; + e.rain_chance4 = row[68] ? static_cast(atoi(row[68])) : 0; + e.rain_duration1 = row[69] ? static_cast(atoi(row[69])) : 0; + e.rain_duration2 = row[70] ? static_cast(atoi(row[70])) : 0; + e.rain_duration3 = row[71] ? static_cast(atoi(row[71])) : 0; + e.rain_duration4 = row[72] ? static_cast(atoi(row[72])) : 0; + e.snow_chance1 = row[73] ? static_cast(atoi(row[73])) : 0; + e.snow_chance2 = row[74] ? static_cast(atoi(row[74])) : 0; + e.snow_chance3 = row[75] ? static_cast(atoi(row[75])) : 0; + e.snow_chance4 = row[76] ? static_cast(atoi(row[76])) : 0; + e.snow_duration1 = row[77] ? static_cast(atoi(row[77])) : 0; + e.snow_duration2 = row[78] ? static_cast(atoi(row[78])) : 0; + e.snow_duration3 = row[79] ? static_cast(atoi(row[79])) : 0; + e.snow_duration4 = row[80] ? static_cast(atoi(row[80])) : 0; e.gravity = row[81] ? strtof(row[81], nullptr) : 0.4; - e.type = static_cast(atoi(row[82])); - e.skylock = static_cast(atoi(row[83])); - e.fast_regen_hp = static_cast(atoi(row[84])); - e.fast_regen_mana = static_cast(atoi(row[85])); - e.fast_regen_endurance = static_cast(atoi(row[86])); - e.npc_max_aggro_dist = static_cast(atoi(row[87])); + e.type = row[82] ? static_cast(atoi(row[82])) : 0; + e.skylock = row[83] ? static_cast(atoi(row[83])) : 0; + e.fast_regen_hp = row[84] ? static_cast(atoi(row[84])) : 180; + e.fast_regen_mana = row[85] ? static_cast(atoi(row[85])) : 180; + e.fast_regen_endurance = row[86] ? static_cast(atoi(row[86])) : 180; + e.npc_max_aggro_dist = row[87] ? static_cast(atoi(row[87])) : 600; e.max_movement_update_range = row[88] ? static_cast(strtoul(row[88], nullptr, 10)) : 600; - e.min_expansion = static_cast(atoi(row[89])); - e.max_expansion = static_cast(atoi(row[90])); + e.min_expansion = row[89] ? static_cast(atoi(row[89])) : -1; + e.max_expansion = row[90] ? static_cast(atoi(row[90])) : -1; e.content_flags = row[91] ? row[91] : ""; e.content_flags_disabled = row[92] ? row[92] : ""; - e.underworld_teleport_index = static_cast(atoi(row[93])); - e.lava_damage = static_cast(atoi(row[94])); - e.min_lava_damage = static_cast(atoi(row[95])); + e.underworld_teleport_index = row[93] ? static_cast(atoi(row[93])) : 0; + e.lava_damage = row[94] ? static_cast(atoi(row[94])) : 50; + e.min_lava_damage = row[95] ? static_cast(atoi(row[95])) : 10; + e.idle_when_empty = row[96] ? static_cast(strtoul(row[96], nullptr, 10)) : 1; + e.seconds_before_idle = row[97] ? static_cast(strtoul(row[97], nullptr, 10)) : 60; return e; } @@ -719,6 +729,8 @@ class BaseZoneRepository { v.push_back(columns[93] + " = " + std::to_string(e.underworld_teleport_index)); v.push_back(columns[94] + " = " + std::to_string(e.lava_damage)); v.push_back(columns[95] + " = " + std::to_string(e.min_lava_damage)); + v.push_back(columns[96] + " = " + std::to_string(e.idle_when_empty)); + v.push_back(columns[97] + " = " + std::to_string(e.seconds_before_idle)); auto results = db.QueryDatabase( fmt::format( @@ -836,6 +848,8 @@ class BaseZoneRepository { v.push_back(std::to_string(e.underworld_teleport_index)); v.push_back(std::to_string(e.lava_damage)); v.push_back(std::to_string(e.min_lava_damage)); + v.push_back(std::to_string(e.idle_when_empty)); + v.push_back(std::to_string(e.seconds_before_idle)); auto results = db.QueryDatabase( fmt::format( @@ -961,6 +975,8 @@ class BaseZoneRepository { v.push_back(std::to_string(e.underworld_teleport_index)); v.push_back(std::to_string(e.lava_damage)); v.push_back(std::to_string(e.min_lava_damage)); + v.push_back(std::to_string(e.idle_when_empty)); + v.push_back(std::to_string(e.seconds_before_idle)); insert_chunks.push_back("(" + Strings::Implode(",", v) + ")"); } @@ -995,7 +1011,7 @@ class BaseZoneRepository { Zone e{}; e.short_name = row[0] ? row[0] : ""; - e.id = static_cast(atoi(row[1])); + e.id = row[1] ? static_cast(atoi(row[1])) : 0; e.file_name = row[2] ? row[2] : ""; e.long_name = row[3] ? row[3] : ""; e.map_file_name = row[4] ? row[4] : ""; @@ -1007,10 +1023,10 @@ class BaseZoneRepository { e.min_level = row[10] ? static_cast(strtoul(row[10], nullptr, 10)) : 0; e.max_level = row[11] ? static_cast(strtoul(row[11], nullptr, 10)) : 255; e.min_status = row[12] ? static_cast(strtoul(row[12], nullptr, 10)) : 0; - e.zoneidnumber = static_cast(atoi(row[13])); + e.zoneidnumber = row[13] ? static_cast(atoi(row[13])) : 0; e.version = row[14] ? static_cast(strtoul(row[14], nullptr, 10)) : 0; - e.timezone = static_cast(atoi(row[15])); - e.maxclients = static_cast(atoi(row[16])); + e.timezone = row[15] ? static_cast(atoi(row[15])) : 0; + e.maxclients = row[16] ? static_cast(atoi(row[16])) : 0; e.ruleset = row[17] ? static_cast(strtoul(row[17], nullptr, 10)) : 0; e.note = row[18] ? row[18] : ""; e.underworld = row[19] ? strtof(row[19], nullptr) : 0; @@ -1048,48 +1064,50 @@ class BaseZoneRepository { e.fog_maxclip4 = row[51] ? strtof(row[51], nullptr) : 450; e.fog_density = row[52] ? strtof(row[52], nullptr) : 0; e.flag_needed = row[53] ? row[53] : ""; - e.canbind = static_cast(atoi(row[54])); - e.cancombat = static_cast(atoi(row[55])); - e.canlevitate = static_cast(atoi(row[56])); - e.castoutdoor = static_cast(atoi(row[57])); + e.canbind = row[54] ? static_cast(atoi(row[54])) : 1; + e.cancombat = row[55] ? static_cast(atoi(row[55])) : 1; + e.canlevitate = row[56] ? static_cast(atoi(row[56])) : 1; + e.castoutdoor = row[57] ? static_cast(atoi(row[57])) : 1; e.hotzone = row[58] ? static_cast(strtoul(row[58], nullptr, 10)) : 0; e.insttype = row[59] ? static_cast(strtoul(row[59], nullptr, 10)) : 0; e.shutdowndelay = row[60] ? strtoull(row[60], nullptr, 10) : 5000; - e.peqzone = static_cast(atoi(row[61])); - e.expansion = static_cast(atoi(row[62])); - e.bypass_expansion_check = static_cast(atoi(row[63])); + e.peqzone = row[61] ? static_cast(atoi(row[61])) : 1; + e.expansion = row[62] ? static_cast(atoi(row[62])) : 0; + e.bypass_expansion_check = row[63] ? static_cast(atoi(row[63])) : 0; e.suspendbuffs = row[64] ? static_cast(strtoul(row[64], nullptr, 10)) : 0; - e.rain_chance1 = static_cast(atoi(row[65])); - e.rain_chance2 = static_cast(atoi(row[66])); - e.rain_chance3 = static_cast(atoi(row[67])); - e.rain_chance4 = static_cast(atoi(row[68])); - e.rain_duration1 = static_cast(atoi(row[69])); - e.rain_duration2 = static_cast(atoi(row[70])); - e.rain_duration3 = static_cast(atoi(row[71])); - e.rain_duration4 = static_cast(atoi(row[72])); - e.snow_chance1 = static_cast(atoi(row[73])); - e.snow_chance2 = static_cast(atoi(row[74])); - e.snow_chance3 = static_cast(atoi(row[75])); - e.snow_chance4 = static_cast(atoi(row[76])); - e.snow_duration1 = static_cast(atoi(row[77])); - e.snow_duration2 = static_cast(atoi(row[78])); - e.snow_duration3 = static_cast(atoi(row[79])); - e.snow_duration4 = static_cast(atoi(row[80])); + e.rain_chance1 = row[65] ? static_cast(atoi(row[65])) : 0; + e.rain_chance2 = row[66] ? static_cast(atoi(row[66])) : 0; + e.rain_chance3 = row[67] ? static_cast(atoi(row[67])) : 0; + e.rain_chance4 = row[68] ? static_cast(atoi(row[68])) : 0; + e.rain_duration1 = row[69] ? static_cast(atoi(row[69])) : 0; + e.rain_duration2 = row[70] ? static_cast(atoi(row[70])) : 0; + e.rain_duration3 = row[71] ? static_cast(atoi(row[71])) : 0; + e.rain_duration4 = row[72] ? static_cast(atoi(row[72])) : 0; + e.snow_chance1 = row[73] ? static_cast(atoi(row[73])) : 0; + e.snow_chance2 = row[74] ? static_cast(atoi(row[74])) : 0; + e.snow_chance3 = row[75] ? static_cast(atoi(row[75])) : 0; + e.snow_chance4 = row[76] ? static_cast(atoi(row[76])) : 0; + e.snow_duration1 = row[77] ? static_cast(atoi(row[77])) : 0; + e.snow_duration2 = row[78] ? static_cast(atoi(row[78])) : 0; + e.snow_duration3 = row[79] ? static_cast(atoi(row[79])) : 0; + e.snow_duration4 = row[80] ? static_cast(atoi(row[80])) : 0; e.gravity = row[81] ? strtof(row[81], nullptr) : 0.4; - e.type = static_cast(atoi(row[82])); - e.skylock = static_cast(atoi(row[83])); - e.fast_regen_hp = static_cast(atoi(row[84])); - e.fast_regen_mana = static_cast(atoi(row[85])); - e.fast_regen_endurance = static_cast(atoi(row[86])); - e.npc_max_aggro_dist = static_cast(atoi(row[87])); + e.type = row[82] ? static_cast(atoi(row[82])) : 0; + e.skylock = row[83] ? static_cast(atoi(row[83])) : 0; + e.fast_regen_hp = row[84] ? static_cast(atoi(row[84])) : 180; + e.fast_regen_mana = row[85] ? static_cast(atoi(row[85])) : 180; + e.fast_regen_endurance = row[86] ? static_cast(atoi(row[86])) : 180; + e.npc_max_aggro_dist = row[87] ? static_cast(atoi(row[87])) : 600; e.max_movement_update_range = row[88] ? static_cast(strtoul(row[88], nullptr, 10)) : 600; - e.min_expansion = static_cast(atoi(row[89])); - e.max_expansion = static_cast(atoi(row[90])); + e.min_expansion = row[89] ? static_cast(atoi(row[89])) : -1; + e.max_expansion = row[90] ? static_cast(atoi(row[90])) : -1; e.content_flags = row[91] ? row[91] : ""; e.content_flags_disabled = row[92] ? row[92] : ""; - e.underworld_teleport_index = static_cast(atoi(row[93])); - e.lava_damage = static_cast(atoi(row[94])); - e.min_lava_damage = static_cast(atoi(row[95])); + e.underworld_teleport_index = row[93] ? static_cast(atoi(row[93])) : 0; + e.lava_damage = row[94] ? static_cast(atoi(row[94])) : 50; + e.min_lava_damage = row[95] ? static_cast(atoi(row[95])) : 10; + e.idle_when_empty = row[96] ? static_cast(strtoul(row[96], nullptr, 10)) : 1; + e.seconds_before_idle = row[97] ? static_cast(strtoul(row[97], nullptr, 10)) : 60; all_entries.push_back(e); } @@ -1115,7 +1133,7 @@ class BaseZoneRepository { Zone e{}; e.short_name = row[0] ? row[0] : ""; - e.id = static_cast(atoi(row[1])); + e.id = row[1] ? static_cast(atoi(row[1])) : 0; e.file_name = row[2] ? row[2] : ""; e.long_name = row[3] ? row[3] : ""; e.map_file_name = row[4] ? row[4] : ""; @@ -1127,10 +1145,10 @@ class BaseZoneRepository { e.min_level = row[10] ? static_cast(strtoul(row[10], nullptr, 10)) : 0; e.max_level = row[11] ? static_cast(strtoul(row[11], nullptr, 10)) : 255; e.min_status = row[12] ? static_cast(strtoul(row[12], nullptr, 10)) : 0; - e.zoneidnumber = static_cast(atoi(row[13])); + e.zoneidnumber = row[13] ? static_cast(atoi(row[13])) : 0; e.version = row[14] ? static_cast(strtoul(row[14], nullptr, 10)) : 0; - e.timezone = static_cast(atoi(row[15])); - e.maxclients = static_cast(atoi(row[16])); + e.timezone = row[15] ? static_cast(atoi(row[15])) : 0; + e.maxclients = row[16] ? static_cast(atoi(row[16])) : 0; e.ruleset = row[17] ? static_cast(strtoul(row[17], nullptr, 10)) : 0; e.note = row[18] ? row[18] : ""; e.underworld = row[19] ? strtof(row[19], nullptr) : 0; @@ -1168,48 +1186,50 @@ class BaseZoneRepository { e.fog_maxclip4 = row[51] ? strtof(row[51], nullptr) : 450; e.fog_density = row[52] ? strtof(row[52], nullptr) : 0; e.flag_needed = row[53] ? row[53] : ""; - e.canbind = static_cast(atoi(row[54])); - e.cancombat = static_cast(atoi(row[55])); - e.canlevitate = static_cast(atoi(row[56])); - e.castoutdoor = static_cast(atoi(row[57])); + e.canbind = row[54] ? static_cast(atoi(row[54])) : 1; + e.cancombat = row[55] ? static_cast(atoi(row[55])) : 1; + e.canlevitate = row[56] ? static_cast(atoi(row[56])) : 1; + e.castoutdoor = row[57] ? static_cast(atoi(row[57])) : 1; e.hotzone = row[58] ? static_cast(strtoul(row[58], nullptr, 10)) : 0; e.insttype = row[59] ? static_cast(strtoul(row[59], nullptr, 10)) : 0; e.shutdowndelay = row[60] ? strtoull(row[60], nullptr, 10) : 5000; - e.peqzone = static_cast(atoi(row[61])); - e.expansion = static_cast(atoi(row[62])); - e.bypass_expansion_check = static_cast(atoi(row[63])); + e.peqzone = row[61] ? static_cast(atoi(row[61])) : 1; + e.expansion = row[62] ? static_cast(atoi(row[62])) : 0; + e.bypass_expansion_check = row[63] ? static_cast(atoi(row[63])) : 0; e.suspendbuffs = row[64] ? static_cast(strtoul(row[64], nullptr, 10)) : 0; - e.rain_chance1 = static_cast(atoi(row[65])); - e.rain_chance2 = static_cast(atoi(row[66])); - e.rain_chance3 = static_cast(atoi(row[67])); - e.rain_chance4 = static_cast(atoi(row[68])); - e.rain_duration1 = static_cast(atoi(row[69])); - e.rain_duration2 = static_cast(atoi(row[70])); - e.rain_duration3 = static_cast(atoi(row[71])); - e.rain_duration4 = static_cast(atoi(row[72])); - e.snow_chance1 = static_cast(atoi(row[73])); - e.snow_chance2 = static_cast(atoi(row[74])); - e.snow_chance3 = static_cast(atoi(row[75])); - e.snow_chance4 = static_cast(atoi(row[76])); - e.snow_duration1 = static_cast(atoi(row[77])); - e.snow_duration2 = static_cast(atoi(row[78])); - e.snow_duration3 = static_cast(atoi(row[79])); - e.snow_duration4 = static_cast(atoi(row[80])); + e.rain_chance1 = row[65] ? static_cast(atoi(row[65])) : 0; + e.rain_chance2 = row[66] ? static_cast(atoi(row[66])) : 0; + e.rain_chance3 = row[67] ? static_cast(atoi(row[67])) : 0; + e.rain_chance4 = row[68] ? static_cast(atoi(row[68])) : 0; + e.rain_duration1 = row[69] ? static_cast(atoi(row[69])) : 0; + e.rain_duration2 = row[70] ? static_cast(atoi(row[70])) : 0; + e.rain_duration3 = row[71] ? static_cast(atoi(row[71])) : 0; + e.rain_duration4 = row[72] ? static_cast(atoi(row[72])) : 0; + e.snow_chance1 = row[73] ? static_cast(atoi(row[73])) : 0; + e.snow_chance2 = row[74] ? static_cast(atoi(row[74])) : 0; + e.snow_chance3 = row[75] ? static_cast(atoi(row[75])) : 0; + e.snow_chance4 = row[76] ? static_cast(atoi(row[76])) : 0; + e.snow_duration1 = row[77] ? static_cast(atoi(row[77])) : 0; + e.snow_duration2 = row[78] ? static_cast(atoi(row[78])) : 0; + e.snow_duration3 = row[79] ? static_cast(atoi(row[79])) : 0; + e.snow_duration4 = row[80] ? static_cast(atoi(row[80])) : 0; e.gravity = row[81] ? strtof(row[81], nullptr) : 0.4; - e.type = static_cast(atoi(row[82])); - e.skylock = static_cast(atoi(row[83])); - e.fast_regen_hp = static_cast(atoi(row[84])); - e.fast_regen_mana = static_cast(atoi(row[85])); - e.fast_regen_endurance = static_cast(atoi(row[86])); - e.npc_max_aggro_dist = static_cast(atoi(row[87])); + e.type = row[82] ? static_cast(atoi(row[82])) : 0; + e.skylock = row[83] ? static_cast(atoi(row[83])) : 0; + e.fast_regen_hp = row[84] ? static_cast(atoi(row[84])) : 180; + e.fast_regen_mana = row[85] ? static_cast(atoi(row[85])) : 180; + e.fast_regen_endurance = row[86] ? static_cast(atoi(row[86])) : 180; + e.npc_max_aggro_dist = row[87] ? static_cast(atoi(row[87])) : 600; e.max_movement_update_range = row[88] ? static_cast(strtoul(row[88], nullptr, 10)) : 600; - e.min_expansion = static_cast(atoi(row[89])); - e.max_expansion = static_cast(atoi(row[90])); + e.min_expansion = row[89] ? static_cast(atoi(row[89])) : -1; + e.max_expansion = row[90] ? static_cast(atoi(row[90])) : -1; e.content_flags = row[91] ? row[91] : ""; e.content_flags_disabled = row[92] ? row[92] : ""; - e.underworld_teleport_index = static_cast(atoi(row[93])); - e.lava_damage = static_cast(atoi(row[94])); - e.min_lava_damage = static_cast(atoi(row[95])); + e.underworld_teleport_index = row[93] ? static_cast(atoi(row[93])) : 0; + e.lava_damage = row[94] ? static_cast(atoi(row[94])) : 50; + e.min_lava_damage = row[95] ? static_cast(atoi(row[95])) : 10; + e.idle_when_empty = row[96] ? static_cast(strtoul(row[96], nullptr, 10)) : 1; + e.seconds_before_idle = row[97] ? static_cast(strtoul(row[97], nullptr, 10)) : 60; all_entries.push_back(e); } @@ -1380,6 +1400,8 @@ class BaseZoneRepository { v.push_back(std::to_string(e.underworld_teleport_index)); v.push_back(std::to_string(e.lava_damage)); v.push_back(std::to_string(e.min_lava_damage)); + v.push_back(std::to_string(e.idle_when_empty)); + v.push_back(std::to_string(e.seconds_before_idle)); auto results = db.QueryDatabase( fmt::format( @@ -1498,6 +1520,8 @@ class BaseZoneRepository { v.push_back(std::to_string(e.underworld_teleport_index)); v.push_back(std::to_string(e.lava_damage)); v.push_back(std::to_string(e.min_lava_damage)); + v.push_back(std::to_string(e.idle_when_empty)); + v.push_back(std::to_string(e.seconds_before_idle)); insert_chunks.push_back("(" + Strings::Implode(",", v) + ")"); } diff --git a/common/ruletypes.h b/common/ruletypes.h index 07e3f57135..da1bec40c1 100644 --- a/common/ruletypes.h +++ b/common/ruletypes.h @@ -338,7 +338,6 @@ RULE_BOOL(Zone, UseZoneController, true, "Enables the ability to use persistent RULE_BOOL(Zone, EnableZoneControllerGlobals, false, "Enables the ability to use quest globals with the zone controller NPC") RULE_INT(Zone, GlobalLootMultiplier, 1, "Sets Global Loot drop multiplier for database based drops, useful for double, triple loot etc") RULE_BOOL(Zone, KillProcessOnDynamicShutdown, true, "When process has booted a zone and has hit its zone shut down timer, it will hard kill the process to free memory back to the OS") -RULE_INT(Zone, SecondsBeforeIdle, 60, "Seconds before IDLE_WHEN_EMPTY define kicks in") RULE_INT(Zone, SpawnEventMin, 3, "When strict is set in spawn_events, specifies the max EQ minutes into the trigger hour a spawn_event will fire. Going below 3 may cause the spawn_event to not fire.") RULE_INT(Zone, ForageChance, 25, "Chance of foraging from zone table vs global table") RULE_CATEGORY_END() diff --git a/common/version.h b/common/version.h index 6dd23eb222..b61ffc8748 100644 --- a/common/version.h +++ b/common/version.h @@ -42,7 +42,7 @@ * Manifest: https://github.com/EQEmu/Server/blob/master/utils/sql/db_update_manifest.txt */ -#define CURRENT_BINARY_DATABASE_VERSION 9251 +#define CURRENT_BINARY_DATABASE_VERSION 9252 #define CURRENT_BINARY_BOTS_DATABASE_VERSION 9041 diff --git a/common/zone_store.cpp b/common/zone_store.cpp index 7bcb2cb6f6..502c2e126f 100644 --- a/common/zone_store.cpp +++ b/common/zone_store.cpp @@ -1341,3 +1341,37 @@ int ZoneStore::GetZoneMinimumLavaDamage(uint32 zone_id, int version) return 0; } + +uint8 ZoneStore::GetZoneIdleWhenEmpty(uint32 zone_id, int version) +{ + for (auto &z: m_zones) { + if (z.zoneidnumber == zone_id && z.version == version) { + return z.idle_when_empty; + } + } + + for (auto &z: m_zones) { + if (z.zoneidnumber == zone_id && z.version == 0) { + return z.idle_when_empty; + } + } + + return 1; +} + +uint32 ZoneStore::GetZoneSecondsBeforeIdle(uint32 zone_id, int version) +{ + for (auto &z: m_zones) { + if (z.zoneidnumber == zone_id && z.version == version) { + return z.seconds_before_idle; + } + } + + for (auto &z: m_zones) { + if (z.zoneidnumber == zone_id && z.version == 0) { + return z.seconds_before_idle; + } + } + + return 60; +} diff --git a/common/zone_store.h b/common/zone_store.h index bc245643a2..fcc94a2d9e 100644 --- a/common/zone_store.h +++ b/common/zone_store.h @@ -100,6 +100,8 @@ class ZoneStore { int GetZoneUnderworldTeleportIndex(uint32 zone_id, int version = 0); int GetZoneLavaDamage(uint32 zone_id, int version = 0); int GetZoneMinimumLavaDamage(uint32 zone_id, int version = 0); + uint8 GetZoneIdleWhenEmpty(uint32 zone_id, int version = 0); + uint32 GetZoneSecondsBeforeIdle(uint32 zone_id, int version = 0); private: std::vector m_zones; diff --git a/zone/embparser_api.cpp b/zone/embparser_api.cpp index dd85ebdbd2..bf28c654ba 100644 --- a/zone/embparser_api.cpp +++ b/zone/embparser_api.cpp @@ -5703,6 +5703,26 @@ int Perl__GetZoneMinimumLavaDamage(uint32 zone_id, int version) return zone_store.GetZoneMinimumLavaDamage(zone_id, version); } +uint8 Perl__GetZoneIdleWhenEmpty(uint32 zone_id) +{ + return zone_store.GetZoneIdleWhenEmpty(zone_id); +} + +uint8 Perl__GetZoneIdleWhenEmpty(uint32 zone_id, int version) +{ + return zone_store.GetZoneIdleWhenEmpty(zone_id, version); +} + +uint32 Perl__GetZoneSecondsBeforeIdle(uint32 zone_id) +{ + return zone_store.GetZoneSecondsBeforeIdle(zone_id); +} + +uint32 Perl__GetZoneSecondsBeforeIdle(uint32 zone_id, int version) +{ + return zone_store.GetZoneSecondsBeforeIdle(zone_id, version); +} + void Perl__send_channel_message(uint8 channel_number, uint32 guild_id, uint8 language_id, uint8 language_skill, const char* message) { quest_manager.SendChannelMessage(channel_number, guild_id, language_id, language_skill, message); @@ -5810,6 +5830,8 @@ void perl_register_quest() package.add("GetZoneGraveyardID", (float(*)(uint32, int))&Perl__GetZoneGraveyardID); package.add("GetZoneHotzone", (uint8(*)(uint32))&Perl__GetZoneHotzone); package.add("GetZoneHotzone", (uint8(*)(uint32, int))&Perl__GetZoneHotzone); + package.add("GetZoneIdleWhenEmpty", (uint8(*)(uint32))&Perl__GetZoneIdleWhenEmpty); + package.add("GetZoneIdleWhenEmpty", (uint8(*)(uint32, int))&Perl__GetZoneIdleWhenEmpty); package.add("GetZoneInstanceType", (uint8(*)(uint32))&Perl__GetZoneInstanceType); package.add("GetZoneInstanceType", (uint8(*)(uint32, int))&Perl__GetZoneInstanceType); package.add("GetZoneID", &Perl__GetZoneID); @@ -5890,6 +5912,8 @@ void perl_register_quest() package.add("GetZoneSafeY", (float(*)(uint32, int))&Perl__GetZoneSafeY); package.add("GetZoneSafeZ", (float(*)(uint32))&Perl__GetZoneSafeZ); package.add("GetZoneSafeZ", (float(*)(uint32, int))&Perl__GetZoneSafeZ); + package.add("GetZoneSecondsBeforeIdle", (uint32(*)(uint32))&Perl__GetZoneSecondsBeforeIdle); + package.add("GetZoneSecondsBeforeIdle", (uint32(*)(uint32, int))&Perl__GetZoneSecondsBeforeIdle); package.add("GetZoneShutdownDelay", (uint64(*)(uint32))&Perl__GetZoneShutdownDelay); package.add("GetZoneShutdownDelay", (uint64(*)(uint32, int))&Perl__GetZoneShutdownDelay); package.add("GetZoneSky", (uint8(*)(uint32))&Perl__GetZoneSky); diff --git a/zone/entity.cpp b/zone/entity.cpp index 69d9dc000d..b5c79b994c 100644 --- a/zone/entity.cpp +++ b/zone/entity.cpp @@ -411,10 +411,12 @@ void EntityList::RaidProcess() void EntityList::DoorProcess() { -#ifdef IDLE_WHEN_EMPTY - if (numclients < 1) - return; -#endif + if (zone && zone->IsIdleWhenEmpty()) { + if (numclients < 1) { + return; + } + } + if (door_list.empty()) { door_timer.Disable(); return; @@ -480,46 +482,76 @@ void EntityList::MobProcess() size_t sz = mob_list.size(); -#ifdef IDLE_WHEN_EMPTY - static int old_client_count=0; + static int old_client_count = 0; static Timer *mob_settle_timer = new Timer(); - if (numclients == 0 && old_client_count > 0 && - RuleI(Zone, SecondsBeforeIdle) > 0) { - // Start Timer to allow any mobs that chased chars from zone - // to return home. - mob_settle_timer->Start(RuleI(Zone, SecondsBeforeIdle) * 1000); - } + if (zone->IsIdleWhenEmpty()) { + if ( + numclients == 0 && + old_client_count > 0 && + zone->GetSecondsBeforeIdle() > 0 + ) { + if (!zone->IsIdle()) { + LogInfo( + "Zone will go into an idle state after [{}] second{}.", + zone->GetSecondsBeforeIdle(), + zone->GetSecondsBeforeIdle() != 1 ? "s" : "" + ); + } - old_client_count = numclients; + mob_settle_timer->Start(zone->GetSecondsBeforeIdle() * 1000); + } - // Disable settle timer if someone zones into empty zone - if (numclients > 0 || mob_settle_timer->Check()) { - mob_settle_timer->Disable(); - } + old_client_count = numclients; + + if (numclients == 0 && mob_settle_timer->Check()) { + if (!zone->IsIdle()) { + LogInfo( + "Zone has gone idle after [{}] second{}.", + zone->GetSecondsBeforeIdle(), + zone->GetSecondsBeforeIdle() != 1 ? "s" : "" + ); + + zone->SetIsIdle(true); + } + } - Spawn2* s2 = mob->CastToNPC()->respawn2; + // Disable settle timer if someone zones into empty zone + if (numclients > 0 || mob_settle_timer->Check()) { + if (zone->IsIdle()) { + LogInfo("Zone is no longer idle."); + + zone->SetIsIdle(false); + } + + mob_settle_timer->Disable(); + } - // Perform normal mob processing if any of these are true: - // -- zone is not empty - // -- a quest has turned it on for this zone while zone is idle - // -- the entity's spawn2 point is marked as path_while_zone_idle - // -- the zone is newly empty and we're allowing mobs to settle - if (zone->process_mobs_while_empty || numclients > 0 || - (s2 && s2->PathWhenZoneIdle()) || mob_settle_timer->Enabled()) { + Spawn2* s2 = mob->CastToNPC()->respawn2; + + // Perform normal mob processing if any of these are true: + // -- zone is not empty + // -- a quest has turned it on for this zone while zone is idle + // -- the entity's spawn2 point is marked as path_while_zone_idle + // -- the zone is newly empty and we're allowing mobs to settle + if ( + numclients > 0 || + (s2 && s2->PathWhenZoneIdle()) || + mob_settle_timer->Enabled() + ) { + mob_dead = !mob->Process(); + } else { + // spawn_events can cause spawns and deaths while zone empty. + // At the very least, process that. + mob_dead = mob->CastToNPC()->GetDepop(); + } + } else { mob_dead = !mob->Process(); } - else { - // spawn_events can cause spawns and deaths while zone empty. - // At the very least, process that. - mob_dead = mob->CastToNPC()->GetDepop(); - } -#else - mob_dead = !mob->Process(); -#endif + size_t a_sz = mob_list.size(); - if(a_sz > sz) { + if (a_sz > sz) { //increased size can potentially screw with iterators so reset it to current value //if buckets are re-orderered we may skip a process here and there but since //process happens so often it shouldn't matter much @@ -529,30 +561,30 @@ void EntityList::MobProcess() ++it; } - if(mob_dead) { - if(mob->IsMerc()) { + if (mob_dead) { + if (mob->IsMerc()) { entity_list.RemoveMerc(id); - } - else if(mob->IsBot()) { + } else if (mob->IsBot()) { entity_list.RemoveBot(id); - } - else if(mob->IsNPC()) { + } else if (mob->IsNPC()) { entity_list.RemoveNPC(id); - } - else { + } else { #ifdef _WINDOWS struct in_addr in; in.s_addr = mob->CastToClient()->GetIP(); LogInfo("Dropping client: Process=false, ip=[{}] port=[{}]", inet_ntoa(in), mob->CastToClient()->GetPort()); #endif - Group *g = GetGroupByMob(mob); - if(g) { + + Group* g = GetGroupByMob(mob); + if (g) { g->DelMember(mob); } - Raid *r = entity_list.GetRaidByClient(mob->CastToClient()); - if(r) { + + Raid* r = entity_list.GetRaidByClient(mob->CastToClient()); + if (r) { r->MemberZoned(mob->CastToClient()); } + entity_list.RemoveClient(id); } diff --git a/zone/lua_general.cpp b/zone/lua_general.cpp index da3c9d4b9a..18ac9a683c 100644 --- a/zone/lua_general.cpp +++ b/zone/lua_general.cpp @@ -4743,6 +4743,26 @@ int lua_get_zone_minimum_lava_damage(uint32 zone_id, int version) return zone_store.GetZoneMinimumLavaDamage(zone_id, version); } +uint8 lua_get_zone_idle_when_empty(uint32 zone_id) +{ + return zone_store.GetZoneIdleWhenEmpty(zone_id); +} + +uint8 lua_get_zone_idle_when_empty(uint32 zone_id, int version) +{ + return zone_store.GetZoneIdleWhenEmpty(zone_id, version); +} + +uint32 lua_get_zone_seconds_before_idle(uint32 zone_id) +{ + return zone_store.GetZoneSecondsBeforeIdle(zone_id); +} + +uint32 lua_get_zone_seconds_before_idle(uint32 zone_id, int version) +{ + return zone_store.GetZoneSecondsBeforeIdle(zone_id, version); +} + void lua_send_channel_message(uint8 channel_number, uint32 guild_id, uint8 language_id, uint8 language_skill, const char* message) { quest_manager.SendChannelMessage(channel_number, guild_id, language_id, language_skill, message); @@ -6034,6 +6054,10 @@ luabind::scope lua_register_general() { luabind::def("get_zone_lava_damage", (int(*)(uint32,int))&lua_get_zone_lava_damage), luabind::def("get_zone_minimum_lava_damage", (int(*)(uint32))&lua_get_zone_minimum_lava_damage), luabind::def("get_zone_minimum_lava_damage", (int(*)(uint32,int))&lua_get_zone_minimum_lava_damage), + luabind::def("get_zone_idle_when_empty", (uint8(*)(uint32))&lua_get_zone_idle_when_empty), + luabind::def("get_zone_idle_when_empty", (uint8(*)(uint32,int))&lua_get_zone_idle_when_empty), + luabind::def("get_zone_seconds_before_idle", (uint32(*)(uint32))&lua_get_zone_seconds_before_idle), + luabind::def("get_zone_seconds_before_idle", (uint32(*)(uint32,int))&lua_get_zone_seconds_before_idle), luabind::def("send_channel_message", (void(*)(uint8,uint32,uint8,uint8,const char*))&lua_send_channel_message), luabind::def("send_channel_message", (void(*)(Lua_Client,uint8,uint32,uint8,uint8,const char*))&lua_send_channel_message), luabind::def("send_channel_message", (void(*)(Lua_Client,const char*,uint8,uint32,uint8,uint8,const char*))&lua_send_channel_message), diff --git a/zone/questmgr.cpp b/zone/questmgr.cpp index b26ead772b..d78394bed9 100644 --- a/zone/questmgr.cpp +++ b/zone/questmgr.cpp @@ -933,12 +933,13 @@ void QuestManager::repopzone() { } } -void QuestManager::processmobswhilezoneempty(bool on) { - if(zone) { - zone->process_mobs_while_empty = on; - } - else { - LogQuests("QuestManager::processmobswhilezoneempty called with nullptr zone. Probably syntax error in quest file"); +void QuestManager::processmobswhilezoneempty(bool idle_when_empty) { + if (zone) { + zone->SetIdleWhenEmpty(idle_when_empty); + } else { + LogQuests( + "QuestManager::processmobswhilezoneempty called with nullptr zone. Probably syntax error in quest file" + ); } } diff --git a/zone/questmgr.h b/zone/questmgr.h index ff06783bd4..02218818c6 100644 --- a/zone/questmgr.h +++ b/zone/questmgr.h @@ -106,7 +106,7 @@ class QuestManager { void depopall(int npc_type = 0); void depopzone(bool StartSpawnTimer = true); void repopzone(); - void processmobswhilezoneempty(bool on); + void processmobswhilezoneempty(bool idle_when_empty); void settarget(const char *type, int target_id); void follow(int entity_id, int distance); void sfollow(); diff --git a/zone/zone.cpp b/zone/zone.cpp index 7650e36c1e..561f903111 100644 --- a/zone/zone.cpp +++ b/zone/zone.cpp @@ -974,7 +974,6 @@ Zone::Zone(uint32 in_zoneid, uint32 in_instanceid, const char* in_short_name) default_ruleset = 0; is_zone_time_localized = false; - process_mobs_while_empty = false; loglevelvar = 0; merchantvar = 0; @@ -984,13 +983,17 @@ Zone::Zone(uint32 in_zoneid, uint32 in_instanceid, const char* in_short_name) short_name = strcpy(new char[strlen(in_short_name)+1], in_short_name); strlwr(short_name); memset(file_name, 0, sizeof(file_name)); - long_name = 0; - aggroedmobs =0; + + long_name = 0; + aggroedmobs = 0; m_graveyard_id = 0; pgraveyard_zoneid = 0; m_max_clients = 0; pvpzone = false; + SetIdleWhenEmpty(true); + SetSecondsBeforeIdle(60); + if (database.GetServerType() == 1) { pvpzone = true; } @@ -1006,6 +1009,9 @@ Zone::Zone(uint32 in_zoneid, uint32 in_instanceid, const char* in_short_name) m_graveyard_id = z->graveyard_id; m_max_clients = z->maxclients; + SetIdleWhenEmpty(z->idle_when_empty); + SetSecondsBeforeIdle(z->seconds_before_idle); + if (z->file_name.empty()) { strcpy(file_name, short_name); } @@ -1391,6 +1397,9 @@ bool Zone::LoadZoneCFG(const char* filename, uint16 instance_version) m_graveyard_id = z->graveyard_id; m_max_clients = z->maxclients; + SetIdleWhenEmpty(z->idle_when_empty); + SetSecondsBeforeIdle(z->seconds_before_idle); + // safe coordinates m_safe_points.x = z->safe_x; m_safe_points.y = z->safe_y; @@ -3230,3 +3239,33 @@ void Zone::SetEXPModifier(Client* c, float exp_modifier) m ); } + +bool Zone::IsIdleWhenEmpty() const +{ + return m_idle_when_empty; +} + +void Zone::SetIdleWhenEmpty(bool idle_when_empty) +{ + Zone::m_idle_when_empty = idle_when_empty; +} + +uint32 Zone::GetSecondsBeforeIdle() const +{ + return m_seconds_before_idle; +} + +void Zone::SetSecondsBeforeIdle(uint32 seconds_before_idle) +{ + Zone::m_seconds_before_idle = seconds_before_idle; +} + +bool Zone::IsIdle() const +{ + return m_is_idle; +} + +void Zone::SetIsIdle(bool m_is_idle) +{ + Zone::m_is_idle = m_is_idle; +} diff --git a/zone/zone.h b/zone/zone.h index 51af2a1a2e..c5e4755c0e 100755 --- a/zone/zone.h +++ b/zone/zone.h @@ -105,7 +105,12 @@ class Zone { AA::Ability *GetAlternateAdvancementAbilityByRank(int rank_id); AA::Rank *GetAlternateAdvancementRank(int rank_id); bool is_zone_time_localized; - bool process_mobs_while_empty; + bool IsIdle() const; + void SetIsIdle(bool m_is_idle); + bool IsIdleWhenEmpty() const; + void SetIdleWhenEmpty(bool idle_when_empty); + uint32 GetSecondsBeforeIdle() const; + void SetSecondsBeforeIdle(uint32 seconds_before_idle); bool AggroLimitReached() { return (aggroedmobs > 10) ? true : false; } bool AllowMercs() const { return (allow_mercs); } bool CanBind() const { return (can_bind); } @@ -436,6 +441,9 @@ class Zone { uint32 m_max_clients; uint32 zoneid; uint32 m_last_ucss_update; + bool m_idle_when_empty; + uint32 m_seconds_before_idle; + bool m_is_idle; GlobalLootManager m_global_loot; LinkedList client_auth_list;