فهرست منبع

Refactored a bit of the instance modes
* Follow up to c97be60.
* Added some safety measures when sending the instance name to the client.
* Adjusted the stored maps of instances to dynamic arrays to conserve memory.
- Max amount of possible maps increased to 255.
* Removed Map1 from the instance_db as the EnterMap is always the same value.
* Instances will now be destroyed even if the owner cannot be found.
* Corrected the return value when entering an instance when a character, party, or guild does not have an instance.
* Corrected a few script commands to properly stop the script engine on failures for instance script commands.
* Removed the instance name lookup for when entering an instance as instance_id is now passed.
- Adjusted script command instance_enter to no longer require the instance name.
* Adjusted script command instance_create to keep backwards compatibility.
* Cleaned up script command instance_warpall.
* Updated script documentation to reflect changes.
* Updated instance_db to reflect changes.

aleos89 9 سال پیش
والد
کامیت
2cfb844429
10فایلهای تغییر یافته به همراه223 افزوده شده و 217 حذف شده
  1. 3 2
      db/import-tmpl/instance_db.txt
  2. 7 5
      db/pre-re/instance_db.txt
  3. 16 14
      db/re/instance_db.txt
  4. 17 17
      doc/script_commands.txt
  5. 6 2
      src/map/clif.c
  6. 69 100
      src/map/instance.c
  7. 31 18
      src/map/instance.h
  8. 4 4
      src/map/map.c
  9. 2 2
      src/map/npc.c
  10. 68 53
      src/map/script.c

+ 3 - 2
db/import-tmpl/instance_db.txt

@@ -1,5 +1,6 @@
 // Instance Database
 //
 // Structure of Database:
-// ID,Name,LimitTime,IdleTimeout,EnterMap,EnterX,EnterY,Map1,Map2,Map3,Map4,Map5,Map6,Map7,Map8
-
+// ID,Name,LimitTime,IdleTimeOut,EnterMap,EnterX,EnterY,Map2,Map3,Map4,Map5,Map6,Map7,Map8,Map9,Map10
+//
+// EnterMap is considered as Map1

+ 7 - 5
db/pre-re/instance_db.txt

@@ -1,9 +1,11 @@
 // Instance Database
 //
 // Structure of Database:
-// ID,Name,LimitTime,IdleTimeout,EnterMap,EnterX,EnterY,Map1,Map2,Map3,Map4,Map5,Map6,Map7,Map8
+// ID,Name,LimitTime,IdleTimeOut,EnterMap,EnterX,EnterY,Map2,Map3,Map4,Map5,Map6,Map7,Map8,Map9,Map10
+//
+// EnterMap is considered as Map1
 
-1,Endless Tower,14400,300,1@tower,50,355,1@tower,2@tower,3@tower,4@tower,5@tower,6@tower
-2,Sealed Catacomb,7200,300,1@cata,100,224,1@cata,2@cata
-3,Orc's Memory,3600,300,1@orcs,179,15,1@orcs,2@orcs
-4,Nidhoggur's Nest,14400,300,1@nyd,32,36,1@nyd,2@nyd
+1,Endless Tower,14400,300,1@tower,50,355,2@tower,3@tower,4@tower,5@tower,6@tower
+2,Sealed Catacomb,7200,300,1@cata,100,224,2@cata
+3,Orc's Memory,3600,300,1@orcs,179,15,2@orcs
+4,Nidhoggur's Nest,14400,300,1@nyd,32,36,2@nyd

+ 16 - 14
db/re/instance_db.txt

@@ -1,18 +1,20 @@
 // Instance Database
 //
 // Structure of Database:
-// ID,Name,LimitTime,IdleTime,EnterMap,EnterX,EnterY,Map1,Map2,Map3,Map4,Map5,Map6,Map7,Map8
+// ID,Name,LimitTime,IdleTimeOut,EnterMap,EnterX,EnterY,Map2,Map3,Map4,Map5,Map6,Map7,Map8,Map9,Map10
+//
+// EnterMap is considered as Map1
 
-1,Endless Tower,14400,300,1@tower,50,355,1@tower,2@tower,3@tower,4@tower,5@tower,6@tower
-2,Sealed Catacomb,7200,300,1@cata,100,224,1@cata,2@cata
-3,Orc's Memory,3600,300,1@orcs,179,15,1@orcs,2@orcs
-4,Nidhoggur's Nest,14400,300,1@nyd,32,36,1@nyd,2@nyd
-5,Mistwood Maze,7200,300,1@mist,89,29,1@mist
-6,Culvert,3600,300,1@pump,63,98,1@pump,2@pump
-7,Octopus Cave,3600,300,1@cash,199,99,1@cash
-8,Bangungot Hospital 2F,3600,300,1@ma_h,40,157,1@ma_h
-9,Buwaya Cave,3600,300,1@ma_c,35,57,1@ma_c
-10,Bakonawa Lake,7200,300,1@ma_b,64,51,1@ma_b
-11,Wolfchev's Laboratory,14400,300,1@lhz,45,148,1@lhz
-12,Old Glast Heim,3600,300,1@gl_k,150,20,1@gl_k,2@gl_k
-13,Eclage Interior,1200,300,1@ecl,60,50,1@ecl
+1,Endless Tower,14400,300,1@tower,50,355,2@tower,3@tower,4@tower,5@tower,6@tower
+2,Sealed Catacomb,7200,300,1@cata,100,224,2@cata
+3,Orc's Memory,3600,300,1@orcs,179,15,2@orcs
+4,Nidhoggur's Nest,14400,300,1@nyd,32,36,2@nyd
+5,Mistwood Maze,7200,300,1@mist,89,29
+6,Culvert,3600,300,1@pump,63,98,2@pump
+7,Octopus Cave,3600,300,1@cash,199,99
+8,Bangungot Hospital 2F,3600,300,1@ma_h,40,157
+9,Buwaya Cave,3600,300,1@ma_c,35,57
+10,Bakonawa Lake,7200,300,1@ma_b,64,51
+11,Wolfchev's Laboratory,14400,300,1@lhz,45,148
+12,Old Glast Heim,3600,300,1@gl_k,150,20,2@gl_k
+13,Eclage Interior,1200,300,1@ecl,60,50

