Explorar o código

Fixes instances with infinite duration (#7547)

* Fixes #6230 and fixes #7472.
* Resolves an issue where instances that have infinite TimeLimit or IdleTimeOut would instantly become destroyed.
* Fixes an issue where do_final_instance() could end up on an invalid iterator value resulting in memleaks/crashes on map server closure.
* Add missing constructor parameters.
Thanks to @secretdataz, @Lemongrass3110, and @mazvi!
Aleos %!s(int64=2) %!d(string=hai) anos
pai
achega
3464292a31
Modificáronse 2 ficheiros con 27 adicións e 10 borrados
  1. 23 10
      src/map/instance.cpp
  2. 4 0
      src/map/instance.hpp

+ 23 - 10
src/map/instance.cpp

@@ -93,13 +93,15 @@ uint64 InstanceDatabase::parseBodyNode(const ryml::NodeRef& node) {
 		if (!this->asInt64(node, "TimeLimit", limit))
 			return 0;
 
-		if (limit == 0) // Infinite duration
-			limit = INT64_MAX;
-
 		instance->limit = limit;
+
+		// Infinite duration
+		instance->infinite_limit = (limit == 0);
 	} else {
-		if (!exists)
+		if (!exists) {
 			instance->limit = 3600;
+			instance->infinite_limit = false;
+		}
 	}
 
 	if (this->nodeExists(node, "IdleTimeOut")) {
@@ -108,13 +110,15 @@ uint64 InstanceDatabase::parseBodyNode(const ryml::NodeRef& node) {
 		if (!this->asInt64(node, "IdleTimeOut", idle))
 			return 0;
 
-		if (idle == 0) // Infinite duration
-			idle = INT64_MAX;
-
 		instance->timeout = idle;
+
+		// Infinite duration
+		instance->infinite_timeout = (idle == 0);
 	} else {
-		if (!exists)
+		if (!exists) {
 			instance->timeout = 300;
+			instance->infinite_timeout = false;
+		}
 	}
 
 	if (this->nodeExists(node, "NoNpc")) {
@@ -404,6 +408,10 @@ bool instance_startkeeptimer(std::shared_ptr<s_instance_data> idata, int instanc
 	if (!db)
 		return false;
 
+	// Infinite duration instance
+	if (db->infinite_limit)
+		return true;
+
 	// Add timer
 	idata->keep_limit = time(nullptr) + db->limit;
 	idata->keep_timer = add_timer(gettick() + db->limit * 1000, instance_delete_timer, instance_id, 0);
@@ -451,6 +459,10 @@ bool instance_startidletimer(std::shared_ptr<s_instance_data> idata, int instanc
 	if (!db)
 		return false;
 
+	// Infinite idle duration instance
+	if (db->infinite_timeout)
+		return true;
+
 	// Add the timer
 	idata->idle_limit = time(nullptr) + db->timeout;
 	idata->idle_timer = add_timer(gettick() + db->timeout * 1000, instance_delete_timer, instance_id, 0);
@@ -1294,6 +1306,7 @@ void do_init_instance(void) {
  * Finalizes the instances and instance database
  */
 void do_final_instance(void) {
-	for (const auto &it : instances)
-		instance_destroy(it.first);
+	// Since instance_destroy() modifies the unordered_map, make sure iteration always restarts.
+	for (auto it = instances.begin(); it != instances.end(); it = instances.begin())
+		instance_destroy(it->first);
 }

+ 4 - 0
src/map/instance.hpp

@@ -81,6 +81,8 @@ struct s_instance_data {
 		keep_timer(INVALID_TIMER),
 		idle_limit(0),
 		idle_timer(INVALID_TIMER),
+		nonpc(false),
+		nomapflag(false),
 		regs(),
 		map() { }
 };
@@ -94,6 +96,8 @@ struct s_instance_db {
 	bool nonpc;
 	bool nomapflag;
 	bool destroyable; ///< Destroyable flag
+	bool infinite_limit; ///< Infinite limit flag
+	bool infinite_timeout; ///< Infinite timeout limit flag
 	struct point enter; ///< Instance entry point
 	std::vector<int16> maplist; ///< Maps in instance
 };