diff --git a/db/import-tmpl/instance_db.txt b/db/import-tmpl/instance_db.txt deleted file mode 100644 index 5672eec9e4..0000000000 --- a/db/import-tmpl/instance_db.txt +++ /dev/null @@ -1,6 +0,0 @@ -// Instance Database -// -// Structure of Database: -// ID,Name,LimitTime,IdleTimeOut,EnterMap,EnterX,EnterY,Map2,Map3,...,Map255 -// -// EnterMap is considered as Map1 diff --git a/db/import-tmpl/instance_db.yml b/db/import-tmpl/instance_db.yml new file mode 100644 index 0000000000..16dd9900e3 --- /dev/null +++ b/db/import-tmpl/instance_db.yml @@ -0,0 +1,38 @@ +# This file is a part of rAthena. +# Copyright(C) 2019 rAthena Development Team +# https://rathena.org - https://github.com/rathena +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . +# +########################################################################### +# Instance Database +########################################################################### +# +# Instance Settings +# +########################################################################### +# - Id Instance ID. +# Name Instance Name. +# TimeLimit Total lifetime of instance in seconds. (Default: 3600) +# IdleTimeOut Time before an idle instance is destroyed in seconds. (Default: 300) +# Enter: Instance entrance coordinates. +# Map Map Name where players start. +# X X Coordinate where players start. +# Y Y Coordinate where players start. +# AdditionalMaps: List of maps that are part of an instance. (Optional) +########################################################################### + +Header: + Type: INSTANCE_DB + Version: 1 diff --git a/db/instance_db.yml b/db/instance_db.yml new file mode 100644 index 0000000000..03bcfba90c --- /dev/null +++ b/db/instance_db.yml @@ -0,0 +1,46 @@ +# This file is a part of rAthena. +# Copyright(C) 2019 rAthena Development Team +# https://rathena.org - https://github.com/rathena +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . +# +########################################################################### +# Instance Database +########################################################################### +# +# Instance Settings +# +########################################################################### +# - Id Instance ID. +# Name Instance Name. +# TimeLimit Total lifetime of instance in seconds. (Default: 3600) +# IdleTimeOut Time before an idle instance is destroyed in seconds. (Default: 300) +# Enter: Instance entrance coordinates. +# Map Map Name where players start. +# X X Coordinate where players start. +# Y Y Coordinate where players start. +# AdditionalMaps: List of maps that are part of an instance. (Optional) +########################################################################### + +Header: + Type: INSTANCE_DB + Version: 1 + +Footer: + Imports: + - Path: db/pre-re/instance_db.yml + Mode: Prerenewal + - Path: db/re/instance_db.yml + Mode: Renewal + - Path: db/import/instance_db.yml diff --git a/db/pre-re/instance_db.txt b/db/pre-re/instance_db.txt deleted file mode 100644 index e9cf668f15..0000000000 --- a/db/pre-re/instance_db.txt +++ /dev/null @@ -1,11 +0,0 @@ -// Instance Database -// -// Structure of Database: -// ID,Name,LimitTime,IdleTimeOut,EnterMap,EnterX,EnterY,Map2,Map3,...,Map255 -// -// EnterMap is considered as Map1 - -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 diff --git a/db/pre-re/instance_db.yml b/db/pre-re/instance_db.yml new file mode 100644 index 0000000000..fdd82c99cc --- /dev/null +++ b/db/pre-re/instance_db.yml @@ -0,0 +1,79 @@ +# This file is a part of rAthena. +# Copyright(C) 2019 rAthena Development Team +# https://rathena.org - https://github.com/rathena +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . +# +########################################################################### +# Instance Database +########################################################################### +# +# Instance Settings +# +########################################################################### +# - Id Instance ID. +# Name Instance Name. +# TimeLimit Total lifetime of instance in seconds. (Default: 3600) +# IdleTimeOut Time before an idle instance is destroyed in seconds. (Default: 300) +# Enter: Instance entrance coordinates. +# Map Map Name where players start. +# X X Coordinate where players start. +# Y Y Coordinate where players start. +# AdditionalMaps: List of maps that are part of an instance. (Optional) +########################################################################### + +Header: + Type: INSTANCE_DB + Version: 1 + +Body: + - Id: 1 + Name: Endless Tower + TimeLimit: 14400 + Enter: + Map: 1@tower + X: 50 + Y: 355 + AdditionalMaps: + 2@tower: true + 3@tower: true + 4@tower: true + 5@tower: true + 6@tower: true + - Id: 2 + Name: Sealed Catacomb + TimeLimit: 7200 + Enter: + Map: 1@cata + X: 100 + Y: 224 + AdditionalMaps: + 2@cata: true + - Id: 3 + Name: Orc's Memory + Enter: + Map: 1@orcs + X: 179 + Y: 15 + AdditionalMaps: + 2@orcs: true + - Id: 4 + Name: Nidhoggur's Nest + TimeLimit: 14400 + Enter: + Map: 1@nyd + X: 32 + Y: 36 + AdditionalMaps: + 2@nyd: true diff --git a/db/re/instance_db.txt b/db/re/instance_db.txt deleted file mode 100644 index a0443ffb8d..0000000000 --- a/db/re/instance_db.txt +++ /dev/null @@ -1,40 +0,0 @@ -// Instance Database -// -// Structure of Database: -// ID,Name,LimitTime,IdleTimeOut,EnterMap,EnterX,EnterY,Map2,...,Map255 -// -// EnterMap is considered as Map1 - -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 -14,Sara's Memories,3600,300,1@sara,250,155 -15,Geffen Magic Tournament,7200,300,1@gef,119,209,1@gef_in,1@ge_st -16,Horror Toy Factory,3600,300,1@xm_d,111,22 -17,Faceworm's Nest,3600,300,1@face,112,370 -18,Ghost Palace,3600,300,1@spa,42,196 -19,Devil's Tower,3600,300,1@tnm1,50,104,1@tnm2,1@tnm3 -20,Assault on the Airship,3600,300,1@air1,244,73,1@air2 -21,Fenrir and Sarah,3600,300,1@glast,367,304 -// 22,Wave Mode - Forest,3600,300,1@def01,50,21 -// 23,Wave Mode - Sky,3600,300,1@def02,29,35 -24,Nightmarish Jitterbug,3600,300,1@jtb,16,17 -25,Isle of Bios,3600,300,1@dth1,17,93,1@dth2,1@dth3 -26,Morse's Cave,3600,300,1@rev,26,181 -// 27,Temple of the Demon God,3600,300,1@eom,101,16 -28,Central Laboratory,3600,300,1@lab,120,30 -29,Last room,3600,300,1@uns,145,35 -// 30,Charleston in Distress,3600,300,1@mcd,127,282 -31,Ritual of Blessing,3600,300,2@mir,101,12 -32,Room of Consciousness,3600,300,1@mir,101,10 -// 33,Sky Fortress Invasion,3600,300,1@sthb,54,67,1@sthc,1@sthd diff --git a/db/re/instance_db.yml b/db/re/instance_db.yml new file mode 100644 index 0000000000..1bc5d59ef4 --- /dev/null +++ b/db/re/instance_db.yml @@ -0,0 +1,276 @@ +# This file is a part of rAthena. +# Copyright(C) 2019 rAthena Development Team +# https://rathena.org - https://github.com/rathena +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . +# +########################################################################### +# Instance Database +########################################################################### +# +# Instance Settings +# +########################################################################### +# - Id Instance ID. +# Name Instance Name. +# TimeLimit Total lifetime of instance in seconds. (Default: 3600) +# IdleTimeOut Time before an idle instance is destroyed in seconds. (Default: 300) +# Enter: Instance entrance coordinates. +# Map Map Name where players start. +# X X Coordinate where players start. +# Y Y Coordinate where players start. +# AdditionalMaps: List of maps that are part of an instance. (Optional) +########################################################################### + +Header: + Type: INSTANCE_DB + Version: 1 + +Body: + - Id: 1 + Name: Endless Tower + TimeLimit: 14400 + Enter: + Map: 1@tower + X: 50 + Y: 355 + AdditionalMaps: + 2@tower: true + 3@tower: true + 4@tower: true + 5@tower: true + 6@tower: true + - Id: 2 + Name: Sealed Catacomb + TimeLimit: 7200 + Enter: + Map: 1@cata + X: 100 + Y: 224 + AdditionalMaps: + 2@cata: true + - Id: 3 + Name: Orc's Memory + Enter: + Map: 1@orcs + X: 179 + Y: 15 + AdditionalMaps: + 2@orcs: true + - Id: 4 + Name: Nidhoggur's Nest + TimeLimit: 14400 + Enter: + Map: 1@nyd + X: 32 + Y: 36 + AdditionalMaps: + 2@nyd: true + - Id: 5 + Name: Mistwood Maze + TimeLimit: 7200 + Enter: + Map: 1@mist + X: 89 + Y: 29 + - Id: 6 + Name: Culvert + Enter: + Map: 1@pump + X: 63 + Y: 98 + AdditionalMaps: + 2@pump: true + - Id: 7 + Name: Octopus Cave + Enter: + Map: 1@cash + X: 199 + Y: 99 + - Id: 8 + Name: Bangungot Hospital 2F + Enter: + Map: 1@ma_h + X: 40 + Y: 157 + - Id: 9 + Name: Buwaya Cave + Enter: + Map: 1@ma_c + X: 35 + Y: 57 + - Id: 10 + Name: Bakonawa Lake + TimeLimit: 7200 + Enter: + Map: 1@ma_b + X: 64 + Y: 51 + - Id: 11 + Name: Wolfchev's Laboratory + TimeLimit: 14400 + Enter: + Map: 1@lhz + X: 45 + Y: 148 + - Id: 12 + Name: Old Glast Heim + Enter: + Map: 1@gl_k + X: 150 + Y: 20 + AdditionalMaps: + 2@gl_k: true + - Id: 13 + Name: Eclage Interior + TimeLimit: 1200 + Enter: + Map: 1@ecl + X: 60 + Y: 50 + - Id: 14 + Name: Sara's Memories + Enter: + Map: 1@sara + X: 250 + Y: 155 + - Id: 15 + Name: Geffen Magic Tournament + TimeLimit: 7200 + Enter: + Map: 1@gef + X: 119 + Y: 209 + AdditionalMaps: + 1@gef_in: true + 1@ge_st: true + - Id: 16 + Name: Horror Toy Factory + Enter: + Map: 1@xm_d + X: 111 + Y: 22 + - Id: 17 + Name: Faceworm's Nest + Enter: + Map: 1@face + X: 112 + Y: 370 + - Id: 18 + Name: Ghost Palace + Enter: + Map: 1@spa + X: 42 + Y: 196 + - Id: 19 + Name: Devil's Tower + Enter: + Map: 1@tnm1 + X: 50 + Y: 104 + AdditionalMaps: + 1@tnm2: true + 1@tnm3: true + - Id: 20 + Name: Assault on the Airship + Enter: + Map: 1@air1 + X: 244 + Y: 73 + AdditionalMaps: + 1@air2: true + - Id: 21 + Name: Fenrir and Sarah + Enter: + Map: 1@glast + X: 367 + Y: 304 +# - Id: 22 +# Name: Wave Mode - Forest +# Enter: +# Map: 1@def01 +# X: 50 +# Y: 21 +# - Id: 23 +# Name: Wave Mode - Sky +# Enter: +# Map: 1@def02 +# X: 29 +# Y: 35 + - Id: 24 + Name: Nightmarish Jitterbug + Enter: + Map: 1@jtb + X: 16 + Y: 17 + - Id: 25 + Name: Isle of Bios + Enter: + Map: 1@dth1 + X: 17 + Y: 93 + AdditionalMaps: + 1@dth2: true + 1@dth3: true + - Id: 26 + Name: Morse's Cave + Enter: + Map: 1@rev + X: 26 + Y: 181 +# - Id: 27 +# Name: Temple of the Demon God +# Enter: +# Map: 1@eom +# X: 101 +# Y: 16 + - Id: 28 + Name: Central Laboratory + Enter: + Map: 1@lab + X: 120 + Y: 30 + - Id: 29 + Name: Last room + Enter: + Map: 1@uns + X: 145 + Y: 35 +# - Id: 30 +# Name: Charleston in Distress +# Enter: +# Map: 1@mcd +# X: 127 +# Y: 282 + - Id: 31 + Name: Ritual of Blessing + Enter: + Map: 2@mir + X: 101 + Y: 12 + - Id: 32 + Name: Room of Consciousness + Enter: + Map: 1@mir + X: 101 + Y: 10 +# - Id: 33 +# Name: Sky Fortress Invasion +# Enter: +# Map: 1@sthb +# X: 54 +# Y: 67 +# AdditionalMaps: +# 1@sthc: true +# 1@sthd: true diff --git a/db/readme.md b/db/readme.md index eeddb1faed..094a296691 100644 --- a/db/readme.md +++ b/db/readme.md @@ -38,10 +38,18 @@ We want to add our own custom achievement that can be given to a player via an N --- We want to add our own customized Housing Instance. -#### /db/import/instance_db.txt +#### /db/import/instance_db.yml - // ID,Name,LimitTime,IdleTimeOut,EnterMap,EnterX,EnterY,Map2,Map3,...,Map255 - 35,Home,3600,900,1@home,24,6,2@home,3@home + - Id: 35 + Name: Home + IdleTimeOut: 900 + Enter: + Map: 1@home + X: 24 + Y: 6 + AdditionalMaps: + - Map: 2@home + - Map: 3@home ### Mob Alias diff --git a/doc/script_commands.txt b/doc/script_commands.txt index f4be22da15..d254890b9f 100644 --- a/doc/script_commands.txt +++ b/doc/script_commands.txt @@ -8938,7 +8938,7 @@ with the given character id. *instance_create(""{,{,}}); Creates an instance for the of . The instance name, along with -all other instance data, is read from 'db/(pre-)re/instance_db.txt'. Upon success, +all other instance data, is read from 'db/(pre-)re/instance_db.yml'. 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 the instance. @@ -8971,7 +8971,7 @@ This will also trigger the "OnInstanceDestroy" label in all NPCs inside the inst Warps the attached player to the specified . If no ID is specified, the IM_PARTY instance the invoking player is attached to is used. -The map and coordinates are located in 'db/(pre-)re/instance_db.txt'. +The map and coordinates are located in 'db/(pre-)re/instance_db.yml'. The command returns IE_OK upon success, and these values upon failure: IE_NOMEMBER: Party/Guild/Clan not found (for party/guild/clan modes). diff --git a/doc/yaml/db/instance_db.yml b/doc/yaml/db/instance_db.yml new file mode 100644 index 0000000000..b7c1cf8ffe --- /dev/null +++ b/doc/yaml/db/instance_db.yml @@ -0,0 +1,17 @@ +########################################################################### +# Instance Database +########################################################################### +# +# Instance Settings +# +########################################################################### +# - Id Instance ID. +# Name Instance Name. +# TimeLimit Total lifetime of instance in seconds. (Default: 3600) +# IdleTimeOut Time before an idle instance is destroyed in seconds. (Default: 300) +# Enter: Instance entrance coordinates. +# Map Map Name where players start. +# X X Coordinate where players start. +# Y Y Coordinate where players start. +# AdditionalMaps: List of maps that are part of an instance. (Optional) +########################################################################### diff --git a/src/common/utilities.hpp b/src/common/utilities.hpp index e00228a378..15ba32cef6 100644 --- a/src/common/utilities.hpp +++ b/src/common/utilities.hpp @@ -175,6 +175,23 @@ namespace rathena { vector.erase(vector.begin() + index); } + /** + * Determine if a value exists in the vector and then erase it + * @param vector: Vector to erase value from + * @param value: Value to remove + */ + template void vector_erase_if_exists(std::vector &vector, V value) { + auto it = std::find(vector.begin(), vector.end(), value); + + if (it != vector.end()) { + if (vector.size() == 1) { + vector.clear(); + vector.shrink_to_fit(); + } else + vector.erase(it); + } + } + bool safe_addition( int64 a, int64 b, int64& result ); bool safe_substraction( int64 a, int64 b, int64& result ); bool safe_multiplication( int64 a, int64 b, int64& result ); diff --git a/src/map/atcommand.cpp b/src/map/atcommand.cpp index e8a86d415a..b0f9312b18 100644 --- a/src/map/atcommand.cpp +++ b/src/map/atcommand.cpp @@ -4030,8 +4030,8 @@ ACMD_FUNC(reload) { if (quest_db.reload()) clif_displaymessage(fd, msg_txt(sd,1377)); // Quest database has been reloaded. } else if (strstr(command, "instancedb") || strncmp(message, "instancedb", 4) == 0) { - instance_reload(); - clif_displaymessage(fd, msg_txt(sd,516)); // Instance database has been reloaded. + if (instance_db.reload()) + clif_displaymessage(fd, msg_txt(sd,516)); // Instance database has been reloaded. } else if (strstr(command, "achievementdb") || strncmp(message, "achievementdb", 4) == 0) { achievement_db_reload(); clif_displaymessage(fd, msg_txt(sd,771)); // Achievement database has been reloaded. diff --git a/src/map/clif.cpp b/src/map/clif.cpp index 8b767091cc..596b1fc482 100644 --- a/src/map/clif.cpp +++ b/src/map/clif.cpp @@ -17793,26 +17793,25 @@ void clif_font(struct map_session_data *sd) /// Required to start the instancing information window on Client /// This window re-appears each "refresh" of client automatically until the keep_limit reaches 0. /// S 0x2cb .61B .W -void clif_instance_create(unsigned short instance_id, int num) +void clif_instance_create(int 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]; - instance_getsd(instance_id, &sd, &target); + instance_getsd(instance_id, sd, &target); if (!sd) return; - db = instance_searchtype_db(instance_data[instance_id].type); + std::shared_ptr db = instance_db.find(util::umap_find(instances, instance_id)->id); if (!db) return; WBUFW(buf,0) = 0x2cb; - safestrncpy(WBUFCP(buf,2), StringBuf_Value(db->name), INSTANCE_NAME_LENGTH); + safestrncpy(WBUFCP(buf,2), db->name.c_str(), INSTANCE_NAME_LENGTH); WBUFW(buf,63) = num; clif_send(buf,packet_len(0x2cb),&sd->bl,target); #endif @@ -17822,14 +17821,14 @@ void clif_instance_create(unsigned short instance_id, int num) /// To announce Instancing queue creation if no maps available /// S 0x2cc .W -void clif_instance_changewait(unsigned short instance_id, int num) +void clif_instance_changewait(int instance_id, int num) { #if PACKETVER >= 20071128 struct map_session_data *sd = NULL; enum send_target target = PARTY; unsigned char buf[4]; - instance_getsd(instance_id, &sd, &target); + instance_getsd(instance_id, sd, &target); if (!sd) return; @@ -17844,26 +17843,25 @@ void clif_instance_changewait(unsigned short instance_id, int num) /// Notify the current status to members /// S 0x2cd .61B .L .L -void clif_instance_status(unsigned short instance_id, unsigned int limit1, unsigned int limit2) +void clif_instance_status(int 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]; - instance_getsd(instance_id, &sd, &target); + instance_getsd(instance_id, sd, &target); if (!sd) return; - db = instance_searchtype_db(instance_data[instance_id].type); + std::shared_ptr db = instance_db.find(util::umap_find(instances, instance_id)->id); if (!db) return; WBUFW(buf,0) = 0x2cd; - safestrncpy(WBUFCP(buf,2), StringBuf_Value(db->name), INSTANCE_NAME_LENGTH); + safestrncpy(WBUFCP(buf,2), db->name.c_str(), INSTANCE_NAME_LENGTH); WBUFL(buf,63) = limit1; WBUFL(buf,67) = limit2; clif_send(buf,packet_len(0x2cd),&sd->bl,target); @@ -17879,14 +17877,14 @@ void clif_instance_status(unsigned short instance_id, unsigned int limit1, unsig /// 2 = The Memorial Dungeon's entry time limit expired; it has been destroyed /// 3 = The Memorial Dungeon has been removed. /// 4 = Create failure (removes the instance window) -void clif_instance_changestatus(unsigned int instance_id, int type, unsigned int limit) +void clif_instance_changestatus(int instance_id, e_instance_notify type, unsigned int limit) { #if PACKETVER >= 20071128 struct map_session_data *sd = NULL; enum send_target target = PARTY; unsigned char buf[10]; - instance_getsd(instance_id, &sd, &target); + instance_getsd(instance_id, sd, &target); if (!sd) return; diff --git a/src/map/clif.hpp b/src/map/clif.hpp index b52761d30b..6d8c3ed64d 100644 --- a/src/map/clif.hpp +++ b/src/map/clif.hpp @@ -43,6 +43,7 @@ struct achievement; struct guild_log_entry; enum e_guild_storage_log : uint16; enum e_bg_queue_apply_ack : uint16; +enum e_instance_notify : uint8; enum e_PacketDBVersion { // packet DB MIN_PACKET_DB = 0x064, @@ -835,10 +836,10 @@ void clif_bg_queue_lobby_notify(const char *name, struct map_session_data *sd); void clig_bg_queue_ack_lobby(bool result, const char *name, const char *lobbyname, struct map_session_data *sd); // Instancing -void clif_instance_create(unsigned short instance_id, int num); -void clif_instance_changewait(unsigned short instance_id, int num); -void clif_instance_status(unsigned short instance_id, unsigned int limit1, unsigned int limit2); -void clif_instance_changestatus(unsigned int instance_id, int type, unsigned int limit); +void clif_instance_create(int instance_id, int num); +void clif_instance_changewait(int instance_id, int num); +void clif_instance_status(int instance_id, unsigned int limit1, unsigned int limit2); +void clif_instance_changestatus(int instance_id, e_instance_notify type, unsigned int limit); // Custom Fonts void clif_font(struct map_session_data *sd); diff --git a/src/map/instance.cpp b/src/map/instance.cpp index a14392eac2..f56c768bc7 100644 --- a/src/map/instance.cpp +++ b/src/map/instance.cpp @@ -4,6 +4,7 @@ #include "instance.hpp" #include +#include #include "../common/cbasetypes.hpp" #include "../common/db.hpp" @@ -14,6 +15,7 @@ #include "../common/socket.hpp" #include "../common/strlib.hpp" #include "../common/timer.hpp" +#include "../common/utilities.hpp" #include "clan.hpp" #include "clif.hpp" @@ -23,283 +25,445 @@ #include "party.hpp" #include "pc.hpp" -#define INSTANCE_INTERVAL 60000 // Interval used to check when an instance is to be destroyed (ms) +using namespace rathena; -struct instance_data instance_data[MAX_INSTANCE_DATA]; -struct eri *instance_maps_ers = NULL; ///< Array of maps per instance - -int16 instance_start = 0; - -static DBMap *InstanceDB; /// Instance DB: struct instance_db, key: id -static DBMap *InstanceNameDB; /// instance id, key: name - -static struct { - int id[MAX_INSTANCE_DATA]; - int count; +/// Instance Idle Queue data +struct s_instance_wait { + std::deque id; int timer; } instance_wait; -/*========================================== - * Searches for an instance ID in the database - *------------------------------------------*/ -struct instance_db *instance_searchtype_db(unsigned short instance_id) { - return (struct instance_db *)uidb_get(InstanceDB,instance_id); +#define INSTANCE_INTERVAL 60000 // Interval used to check when an instance is to be destroyed (ms) + +int16 instance_start = 0; // Instance MapID start +int instance_count = 1; // Total created instances + +std::unordered_map> instances; + +const std::string InstanceDatabase::getDefaultLocation() { + return std::string(db_path) + "/instance_db.yml"; } -static uint16 instance_name2id(const char *instance_name) { - return (uint16)strdb_uiget(InstanceNameDB,instance_name); +/** + * Reads and parses an entry from the instance_db. + * @param node: YAML node containing the entry. + * @return count of successfully parsed rows + */ +uint64 InstanceDatabase::parseBodyNode(const YAML::Node &node) { + int32 instance_id = 0; + + if (!this->asInt32(node, "Id", instance_id)) + return 0; + + if (instance_id <= 0) { + this->invalidWarning(node, "Instance Id is invalid. Valid range 1~%d, skipping.\n", INT_MAX); + return 0; + } + + std::shared_ptr instance = this->find(instance_id); + bool exists = instance != nullptr; + + if (!exists) { + if (!this->nodesExist(node, { "Name", "Enter" })) + return 0; + + instance = std::make_shared(); + instance->id = instance_id; + } + + if (this->nodeExists(node, "Name")) { + std::string name; + + if (!this->asString(node, "Name", name)) + return 0; + + for (const auto &instance : instance_db) { + if (instance.second->name.compare(name) == 0) { + this->invalidWarning(node["Name"], "Instance name %s already exists, skipping.\n", name.c_str()); + return 0; + } + } + + instance->name = name; + } + + if (this->nodeExists(node, "TimeLimit")) { + uint32 limit; + + if (!this->asUInt32(node, "TimeLimit", limit)) + return 0; + + instance->limit = limit; + } else { + if (!exists) + instance->limit = 3600; + } + + if (this->nodeExists(node, "IdleTimeOut")) { + uint32 idle; + + if (!this->asUInt32(node, "IdleTimeOut", idle)) + return 0; + + instance->timeout = idle; + } else { + if (!exists) + instance->timeout = 300; + } + + /* + if (this->nodeExists(node, "Destroyable")) { + bool destroy; + + if (!this->asBool(node, "Destroyable", destroy)) + return 0; + + instance->destroyable = destroy; + } else { + if (!exists) + instance->destroyable = true; + } + */ + + if (this->nodeExists(node, "Enter")) { + const YAML::Node &enterNode = node["Enter"]; + + if (!this->nodesExist(enterNode, { "Map", "X", "Y" })) + return 0; + + if (this->nodeExists(enterNode, "Map")) { + std::string map; + + if (!this->asString(enterNode, "Map", map)) + return 0; + + int16 m = map_mapname2mapid(map.c_str()); + + if (m == -1) { + this->invalidWarning(enterNode["Map"], "Map %s is not a valid map, skipping.\n", map.c_str()); + return 0; + } + + instance->enter.map = m; + } + + if (this->nodeExists(enterNode, "X")) { + int16 x; + + if (!this->asInt16(enterNode, "X", x)) + return 0; + + instance->enter.x = x; + } + + if (this->nodeExists(enterNode, "Y")) { + int16 y; + + if (!this->asInt16(enterNode, "Y", y)) + return 0; + + instance->enter.y = y; + } + } + + if (this->nodeExists(node, "AdditionalMaps")) { + const YAML::Node &mapNode = node["AdditionalMaps"]; + + for (const auto &mapIt : mapNode) { + std::string map = mapIt.first.as(); + int16 m = map_mapname2mapid(map.c_str()); + + if (m == instance->enter.map) { + this->invalidWarning(mapNode, "Additional Map %s is already listed as the EnterMap.\n", map.c_str()); + continue; + } + + if (m == -1) { + this->invalidWarning(mapNode, "Additional Map %s is not a valid map, skipping.\n", map.c_str()); + return 0; + } + + bool active; + + if (!this->asBool(mapNode, map, active)) + return 0; + + if (active) + instance->maplist.push_back(m); + else + util::vector_erase_if_exists(instance->maplist, m); + } + } + + if (!exists) + this->put(instance_id, instance); + + return 1; } -/*========================================== +InstanceDatabase instance_db; + +/** * Searches for an instance name in the database - *------------------------------------------*/ -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); + * @param instance_name: Instance to search for + * @return shared_ptr of instance or nullptr on failure + */ +std::shared_ptr instance_search_db_name(const char *instance_name) +{ + for (const auto &it : instance_db) { + if (!strcmp(it.second->name.c_str(), instance_name)) + return it.second; + } + + return nullptr; } /** * Search for a sd of an Instance * @param instance_id: Instance ID - * @param sd: Player data to attach + * @param sd: Pointer to player data * @param target: Target display type */ -void instance_getsd(unsigned short instance_id, struct map_session_data **sd, enum send_target *target) { - switch(instance_data[instance_id].mode) { +void instance_getsd(int instance_id, struct map_session_data *&sd, enum send_target *target) { + std::shared_ptr idata = util::umap_find(instances, instance_id); + + if (!idata) { + sd = nullptr; + return; + } + + switch(idata->mode) { case IM_NONE: - (*sd) = NULL; + sd = nullptr; (*target) = SELF; break; case IM_GUILD: - (*sd) = guild_getavailablesd(guild_search(instance_data[instance_id].owner_id)); + sd = guild_getavailablesd(guild_search(idata->owner_id)); (*target) = GUILD; break; case IM_PARTY: - (*sd) = party_getavailablesd(party_search(instance_data[instance_id].owner_id)); + sd = party_getavailablesd(party_search(idata->owner_id)); (*target) = PARTY; break; case IM_CHAR: - (*sd) = map_charid2sd(instance_data[instance_id].owner_id); + sd = map_charid2sd(idata->owner_id); (*target) = SELF; break; case IM_CLAN: - (*sd) = clan_getavailablesd(clan_search(instance_data[instance_id].owner_id)); + sd = clan_getavailablesd(clan_search(idata->owner_id)); (*target) = CLAN; } return; } -/*========================================== +/** * Deletes an instance timer (Destroys instance) - *------------------------------------------*/ + */ static TIMER_FUNC(instance_delete_timer){ instance_destroy(id); return 0; } -/*========================================== +/** * Create subscription timer - *------------------------------------------*/ + */ static TIMER_FUNC(instance_subscription_timer){ - int i, ret; - unsigned short instance_id = instance_wait.id[0]; - struct map_session_data *sd = NULL; - struct party_data *pd = NULL; - struct guild *gd = NULL; - struct clan *cd = NULL; - enum instance_mode mode; + int instance_id = instance_wait.id[0]; - if(instance_wait.count == 0 || instance_id == 0) + if (instance_id <= 0 || instance_wait.id.empty()) return 0; - // Check that maps have been added - ret = instance_addmap(instance_id); - mode = instance_data[instance_id].mode; + std::shared_ptr idata = util::umap_find(instances, instance_id); + + if (!idata) + return 0; + + struct map_session_data *sd; + struct party_data *pd; + struct guild *gd; + struct clan *cd; + e_instance_mode mode = idata->mode; + int ret = instance_addmap(instance_id); // Check that maps have been added switch(mode) { case IM_NONE: break; case IM_CHAR: - if (ret == 0 && (sd = map_charid2sd(instance_data[instance_id].owner_id)) != NULL) // If no maps are created, tell player to wait + if (ret == 0 && (sd = map_charid2sd(idata->owner_id))) // If no maps are created, tell player to wait clif_instance_changewait(instance_id, 0xffff); break; case IM_PARTY: - if (ret == 0 && (pd = party_search(instance_data[instance_id].owner_id)) != NULL) // If no maps are created, tell party to wait + if (ret == 0 && (pd = party_search(idata->owner_id))) // If no maps are created, tell party to wait clif_instance_changewait(instance_id, 0xffff); break; case IM_GUILD: - if (ret == 0 && (gd = guild_search(instance_data[instance_id].owner_id)) != NULL) // If no maps are created, tell guild to wait + if (ret == 0 && (gd = guild_search(idata->owner_id))) // If no maps are created, tell guild to wait clif_instance_changewait(instance_id, 0xffff); break; case IM_CLAN: - if (ret == 0 && (cd = clan_search(instance_data[instance_id].owner_id)) != NULL) // If no maps are created, tell clan to wait + if (ret == 0 && (cd = clan_search(idata->owner_id))) // If no maps are created, tell clan to wait clif_instance_changewait(instance_id, 0xffff); break; default: return 0; } - instance_wait.count--; - memmove(&instance_wait.id[0],&instance_wait.id[1],sizeof(instance_wait.id[0])*instance_wait.count); - memset(&instance_wait.id[instance_wait.count], 0, sizeof(instance_wait.id[0])); + instance_wait.id.pop_front(); - for(i = 0; i < instance_wait.count; i++) { - if( instance_data[instance_wait.id[i]].state == INSTANCE_IDLE && - ((mode == IM_CHAR && sd != NULL) || (mode == IM_GUILD && gd != NULL) || (mode == IM_PARTY && pd != NULL) || (mode == IM_CLAN && cd != NULL)) - ){ + for (int i = 0; i < instance_wait.id.size(); i++) { + if (idata->state == INSTANCE_IDLE && ((mode == IM_CHAR && sd) || (mode == IM_GUILD && gd) || (mode == IM_PARTY && pd) || (mode == IM_CLAN && cd))) clif_instance_changewait(instance_id, i + 1); - } } - if(instance_wait.count) - instance_wait.timer = add_timer(gettick()+INSTANCE_INTERVAL, instance_subscription_timer, 0, 0); + if (!instance_wait.id.empty()) + instance_wait.timer = add_timer(gettick() + INSTANCE_INTERVAL, instance_subscription_timer, 0, 0); else instance_wait.timer = INVALID_TIMER; return 0; } -/*========================================== +/** * Adds timer back to members entering instance - *------------------------------------------*/ -static int instance_startkeeptimer(struct instance_data *im, unsigned short instance_id) + * @param idata: Instance data + * @param instance_id: Instance ID to notify + * @return True on success or false on failure + */ +bool instance_startkeeptimer(std::shared_ptr idata, int instance_id) { - struct instance_db *db; - - nullpo_retr(0, im); - // No timer - if(im->keep_timer != INVALID_TIMER) - return 1; + if (!idata || idata->keep_timer != INVALID_TIMER) + return false; - if((db = instance_searchtype_db(im->type)) == NULL) - return 1; + std::shared_ptr db = instance_db.find(idata->id); + + if (!db) + return false; // Add timer - im->keep_limit = (unsigned int)time(NULL) + db->limit; - im->keep_timer = add_timer(gettick()+db->limit*1000, instance_delete_timer, instance_id, 0); + idata->keep_limit = static_cast(time(nullptr)) + db->limit; + idata->keep_timer = add_timer(gettick() + db->limit * 1000, instance_delete_timer, instance_id, 0); - switch(im->mode) { + switch(idata->mode) { case IM_NONE: break; case IM_CHAR: - if (map_charid2sd(im->owner_id) != NULL) // Notify player of the added instance timer - clif_instance_status(instance_id, im->keep_limit, im->idle_limit); + if (map_charid2sd(idata->owner_id)) // Notify player of the added instance timer + clif_instance_status(instance_id, idata->keep_limit, idata->idle_limit); break; case IM_PARTY: - if (party_search(im->owner_id) != NULL) // Notify party of the added instance timer - clif_instance_status(instance_id, im->keep_limit, im->idle_limit); + if (party_search(idata->owner_id)) // Notify party of the added instance timer + clif_instance_status(instance_id, idata->keep_limit, idata->idle_limit); break; case IM_GUILD: - if (guild_search(im->owner_id) != NULL) // Notify guild of the added instance timer - clif_instance_status(instance_id, im->keep_limit, im->idle_limit); + if (guild_search(idata->owner_id)) // Notify guild of the added instance timer + clif_instance_status(instance_id, idata->keep_limit, idata->idle_limit); break; case IM_CLAN: - if (clan_search(im->owner_id) != NULL) // Notify clan of the added instance timer - clif_instance_status(instance_id, im->keep_limit, im->idle_limit); + if (clan_search(idata->owner_id)) // Notify clan of the added instance timer + clif_instance_status(instance_id, idata->keep_limit, idata->idle_limit); break; default: - return 1; + return false; } - return 0; + return true; } -/*========================================== - * Creates idle timer - * Default before instance destroy is 5 minutes - *------------------------------------------*/ -static int instance_startidletimer(struct instance_data *im, unsigned short instance_id) +/** + * Creates an idle timer for an instance, default is 5 minutes + * @param idata: Instance data + * @param instance_id: Instance ID to notify + * @param True on success or false on failure + */ +bool instance_startidletimer(std::shared_ptr idata, int instance_id) { - struct instance_db *db; - - nullpo_retr(1, im); - // No current timer - if(im->idle_timer != INVALID_TIMER) - return 1; + if (!idata || idata->idle_timer != INVALID_TIMER) + return false; - if ((db = instance_searchtype_db(im->type)) == NULL) - return 1; + std::shared_ptr db = instance_db.find(idata->id); + + if (!db) + return false; // Add the timer - im->idle_limit = (unsigned int)time(NULL) + db->timeout; - im->idle_timer = add_timer(gettick() + db->timeout * 1000, instance_delete_timer, instance_id, 0); + idata->idle_limit = static_cast(time(nullptr)) + db->timeout; + idata->idle_timer = add_timer(gettick() + db->timeout * 1000, instance_delete_timer, instance_id, 0); - switch(im->mode) { + switch(idata->mode) { case IM_NONE: break; case IM_CHAR: - if (map_charid2sd(im->owner_id) != NULL && instance_searchtype_db(im->type) != NULL) // Notify player of added instance timer - clif_instance_status(instance_id, im->keep_limit, im->idle_limit); + if (map_charid2sd(idata->owner_id)) // Notify player of added instance timer + clif_instance_status(instance_id, idata->keep_limit, idata->idle_limit); break; case IM_PARTY: - if (party_search(im->owner_id) != NULL && instance_searchtype_db(im->type) != NULL) // Notify party of added instance timer - clif_instance_status(instance_id, im->keep_limit, im->idle_limit); + if (party_search(idata->owner_id)) // Notify party of added instance timer + clif_instance_status(instance_id, idata->keep_limit, idata->idle_limit); break; case IM_GUILD: - if (guild_search(im->owner_id) != NULL && instance_searchtype_db(im->type) != NULL) // Notify guild of added instance timer - clif_instance_status(instance_id, im->keep_limit, im->idle_limit); + if (guild_search(idata->owner_id)) // Notify guild of added instance timer + clif_instance_status(instance_id, idata->keep_limit, idata->idle_limit); break; case IM_CLAN: - if (clan_search(im->owner_id) != NULL && instance_searchtype_db(im->type) != NULL) // Notify clan of added instance timer - clif_instance_status(instance_id, im->keep_limit, im->idle_limit); + if (clan_search(idata->owner_id)) // Notify clan of added instance timer + clif_instance_status(instance_id, idata->keep_limit, idata->idle_limit); break; default: - return 1; + return false; } - return 0; + return true; } -/*========================================== - * Delete the idle timer - *------------------------------------------*/ -static int instance_stopidletimer(struct instance_data *im, unsigned short instance_id) +/** + * Remove the idle timer from an instance + * @param idata: Instace data + * @param instance_id: Instance ID to notify + * @return True on success or false on failure + */ +bool instance_stopidletimer(std::shared_ptr idata, int instance_id) { - nullpo_retr(0, im); - // No timer - if(im->idle_timer == INVALID_TIMER) - return 1; + if (!idata || idata->idle_timer == INVALID_TIMER) + return false; // Delete the timer - Party has returned or instance is destroyed - im->idle_limit = 0; - delete_timer(im->idle_timer, instance_delete_timer); - im->idle_timer = INVALID_TIMER; + idata->idle_limit = 0; + delete_timer(idata->idle_timer, instance_delete_timer); + idata->idle_timer = INVALID_TIMER; - switch(im->mode) { + switch(idata->mode) { case IM_NONE: break; case IM_CHAR: - if (map_charid2sd(im->owner_id) != NULL) // Notify the player - clif_instance_changestatus(instance_id, 0, im->idle_limit); + if (map_charid2sd(idata->owner_id)) // Notify the player + clif_instance_changestatus(instance_id, IN_NOTIFY, idata->idle_limit); break; case IM_PARTY: - if (party_search(im->owner_id) != NULL) // Notify the party - clif_instance_changestatus(instance_id, 0, im->idle_limit); + if (party_search(idata->owner_id)) // Notify the party + clif_instance_changestatus(instance_id, IN_NOTIFY, idata->idle_limit); break; case IM_GUILD: - if (guild_search(im->owner_id) != NULL) // Notify the guild - clif_instance_changestatus(instance_id, 0, im->idle_limit); + if (guild_search(idata->owner_id)) // Notify the guild + clif_instance_changestatus(instance_id, IN_NOTIFY, idata->idle_limit); break; case IM_CLAN: - if (clan_search(im->owner_id) != NULL) // Notify the clan - clif_instance_changestatus(instance_id, 0, im->idle_limit); + if (clan_search(idata->owner_id)) // Notify the clan + clif_instance_changestatus(instance_id, IN_NOTIFY, idata->idle_limit); break; default: - return 1; + return false; } - return 0; + return true; } -/*========================================== +/** * Run the OnInstanceInit events for duplicated NPCs - *------------------------------------------*/ + */ static int instance_npcinit(struct block_list *bl, va_list ap) { struct npc_data* nd; @@ -311,9 +475,9 @@ static int instance_npcinit(struct block_list *bl, va_list ap) return npc_instanceinit(nd); } -/*========================================== +/** * Run the OnInstanceDestroy events for duplicated NPCs - *------------------------------------------*/ + */ static int instance_npcdestroy(struct block_list *bl, va_list ap) { struct npc_data* nd; @@ -325,9 +489,9 @@ static int instance_npcdestroy(struct block_list *bl, va_list ap) return npc_instancedestroy(nd); } -/*========================================== - * Add an NPC to an instance - *------------------------------------------*/ +/** + * Update instance with new NPC + */ static int instance_addnpc_sub(struct block_list *bl, va_list ap) { struct npc_data* nd; @@ -339,352 +503,324 @@ static int instance_addnpc_sub(struct block_list *bl, va_list ap) return npc_duplicate4instance(nd, va_arg(ap, int)); } -// Separate function used for reloading -void instance_addnpc(struct instance_data *im) +/** + * Add an NPC to an instance + * @param idata: Instance data + */ +void instance_addnpc(std::shared_ptr idata) { - int i; - // First add the NPCs - for (i = 0; i < im->cnt_map; i++) { - struct map_data *mapdata = map_getmapdata(im->map[i]->src_m); + for (const auto &it : idata->map) { + struct map_data *mapdata = map_getmapdata(it.m); - map_foreachinallarea(instance_addnpc_sub, im->map[i]->src_m, 0, 0, mapdata->xs, mapdata->ys, BL_NPC, im->map[i]->m); + map_foreachinallarea(instance_addnpc_sub, it.src_m, 0, 0, mapdata->xs, mapdata->ys, BL_NPC, it.m); } // Now run their OnInstanceInit - for (i = 0; i < im->cnt_map; i++) { - struct map_data *mapdata = map_getmapdata(im->map[i]->m); - - map_foreachinallarea(instance_npcinit, im->map[i]->m, 0, 0, mapdata->xs, mapdata->ys, BL_NPC, im->map[i]->m); - } + for (const auto &it : idata->map) { + struct map_data *mapdata = map_getmapdata(it.m); + map_foreachinallarea(instance_npcinit, it.m, 0, 0, mapdata->xs, mapdata->ys, BL_NPC, it.m); + } } -/*-------------------------------------- - * name : instance name - * Return value could be - * -4 = no free instances | -3 = already exists | -2 = character/party/guild not found | -1 = invalid type - * On success return instance_id - *--------------------------------------*/ -int instance_create(int owner_id, const char *name, enum instance_mode mode) { - struct instance_db *db = instance_searchname_db(name); - struct map_session_data *sd = NULL; - struct party_data *pd = NULL; - struct guild *gd = NULL; - struct clan* cd = NULL; - unsigned short i; +/** + * Create an instance + * @param owner_id: Owner block ID + * @param name: Instance name + * @param mode: Instance mode + * @return -4 = no free instances | -3 = already exists | -2 = character/party/guild not found | -1 = invalid type | On success return instance_id + */ +int instance_create(int owner_id, const char *name, e_instance_mode mode) { + std::shared_ptr db = instance_search_db_name(name); - nullpo_retr(-1, db); + if (!db) { + ShowError("instance_create: Unknown instance %s creation was attempted.\n", name); + return -1; + } + + struct map_session_data *sd; + struct party_data *pd; + struct guild *gd; + struct clan* cd; switch(mode) { case IM_NONE: break; case IM_CHAR: - if ((sd = map_charid2sd(owner_id)) == NULL) { - ShowError("instance_create: character %d not found for instance '%s'.\n", owner_id, name); + if (!(sd = map_charid2sd(owner_id))) { + ShowError("instance_create: Character %d not found for instance '%s'.\n", owner_id, name); return -2; } - if (sd->instance_id) + if (sd->instance_id > 0) return -3; // Player already instancing break; case IM_PARTY: - if ((pd = party_search(owner_id)) == NULL) { - ShowError("instance_create: party %d not found for instance '%s'.\n", owner_id, name); + if (!(pd = party_search(owner_id))) { + ShowError("instance_create: Party %d not found for instance '%s'.\n", owner_id, name); return -2; } - if (pd->instance_id) + if (pd->instance_id > 0) return -3; // Party already instancing break; case IM_GUILD: - if ((gd = guild_search(owner_id)) == NULL) { - ShowError("instance_create: guild %d not found for instance '%s'.\n", owner_id, name); + if (!(gd = guild_search(owner_id))) { + ShowError("instance_create: Guild %d not found for instance '%s'.\n", owner_id, name); return -2; } - if (gd->instance_id) + if (gd->instance_id > 0) return -3; // Guild already instancing break; case IM_CLAN: - if ((cd = clan_search(owner_id)) == NULL) { - ShowError("instance_create: clan %d not found for instance '%s'.\n", owner_id, name); + if (!(cd = clan_search(owner_id))) { + ShowError("instance_create: Clan %d not found for instance '%s'.\n", owner_id, name); return -2; } - if (cd->instance_id) + if (cd->instance_id > 0) return -3; // Clan already instancing break; default: - ShowError("instance_create: unknown mode %u for owner_id %d and name %s.\n", mode, owner_id, name); + ShowError("instance_create: Unknown mode %u for owner_id %d and name %s.\n", mode, owner_id, name); return -2; } - // Searching a Free Instance - // 0 is ignored as this means "no instance" on maps - ARR_FIND(1, MAX_INSTANCE_DATA, i, instance_data[i].state == INSTANCE_FREE); - if( i >= MAX_INSTANCE_DATA ) + if (instance_count <= 0) return -4; - instance_data[i].type = db->id; - instance_data[i].state = INSTANCE_IDLE; - instance_data[i].owner_id = owner_id; - instance_data[i].mode = mode; - instance_data[i].keep_limit = 0; - instance_data[i].keep_timer = INVALID_TIMER; - instance_data[i].idle_limit = 0; - instance_data[i].idle_timer = INVALID_TIMER; - instance_data[i].regs.vars = i64db_alloc(DB_OPT_RELEASE_DATA); - instance_data[i].regs.arrays = NULL; - instance_data[i].cnt_map = 0; + int instance_id = instance_count++; + std::shared_ptr entry = std::make_shared(); + + entry->id = db->id; + entry->owner_id = owner_id; + entry->mode = mode; + entry->regs.vars = i64db_alloc(DB_OPT_RELEASE_DATA); + entry->regs.arrays = nullptr; + instances.insert({ instance_id, entry }); switch(mode) { case IM_CHAR: - sd->instance_id = i; + sd->instance_id = instance_id; break; case IM_PARTY: - pd->instance_id = i; + pd->instance_id = instance_id; break; case IM_GUILD: - gd->instance_id = i; + gd->instance_id = instance_id; break; case IM_CLAN: - cd->instance_id = i; + cd->instance_id = instance_id; break; } - instance_wait.id[instance_wait.count++] = i; - - clif_instance_create(i, instance_wait.count); - + instance_wait.id.push_back(instance_id); + clif_instance_create(instance_id, instance_wait.id.size()); instance_subscription_timer(0,0,0,0); - ShowInfo("[Instance] Created: %s (%hu).\n", name, i); + ShowInfo("[Instance] Created: %s (%d).\n", name, instance_id); // Start the instance timer on instance creation - instance_startkeeptimer(&instance_data[i], i); + instance_startkeeptimer(entry, instance_id); - return i; + return instance_id; } -/*-------------------------------------- +/** * Adds maps to the instance - *--------------------------------------*/ -int instance_addmap(unsigned short instance_id) { - int i, m; - struct instance_data *im; - struct instance_db *db; - struct s_instance_map *entry; - - if (instance_id == 0) + * @param instance_id: Instance ID to add map to + * @return 0 on failure or map count on success + */ +int instance_addmap(int instance_id) { + if (instance_id <= 0) return 0; - im = &instance_data[instance_id]; + std::shared_ptr idata = util::umap_find(instances, instance_id); // If the instance isn't idle, we can't do anything - if (im->state != INSTANCE_IDLE) + if (idata->state != INSTANCE_IDLE) return 0; - if ((db = instance_searchtype_db(im->type)) == NULL) + std::shared_ptr db = instance_db.find(idata->id); + + if (!db) return 0; // Set to busy, update timers - im->state = INSTANCE_BUSY; - im->idle_limit = (unsigned int)time(NULL) + db->timeout; - im->idle_timer = add_timer(gettick() + db->timeout * 1000, instance_delete_timer, instance_id, 0); + idata->state = INSTANCE_BUSY; + idata->idle_limit = static_cast(time(nullptr)) + db->timeout; + idata->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; - } + int16 m; // 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); + if ((m = map_addinstancemap(db->enter.map, instance_id)) < 0) { + ShowError("instance_addmap: Failed to create initial map for instance '%s' (%d).\n", db->name.c_str(), 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; + + struct s_instance_map entry; + + entry.m = m; + entry.src_m = db->enter.map; + idata->map.push_back(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 '%s' (%hu).\n", StringBuf_Value(db->name), instance_id); + for (const auto &it : db->maplist) { + if ((m = map_addinstancemap(it, instance_id)) < 0) { // An error occured adding a map + ShowError("instance_addmap: No maps added to instance '%s' (%d).\n", db->name.c_str(), instance_id); return 0; } else { - 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; + entry.m = m; + entry.src_m = it; + idata->map.push_back(entry); } } // Create NPCs on all maps - instance_addnpc(im); + instance_addnpc(idata); - switch(im->mode) { + switch(idata->mode) { case IM_NONE: break; case IM_CHAR: - if (map_charid2sd(im->owner_id) != NULL) // Inform player of the created instance - clif_instance_status(instance_id, im->keep_limit, im->idle_limit); + if (map_charid2sd(idata->owner_id)) // Inform player of the created instance + clif_instance_status(instance_id, idata->keep_limit, idata->idle_limit); break; case IM_PARTY: - if (party_search(im->owner_id) != NULL) // Inform party members of the created instance - clif_instance_status(instance_id, im->keep_limit, im->idle_limit); + if (party_search(idata->owner_id)) // Inform party members of the created instance + clif_instance_status(instance_id, idata->keep_limit, idata->idle_limit); break; case IM_GUILD: - if (guild_search(im->owner_id) != NULL) // Inform guild members of the created instance - clif_instance_status(instance_id, im->keep_limit, im->idle_limit); + if (guild_search(idata->owner_id)) // Inform guild members of the created instance + clif_instance_status(instance_id, idata->keep_limit, idata->idle_limit); break; case IM_CLAN: - if (clan_search(im->owner_id) != NULL) // Inform clan members of the created instance - clif_instance_status(instance_id, im->keep_limit, im->idle_limit); + if (clan_search(idata->owner_id)) // Inform clan members of the created instance + clif_instance_status(instance_id, idata->keep_limit, idata->idle_limit); break; default: return 0; } - return im->cnt_map; + return idata->map.size(); } - -/*========================================== - * Returns an instance map ID using a map name - * name : source map - * instance_id : where to search - * result : mapid of map "name" in this instance - *------------------------------------------*/ -int16 instance_mapname2mapid(const char *name, unsigned short instance_id) +/** + * Returns an instance map ID + * @param m: Source map ID + * @param instance_id: Instance to search + * @return Map ID in this instance + */ +int16 instance_mapid(int16 m, int instance_id) { - struct instance_data *im; - int16 m = map_mapname2mapid(name); - char iname[MAP_NAME_LENGTH]; - int i; - if(m < 0) { - ShowError("instance_mapname2mapid: map name %s does not exist.\n",name); - return m; + ShowError("instance_mapid: Map ID %d does not exist.\n", m); + return -1; } - strcpy(iname,name); + std::shared_ptr idata = util::umap_find(instances, instance_id); - if(instance_id == 0 || instance_id > MAX_INSTANCE_DATA) - return m; + if(!idata || idata->state != INSTANCE_BUSY) + return -1; - im = &instance_data[instance_id]; - if(im->state != INSTANCE_BUSY) - return m; + const char *iname = map_mapid2mapname(m); - for(i = 0; i < im->cnt_map; i++) - if(im->map[i]->src_m == m) { + for (const auto &it : idata->map) { + if (it.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),"%hu#%s", instance_id, iname); - } else - snprintf(alt_name, sizeof(alt_name),"%.3hu%s", instance_id, iname); + + if (!(strchr(iname, '@')) && strlen(iname) > 8) { + memmove((void*)iname, iname + (strlen(iname) - 9), strlen(iname)); + snprintf(alt_name, sizeof(alt_name), "%d#%s", instance_id, iname); + } + else + snprintf(alt_name, sizeof(alt_name), "%.3d%s", instance_id, iname); return map_mapname2mapid(alt_name); } + } return m; } -/*========================================== - * Removes a instance, all its maps and npcs. - *------------------------------------------*/ -int instance_destroy(unsigned short instance_id) +/** + * Removes an instance, all its maps, and NPCs. + * @param instance_id: Instance to remove + * @return True on sucess or false on failure + */ +bool instance_destroy(int instance_id) { - struct instance_data *im; - struct map_session_data *sd = NULL; - struct party_data *pd = NULL; - struct guild *gd = NULL; - struct clan *cd = NULL; - int i, type = 0; - unsigned int now = (unsigned int)time(NULL); - enum instance_mode mode; + std::shared_ptr idata = util::umap_find(instances, instance_id); - if(instance_id == 0 || instance_id > MAX_INSTANCE_DATA) - return 1; + if (!idata) + return false; - im = &instance_data[instance_id]; + struct map_session_data *sd; + struct party_data *pd; + struct guild *gd; + struct clan *cd; + e_instance_mode mode = idata->mode; + e_instance_notify type = IN_NOTIFY; - if(im->state == INSTANCE_FREE) - return 1; - - mode = im->mode; switch(mode) { case IM_NONE: break; case IM_CHAR: - sd = map_charid2sd(im->owner_id); + sd = map_charid2sd(idata->owner_id); break; case IM_PARTY: - pd = party_search(im->owner_id); + pd = party_search(idata->owner_id); break; case IM_GUILD: - gd = guild_search(im->owner_id); + gd = guild_search(idata->owner_id); break; case IM_CLAN: - cd = clan_search(im->owner_id); + cd = clan_search(idata->owner_id); break; } - if(im->state == INSTANCE_IDLE) { - for(i = 0; i < instance_wait.count; i++) { - if(instance_wait.id[i] == instance_id) { - instance_wait.count--; - memmove(&instance_wait.id[i],&instance_wait.id[i+1],sizeof(instance_wait.id[0])*(instance_wait.count-i)); - memset(&instance_wait.id[instance_wait.count], 0, sizeof(instance_wait.id[0])); + if(idata->state == INSTANCE_IDLE) { + for (auto instance_it = instance_wait.id.begin(); instance_it != instance_wait.id.end(); ++instance_it) { + if (*instance_it == instance_id) { + instance_wait.id.erase(instance_it); - for(i = 0; i < instance_wait.count; i++) - if(instance_data[instance_wait.id[i]].state == INSTANCE_IDLE) + for (int i = 0; i < instance_wait.id.size(); i++) { + if (util::umap_find(instances, instance_wait.id[i])->state == INSTANCE_IDLE) if ((mode == IM_CHAR && sd) || (mode == IM_PARTY && pd) || (mode == IM_GUILD && gd) || (mode == IM_CLAN && cd)) clif_instance_changewait(instance_id, i + 1); + } - if(instance_wait.count) - instance_wait.timer = add_timer(gettick()+INSTANCE_INTERVAL, instance_subscription_timer, 0, 0); + if (!instance_wait.id.empty()) + instance_wait.timer = add_timer(gettick() + INSTANCE_INTERVAL, instance_subscription_timer, 0, 0); else instance_wait.timer = INVALID_TIMER; - type = 0; break; } } } else { - if(im->keep_limit && im->keep_limit <= now) - type = 1; - else if(im->idle_limit && im->idle_limit <= now) - type = 2; + unsigned int now = static_cast(time(nullptr)); + + if(idata->keep_limit && idata->keep_limit <= now) + type = IN_DESTROY_LIVE_TIMEOUT; + else if(idata->idle_limit && idata->idle_limit <= now) + type = IN_DESTROY_ENTER_TIMEOUT; else - type = 3; + type = IN_DESTROY_USER_REQUEST; // Run OnInstanceDestroy on all NPCs in the instance - for(i = 0; i < im->cnt_map; i++){ - struct map_data *mapdata = map_getmapdata(im->map[i]->m); + for (const auto &it : idata->map) { + struct map_data *mapdata = map_getmapdata(it.m); - map_foreachinallarea(instance_npcdestroy, im->map[i]->m, 0, 0, mapdata->xs, mapdata->ys, BL_NPC, im->map[i]->m); + map_foreachinallarea(instance_npcdestroy, it.m, 0, 0, mapdata->xs, mapdata->ys, BL_NPC, it.m); + map_delinstancemap(it.m); } - - for(i = 0; i < im->cnt_map; i++) { - map_delinstancemap(im->map[i]->m); - ers_free(instance_maps_ers, im->map[i]); - } - im->cnt_map = 0; - aFree(im->map); - im->map = NULL; } - if(im->keep_timer != INVALID_TIMER) { - delete_timer(im->keep_timer, instance_delete_timer); - im->keep_timer = INVALID_TIMER; + if(idata->keep_timer != INVALID_TIMER) { + delete_timer(idata->keep_timer, instance_delete_timer); + idata->keep_timer = INVALID_TIMER; } - if(im->idle_timer != INVALID_TIMER) { - delete_timer(im->idle_timer, instance_delete_timer); - im->idle_timer = INVALID_TIMER; + if(idata->idle_timer != INVALID_TIMER) { + delete_timer(idata->idle_timer, instance_delete_timer); + idata->idle_timer = INVALID_TIMER; } if (mode == IM_CHAR && sd) @@ -697,60 +833,64 @@ int instance_destroy(unsigned short instance_id) cd->instance_id = 0; if (mode != IM_NONE) { - if(type) + if(type != IN_NOTIFY) clif_instance_changestatus(instance_id, type, 0); else clif_instance_changewait(instance_id, 0xffff); } - if( im->regs.vars ) { - db_destroy(im->regs.vars); - im->regs.vars = NULL; + if( idata->regs.vars ) { + db_destroy(idata->regs.vars); + idata->regs.vars = NULL; } - if( im->regs.arrays ) - instance_data[instance_id].regs.arrays->destroy(instance_data[instance_id].regs.arrays, script_free_array_db); + if( idata->regs.arrays ) + idata->regs.arrays->destroy(idata->regs.arrays, script_free_array_db); - ShowInfo("[Instance] Destroyed %hu.\n", instance_id); + ShowInfo("[Instance] Destroyed %d.\n", instance_id); - memset(&instance_data[instance_id], 0, sizeof(instance_data[instance_id])); + instances.erase(instance_id); - return 0; + return true; } -/*========================================== - * Warp a user into instance - *------------------------------------------*/ -enum e_instance_enter instance_enter(struct map_session_data *sd, unsigned short instance_id, const char *name, short x, short y) +/** + * Warp a user into an instance + * @param sd: Player to warp + * @param instance_id: Instance to warp to + * @param name: Map name + * @param x: X coordinate + * @param y: Y coordinate + * @return e_instance_enter value + */ +e_instance_enter instance_enter(struct map_session_data *sd, int instance_id, const char *name, short x, short y) { - struct instance_data *im = NULL; - struct instance_db *db = NULL; - struct party_data *pd = NULL; - struct guild *gd = NULL; - struct clan *cd = NULL; - enum instance_mode mode; - int16 m; - nullpo_retr(IE_OTHER, sd); - if( (db = instance_searchname_db(name)) == NULL ){ - ShowError( "instance_enter: Unknown instance \"%s\".\n", name ); + std::shared_ptr db = instance_search_db_name(name); + + if (!db) { + ShowError("instance_enter: Unknown instance \"%s\".\n", name); return IE_OTHER; } - + // If one of the two coordinates was not given or is below zero, we use the entry point from the database - if( x < 0 || y < 0 ){ + if (x < 0 || y < 0) { x = db->enter.x; y = db->enter.y; } - - // Check if it is a valid instance - if( instance_id == 0 ){ - // im will stay NULL and by default party checks will be used + + std::shared_ptr idata = nullptr; + struct party_data *pd; + struct guild *gd; + struct clan *cd; + e_instance_mode mode; + + if (instance_id <= 0) // Default party checks will be used mode = IM_PARTY; - }else{ - im = &instance_data[instance_id]; - mode = im->mode; + else { + idata = util::umap_find(instances, instance_id); + mode = idata->mode; } switch(mode) { @@ -759,48 +899,50 @@ enum e_instance_enter instance_enter(struct map_session_data *sd, unsigned short case IM_CHAR: if (sd->instance_id == 0) // Player must have an instance return IE_NOINSTANCE; - if (im->owner_id != sd->status.char_id) + if (idata->owner_id != sd->status.char_id) return IE_OTHER; break; case IM_PARTY: if (sd->status.party_id == 0) // Character must be in instance party return IE_NOMEMBER; - if ((pd = party_search(sd->status.party_id)) == NULL) + if (!(pd = party_search(sd->status.party_id))) return IE_NOMEMBER; - if (pd->instance_id == 0 || im == NULL) // Party must have an instance + if (pd->instance_id == 0 || idata == nullptr) // Party must have an instance return IE_NOINSTANCE; - if (im->owner_id != pd->party.party_id) + if (idata->owner_id != pd->party.party_id) return IE_OTHER; break; case IM_GUILD: if (sd->status.guild_id == 0) // Character must be in instance guild return IE_NOMEMBER; - if ((gd = guild_search(sd->status.guild_id)) == NULL) + if (!(gd = guild_search(sd->status.guild_id))) return IE_NOMEMBER; if (gd->instance_id == 0) // Guild must have an instance return IE_NOINSTANCE; - if (im->owner_id != gd->guild_id) + if (idata->owner_id != gd->guild_id) return IE_OTHER; break; case IM_CLAN: if (sd->status.clan_id == 0) // Character must be in instance clan return IE_NOMEMBER; - if ((cd = clan_search(sd->status.clan_id)) == NULL) + if (!(cd = clan_search(sd->status.clan_id))) return IE_NOMEMBER; if (cd->instance_id == 0) // Clan must have an instance return IE_NOINSTANCE; - if (im->owner_id != cd->id) + if (idata->owner_id != cd->id) return IE_OTHER; break; } - if (im->state != INSTANCE_BUSY) + if (idata->state != INSTANCE_BUSY) return IE_OTHER; - if (im->type != db->id) + if (idata->id != db->id) return IE_OTHER; + int16 m; + // Does the instance match? - if ((m = instance_mapname2mapid(StringBuf_Value(db->enter.mapname), instance_id)) < 0) + if ((m = instance_mapid(db->enter.map, instance_id)) < 0) return IE_OTHER; if (pc_setpos(sd, map_id2index(m), x, y, CLR_OUTSIGHT)) @@ -809,293 +951,120 @@ enum e_instance_enter instance_enter(struct map_session_data *sd, unsigned short return IE_OK; } -/*========================================== +/** * Request some info about the instance - *------------------------------------------*/ -int instance_reqinfo(struct map_session_data *sd, unsigned short instance_id) + * @param sd: Player to display info to + * @param instance_id: Instance to request + * @return True on success or false on failure + */ +bool instance_reqinfo(struct map_session_data *sd, int instance_id) { - struct instance_data *im; + nullpo_retr(false, sd); - nullpo_retr(1, sd); + std::shared_ptr idata = util::umap_find(instances, instance_id); - if(instance_id == 0 || instance_id > MAX_INSTANCE_DATA) - return 1; - - im = &instance_data[instance_id]; - - if(instance_searchtype_db(im->type) == NULL) - return 1; + if (!idata || !instance_db.find(idata->id)) + return false; // Say it's created if instance is not busy - if(im->state == INSTANCE_IDLE) { - int i; - - for(i = 0; i < instance_wait.count; i++) { - if(instance_wait.id[i] == instance_id) { + if(idata->state == INSTANCE_IDLE) { + for (int i = 0; i < instance_wait.id.size(); i++) { + if (instance_wait.id[i] == instance_id) { clif_instance_create(instance_id, i + 1); break; } } - } else if(im->state == INSTANCE_BUSY) // Give info on the instance if busy - clif_instance_status(instance_id, im->keep_limit, im->idle_limit); + } else if(idata->state == INSTANCE_BUSY) // Give info on the instance if busy + clif_instance_status(instance_id, idata->keep_limit, idata->idle_limit); - return 0; -} - -/*========================================== - * Add players to the instance (for timers) - *------------------------------------------*/ -int instance_addusers(unsigned short instance_id) -{ - struct instance_data *im; - - if(instance_id == 0 || instance_id > MAX_INSTANCE_DATA) - return 1; - - im = &instance_data[instance_id]; - if(im->state != INSTANCE_BUSY) - return 1; - - // Stop the idle timer if we had one - instance_stopidletimer(im, instance_id); - - // Start the instance keep timer - instance_startkeeptimer(im, instance_id); - - return 0; -} - -/*========================================== - * Delete players from the instance (for timers) - *------------------------------------------*/ -int instance_delusers(unsigned short instance_id) -{ - struct instance_data *im; - int i, users = 0; - - if(instance_id == 0 || instance_id > MAX_INSTANCE_DATA) - return 1; - - im = &instance_data[instance_id]; - if(im->state != INSTANCE_BUSY) - return 1; - - // If no one is in the instance, start the idle timer - for(i = 0; i < im->cnt_map && im->map[i]->m; i++) - users += max(map_getmapdata(im->map[i]->m)->users,0); - - // We check the actual map.users before being updated, hence the 1 - // The instance should be empty if users are now <= 1 - if(users <= 1) - instance_startidletimer(im, instance_id); - - return 0; -} - -static bool instance_db_free_sub(struct instance_db *db); - -/*========================================== - * Read the instance_db.txt file - *------------------------------------------*/ -static bool instance_readdb_sub(char* str[], int columns, int current) -{ - uint8 i,j; - char *ptr; - int id = strtol(str[0], &ptr, 10); - struct instance_db *db; - bool isNew = false; - - if (!id || id > USHRT_MAX || *ptr) { - ShowError("instance_readdb_sub: Cannot add instance with ID '%d'. Valid IDs are 1 ~ %d, skipping...\n", id, USHRT_MAX); - return false; - } - - if (mapindex_name2id(str[4]) == 0) { - ShowError("instance_readdb_sub: Invalid map '%s' as entrance map, skipping...\n", str[4]); - return false; - } - - if (!(db = (struct instance_db *)uidb_get(InstanceDB, id))) { - CREATE(db, struct instance_db, 1); - db->id = id; - db->name = StringBuf_Malloc(); - db->enter.mapname = StringBuf_Malloc(); - isNew = true; - } else { - StringBuf_Clear(db->name); - StringBuf_Clear(db->enter.mapname); - if (db->maplist_count) { - for (i = 0; i < db->maplist_count; i++) - StringBuf_Free(db->maplist[i]); - aFree(db->maplist); - db->maplist = NULL; - } - db->maplist_count = 0; - } - - StringBuf_AppendStr(db->name, str[1]); - - db->limit = strtol(str[2], &ptr, 10); - if (*ptr) { - ShowError("instance_readdb_sub: TimeLimit must be an integer value for instance '%d', skipping...\n", id); - instance_db_free_sub(db); - return false; - } - - db->timeout = strtol(str[3], &ptr, 10); - if (*ptr) { - ShowError("instance_readdb_sub: IdleTimeOut must be an integer value for instance '%d', skipping...\n", id); - instance_db_free_sub(db); - return false; - } - - StringBuf_AppendStr(db->enter.mapname, str[4]); - - db->enter.x = (short)strtol(str[5], &ptr, 10); - if (*ptr) { - ShowError("instance_readdb_sub: EnterX must be an integer value for instance '%d', skipping...\n", id); - instance_db_free_sub(db); - return false; - } - - db->enter.y = (short)strtol(str[6], &ptr, 10); - if (*ptr) { - ShowError("instance_readdb_sub: EnterY must be an integer value for instance '%d', skipping...\n", id); - instance_db_free_sub(db); - return false; - } - - //Instance maps - for (i = 7; i < columns; i++) { - if (strlen(str[i])) { - if (mapindex_name2id(str[i]) == 0) { - ShowWarning("instance_readdb_sub: Invalid map '%s' in maplist, skipping...\n", str[i]); - continue; - } - - if (strcmpi(str[4], str[i]) == 0) { - ShowWarning("instance_readdb_sub: '%s'(Map%d) must not be equal to EnterMap for instance id '%d', skipping...\n", str[i], i - 5, id); - continue; - } - - // Check if the map is in the list already - for (j = 7; j < i; j++) { - // Skip empty columns - if (!strlen(str[j])) { - continue; - } - - if (strcmpi(str[j], str[i]) == 0) { - break; - } - } - - // If it was already in the list - if (j < i) { - ShowWarning("instance_readdb_sub: '%s'(Map%d) was already added for instance id '%d', skipping...\n", str[i], i - 5, id); - continue; // Skip it - } - - RECREATE(db->maplist, StringBuf *, db->maplist_count+1); - db->maplist[db->maplist_count] = StringBuf_Malloc(); - StringBuf_AppendStr(db->maplist[db->maplist_count], str[i]); - db->maplist_count++; - } - } - - if (isNew) { - uidb_put(InstanceDB, id, db); - strdb_uiput(InstanceNameDB, StringBuf_Value(db->name), id); - } return true; } /** - * Free InstanceDB single entry - * @param db Instance Db entry - **/ -static bool instance_db_free_sub(struct instance_db *db) { - if (!db) - return 1; - StringBuf_Free(db->name); - StringBuf_Free(db->enter.mapname); - if (db->maplist_count) { - uint8 i; - for (i = 0; i < db->maplist_count; i++) - StringBuf_Free(db->maplist[i]); - aFree(db->maplist); - } - aFree(db); - return 0; + * Add players to the instance (for timers) -- Unused? + * @param instance_id: Instance to add + * @return True on success or false on failure + */ +bool instance_addusers(int instance_id) +{ + std::shared_ptr idata = util::umap_find(instances, instance_id); + + if(!idata || idata->state != INSTANCE_BUSY) + return false; + + // Stop the idle timer if we had one + instance_stopidletimer(idata, instance_id); + + // Start the instance keep timer + instance_startkeeptimer(idata, instance_id); + + return true; } /** - * Free InstanceDB entries - **/ -static int instance_db_free(DBKey key, DBData *data, va_list ap) { - struct instance_db *db = (struct instance_db *)db_data2ptr(data); - return instance_db_free_sub(db); + * Delete players from the instance (for timers) + * @param instance_id: Instance to remove + * @return True on success or false on failure + */ +bool instance_delusers(int instance_id) +{ + std::shared_ptr idata = util::umap_find(instances, instance_id); + + if(!idata || idata->state != INSTANCE_BUSY) + return false; + + int users = 0; + + // If no one is in the instance, start the idle timer + for (const auto &it : idata->map) + users += max(map_getmapdata(it.m)->users,0); + + // We check the actual map.users before being updated, hence the 1 + // The instance should be empty if users are now <= 1 + if(users <= 1) + instance_startidletimer(idata, instance_id); + + return true; } /** - * Read instance_db.txt files - **/ -void instance_readdb(void) { - const char* filename[] = { DBPATH"instance_db.txt", "import/instance_db.txt"}; - int f; - - for (f = 0; f 0); - } -} - -/** - * Reload Instance DB - **/ -void instance_reload(void) { - InstanceDB->clear(InstanceDB, instance_db_free); - db_clear(InstanceNameDB); - instance_readdb(); -} - -/*========================================== * Reloads the instance in runtime (reloadscript) - *------------------------------------------*/ + */ void do_reload_instance(void) { - struct instance_data *im; - struct instance_db *db = NULL; - struct s_mapiterator* iter; - struct map_session_data *sd; - unsigned short i; + for (const auto &it : instances) { + std::shared_ptr idata = it.second; - for( i = 1; i < MAX_INSTANCE_DATA; i++ ) { - im = &instance_data[i]; - if(!im->cnt_map) + if (!idata || idata->map.empty()) continue; else { // First we load the NPCs again - instance_addnpc(im); + instance_addnpc(idata); // Create new keep timer - if((db = instance_searchtype_db(im->type)) != NULL) - im->keep_limit = (unsigned int)time(NULL) + db->limit; + std::shared_ptr db = instance_db.find(idata->id); + + if (db) + idata->keep_limit = static_cast(time(nullptr)) + db->limit; } } // Reset player to instance beginning - iter = mapit_getallusers(); - for( sd = (TBL_PC*)mapit_first(iter); mapit_exists(iter); sd = (TBL_PC*)mapit_next(iter) ) { + struct s_mapiterator *iter = mapit_getallusers(); + struct map_session_data *sd; + + for (sd = (TBL_PC *)mapit_first(iter); mapit_exists(iter); sd = (TBL_PC *)mapit_next(iter)) { struct map_data *mapdata = map_getmapdata(sd->bl.m); - if(sd && mapdata->instance_id) { - struct party_data *pd = NULL; - struct guild *gd = NULL; - struct clan *cd = NULL; - unsigned short instance_id; + if (sd && mapdata->instance_id > 0) { + struct party_data *pd; + struct guild *gd; + struct clan *cd; + int instance_id; + std::shared_ptr idata = util::umap_find(instances, map[sd->bl.m].instance_id); + std::shared_ptr db = instance_db.find(idata->id); - im = &instance_data[mapdata->instance_id]; - switch (im->mode) { + switch (idata->mode) { case IM_NONE: continue; case IM_CHAR: @@ -1119,42 +1088,35 @@ void do_reload_instance(void) instance_id = cd->instance_id; break; default: - ShowError("do_reload_instance: Unexpected instance mode for instance %s (id=%u, mode=%u).\n", (db) ? StringBuf_Value(db->name) : "Unknown", mapdata->instance_id, (unsigned short)im->mode); + ShowError("do_reload_instance: Unexpected instance mode for instance %s (id=%d, mode=%u).\n", (db) ? db->name.c_str() : "Unknown", mapdata->instance_id, (uint8)idata->mode); continue; } - if((db = instance_searchtype_db(im->type)) != NULL && !instance_enter(sd, instance_id, StringBuf_Value(db->name), -1, -1)) { // All good - clif_displaymessage(sd->fd, msg_txt(sd,515)); // Instance has been reloaded - instance_reqinfo(sd,instance_id); + if (db && instance_enter(sd, instance_id, db->name.c_str(), -1, -1) == IE_OK) { // All good + clif_displaymessage(sd->fd, msg_txt(sd, 515)); // Instance has been reloaded + instance_reqinfo(sd, 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,StringBuf_Value(db->name)); + ShowError("do_reload_instance: Error setting character at instance start: character_id=%d instance=%s.\n", sd->status.char_id, db->name.c_str()); } } mapit_free(iter); } +/** + * Initializes the instance database + */ void do_init_instance(void) { - InstanceDB = uidb_alloc(DB_OPT_BASE); - InstanceNameDB = strdb_alloc((DBOptions)(DB_OPT_DUP_KEY|DB_OPT_RELEASE_DATA),0); - instance_start = map_num; - instance_readdb(); - memset(instance_data, 0, sizeof(instance_data)); - memset(&instance_wait, 0, sizeof(instance_wait)); - instance_wait.timer = -1; - - instance_maps_ers = ers_new(sizeof(struct s_instance_map),"instance.cpp::instance_maps_ers", ERS_OPT_NONE); + instance_db.load(); + instance_wait.timer = INVALID_TIMER; add_timer_func_list(instance_delete_timer,"instance_delete_timer"); add_timer_func_list(instance_subscription_timer,"instance_subscription_timer"); } +/** + * Finalizes the instances and instance database + */ void do_final_instance(void) { - int i; - - for( i = 1; i < MAX_INSTANCE_DATA; i++ ) - instance_destroy(i); - - InstanceDB->destroy(InstanceDB, instance_db_free); - db_destroy(InstanceNameDB); - ers_destroy(instance_maps_ers); + for (const auto &it : instances) + instance_destroy(it.first); } diff --git a/src/map/instance.hpp b/src/map/instance.hpp index 59ac747616..54edbe2da9 100644 --- a/src/map/instance.hpp +++ b/src/map/instance.hpp @@ -4,29 +4,32 @@ #ifndef INSTANCE_HPP #define INSTANCE_HPP -#include "../common/cbasetypes.hpp" -#include "../common/mmo.hpp" // struct point -#include "../common/strlib.hpp" +#include +#include +#include +#include +#include -#include "script.hpp" // struct reg_db +#include "../common/cbasetypes.hpp" +#include "../common/database.hpp" +#include "../common/mmo.hpp" + +#include "script.hpp" enum send_target : uint8; struct block_list; extern int16 instance_start; - -#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 255 // Max number of maps per instance (Enter map is counted as one) - Supports up to 255 maps +extern int instance_count; #define INSTANCE_NAME_LENGTH (60+1) -enum instance_state { - INSTANCE_FREE, +enum e_instance_state : uint8 { INSTANCE_IDLE, INSTANCE_BUSY }; -enum instance_mode { +enum e_instance_mode : uint8 { IM_NONE, IM_CHAR, IM_PARTY, @@ -35,69 +38,92 @@ enum instance_mode { IM_MAX, }; -enum e_instance_enter { - IE_OK = 0, +enum e_instance_enter : uint8 { + IE_OK, IE_NOMEMBER, IE_NOINSTANCE, IE_OTHER }; +enum e_instance_notify : uint8 { + IN_NOTIFY = 0, + IN_DESTROY_LIVE_TIMEOUT, + IN_DESTROY_ENTER_TIMEOUT, + IN_DESTROY_USER_REQUEST, + IN_CREATE_FAIL, +}; + 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 +/// Instance data +struct s_instance_data { + int id; ///< Instance DB ID + e_instance_state state; ///< State of instance + e_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 + int keep_timer; ///< Life time ID unsigned int idle_limit; ///< Idle time of instance - int idle_timer; ///< Remaining idle time of instance + int idle_timer; ///< Idle timer ID 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 + std::vector map; ///< Array of maps in instance + + s_instance_data() : + id(0), + state(INSTANCE_IDLE), + mode(IM_PARTY), + owner_id(0), + keep_limit(0), + keep_timer(INVALID_TIMER), + idle_limit(0), + idle_timer(INVALID_TIMER), + regs(), + map() { } }; -/// Instance DB entry struct -struct instance_db { - unsigned short id; ///< Instance ID - StringBuf *name; ///< Instance name - unsigned int limit, ///< Duration limit +/// Instance DB entry +struct s_instance_db { + int id; ///< Instance DB ID + std::string name; ///< Instance name + uint32 limit, ///< Duration limit timeout; ///< Timeout limit - struct s_MapInfo { - 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 + //bool destroyable; ///< Destroyable flag + struct point enter; ///< Instance entry point + std::vector maplist; ///< Maps in instance }; -extern struct instance_data instance_data[MAX_INSTANCE_DATA]; +class InstanceDatabase : public TypesafeYamlDatabase { +public: + InstanceDatabase() : TypesafeYamlDatabase("INSTANCE_DB", 1) { -struct instance_db *instance_searchtype_db(unsigned short instance_id); -struct instance_db *instance_searchname_db(const char* name); -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); -enum e_instance_enter instance_enter(struct map_session_data *sd, unsigned short instance_id, const char *name, 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); -int16 instance_mapname2mapid(const char *name, unsigned short instance_id); -int instance_addmap(unsigned short instance_id); + const std::string getDefaultLocation(); + uint64 parseBodyNode(const YAML::Node &node); +}; + +extern InstanceDatabase instance_db; + +extern std::unordered_map> instances; + +std::shared_ptr instance_search_db_name(const char* name); +void instance_getsd(int instance_id, struct map_session_data *&sd, enum send_target *target); + +int instance_create(int owner_id, const char *name, e_instance_mode mode); +bool instance_destroy(int instance_id); +e_instance_enter instance_enter(struct map_session_data *sd, int instance_id, const char *name, short x, short y); +bool instance_reqinfo(struct map_session_data *sd, int instance_id); +bool instance_addusers(int instance_id); +bool instance_delusers(int instance_id); +int16 instance_mapid(int16 m, int instance_id); +int instance_addmap(int instance_id); + +void instance_addnpc(std::shared_ptr idata); -void instance_addnpc(struct instance_data *im); -void instance_readdb(void); -void instance_reload(void); void do_reload_instance(void); void do_init_instance(void); void do_final_instance(void); -#if MAX_MAP_PER_INSTANCE > 255 - #error Too many maps per instance defined! Please adjust MAX_MAP_PER_INSTANCE to a lower value. -#endif - #endif /* INSTANCE_HPP */ diff --git a/src/map/map-server.vcxproj b/src/map/map-server.vcxproj index 721e354c7a..b4955a18a7 100644 --- a/src/map/map-server.vcxproj +++ b/src/map/map-server.vcxproj @@ -308,7 +308,7 @@ - + diff --git a/src/map/map.cpp b/src/map/map.cpp index 30747e0b45..5aab4518b8 100644 --- a/src/map/map.cpp +++ b/src/map/map.cpp @@ -2171,12 +2171,12 @@ int map_quit(struct map_session_data *sd) { struct map_data *mapdata = map_getmapdata(sd->bl.m); - if( mapdata->instance_id ) + if( mapdata->instance_id > 0 ) instance_delusers(mapdata->instance_id); unit_remove_map_pc(sd,CLR_RESPAWN); - if( mapdata->instance_id ) { // Avoid map conflicts and warnings on next login + if( mapdata->instance_id > 0 ) { // Avoid map conflicts and warnings on next login int16 m; struct point *pt; if( mapdata->save.map ) @@ -2694,18 +2694,16 @@ bool map_addnpc(int16 m,struct npc_data *nd) /*========================================== * Add an instance map *------------------------------------------*/ -int map_addinstancemap(const char *name, unsigned short instance_id) +int map_addinstancemap(int src_m, int instance_id) { - int16 src_m = map_mapname2mapid(name); - char iname[MAP_NAME_LENGTH]; - size_t num_cell, size; - if(src_m < 0) return -1; + const char *name = map_mapid2mapname(src_m); + if(strlen(name) > 20) { // against buffer overflow - ShowError("map_addisntancemap: can't add long map name \"%s\"\n", name); + ShowError("map_addinstancemap: can't add long map name \"%s\"\n", name); return -2; } @@ -2727,18 +2725,19 @@ int map_addinstancemap(const char *name, unsigned short instance_id) struct map_data *src_map = map_getmapdata(src_m); struct map_data *dst_map = map_getmapdata(dst_m); + char iname[MAP_NAME_LENGTH]; strcpy(iname, name); // Alter the name // Due to this being custom we only worry about preserving as many characters as necessary for accurate map distinguishing // This also allows us to maintain complete independence with main map functions - if((strchr(iname,'@') == NULL) && strlen(iname) > 8) { - memmove(iname, iname+(strlen(iname)-9), strlen(iname)); - snprintf(dst_map->name, sizeof(dst_map->name),"%hu#%s", instance_id, iname); + if ((strchr(iname, '@') == NULL) && strlen(iname) > 8) { + memmove(iname, iname + (strlen(iname) - 9), strlen(iname)); + snprintf(dst_map->name, sizeof(dst_map->name), "%d#%s", (instance_id % 1000), iname); } else - snprintf(dst_map->name, sizeof(dst_map->name),"%.3hu%s", instance_id, iname); - dst_map->name[MAP_NAME_LENGTH-1] = '\0'; + snprintf(dst_map->name, sizeof(dst_map->name), "%.3d%s", (instance_id % 1000), iname); + dst_map->name[MAP_NAME_LENGTH - 1] = '\0'; dst_map->m = dst_m; dst_map->instance_id = instance_id; @@ -2756,11 +2755,13 @@ int map_addinstancemap(const char *name, unsigned short instance_id) dst_map->npc_num_warp = 0; // Reallocate cells - num_cell = dst_map->xs * dst_map->ys; + size_t num_cell = dst_map->xs * dst_map->ys; + CREATE( dst_map->cell, struct mapcell, num_cell ); memcpy( dst_map->cell, src_map->cell, num_cell * sizeof(struct mapcell) ); - size = dst_map->bxs * dst_map->bys * sizeof(struct block_list*); + size_t size = dst_map->bxs * dst_map->bys * sizeof(struct block_list*); + dst_map->block = (struct block_list **)aCalloc(1,size); dst_map->block_mob = (struct block_list **)aCalloc(1,size); @@ -2829,7 +2830,7 @@ int map_delinstancemap(int m) { struct map_data *mapdata = map_getmapdata(m); - if(m < 0 || !mapdata->instance_id) + if(m < 0 || mapdata->instance_id <= 0) return 0; // Kick everyone out @@ -2972,7 +2973,7 @@ void map_removemobs(int16 m) return; //Mobs are already scheduled for removal // Don't remove mobs on instance map - if (mapdata->instance_id) + if (mapdata->instance_id > 0) return; mapdata->mob_delete_timer = add_timer(gettick()+battle_config.mob_remove_delay, map_removemobs_timer, m, 0); @@ -2991,17 +2992,15 @@ const char* map_mapid2mapname(int m) if (!mapdata) return ""; - if (mapdata->instance_id) { // Instance map check - struct instance_data *im = &instance_data[mapdata->instance_id]; + if (mapdata->instance_id > 0) { // Instance map check + std::shared_ptr idata = util::umap_find(instances, map[m].instance_id); - if (!im) // This shouldn't happen but if it does give them the map we intended to give + if (!idata) // This shouldn't happen but if it does give them the map we intended to give return mapdata->name; else { - uint8 i; - - 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; + for (const auto &it : idata->map) { // Loop to find the src map we want + if (it.m == m) + return map_getmapdata(it.src_m)->name; } } } diff --git a/src/map/map.hpp b/src/map/map.hpp index fc9bb7ee0c..7fa4a8b598 100644 --- a/src/map/map.hpp +++ b/src/map/map.hpp @@ -748,7 +748,7 @@ struct map_data { int mob_delete_timer; // Timer ID for map_removemobs_timer [Skotlex] // Instance Variables - unsigned short instance_id; + int instance_id; int instance_src_map; /* rAthena Local Chat */ @@ -1035,7 +1035,7 @@ void map_clearflooritem(struct block_list* bl); int map_addflooritem(struct item *item, int amount, int16 m, int16 x, int16 y, int first_charid, int second_charid, int third_charid, int flags, unsigned short mob_id, bool canShowEffect = false); // instances -int map_addinstancemap(const char *name, unsigned short instance_id); +int map_addinstancemap(int src_m, int instance_id); int map_delinstancemap(int m); void map_data_copyall(void); void map_data_copy(struct map_data *dst_map, struct map_data *src_map); diff --git a/src/map/npc.cpp b/src/map/npc.cpp index 39981bb254..d12e24df3a 100644 --- a/src/map/npc.cpp +++ b/src/map/npc.cpp @@ -16,6 +16,7 @@ #include "../common/showmsg.hpp" #include "../common/strlib.hpp" #include "../common/timer.hpp" +#include "../common/utilities.hpp" #include "../common/utils.hpp" #include "battle.hpp" @@ -33,6 +34,8 @@ #include "pet.hpp" #include "script.hpp" // script_config +using namespace rathena; + struct npc_data* fake_nd; // linked list of npc source files @@ -3489,7 +3492,7 @@ int npc_duplicate4instance(struct npc_data *snd, int16 m) { char newname[NPC_NAME_LENGTH+1]; struct map_data *mapdata = map_getmapdata(m); - if( mapdata->instance_id == 0 ) + if( mapdata->instance_id <= 0 ) return 1; snprintf(newname, ARRAYLENGTH(newname), "dup_%d_%d", mapdata->instance_id, snd->bl.id); @@ -3500,15 +3503,17 @@ int npc_duplicate4instance(struct npc_data *snd, int16 m) { if( snd->subtype == NPCTYPE_WARP ) { // Adjust destination, if instanced struct npc_data *wnd = NULL; // New NPC - struct instance_data *im = &instance_data[mapdata->instance_id]; - int dm = map_mapindex2mapid(snd->u.warp.mapindex), imap = 0, i; + std::shared_ptr idata = util::umap_find(instances, mapdata->instance_id); + int dm = map_mapindex2mapid(snd->u.warp.mapindex), imap = 0; + if( dm < 0 ) return 1; - for(i = 0; i < im->cnt_map; i++) - if(im->map[i]->m && map_mapname2mapid(map_getmapdata(im->map[i]->src_m)->name) == dm) { - imap = map_mapname2mapid(map_getmapdata(im->map[i]->m)->name); + for (const auto &it : idata->map) { + if (it.m && map_mapname2mapid(map_getmapdata(it.src_m)->name) == dm) { + imap = map_mapname2mapid(map_getmapdata(it.m)->name); break; // Instance map matches destination, update to instance map } + } if(!imap) imap = map_mapname2mapid(map_getmapdata(dm)->name); diff --git a/src/map/script.cpp b/src/map/script.cpp index e9e10a6437..087ba7246d 100644 --- a/src/map/script.cpp +++ b/src/map/script.cpp @@ -385,7 +385,7 @@ static struct linkdb_node *sleep_db; // int oid -> struct script_state * *------------------------------------------*/ const char* parse_subexpr(const char* p,int limit); int run_func(struct script_state *st); -unsigned short script_instancegetid(struct script_state *st, enum instance_mode mode = IM_NONE); +int script_instancegetid(struct script_state *st, e_instance_mode mode = IM_NONE); const char* script_op2name(int op) { @@ -2759,9 +2759,10 @@ struct script_data *get_val_(struct script_state* st, struct script_data* data, if (data->ref) n = data->ref->vars; else { - unsigned short instance_id = script_instancegetid(st); - if (instance_id != 0) - n = instance_data[instance_id].regs.vars; + std::shared_ptr idata = util::umap_find(instances, script_instancegetid(st)); + + if (idata) + n = idata->regs.vars; } if (n) data->u.str = (char*)i64db_get(n,reference_getuid(data)); @@ -2824,9 +2825,10 @@ struct script_data *get_val_(struct script_state* st, struct script_data* data, if (data->ref) n = data->ref->vars; else { - unsigned short instance_id = script_instancegetid(st); - if (instance_id != 0) - n = instance_data[instance_id].regs.vars; + std::shared_ptr idata = util::umap_find(instances, script_instancegetid(st)); + + if (idata) + n = idata->regs.vars; } if (n) data->u.num = i64db_i64get(n,reference_getuid(data)); @@ -3071,10 +3073,10 @@ struct reg_db *script_array_src(struct script_state *st, struct map_session_data if (ref) src = ref; else { - unsigned short instance_id = script_instancegetid(st); + std::shared_ptr idata = util::umap_find(instances, script_instancegetid(st)); - if (instance_id != 0) - src = &instance_data[instance_id].regs; + if (idata) + src = &idata->regs; } break; } @@ -3198,11 +3200,10 @@ bool set_reg_str( struct script_state* st, struct map_session_data* sd, int64 nu if( ref ){ src = ref; }else{ - unsigned short instance_id = script_instancegetid( st ); + std::shared_ptr idata = util::umap_find(instances, script_instancegetid(st)); - if( instance_id != 0 ){ - src = &instance_data[instance_id].regs; - } + if (idata) + src = &idata->regs; } if( src ){ @@ -3293,11 +3294,10 @@ bool set_reg_num( struct script_state* st, struct map_session_data* sd, int64 nu if( ref ){ src = ref; }else{ - unsigned short instance_id = script_instancegetid( st ); + std::shared_ptr idata = util::umap_find(instances, script_instancegetid(st)); - if( instance_id != 0 ){ - src = &instance_data[instance_id].regs; - } + if (idata) + src = &idata->regs; } if( src ){ @@ -20091,9 +20091,9 @@ BUILDIN_FUNC(bg_info) * @param mode: Instance mode * @return instance ID on success or 0 otherwise */ -unsigned short script_instancegetid(struct script_state* st, enum instance_mode mode) +int script_instancegetid(struct script_state* st, e_instance_mode mode) { - unsigned short instance_id = 0; + int instance_id = 0; if (mode == IM_NONE) { struct npc_data *nd = map_id2nd(st->oid); @@ -20106,27 +20106,27 @@ unsigned short script_instancegetid(struct script_state* st, enum instance_mode if (sd) { switch (mode) { case IM_CHAR: - if (sd->instance_id) + if (sd->instance_id > 0) instance_id = sd->instance_id; break; case IM_PARTY: { struct party_data *pd = party_search(sd->status.party_id); - if (pd && pd->instance_id) + if (pd && pd->instance_id > 0) instance_id = pd->instance_id; } break; case IM_GUILD: { struct guild *gd = guild_search(sd->status.guild_id); - if (gd && gd->instance_id) + if (gd && gd->instance_id > 0) instance_id = gd->instance_id; } break; case IM_CLAN: { struct clan *cd = clan_search(sd->status.clan_id); - if (cd && cd->instance_id) + if (cd && cd->instance_id > 0) instance_id = cd->instance_id; } break; @@ -20145,11 +20145,11 @@ unsigned short script_instancegetid(struct script_state* st, enum instance_mode *------------------------------------------*/ BUILDIN_FUNC(instance_create) { - enum instance_mode mode = IM_PARTY; + e_instance_mode mode = IM_PARTY; int owner_id = 0; if (script_hasdata(st, 3)) { - mode = static_cast(script_getnum(st, 3)); + mode = static_cast(script_getnum(st, 3)); if (mode < IM_NONE || mode >= IM_MAX) { ShowError("buildin_instance_create: Unknown instance mode %d for '%s'\n", mode, script_getstr(st, 2)); @@ -20200,7 +20200,7 @@ BUILDIN_FUNC(instance_create) *------------------------------------------*/ BUILDIN_FUNC(instance_destroy) { - unsigned short instance_id; + int instance_id; if( script_hasdata(st,2) ) instance_id = script_getnum(st,2); @@ -20208,7 +20208,7 @@ BUILDIN_FUNC(instance_destroy) instance_id = script_instancegetid(st); if( instance_id == 0 ) { - ShowError("buildin_instance_destroy: Trying to destroy invalid instance %hu.\n", instance_id); + ShowError("buildin_instance_destroy: Trying to destroy invalid instance %d.\n", instance_id); return SCRIPT_CMD_FAILURE; } @@ -20229,7 +20229,7 @@ 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; - unsigned short instance_id; + int instance_id; if (script_hasdata(st, 6)) instance_id = script_getnum(st, 6); @@ -20253,7 +20253,7 @@ BUILDIN_FUNC(instance_enter) BUILDIN_FUNC(instance_npcname) { const char *str; - unsigned short instance_id = 0; + int instance_id; struct npc_data *nd; str = script_getstr(st,2); @@ -20262,12 +20262,12 @@ BUILDIN_FUNC(instance_npcname) else instance_id = script_instancegetid(st); - if( instance_id && (nd = npc_name2id(str)) != NULL ) { + if( instance_id > 0 && (nd = npc_name2id(str)) != NULL ) { static char npcname[NAME_LENGTH]; - snprintf(npcname, sizeof(npcname), "dup_%hu_%d", instance_id, nd->bl.id); + snprintf(npcname, sizeof(npcname), "dup_%d_%d", instance_id, nd->bl.id); script_pushconststr(st,npcname); } else { - ShowError("buildin_instance_npcname: Invalid instance NPC (instance_id: %hu, NPC name: \"%s\".)\n", instance_id, str); + ShowError("buildin_instance_npcname: Invalid instance NPC (instance_id: %d, NPC name: \"%s\".)\n", instance_id, str); st->state = END; return SCRIPT_CMD_FAILURE; } @@ -20284,7 +20284,7 @@ BUILDIN_FUNC(instance_mapname) { const char *str; int16 m; - unsigned short instance_id = 0; + int instance_id; str = script_getstr(st,2); @@ -20294,7 +20294,7 @@ BUILDIN_FUNC(instance_mapname) instance_id = script_instancegetid(st); // Check that instance mapname is a valid map - if(!instance_id || (m = instance_mapname2mapid(str,instance_id)) < 0) + if(instance_id <= 0 || (m = instance_mapid(map_mapname2mapid(str), instance_id)) < 0) script_pushconststr(st, ""); else script_pushconststr(st, map_getmapdata(m)->name); @@ -20319,7 +20319,7 @@ BUILDIN_FUNC(instance_id) } } - script_pushint(st, script_instancegetid(st, static_cast(mode))); + script_pushint(st, script_instancegetid(st, static_cast(mode))); return SCRIPT_CMD_SUCCESS; } @@ -20333,9 +20333,8 @@ 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; + int instance_id = va_arg(ap, int); + struct map_session_data *sd; nullpo_retr(0, bl); @@ -20343,8 +20342,15 @@ static int buildin_instance_warpall_sub(struct block_list *bl, va_list ap) return 0; sd = (TBL_PC *)bl; - owner_id = instance_data[instance_id].owner_id; - switch(instance_data[instance_id].mode) { + + std::shared_ptr idata = util::umap_find(instances, instance_id); + + if (!idata) + return 0; + + int owner_id = idata->owner_id; + + switch(idata->mode) { case IM_NONE: break; case IM_CHAR: @@ -20370,8 +20376,8 @@ static int buildin_instance_warpall_sub(struct block_list *bl, va_list ap) BUILDIN_FUNC(instance_warpall) { - int16 m, i; - unsigned short instance_id; + int16 m; + int instance_id; const char *mapn; int x, y; @@ -20383,11 +20389,18 @@ BUILDIN_FUNC(instance_warpall) else instance_id = script_instancegetid(st, IM_PARTY); - if( !instance_id || (m = map_mapname2mapid(mapn)) < 0 || (m = instance_mapname2mapid(map_getmapdata(m)->name,instance_id)) < 0) + if( instance_id <= 0 || (m = map_mapname2mapid(mapn)) < 0 || (m = instance_mapid(m, instance_id)) < 0) return SCRIPT_CMD_FAILURE; - 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, instance_id); + std::shared_ptr idata = util::umap_find(instances, instance_id); + + if (!idata) { + ShowError("buildin_instance_warpall: Instance is not found.\n"); + return SCRIPT_CMD_FAILURE; + } + + for(const auto &it : idata->map) + map_foreachinmap(buildin_instance_warpall_sub, it.m, BL_PC, map_id2index(m), x, y, instance_id); return SCRIPT_CMD_SUCCESS; } @@ -20399,7 +20412,7 @@ BUILDIN_FUNC(instance_warpall) * Using 0 for will auto-detect the id. *------------------------------------------*/ BUILDIN_FUNC(instance_announce) { - unsigned short instance_id = script_getnum(st,2); + int instance_id = script_getnum(st,2); const char *mes = script_getstr(st,3); int flag = script_getnum(st,4); const char *fontColor = script_hasdata(st,5) ? script_getstr(st,5) : NULL; @@ -20407,20 +20420,19 @@ BUILDIN_FUNC(instance_announce) { int fontSize = script_hasdata(st,7) ? script_getnum(st,7) : 12; // default fontSize int fontAlign = script_hasdata(st,8) ? script_getnum(st,8) : 0; // default fontAlign int fontY = script_hasdata(st,9) ? script_getnum(st,9) : 0; // default fontY - int i; - if( instance_id == 0 ) { + if (instance_id <= 0) instance_id = script_instancegetid(st); - } - if( !instance_id && &instance_data[instance_id] != NULL) { - ShowError("buildin_instance_announce: Intance is not found.\n"); + std::shared_ptr idata = util::umap_find(instances, instance_id); + + if (instance_id <= 0 || !idata) { + ShowError("buildin_instance_announce: Instance not found.\n"); return SCRIPT_CMD_FAILURE; } - 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, - mes, strlen(mes)+1, flag&BC_COLOR_MASK, fontColor, fontType, fontSize, fontAlign, fontY); + for (const auto &it : idata->map) + map_foreachinmap(buildin_announce_sub, it.m, BL_PC, mes, strlen(mes)+1, flag&BC_COLOR_MASK, fontColor, fontType, fontSize, fontAlign, fontY); return SCRIPT_CMD_SUCCESS; } @@ -20438,7 +20450,7 @@ BUILDIN_FUNC(instance_announce) { BUILDIN_FUNC(instance_check_party) { int amount, min, max, i, party_id, c = 0; - struct party_data *p = NULL; + struct party_data *p; amount = script_hasdata(st,3) ? script_getnum(st,3) : 1; // Amount of needed Partymembers for the Instance. min = script_hasdata(st,4) ? script_getnum(st,4) : 1; // Minimum Level needed to join the Instance. @@ -20620,9 +20632,9 @@ BUILDIN_FUNC(instance_info) const char* name = script_getstr(st, 2); int type = script_getnum(st, 3); int index = 0; - struct instance_db *db = instance_searchname_db(name); + std::shared_ptr db = instance_search_db_name(name); - if( !db ){ + if (!db) { ShowError( "buildin_instance_info: Unknown instance name \"%s\".\n", name ); script_pushint(st, -1); return SCRIPT_CMD_FAILURE; @@ -20639,7 +20651,7 @@ BUILDIN_FUNC(instance_info) script_pushint(st, db->timeout); break; case IIT_ENTER_MAP: - script_pushstrcopy(st, StringBuf_Value(db->enter.mapname)); + script_pushstrcopy(st, map_mapid2mapname(db->enter.map)); break; case IIT_ENTER_X: script_pushint(st, db->enter.x); @@ -20648,7 +20660,7 @@ BUILDIN_FUNC(instance_info) script_pushint(st, db->enter.y); break; case IIT_MAPCOUNT: - script_pushint(st, db->maplist_count); + script_pushint(st, db->maplist.size()); break; case IIT_MAP: if( !script_hasdata(st, 4) || script_isstring(st, 4) ){ @@ -20671,7 +20683,7 @@ BUILDIN_FUNC(instance_info) return SCRIPT_CMD_FAILURE; } - script_pushstrcopy(st, StringBuf_Value(db->maplist[index])); + script_pushstrcopy(st, map_mapid2mapname(db->maplist[index])); break; default: @@ -20705,14 +20717,14 @@ BUILDIN_FUNC(instance_live_info) else id = script_getnum(st, 3); - struct instance_db *db = nullptr; - struct instance_data *im = nullptr; + std::shared_ptr db = nullptr; + std::shared_ptr im = nullptr; - if (id > 0 && id < MAX_INSTANCE_DATA) { - im = &instance_data[id]; + if (id > 0 && id < INT_MAX) { + im = util::umap_find(instances, id); if (im) - db = instance_searchtype_db(im->type); + db = instance_db.find(im->id); } if (!im || !db) { @@ -20725,7 +20737,7 @@ BUILDIN_FUNC(instance_live_info) switch( type ) { case ILI_NAME: - script_pushstrcopy(st, StringBuf_Value(db->name)); + script_pushstrcopy(st, db->name.c_str()); break; case ILI_MODE: script_pushint(st, im->mode); @@ -24533,16 +24545,16 @@ BUILDIN_FUNC(getvariableofinstance) return SCRIPT_CMD_FAILURE; } - unsigned short instance_id = script_getnum(st, 3); + int instance_id = script_getnum(st, 3); - if (instance_id == 0 || instance_id > MAX_INSTANCE_DATA) { + if (instance_id <= 0) { ShowError("buildin_getvariableofinstance: Invalid instance ID %d.\n", instance_id); script_pushnil(st); st->state = END; return SCRIPT_CMD_FAILURE; } - struct instance_data *im = &instance_data[instance_id]; + std::shared_ptr im = util::umap_find(instances, instance_id); if (im->state != INSTANCE_BUSY) { ShowError("buildin_getvariableofinstance: Unknown instance ID %d.\n", instance_id); diff --git a/src/tool/csv2yaml.cpp b/src/tool/csv2yaml.cpp index 2ed9d6c44e..1c621f5af3 100644 --- a/src/tool/csv2yaml.cpp +++ b/src/tool/csv2yaml.cpp @@ -51,6 +51,8 @@ using namespace rathena; +#define MAX_MAP_PER_INSTANCE 255 + #ifndef WIN32 int getch( void ){ struct termios oldattr, newattr; @@ -100,6 +102,7 @@ static bool skill_parse_row_copyabledb(char* split[], int columns, int current); static bool skill_parse_row_nonearnpcrangedb(char* split[], int columns, int current); static bool skill_parse_row_skilldb(char* split[], int columns, int current); static bool quest_read_db(char *split[], int columns, int current); +static bool instance_readdb_sub(char* str[], int columns, int current); // Constants for conversion std::unordered_map aegis_itemnames; @@ -360,6 +363,12 @@ int do_init( int argc, char** argv ){ return 0; } + if (process("INSTANCE_DB", 1, root_paths, "instance_db", [](const std::string& path, const std::string& name_ext) -> bool { + return sv_readdb(path.c_str(), name_ext.c_str(), ',', 7, 7 + MAX_MAP_PER_INSTANCE, -1, &instance_readdb_sub, false); + })) { + return 0; + } + // TODO: add implementations ;-) return 0; @@ -2537,3 +2546,43 @@ static bool quest_read_db(char *split[], int columns, int current) { return true; } + +// Copied and adjusted from instance.cpp +static bool instance_readdb_sub(char* str[], int columns, int current) { + body << YAML::BeginMap; + body << YAML::Key << "Id" << YAML::Value << atoi(str[0]); + body << YAML::Key << "Name" << YAML::Value << str[1]; + if (atoi(str[2]) != 3600) + body << YAML::Key << "TimeLimit" << YAML::Value << atoi(str[2]); + if (atoi(str[3]) != 300) + body << YAML::Key << "IdleTimeOut" << YAML::Value << atoi(str[3]); + body << YAML::Key << "Enter"; + body << YAML::BeginMap; + body << YAML::Key << "Map" << YAML::Value << str[4]; + body << YAML::Key << "X" << YAML::Value << atoi(str[5]); + body << YAML::Key << "Y" << YAML::Value << atoi(str[6]); + body << YAML::EndMap; + + if (columns > 7) { + body << YAML::Key << "AdditionalMaps"; + body << YAML::BeginSeq; + + for (int i = 7; i < columns; i++) { + if (!strlen(str[i])) + continue; + + if (strcmpi(str[4], str[i]) == 0) + continue; + + body << YAML::BeginMap; + body << YAML::Key << str[i] << YAML::Value << "true"; + body << YAML::EndMap; + } + + body << YAML::EndSeq; + } + + body << YAML::EndMap; + + return true; +}