+ 17 - 17
doc/script_commands.txt

@@ -3,7 +3,7 @@
 //===== By: ==================================================
 //= rAthena Dev Team
 //===== Last Updated: ========================================
-//= 20150610
+//= 20160523
 //===== Description: =========================================
 //= A reference manual for the rAthena scripting language.
 //= Commands are sorted depending on their functionality.
@@ -8113,9 +8113,9 @@ This command will open a book item at the specified page.
 ========================
 ---------------------------------------
 
-*instance_create("<instance name>",<owner_id>{,<mode>});
+*instance_create("<instance name>"{,<instance mode>{,<owner id>}});
 
-Creates an instance for the <owner_id> of <mode>. The instance name, along with
+Creates an instance for the <owner id> of <mode>. The instance name, along with
 all other instance data, is read from 'db/(pre-)re/instance_db.txt'. Upon success,
 the command generates a unique instance ID, duplicates all listed maps and NPCs,
 sets the alive time, and triggers the "OnInstanceInit" label in all NPCs inside
@@ -8129,7 +8129,7 @@ Instance Mode options:
 
 The command returns the instance ID upon success, and these values upon failure:
  -1: Invalid type.
- -2: Party not found.
+ -2: Character/Party/Guild not found.
  -3: Instance already exists.
  -4: No free instances (MAX_INSTANCE exceeded).
 
@@ -8139,13 +8139,13 @@ The command returns the instance ID upon success, and these values upon failure:
 
 Destroys instance with the ID <instance id>. If no ID is specified, the instance
 the script is attached to is used. If the script is not attached to an instance,
-the instance of the currently attached player is used (if it is a party or guild
-mode). If it is not owned by anyone, no player needs to be attached. If that
-fails, the script will come to a halt.
+the instance of the currently attached player is used (if it is a character, party,
+or guild mode). If it is not owned by anyone, no player needs to be attached. If
+that fails, the script will come to a halt.
 
 ---------------------------------------
 
-*instance_enter("<instance name>"{,<x>,<y>,<char_id>});
+*instance_enter({<x>,<y>,<char_id>});
 
 Warps player to the specified instance after the script terminates. The map and
 coordinates are located in 'db/(pre-)re/instance_db.txt'.
@@ -8163,8 +8163,8 @@ Put -1 for x and y if want to warp player with default entrance coordinates.
 
 Returns the unique name of the instanced script. If no ID is specified,
 the instance the script is attached to is used. If the script is not attached to
-an instance, the instance of the currently attached player's party is used. If
-that fails, the script will come to a halt.
+an instance, the instance of the currently attached NPC, player, party, or guild
+is used. If that fails, the script will come to a halt.
 
 ---------------------------------------
 
@@ -8173,8 +8173,8 @@ that fails, the script will come to a halt.
 Returns the unique name of the instanced map. If no instance ID is specified,
 the instance the script is attached to is used. If the script is not attached to
 an instance, the instance of the currently attached player is used (if it is a
-party or guild mode). If it is not owned by anyone, no player needs to be
-attached. If that fails, the command returns an empty string instead.
+character, party, or guild mode). If it is not owned by anyone, no player needs
+to be attached. If that fails, the command returns an empty string instead.
 
 ---------------------------------------
 
@@ -8182,7 +8182,7 @@ attached. If that fails, the command returns an empty string instead.
 
 Returns the unique instance id of the attached script. If the script is not
 attached to an instance, the instance of the currently attached player is
-used (if it is a party or guild mode). If it is not owned by anyone, no
+used (if it is a character, party, or guild mode). If it is not owned by anyone, no
 player needs to be attached. If that fails, the function will return 0.
 
 ---------------------------------------
@@ -8192,8 +8192,8 @@ player needs to be attached. If that fails, the function will return 0.
 Warps all players in the instance <instance id> to <map name> at given
 coordinates. If no ID is specified, the instance the script is attached to
 is used. If the script is not attached to an instance, the instance of the
-currently attached player is used (if it is a party or guild mode). If it
-is not owned by anyone, no player needs to be attached. If that
+currently attached player is used (if it is a character, party, or guild
+mode). If it is not owned by anyone, no player needs to be attached. If that
 fails, the script will come to a halt.
 
 ---------------------------------------
@@ -8203,8 +8203,8 @@ fails, the script will come to a halt.
 Broadcasts a message to all players in the instance <instance id> currently
 residing on an instance map. If -1 is specified for <instance id>, the instance
 the script is attached to is used. If the script is not attached to an instance,
-the instance of the currently attached player is used (if it is a party or guild
-mode). If it is not owned by anyone, no player needs to be attached.
+the instance of the currently attached player is used (if it is a character,
+party, or guild mode). If it is not owned by anyone, no player needs to be attached.
 
 For details on the other parameters, see 'announce'.
 

+ 6 - 2
src/map/clif.c

@@ -16578,6 +16578,7 @@ void clif_font(struct map_session_data *sd)
 void clif_instance_create(unsigned short instance_id, int num)
 {
 #if PACKETVER >= 20071128
+	struct instance_db *db = NULL;
 	struct map_session_data *sd = NULL;
 	enum send_target target = PARTY;
 	unsigned char buf[65];
@@ -16587,8 +16588,9 @@ void clif_instance_create(unsigned short instance_id, int num)
 	if (!sd)
 		return;
 
+	db = instance_searchtype_db(instance_data[instance_id].type);
 	WBUFW(buf,0) = 0x2cb;
-	memcpy(WBUFP(buf,2), instance_data[instance_id].name, 62);
+	safestrncpy(WBUFP(buf,2), StringBuf_Value(db->name), INSTANCE_NAME_LENGTH);
 	WBUFW(buf,63) = num;
 	clif_send(buf,packet_len(0x2cb),&sd->bl,target);
 #endif
@@ -16623,6 +16625,7 @@ void clif_instance_changewait(unsigned short instance_id, int num)
 void clif_instance_status(unsigned short instance_id, unsigned int limit1, unsigned int limit2)
 {
 #if PACKETVER >= 20071128
+	struct instance_db *db = NULL;
 	struct map_session_data *sd = NULL;
 	enum send_target target = PARTY;
 	unsigned char buf[71];
@@ -16632,8 +16635,9 @@ void clif_instance_status(unsigned short instance_id, unsigned int limit1, unsig
 	if (!sd)
 		return;
 
+	db = instance_searchtype_db(instance_data[instance_id].type);
 	WBUFW(buf,0) = 0x2cd;
-	memcpy(WBUFP(buf,2), instance_data[instance_id].name, 62);
+	safestrncpy(WBUFP(buf,2), StringBuf_Value(db->name), INSTANCE_NAME_LENGTH);
 	WBUFL(buf,63) = limit1;
 	WBUFL(buf,67) = limit2;
 	clif_send(buf,packet_len(0x2cd),&sd->bl,target);

+ 69 - 100
src/map/instance.c

@@ -25,20 +25,7 @@
 int instance_start = 0; // To keep the last index + 1 of normal map inserted in the map[ARRAY]
 
 struct instance_data instance_data[MAX_INSTANCE_DATA];
-
-/// Instance DB entry struct
-struct instance_db {
-	unsigned short id; ///< Instance ID
-	StringBuf *name; ///< Instance name
-	unsigned int limit, ///< Duration limit
-				 timeout; ///< Timeout limit
-	struct {
-		StringBuf *mapname; ///< Mapname, the limit should be MAP_NAME_LENGTH_EXT
-		unsigned short x, y; ///< Map coordinates
-	} enter;
-	StringBuf **maplist; ///< Used maps in instance, the limit should be MAP_NAME_LENGTH_EXT
-	uint8 maplist_count; ///< Number of used maps
-};
+struct eri *instance_maps_ers = NULL; ///< Array of maps per instance
 
 static DBMap *InstanceDB; /// Instance DB: struct instance_db, key: id
 static DBMap *InstanceNameDB; /// instance id, key: name
@@ -52,7 +39,7 @@ static struct {
 /*==========================================
  * Searches for an instance ID in the database
  *------------------------------------------*/
-static struct instance_db *instance_searchtype_db(unsigned short instance_id) {
+struct instance_db *instance_searchtype_db(unsigned short instance_id) {
 	return (struct instance_db *)uidb_get(InstanceDB,instance_id);
 }
 
@@ -60,6 +47,16 @@ static uint16 instance_name2id(const char *instance_name) {
 	return (uint16)strdb_uiget(InstanceNameDB,instance_name);
 }
 
+/*==========================================
+ * Searches for an instance name in the database
+ *------------------------------------------*/
+static struct instance_db *instance_searchname_db(const char *instance_name) {
+	uint16 id = instance_name2id(instance_name);
+	if (id == 0)
+		return NULL;
+	return (struct instance_db *)uidb_get(InstanceDB,id);
+}
+
 /**
  * Search for a sd of an Instance
  * @param instance_id: Instance ID
@@ -88,16 +85,6 @@ void instance_getsd(unsigned short instance_id, struct map_session_data **sd, en
 	return;
 }
 
-/*==========================================
- * Searches for an instance name in the database
- *------------------------------------------*/
-static struct instance_db *instance_searchname_db(const char *instance_name) {
-	uint16 id = instance_name2id(instance_name);
-	if (id == 0)
-		return NULL;
-	return (struct instance_db *)uidb_get(InstanceDB,id);
-}
-
 /*==========================================
  * Deletes an instance timer (Destroys instance)
  *------------------------------------------*/
@@ -324,11 +311,11 @@ void instance_addnpc(struct instance_data *im)
 
 	// First add the NPCs
 	for(i = 0; i < im->cnt_map; i++)
-		map_foreachinarea(instance_addnpc_sub, im->map[i].src_m, 0, 0, map[im->map[i].src_m].xs, map[im->map[i].src_m].ys, BL_NPC, im->map[i].m);
+		map_foreachinarea(instance_addnpc_sub, im->map[i]->src_m, 0, 0, map[im->map[i]->src_m].xs, map[im->map[i]->src_m].ys, BL_NPC, im->map[i]->m);
 
 	// Now run their OnInstanceInit
 	for(i = 0; i < im->cnt_map; i++)
-		map_foreachinarea(instance_npcinit, im->map[i].m, 0, 0, map[im->map[i].m].xs, map[im->map[i].m].ys, BL_NPC, im->map[i].m);
+		map_foreachinarea(instance_npcinit, im->map[i]->m, 0, 0, map[im->map[i]->m].xs, map[im->map[i]->m].ys, BL_NPC, im->map[i]->m);
 
 }
 
@@ -395,8 +382,7 @@ int instance_create(int owner_id, const char *name, enum instance_mode mode) {
 	instance_data[i].idle_timer = INVALID_TIMER;
 	instance_data[i].regs.vars = i64db_alloc(DB_OPT_RELEASE_DATA);
 	instance_data[i].regs.arrays = NULL;
-	safestrncpy(instance_data[i].name, name, sizeof(instance_data[i].name));
-	memset(instance_data[i].map, 0, sizeof(instance_data[i].map));
+	instance_data[i].cnt_map = 0;
 
 	switch(mode) {
 		case IM_CHAR:
@@ -416,7 +402,7 @@ int instance_create(int owner_id, const char *name, enum instance_mode mode) {
 
 	instance_subscription_timer(0,0,0,0);
 
-	ShowInfo("[Instance] Created: %s.\n", name);
+	ShowInfo("[Instance] Created: %s (%hu).\n", name, i);
 
 	return i;
 }
@@ -424,12 +410,11 @@ int instance_create(int owner_id, const char *name, enum instance_mode mode) {
 /*--------------------------------------
  * Adds maps to the instance
  *--------------------------------------*/
-int instance_addmap(unsigned short instance_id)
-{
+int instance_addmap(unsigned short instance_id) {
 	int i, m;
-	int cnt_map = 0;
 	struct instance_data *im;
 	struct instance_db *db;
+	struct s_instance_map *entry;
 
 	if (instance_id == 0)
 		return 0;
@@ -449,22 +434,39 @@ int instance_addmap(unsigned short instance_id)
 	im->idle_timer = add_timer(gettick() + db->timeout * 1000, instance_delete_timer, instance_id, 0);
 
 	// Add the maps
+	if (db->maplist_count > MAX_MAP_PER_INSTANCE) {
+		ShowError("instance_addmap: Too many maps (%d) created for a single instance '%s' (%hu).\n", db->maplist_count, StringBuf_Value(db->name), instance_id);
+		return 0;
+	}
+
+	// Add initial map
+	if ((m = map_addinstancemap(StringBuf_Value(db->enter.mapname), instance_id)) < 0) {
+		ShowError("instance_addmap: Failed to create initial map for instance '%s' (%hu).\n", StringBuf_Value(db->name), instance_id);
+		return 0;
+	}
+	entry = ers_alloc(instance_maps_ers, struct s_instance_map);
+	entry->m = m;
+	entry->src_m = map_mapname2mapid(StringBuf_Value(db->enter.mapname));
+	RECREATE(im->map, struct s_instance_map *, im->cnt_map + 1);
+	im->map[im->cnt_map++] = entry;
+
+	// Add extra maps (if any)
 	for(i = 0; i < db->maplist_count; i++) {
 		if(strlen(StringBuf_Value(db->maplist[i])) < 1)
 			continue;
 		else if( (m = map_addinstancemap(StringBuf_Value(db->maplist[i]), instance_id)) < 0) {
 			// An error occured adding a map
-			ShowError("instance_addmap: No maps added to instance %d.\n",instance_id);
+			ShowError("instance_addmap: No maps added to instance '%s' (%hu).\n", StringBuf_Value(db->name), instance_id);
 			return 0;
 		} else {
-			im->map[cnt_map].m = m;
-			im->map[cnt_map].src_m = map_mapname2mapid(StringBuf_Value(db->maplist[i]));
-			cnt_map++;
+			entry = ers_alloc(instance_maps_ers, struct s_instance_map);
+			entry->m = m;
+			entry->src_m = map_mapname2mapid(StringBuf_Value(db->maplist[i]));
+			RECREATE(im->map, struct s_instance_map *, im->cnt_map + 1);
+			im->map[im->cnt_map++] = entry;
 		}
 	}
 
-	im->cnt_map = cnt_map;
-
 	// Create NPCs on all maps
 	instance_addnpc(im);
 
@@ -487,7 +489,7 @@ int instance_addmap(unsigned short instance_id)
 			return 0;
 	}
 
-	return cnt_map;
+	return im->cnt_map;
 }
 
 
@@ -518,14 +520,14 @@ int instance_mapname2mapid(const char *name, unsigned short instance_id)
 	if(im->state != INSTANCE_BUSY)
 		return m;
 
-	for(i = 0; i < MAX_MAP_PER_INSTANCE; i++)
-		if(im->map[i].src_m == m) {
+	for(i = 0; i < im->cnt_map; i++)
+		if(im->map[i]->src_m == m) {
 			char alt_name[MAP_NAME_LENGTH];
 			if((strchr(iname,'@') == NULL) && strlen(iname) > 8) {
 				memmove(iname, iname+(strlen(iname)-9), strlen(iname));
-				snprintf(alt_name, sizeof(alt_name),"%d#%s", instance_id, iname);
+				snprintf(alt_name, sizeof(alt_name),"%hu#%s", instance_id, iname);
 			} else
-				snprintf(alt_name, sizeof(alt_name),"%.3d%s", instance_id, iname);
+				snprintf(alt_name, sizeof(alt_name),"%.3hu%s", instance_id, iname);
 			return map_mapname2mapid(alt_name);
 		}
 
@@ -554,31 +556,6 @@ int instance_destroy(unsigned short instance_id)
 		return 1;
 
 	mode = im->mode;
-	switch(mode) {
-		case IM_NONE:
-			break;
-		case IM_CHAR:
-			if ((sd = map_id2sd(im->owner_id)) == NULL) {
-				ShowError("instance_destroy: character %d not found for instance '%s'.\n", im->owner_id, im->name);
-				return 1;
-			}
-			break;
-		case IM_PARTY:
-			if ((p = party_search(im->owner_id)) == NULL) {
-				ShowError("instance_destroy: party %d not found for instance '%s'.\n", im->owner_id, im->name);
-				return 1;
-			}
-			break;
-		case IM_GUILD:
-			if ((g = guild_search(im->owner_id)) == NULL) {
-				ShowError("instance_destroy: guild %d not found for instance '%s'.\n", im->owner_id, im->name);
-				return 1;
-			}
-			break;
-		default:
-			ShowError("instance_destroy: unknown owner type %u for owner_id %d and name %s.\n", mode, im->owner_id, im->name);
-			return 1;
-	}
 
 	if(im->state == INSTANCE_IDLE) {
 		for(i = 0; i < instance_wait.count; i++) {
@@ -608,8 +585,10 @@ int instance_destroy(unsigned short instance_id)
 		else
 			type = 3;
 
-		for(i = 0; i < im->cnt_map; i++)
-			map_delinstancemap(im->map[i].m);
+		for(i = 0; i < im->cnt_map; i++) {
+			map_delinstancemap(im->map[i]->m);
+			ers_free(instance_maps_ers, im->map[i]);
+		}
 	}
 
 	if(im->keep_timer != INVALID_TIMER) {
@@ -643,7 +622,7 @@ int instance_destroy(unsigned short instance_id)
 	if( im->regs.arrays )
 		instance_data[instance_id].regs.arrays->destroy(instance_data[instance_id].regs.arrays, script_free_array_db);
 
-	ShowInfo("[Instance] Destroyed %d.\n", instance_id);
+	ShowInfo("[Instance] Destroyed %hu.\n", instance_id);
 
 	memset(&instance_data[instance_id], 0, sizeof(instance_data[instance_id]));
 
@@ -653,29 +632,29 @@ int instance_destroy(unsigned short instance_id)
 /*==========================================
  * Allows a user to enter an instance
  *------------------------------------------*/
-int instance_enter(struct map_session_data *sd, unsigned short instance_id, const char *name)
+int instance_enter(struct map_session_data *sd, unsigned short instance_id)
 {
-	struct instance_db *db = instance_searchname_db(name);
+	struct instance_db *db = instance_searchtype_db(instance_data[instance_id].type);
 
 	nullpo_retr(-1, sd);
-	nullpo_retr(3, db);
 
-	return instance_enter_position(sd, instance_id, name, db->enter.x, db->enter.y);
+	if (db == NULL)
+		return 2;
+
+	return instance_enter_position(sd, instance_id, db->enter.x, db->enter.y);
 }
 
 /*==========================================
  * Warp a user into instance
  *------------------------------------------*/
-int instance_enter_position(struct map_session_data *sd, unsigned short instance_id, const char *name, short x, short y)
+int instance_enter_position(struct map_session_data *sd, unsigned short instance_id, short x, short y)
 {
 	struct instance_data *im = &instance_data[instance_id];
-	struct instance_db *db = instance_searchname_db(name);
 	struct party_data *p = NULL;
 	struct guild *g = NULL;
 	int16 m;
 
 	nullpo_retr(-1, sd);
-	nullpo_retr(3, db);
 
 	switch(instance_data[instance_id].mode) {
 		case IM_NONE:
@@ -710,11 +689,9 @@ int instance_enter_position(struct map_session_data *sd, unsigned short instance
 
 	if (im->state != INSTANCE_BUSY)
 		return 3;
-	if (im->type != db->id)
-		return 3;
 
 	// Does the instance match?
-	if ((m = instance_mapname2mapid(StringBuf_Value(db->enter.mapname), instance_id)) < 0)
+	if ((m = instance_mapname2mapid(map_mapid2mapname(im->map[0]->m), instance_id)) < 0)
 		return 3;
 
 	if (pc_setpos(sd, map_id2index(m), x, y, CLR_OUTSIGHT))
@@ -735,7 +712,6 @@ int instance_enter_position(struct map_session_data *sd, unsigned short instance
 int instance_reqinfo(struct map_session_data *sd, unsigned short instance_id)
 {
 	struct instance_data *im;
-	struct instance_db *db;
 
 	nullpo_retr(1, sd);
 
@@ -744,7 +720,7 @@ int instance_reqinfo(struct map_session_data *sd, unsigned short instance_id)
 
 	im = &instance_data[instance_id];
 
-	if((db = instance_searchtype_db(im->type)) == NULL)
+	if(instance_searchtype_db(im->type) == NULL)
 		return 1;
 
 	// Say it's created if instance is not busy
@@ -802,8 +778,8 @@ int instance_delusers(unsigned short instance_id)
 		return 1;
 
 	// If no one is in the instance, start the idle timer
-	for(i = 0; im->map[i].m && i < MAX_MAP_PER_INSTANCE; i++)
-		if(map[im->map[i].m].users > 1) // We check this before the actual map.users are updated, hence the 1
+	for(i = 0; im->map[i]->m && i > im->cnt_map; i++)
+		if(map[im->map[i]->m].users > 1) // We check this before the actual map.users are updated, hence the 1
 			idle++;
 
 	if(!idle) // If idle wasn't added to, we know no one was in the instance
@@ -822,7 +798,7 @@ static bool instance_readdb_sub(char* str[], int columns, int current)
 	uint8 i;
 	int id = atoi(str[0]);
 	struct instance_db *db;
-	bool isNew = false, defined = false;
+	bool isNew = false;
 
 	if (!id || id >  USHRT_MAX) {
 		ShowError("instance_readdb_sub: Cannot add instance with ID '%d'. Valid ID is 1 ~ %d.\n", id, USHRT_MAX);
@@ -868,21 +844,11 @@ static bool instance_readdb_sub(char* str[], int columns, int current)
 			}
 			RECREATE(db->maplist, StringBuf *, db->maplist_count+1);
 			db->maplist[db->maplist_count] = StringBuf_Malloc();
-			if (strcmpi(str[i], str[4]) == 0)
-				defined = true;
 			StringBuf_AppendStr(db->maplist[db->maplist_count], str[i]);
 			db->maplist_count++;
 		}
 	}
 
-	if (!defined) {
-		ShowError("instance_readdb_sub: The entrance map is not defined in instance map list.\n");
-		instance_db_free_sub(db);
-		if (!isNew)
-			uidb_remove(InstanceDB,id);
-		return false;
-	}
-
 	if (isNew) {
 		uidb_put(InstanceDB, id, db);
 		strdb_uiput(InstanceNameDB, StringBuf_Value(db->name), id);
@@ -925,7 +891,7 @@ void instance_readdb(void) {
 	int f;
 
 	for (f = 0; f<ARRAYLENGTH(filename); f++) {
-		sv_readdb(db_path, filename[f], ',', 8, 8+MAX_MAP_PER_INSTANCE, -1, &instance_readdb_sub, f);
+		sv_readdb(db_path, filename[f], ',', 7, 7+MAX_MAP_PER_INSTANCE, -1, &instance_readdb_sub, f);
 	}
 }
 
@@ -975,11 +941,11 @@ void do_reload_instance(void)
 			if (instance_data[map[sd->bl.m].instance_id].mode == IM_GUILD && (!(g = guild_search(sd->status.guild_id)) || g->instance_id != map[sd->bl.m].instance_id)) // Someone not in guild is on instance map
 				continue;
 			im = &instance_data[p->instance_id];
-			if((db = instance_searchtype_db(im->type)) != NULL && !instance_enter(sd, i, StringBuf_Value(db->name))) { // All good
+			if((db = instance_searchtype_db(im->type)) != NULL && !instance_enter(sd, i)) { // All good
 				clif_displaymessage(sd->fd, msg_txt(sd,515)); // Instance has been reloaded
 				instance_reqinfo(sd,p->instance_id);
 			} else // Something went wrong
-				ShowError("do_reload_instance: Error setting character at instance start: character_id=%d instance=%s.\n",sd->status.char_id,db->name);
+				ShowError("do_reload_instance: Error setting character at instance start: character_id=%d instance=%s.\n",sd->status.char_id,StringBuf_Value(db->name));
 		}
 	mapit_free(iter);
 }
@@ -993,6 +959,8 @@ void do_init_instance(void) {
 	memset(&instance_wait, 0, sizeof(instance_wait));
 	instance_wait.timer = -1;
 
+	instance_maps_ers = ers_new(sizeof(struct s_instance_map),"instance.c::instance_maps_ers", ERS_OPT_NONE);
+
 	add_timer_func_list(instance_delete_timer,"instance_delete_timer");
 	add_timer_func_list(instance_subscription_timer,"instance_subscription_timer");
 }
@@ -1000,6 +968,7 @@ void do_init_instance(void) {
 void do_final_instance(void) {
 	int i;
 
+	ers_destroy(instance_maps_ers);
 	for( i = 1; i < MAX_INSTANCE_DATA; i++ )
 		instance_destroy(i);
 

+ 31 - 18
src/map/instance.h

@@ -9,8 +9,8 @@
 
 struct block_list;
 
-#define MAX_INSTANCE_DATA	300	// Essentially how many instances we can create, but instance creation is primarily decided by MAX_MAP_PER_SERVER	
-#define MAX_MAP_PER_INSTANCE 	10	// Max number of maps per instance
+#define MAX_INSTANCE_DATA		300	// Essentially how many instances we can create, but instance creation is primarily decided by MAX_MAP_PER_SERVER
+#define MAX_MAP_PER_INSTANCE	9	// Max number of maps per instance (Enter map is counted as one) - Supports up to 255 maps
 
 #define INSTANCE_NAME_LENGTH (60+1)
 
@@ -28,35 +28,48 @@ enum instance_mode {
 	IM_MAX,
 };
 
-struct instance_data {
-	unsigned short type, ///< Instance DB ID
-		cnt_map;
-	char name[INSTANCE_NAME_LENGTH];
-	enum instance_state state;
-	enum instance_mode mode;
-	int owner_id;
-	unsigned int keep_limit;
-	int keep_timer;
-	unsigned int idle_limit;
-	int idle_timer;
+struct s_instance_map {
+	int16 m, src_m;
+};
 
+struct instance_data {
+	unsigned short type; ///< Instance DB ID
+	enum instance_state state; ///< State of instance
+	enum instance_mode mode; ///< Mode of instance
+	int owner_id; ///< Owner ID of instance
+	unsigned int keep_limit; ///< Life time of instance
+	int keep_timer; ///< Remaining life time of instance
+	unsigned int idle_limit; ///< Idle time of instance
+	int idle_timer; ///< Remaining idle time of instance
 	struct reg_db regs; ///< Instance variables for scripts
+	struct s_instance_map **map; ///< Dynamic array of maps in instance
+	uint8 cnt_map; ///< Number of maps in an instance
+};
 
+/// Instance DB entry struct
+struct instance_db {
+	unsigned short id; ///< Instance ID
+	StringBuf *name; ///< Instance name
+	unsigned int limit, ///< Duration limit
+		timeout; ///< Timeout limit
 	struct {
-		int m;
-		int src_m;
-	} map[MAX_MAP_PER_INSTANCE];
+		StringBuf *mapname; ///< Mapname, the limit should be MAP_NAME_LENGTH_EXT
+		short x, y; ///< Map coordinates
+	} enter;
+	StringBuf **maplist; ///< Used maps in instance, the limit should be MAP_NAME_LENGTH_EXT
+	uint8 maplist_count; ///< Number of used maps
 };
 
 extern int instance_start;
 extern struct instance_data instance_data[MAX_INSTANCE_DATA];
 
+struct instance_db *instance_searchtype_db(unsigned short instance_id);
 void instance_getsd(unsigned short instance_id, struct map_session_data **sd, enum send_target *target);
 
 int instance_create(int owner_id, const char *name, enum instance_mode mode);
 int instance_destroy(unsigned short instance_id);
-int instance_enter(struct map_session_data *sd, unsigned short instance_id, const char *name);
-int instance_enter_position(struct map_session_data *sd, unsigned short instance_id, const char *name, short x, short y);
+int instance_enter(struct map_session_data *sd, unsigned short instance_id);
+int instance_enter_position(struct map_session_data *sd, unsigned short instance_id, short x, short y);
 int instance_reqinfo(struct map_session_data *sd, unsigned short instance_id);
 int instance_addusers(unsigned short instance_id);
 int instance_delusers(unsigned short instance_id);

+ 4 - 4
src/map/map.c

@@ -2823,11 +2823,11 @@ const char* map_mapid2mapname(int m)
 		if (!im) // This shouldn't happen but if it does give them the map we intended to give
 			return map[m].name;
 		else {
-			int i;
+			uint8 i;
 
-			for (i = 0; i < MAX_MAP_PER_INSTANCE; i++) { // Loop to find the src map we want
-				if (im->map[i].m == m)
-					return map[im->map[i].src_m].name;
+			for (i = 0; i < im->cnt_map; i++) { // Loop to find the src map we want
+				if (im->map[i]->m == m)
+					return map[im->map[i]->src_m].name;
 			}
 		}
 	}

+ 2 - 2
src/map/npc.c

@@ -3167,8 +3167,8 @@ int npc_duplicate4instance(struct npc_data *snd, int16 m) {
 		int dm = map_mapindex2mapid(snd->u.warp.mapindex), imap = 0, i;
 		if( dm < 0 ) return 1;
 
-		for(i = 0; i < MAX_MAP_PER_INSTANCE; i++)
-			if(im->map[i].m && map_mapname2mapid(map[im->map[i].src_m].name) == dm) {
+		for(i = 0; i < im->cnt_map; i++)
+			if(im->map[i]->m && map_mapname2mapid(map[im->map[i]->src_m].name) == dm) {
 				imap = map_mapname2mapid(map[m].name);
 				break; // Instance map matches destination, update to instance map
 			}

+ 68 - 53
src/map/script.c

@@ -18860,17 +18860,42 @@ unsigned short script_instancegetid(struct script_state* st)
 BUILDIN_FUNC(instance_create)
 {
 	enum instance_mode mode = IM_PARTY;
-	int owner_id;
+	int owner_id = 0;
 
-	owner_id = script_getnum(st, 3);
-	if (script_hasdata(st, 4)) {
-		mode = script_getnum(st, 4);
+	if (script_hasdata(st, 3)) {
+		mode = script_getnum(st, 3);
 
 		if (mode < IM_NONE || mode >= IM_MAX) {
 			ShowError("buildin_instance_create: Unknown instance owner type %d for '%s'\n", mode, script_getstr(st, 2));
 			return SCRIPT_CMD_FAILURE;
 		}
 	}
+	if (script_hasdata(st, 4))
+		owner_id = script_getnum(st, 4);
+	else {
+		struct map_session_data *sd = NULL;
+
+		sd = script_rid2sd(st); // If sd is NULL, instance_create will return -2.
+		switch(mode) {
+			case IM_NONE:
+				owner_id = st->oid;
+				break;
+			case IM_CHAR:
+				if (sd)
+					owner_id = sd->status.char_id;
+				break;
+			case IM_PARTY:
+				if (sd)
+					owner_id = sd->status.party_id;
+				break;
+			case IM_GUILD:
+				if (sd)
+					owner_id = sd->status.guild_id;
+			default:
+				ShowError("buildin_instance_create: Invalid instance mode (instance name: %s)\n", script_getstr(st, 2));
+				return SCRIPT_CMD_FAILURE;
+		}
+	}
 
 	script_pushint(st, instance_create(owner_id, script_getstr(st, 2), mode));
 	return SCRIPT_CMD_SUCCESS;
@@ -18893,7 +18918,7 @@ BUILDIN_FUNC(instance_destroy)
 
 	if( instance_id <= 0 || instance_id >= MAX_MAP_PER_SERVER ) {
 		ShowError("buildin_instance_destroy: Trying to destroy invalid instance %d.\n", instance_id);
-		return SCRIPT_CMD_SUCCESS;
+		return SCRIPT_CMD_FAILURE;
 	}
 
 	instance_destroy(instance_id);
@@ -18911,16 +18936,16 @@ BUILDIN_FUNC(instance_destroy)
 BUILDIN_FUNC(instance_enter)
 {
 	struct map_session_data *sd = NULL;
-	int x = script_hasdata(st,3) ? script_getnum(st, 3) : -1;
-	int y = script_hasdata(st,4) ? script_getnum(st, 4) : -1;
+	int x = script_hasdata(st,2) ? script_getnum(st, 2) : -1;
+	int y = script_hasdata(st,3) ? script_getnum(st, 3) : -1;
 
-	if (!script_charid2sd(5,sd))
+	if (!script_charid2sd(4,sd))
 		return SCRIPT_CMD_FAILURE;
 
 	if (x != -1 && y != -1)
-		script_pushint(st, instance_enter_position(sd, script_instancegetid(st), script_getstr(st, 2), x, y));
+		script_pushint(st, instance_enter_position(sd, script_instancegetid(st), x, y));
 	else
-		script_pushint(st, instance_enter(sd, script_instancegetid(st), script_getstr(st, 2)));
+		script_pushint(st, instance_enter(sd, script_instancegetid(st)));
 
 	return SCRIPT_CMD_SUCCESS;
 }
@@ -18988,16 +19013,7 @@ BUILDIN_FUNC(instance_mapname)
  *------------------------------------------*/
 BUILDIN_FUNC(instance_id)
 {
-	unsigned short instance_id;
-
-	instance_id = script_instancegetid(st);
-
-	if(!instance_id) {
-		//ShowError("buildin_instance_id: No instance attached to NPC or player");
-		script_pushint(st, 0);
-		return SCRIPT_CMD_SUCCESS;
-	}
-	script_pushint(st, instance_id);
+	script_pushint(st, script_instancegetid(st));
 	return SCRIPT_CMD_SUCCESS;
 }
 
@@ -19011,22 +19027,40 @@ static int buildin_instance_warpall_sub(struct block_list *bl, va_list ap)
 	unsigned int m = va_arg(ap,unsigned int);
 	int x = va_arg(ap,int);
 	int y = va_arg(ap,int);
+	unsigned short instance_id = va_arg(ap,unsigned int);
+	struct map_session_data *sd = NULL;
+	int owner_id = 0;
 
 	nullpo_retr(0, bl);
 
 	if (bl->type != BL_PC)
 		return 0;
 
-	pc_setpos((TBL_PC *)bl, m, x, y, CLR_TELEPORT);
+	sd = (TBL_PC *)bl;
+	owner_id = instance_data[instance_id].owner_id;
+	switch(instance_data[instance_id].mode) {
+		case IM_NONE:
+			break;
+		case IM_CHAR:
+			if (sd->status.char_id != owner_id)
+				return 0;
+			break;
+		case IM_PARTY:
+			if (sd->status.party_id != owner_id)
+				return 0;
+			break;
+		case IM_GUILD:
+			if (sd->status.guild_id != owner_id)
+				return 0;
+	}
 
-	return 0;
+	pc_setpos(sd, m, x, y, CLR_TELEPORT);
+
+	return 1;
 }
 
 BUILDIN_FUNC(instance_warpall)
 {
-	struct map_session_data *sd = NULL;
-	struct party_data *p = NULL;
-	struct guild *g = NULL;
 	int16 m, i;
 	unsigned short instance_id;
 	const char *mapn;
@@ -19043,27 +19077,8 @@ BUILDIN_FUNC(instance_warpall)
 	if( !instance_id || (m = map_mapname2mapid(mapn)) < 0 || (m = instance_mapname2mapid(map[m].name,instance_id)) < 0)
 		return SCRIPT_CMD_FAILURE;
 
-	switch(instance_data[instance_id].mode) {
-		case IM_NONE:
-			break;
-		case IM_CHAR:
-			if (!(sd = map_id2sd(instance_data[instance_id].owner_id)))
-				return SCRIPT_CMD_FAILURE;
-			break;
-		case IM_PARTY:
-			if (!(p = party_search(instance_data[instance_id].owner_id)))
-				return SCRIPT_CMD_FAILURE;
-			break;
-		case IM_GUILD:
-			if (!(g = guild_search(instance_data[instance_id].owner_id)))
-				return SCRIPT_CMD_FAILURE;
-		default:
-			ShowError("buildin_instance_warpall: Invalid instance owner type (instance_id: %d)\n", instance_id);
-			break;
-	}
-
 	for(i = 0; i < instance_data[instance_id].cnt_map; i++)
-		map_foreachinmap(buildin_instance_warpall_sub, instance_data[instance_id].map[i].m, BL_PC, map_id2index(m), x, y);
+		map_foreachinmap(buildin_instance_warpall_sub, instance_data[instance_id].map[i]->m, BL_PC, map_id2index(m), x, y, instance_id);
 
 	return SCRIPT_CMD_SUCCESS;
 }
@@ -19095,7 +19110,7 @@ BUILDIN_FUNC(instance_announce) {
 	}
 
 	for( i = 0; i < instance_data[instance_id].cnt_map; i++ )
-		map_foreachinmap(buildin_announce_sub, instance_data[instance_id].map[i].m, BL_PC,
+		map_foreachinmap(buildin_announce_sub, instance_data[instance_id].map[i]->m, BL_PC,
 						 mes, strlen(mes)+1, flag&BC_COLOR_MASK, fontColor, fontType, fontSize, fontAlign, fontY);
 
 	return SCRIPT_CMD_SUCCESS;
@@ -19130,11 +19145,11 @@ BUILDIN_FUNC(instance_check_party)
 
 	if( script_hasdata(st,2) )
 		party_id = script_getnum(st,2);
-	else return SCRIPT_CMD_SUCCESS;
+	else return SCRIPT_CMD_FAILURE;
 
 	if( !(p = party_search(party_id)) ) {
 		script_pushint(st, 0); // Returns false if party does not exist.
-		return SCRIPT_CMD_SUCCESS;
+		return SCRIPT_CMD_FAILURE;
 	}
 
 	for( i = 0; i < MAX_PARTY; i++ ) {
@@ -19172,7 +19187,7 @@ BUILDIN_FUNC(instance_check_party)
  *------------------------------------------*/
 BUILDIN_FUNC(instance_check_guild)
 {
-	int amount, min, max, i, guild_id, c = 0;
+	int amount, min, max, i, guild_id = 0, c = 0;
 	struct guild *g = NULL;
 
 	amount = script_hasdata(st,3) ? script_getnum(st,3) : 1; // Amount of needed Guild members for the Instance.
@@ -19190,11 +19205,11 @@ BUILDIN_FUNC(instance_check_guild)
 	if (script_hasdata(st,2))
 		guild_id = script_getnum(st,2);
 	else
-		return SCRIPT_CMD_SUCCESS;
+		return SCRIPT_CMD_FAILURE;
 
 	if (!(g = guild_search(guild_id))) {
 		script_pushint(st, 0); // Returns false if guild does not exist.
-		return SCRIPT_CMD_SUCCESS;
+		return SCRIPT_CMD_FAILURE;
 	}
 
 	for(i = 0; i < MAX_GUILD; i++) {
@@ -21891,10 +21906,10 @@ struct script_function buildin_func[] = {
 	BUILDIN_DEF(bg_updatescore,"sii"),
 
 	// Instancing
-	BUILDIN_DEF(instance_create,"si?"),
+	BUILDIN_DEF(instance_create,"s??"),
 	BUILDIN_DEF(instance_destroy,"?"),
 	BUILDIN_DEF(instance_id,""),
-	BUILDIN_DEF(instance_enter,"s???"),
+	BUILDIN_DEF(instance_enter,"???"),
 	BUILDIN_DEF(instance_npcname,"s?"),
 	BUILDIN_DEF(instance_mapname,"s?"),
 	BUILDIN_DEF(instance_warpall,"sii?"),