diff options
author | shennetsind <ind@henn.et> | 2014-01-11 14:02:59 -0200 |
---|---|---|
committer | shennetsind <ind@henn.et> | 2014-01-11 14:02:59 -0200 |
commit | 82b583b5ef4e729ad2c3c74b26adce16a145605a (patch) | |
tree | 5bb20b90edd899b06abe9853dba06383a9379c06 | |
parent | 56649bda4b2f62cf42847830546b5856234b3178 (diff) | |
download | hercules-82b583b5ef4e729ad2c3c74b26adce16a145605a.tar.gz hercules-82b583b5ef4e729ad2c3c74b26adce16a145605a.tar.bz2 hercules-82b583b5ef4e729ad2c3c74b26adce16a145605a.tar.xz hercules-82b583b5ef4e729ad2c3c74b26adce16a145605a.zip |
Hercules 1st 2014 MegaPatch
http://hercules.ws/board/topic/3886-hercules-1st-2014-megapatch/
Signed-off-by: shennetsind <ind@henn.et>
34 files changed, 2144 insertions, 1274 deletions
diff --git a/conf/inter-server.conf b/conf/inter-server.conf index d4a75ee51..96d43973b 100644 --- a/conf/inter-server.conf +++ b/conf/inter-server.conf @@ -76,6 +76,8 @@ mysql_reconnect_count:1 //Shared interreg_db: interreg +global_acc_reg_num_db: global_acc_reg_num_db +global_acc_reg_str_db: global_acc_reg_str_db // Char Database Tables char_db: char @@ -85,7 +87,6 @@ cart_db: cart_inventory inventory_db: inventory charlog_db: charlog storage_db: storage -reg_db: global_reg_value skill_db: skill interlog_db: interlog memo_db: memo @@ -110,6 +111,10 @@ mercenary_owner_db: mercenary_owner ragsrvinfo_db: ragsrvinfo elemental_db: elemental account_data_db: account_data +acc_reg_num_db: acc_reg_num_db +acc_reg_str_db: acc_reg_str_db +char_reg_str_db: char_reg_str_db +char_reg_num_db: char_reg_num_db // Map Database Tables item_db_db: item_db diff --git a/sql-files/main.sql b/sql-files/main.sql index 30882546e..398bdcfe4 100644 --- a/sql-files/main.sql +++ b/sql-files/main.sql @@ -189,20 +189,6 @@ CREATE TABLE IF NOT EXISTS `hotkey` ( ) ENGINE=MyISAM; -- --- Table structure for table `global_reg_value` --- - -CREATE TABLE IF NOT EXISTS `global_reg_value` ( - `char_id` int(11) unsigned NOT NULL default '0', - `str` varchar(255) NOT NULL default '', - `value` varchar(255) NOT NULL default '0', - `type` tinyint(1) NOT NULL default '3', - `account_id` int(11) unsigned NOT NULL default '0', - PRIMARY KEY (`char_id`,`str`,`account_id`), - KEY `account_id` (`account_id`) -) ENGINE=MyISAM; - --- -- Table structure for table `guild` -- @@ -462,11 +448,10 @@ INSERT INTO `login` (`account_id`, `userid`, `user_pass`, `sex`, `email`) VALUES -- CREATE TABLE IF NOT EXISTS `mapreg` ( - `varname` varchar(32) NOT NULL, + `varname` varchar(32) BINARY NOT NULL, `index` int(11) unsigned NOT NULL default '0', `value` varchar(255) NOT NULL, - KEY `varname` (`varname`), - KEY `index` (`index`) + PRIMARY KEY (`varname`,`index`) ) ENGINE=MyISAM; -- @@ -672,6 +657,7 @@ INSERT INTO `sql_updates` (`timestamp`) VALUES (1383955424); INSERT INTO `sql_updates` (`timestamp`) VALUES (1384545461); INSERT INTO `sql_updates` (`timestamp`) VALUES (1384588175); INSERT INTO `sql_updates` (`timestamp`) VALUES (1387844126); +INSERT INTO `sql_updates` (`timestamp`) VALUES (1388854043); INSERT INTO `sql_updates` (`timestamp`) VALUES (1389028967); -- @@ -745,6 +731,85 @@ CREATE TABLE IF NOT EXISTS `npc_market_data` ( ) ENGINE=MyISAM; -- +-- Table structure for table `acc_reg_num_db` +-- + +CREATE TABLE IF NOT EXISTS `acc_reg_num_db` ( + `account_id` int(11) unsigned NOT NULL default '0', + `key` varchar(32) BINARY NOT NULL default '', + `index` int(11) unsigned NOT NULL default '0', + `value` int(11) NOT NULL default '0', + PRIMARY KEY (`account_id`,`key`,`index`), + KEY `account_id` (`account_id`) +) ENGINE=MyISAM; + +-- +-- Table structure for table `acc_reg_str_db` +-- + +CREATE TABLE IF NOT EXISTS `acc_reg_str_db` ( + `account_id` int(11) unsigned NOT NULL default '0', + `key` varchar(32) BINARY NOT NULL default '', + `index` int(11) unsigned NOT NULL default '0', + `value` varchar(254) NOT NULL default '0', + PRIMARY KEY (`account_id`,`key`,`index`), + KEY `account_id` (`account_id`) +) ENGINE=MyISAM; + +-- +-- Table structure for table `char_reg_num_db` +-- + +CREATE TABLE IF NOT EXISTS `char_reg_num_db` ( + `char_id` int(11) unsigned NOT NULL default '0', + `key` varchar(32) BINARY NOT NULL default '', + `index` int(11) unsigned NOT NULL default '0', + `value` int(11) NOT NULL default '0', + PRIMARY KEY (`char_id`,`key`,`index`), + KEY `char_id` (`char_id`) +) ENGINE=MyISAM; + +-- +-- Table structure for table `char_reg_str_db` +-- + +CREATE TABLE IF NOT EXISTS `char_reg_str_db` ( + `char_id` int(11) unsigned NOT NULL default '0', + `key` varchar(32) BINARY NOT NULL default '', + `index` int(11) unsigned NOT NULL default '0', + `value` varchar(254) NOT NULL default '0', + PRIMARY KEY (`char_id`,`key`,`index`), + KEY `char_id` (`char_id`) +) ENGINE=MyISAM; + +-- +-- Table structure for table `global_acc_reg_num_db` +-- + +CREATE TABLE IF NOT EXISTS `global_acc_reg_num_db` ( + `account_id` int(11) unsigned NOT NULL default '0', + `key` varchar(32) BINARY NOT NULL default '', + `index` int(11) unsigned NOT NULL default '0', + `value` int(11) NOT NULL default '0', + PRIMARY KEY (`account_id`,`key`,`index`), + KEY `account_id` (`account_id`) +) ENGINE=MyISAM; + +-- +-- Table structure for table `global_acc_reg_str_db` +-- + + +CREATE TABLE IF NOT EXISTS `global_acc_reg_str_db` ( + `account_id` int(11) unsigned NOT NULL default '0', + `key` varchar(32) BINARY NOT NULL default '', + `index` int(11) unsigned NOT NULL default '0', + `value` varchar(254) NOT NULL default '0', + PRIMARY KEY (`account_id`,`key`,`index`), + KEY `account_id` (`account_id`) +) ENGINE=MyISAM; + +-- -- Table structure for table `autotrade_merchants` -- diff --git a/sql-files/upgrades/2014-01-04--16-47.sql b/sql-files/upgrades/2014-01-04--16-47.sql new file mode 100644 index 000000000..a60ec1d24 --- /dev/null +++ b/sql-files/upgrades/2014-01-04--16-47.sql @@ -0,0 +1,61 @@ +#1388854043 +ALTER TABLE `mapreg` ADD PRIMARY KEY (`varname`, `index`); +ALTER TABLE `mapreg` DROP INDEX `varname`; +ALTER TABLE `mapreg` DROP INDEX `index`; +ALTER TABLE `mapreg` MODIFY `varname` varchar(32) BINARY NOT NULL; +CREATE TABLE IF NOT EXISTS `acc_reg_num_db` ( + `account_id` int(11) unsigned NOT NULL default '0', + `key` varchar(32) BINARY NOT NULL default '', + `index` int(11) unsigned NOT NULL default '0', + `value` int(11) NOT NULL default '0', + PRIMARY KEY (`account_id`,`key`,`index`), + KEY `account_id` (`account_id`) +) ENGINE=MyISAM; +CREATE TABLE IF NOT EXISTS `acc_reg_str_db` ( + `account_id` int(11) unsigned NOT NULL default '0', + `key` varchar(32) BINARY NOT NULL default '', + `index` int(11) unsigned NOT NULL default '0', + `value` varchar(254) NOT NULL default '0', + PRIMARY KEY (`account_id`,`key`,`index`), + KEY `account_id` (`account_id`) +) ENGINE=MyISAM; +CREATE TABLE IF NOT EXISTS `char_reg_num_db` ( + `char_id` int(11) unsigned NOT NULL default '0', + `key` varchar(32) BINARY NOT NULL default '', + `index` int(11) unsigned NOT NULL default '0', + `value` int(11) NOT NULL default '0', + PRIMARY KEY (`char_id`,`key`,`index`), + KEY `char_id` (`char_id`) +) ENGINE=MyISAM; +CREATE TABLE IF NOT EXISTS `char_reg_str_db` ( + `char_id` int(11) unsigned NOT NULL default '0', + `key` varchar(32) BINARY NOT NULL default '', + `index` int(11) unsigned NOT NULL default '0', + `value` varchar(254) NOT NULL default '0', + PRIMARY KEY (`char_id`,`key`,`index`), + KEY `char_id` (`char_id`) +) ENGINE=MyISAM; +CREATE TABLE IF NOT EXISTS `global_acc_reg_num_db` ( + `account_id` int(11) unsigned NOT NULL default '0', + `key` varchar(32) BINARY NOT NULL default '', + `index` int(11) unsigned NOT NULL default '0', + `value` int(11) NOT NULL default '0', + PRIMARY KEY (`account_id`,`key`,`index`), + KEY `account_id` (`account_id`) +) ENGINE=MyISAM; +CREATE TABLE IF NOT EXISTS `global_acc_reg_str_db` ( + `account_id` int(11) unsigned NOT NULL default '0', + `key` varchar(32) BINARY NOT NULL default '', + `index` int(11) unsigned NOT NULL default '0', + `value` varchar(254) NOT NULL default '0', + PRIMARY KEY (`account_id`,`key`,`index`), + KEY `account_id` (`account_id`) +) ENGINE=MyISAM; +INSERT INTO `acc_reg_num_db` (`account_id`, `key`, `index`, `value`) SELECT `account_id`, `str`, 0, `value` FROM `global_reg_value` WHERE `type` = 2 AND `str` NOT LIKE '%$'; +INSERT INTO `acc_reg_str_db` (`account_id`, `key`, `index`, `value`) SELECT `account_id`, `str`, 0, `value` FROM `global_reg_value` WHERE `type` = 2 AND `str` LIKE '%$'; +INSERT INTO `char_reg_num_db` (`char_id`, `key`, `index`, `value`) SELECT `char_id`, `str`, 0, `value` FROM `global_reg_value` WHERE `type` = 3 AND `str` NOT LIKE '%$'; +INSERT INTO `char_reg_str_db` (`char_id`, `key`, `index`, `value`) SELECT `char_id`, `str`, 0, `value` FROM `global_reg_value` WHERE `type` = 3 AND `str` LIKE '%$'; +INSERT INTO `global_acc_reg_num_db` (`account_id`, `key`, `index`, `value`) SELECT `account_id`, `str`, 0, `value` FROM `global_reg_value` WHERE `type` = 1 AND `str` NOT LIKE '%$'; +INSERT INTO `global_acc_reg_str_db` (`account_id`, `key`, `index`, `value`) SELECT `account_id`, `str`, 0, `value` FROM `global_reg_value` WHERE `type` = 1 AND `str` LIKE '%$'; +#DROP TABLE `global_reg_value`; +INSERT INTO `sql_updates` (`timestamp`) VALUES (1388854043);
\ No newline at end of file diff --git a/sql-files/upgrades/index.txt b/sql-files/upgrades/index.txt index ce75fa882..bf404d919 100644 --- a/sql-files/upgrades/index.txt +++ b/sql-files/upgrades/index.txt @@ -16,4 +16,5 @@ 2013-11-16--07-49.sql 2013-11-18--08-23.sql 2013-12-24--00-15.sql +2014-01-04--16-47.sql 2014-01-06--17-22.sql
\ No newline at end of file diff --git a/src/char/char.c b/src/char/char.c index 9b3f1443d..a6588dc09 100644 --- a/src/char/char.c +++ b/src/char/char.c @@ -45,7 +45,6 @@ char inventory_db[256] = "inventory"; char charlog_db[256] = "charlog"; char storage_db[256] = "storage"; char interlog_db[256] = "interlog"; -char reg_db[256] = "global_reg_value"; char skill_db[256] = "skill"; char memo_db[256] = "memo"; char guild_db[256] = "guild"; @@ -71,6 +70,10 @@ char ragsrvinfo_db[256] = "ragsrvinfo"; char elemental_db[256] = "elemental"; char interreg_db[32] = "interreg"; char account_data_db[256] = "account_data"; +char acc_reg_num_db[32] = "acc_reg_num_db"; +char acc_reg_str_db[32] = "acc_reg_str_db"; +char char_reg_str_db[32] = "char_reg_str_db"; +char char_reg_num_db[32] = "char_reg_num_db"; // show loading/saving messages int save_log = 1; @@ -1793,7 +1796,9 @@ int delete_char_sql(int char_id) Sql_ShowDebug(sql_handle); /* delete character registry */ - if( SQL_ERROR == SQL->Query(sql_handle, "DELETE FROM `%s` WHERE `type`=3 AND `char_id`='%d'", reg_db, char_id) ) + if( SQL_ERROR == SQL->Query(sql_handle, "DELETE FROM `%s` WHERE `char_id`='%d'", char_reg_str_db, char_id) ) + Sql_ShowDebug(sql_handle); + if( SQL_ERROR == SQL->Query(sql_handle, "DELETE FROM `%s` WHERE `char_id`='%d'", char_reg_num_db, char_id) ) Sql_ShowDebug(sql_handle); /* delete skills */ @@ -2460,17 +2465,12 @@ int parse_fromlogin(int fd) { break; // reply to an account_reg2 registry request - case 0x2729: + case 0x3804: if (RFIFOREST(fd) < 4 || RFIFOREST(fd) < RFIFOW(fd,2)) return 0; - - { //Receive account_reg2 registry, forward to map servers. - unsigned char buf[13+ACCOUNT_REG2_NUM*sizeof(struct global_reg)]; - memcpy(buf,RFIFOP(fd,0), RFIFOW(fd,2)); - WBUFW(buf,0) = 0x3804; //Map server can now receive all kinds of reg values with the same packet. [Skotlex] - mapif_sendall(buf, WBUFW(buf,2)); + //Receive account_reg2 registry, forward to map servers. + mapif_sendall(RFIFOP(fd, 0), RFIFOW(fd,2)); RFIFOSKIP(fd, RFIFOW(fd,2)); - } break; // State change of account/ban notification (from login-server) @@ -2603,21 +2603,76 @@ int request_accreg2(int account_id, int char_id) } return 0; } - -//Send packet forward to login-server for account saving -int save_accreg2(unsigned char* buf, int len) -{ - if (login_fd > 0) { - WFIFOHEAD(login_fd,len+4); - memcpy(WFIFOP(login_fd,4), buf, len); - WFIFOW(login_fd,0) = 0x2728; - WFIFOW(login_fd,2) = len+4; - WFIFOSET(login_fd,len+4); - return 1; +/** + * Handles global account reg saving that continues with global_accreg_to_login_add and global_accreg_to_send + **/ +void global_accreg_to_login_start (int account_id, int char_id) { + WFIFOHEAD(login_fd, 60000 + 300); + WFIFOW(login_fd,0) = 0x2728; + WFIFOW(login_fd,2) = 14; + WFIFOL(login_fd,4) = account_id; + WFIFOL(login_fd,8) = char_id; + WFIFOW(login_fd,12) = 0;/* count */ +} +/** + * Completes global account reg saving that starts global_accreg_to_login_start and continues with global_accreg_to_login_add + **/ +void global_accreg_to_login_send (void) { + WFIFOSET(login_fd, WFIFOW(login_fd,2)); +} +/** + * Handles global account reg saving that starts global_accreg_to_login_start and ends with global_accreg_to_send + **/ +void global_accreg_to_login_add (const char *key, unsigned int index, intptr_t val, bool is_string) { + int nlen = WFIFOW(login_fd, 2); + size_t len; + + len = strlen(key)+1; + + WFIFOB(login_fd, nlen) = (unsigned char)len;/* won't be higher; the column size is 32 */ + nlen += 1; + + safestrncpy((char*)WFIFOP(login_fd,nlen), key, len); + nlen += len; + + WFIFOL(login_fd, nlen) = index; + nlen += 4; + + if( is_string ) { + WFIFOB(login_fd, nlen) = val ? 2 : 3; + nlen += 1; + + if( val ) { + char *sval = (char*)val; + len = strlen(sval)+1; + + WFIFOB(login_fd, nlen) = (unsigned char)len;/* won't be higher; the column size is 254 */ + nlen += 1; + + safestrncpy((char*)WFIFOP(login_fd,nlen), sval, len); + nlen += len; + } + + } else { + WFIFOB(login_fd, nlen) = val ? 0 : 1; + nlen += 1; + + if( val ) { + WFIFOL(login_fd, nlen) = (int)val; + nlen += 4; + } + + } + + WFIFOW(login_fd,12) += 1; + + WFIFOW(login_fd, 2) = nlen; + if( WFIFOW(login_fd, 2) > 60000 ) { + int account_id = WFIFOL(login_fd,4), char_id = WFIFOL(login_fd,8); + global_accreg_to_login_send(); + global_accreg_to_login_start(account_id,char_id);/* prepare next */ } - return 0; } - void char_read_fame_list(void) { int i; char* data; @@ -4901,8 +4956,6 @@ void sql_config_read(const char* cfgName) safestrncpy(charlog_db, w2, sizeof(charlog_db)); else if(!strcmpi(w1,"storage_db")) safestrncpy(storage_db, w2, sizeof(storage_db)); - else if(!strcmpi(w1,"reg_db")) - safestrncpy(reg_db, w2, sizeof(reg_db)); else if(!strcmpi(w1,"skill_db")) safestrncpy(skill_db, w2, sizeof(skill_db)); else if(!strcmpi(w1,"interlog_db")) @@ -4955,6 +5008,15 @@ void sql_config_read(const char* cfgName) safestrncpy(interreg_db,w2,sizeof(interreg_db)); else if(!strcmpi(w1,"account_data_db")) safestrncpy(account_data_db,w2,sizeof(account_data_db)); + else if(!strcmpi(w1,"char_reg_num_db")) + safestrncpy(char_reg_num_db, w2, sizeof(char_reg_num_db)); + else if(!strcmpi(w1,"char_reg_str_db")) + safestrncpy(char_reg_str_db, w2, sizeof(char_reg_str_db)); + else if(!strcmpi(w1,"acc_reg_str_db")) + safestrncpy(acc_reg_str_db, w2, sizeof(acc_reg_str_db)); + else if(!strcmpi(w1,"acc_reg_num_db")) + safestrncpy(acc_reg_num_db, w2, sizeof(acc_reg_num_db)); + //support the import command, just like any other config else if(!strcmpi(w1,"import")) sql_config_read(w2); diff --git a/src/char/char.h b/src/char/char.h index c6aa1b66d..3e3774c1f 100644 --- a/src/char/char.h +++ b/src/char/char.h @@ -1,5 +1,6 @@ -// Copyright (c) Athena Dev Teams - Licensed under GNU GPL -// For more information, see LICENCE in the main folder +// Copyright (c) Hercules Dev Team, licensed under GNU GPL. +// See the LICENSE file +// Portions Copyright (c) Athena Dev Teams #ifndef _CHAR_SQL_H_ #define _CHAR_SQL_H_ @@ -68,7 +69,6 @@ int char_child(int parent_id, int child_id); int char_family(int pl1,int pl2,int pl3); int request_accreg2(int account_id, int char_id); -int save_accreg2(unsigned char* buf, int len); int login_fd; extern int char_name_option; extern char char_name_letters[]; @@ -83,7 +83,6 @@ extern char inventory_db[256]; extern char charlog_db[256]; extern char storage_db[256]; extern char interlog_db[256]; -extern char reg_db[256]; extern char skill_db[256]; extern char memo_db[256]; extern char guild_db[256]; @@ -106,6 +105,10 @@ extern char mercenary_owner_db[256]; extern char ragsrvinfo_db[256]; extern char elemental_db[256]; extern char interreg_db[32]; +extern char acc_reg_num_db[32]; +extern char acc_reg_str_db[32]; +extern char char_reg_str_db[32]; +extern char char_reg_num_db[32]; extern int db_use_sql_item_db; extern int db_use_sql_mob_db; @@ -114,4 +117,8 @@ extern int db_use_sql_mob_skill_db; extern int guild_exp_rate; extern int log_inter; +void global_accreg_to_login_start (int account_id, int char_id); +void global_accreg_to_login_send (void); +void global_accreg_to_login_add (const char *key, unsigned int index, intptr_t val, bool is_string); + #endif /* _CHAR_SQL_H_ */ diff --git a/src/char/inter.c b/src/char/inter.c index 63e1564ff..34ecebd36 100644 --- a/src/char/inter.c +++ b/src/char/inter.c @@ -42,7 +42,6 @@ char char_server_pw[32] = "ragnarok"; char char_server_db[32] = "ragnarok"; char default_codepage[32] = ""; //Feature by irmin. -static struct accreg *accreg_pt; unsigned int party_share_level = 10; // recv. packet list @@ -592,127 +591,225 @@ void mapif_parse_accinfo(int fd) { return; } -//-------------------------------------------------------- -// Save registry to sql -int inter_accreg_tosql(int account_id, int char_id, struct accreg* reg, int type) -{ - struct global_reg* r; - StringBuf buf; - int i; - - if( account_id <= 0 ) - return 0; - reg->account_id = account_id; - reg->char_id = char_id; - - //`global_reg_value` (`type`, `account_id`, `char_id`, `str`, `value`) - switch( type ) - { - case 3: //Char Reg - if( SQL_ERROR == SQL->Query(sql_handle, "DELETE FROM `%s` WHERE `type`=3 AND `char_id`='%d'", reg_db, char_id) ) - Sql_ShowDebug(sql_handle); - account_id = 0; - break; - case 2: //Account Reg - if( SQL_ERROR == SQL->Query(sql_handle, "DELETE FROM `%s` WHERE `type`=2 AND `account_id`='%d'", reg_db, account_id) ) - Sql_ShowDebug(sql_handle); - char_id = 0; - break; - case 1: //Account2 Reg - ShowError("inter_accreg_tosql: Char server shouldn't handle type 1 registry values (##). That is the login server's work!\n"); - return 0; - default: - ShowError("inter_accreg_tosql: Invalid type %d\n", type); - return 0; - } - - if( reg->reg_num <= 0 ) - return 0; - - StrBuf->Init(&buf); - StrBuf->Printf(&buf, "INSERT INTO `%s` (`type`,`account_id`,`char_id`,`str`,`value`) VALUES ", reg_db); - - for( i = 0; i < reg->reg_num; ++i ) { - r = ®->reg[i]; - if( r->str[0] != '\0' && r->value[0] != '\0' ) { - char str[32]; - char val[256]; - - if( i > 0 ) - StrBuf->AppendStr(&buf, ","); - - SQL->EscapeString(sql_handle, str, r->str); - SQL->EscapeString(sql_handle, val, r->value); - - StrBuf->Printf(&buf, "('%d','%d','%d','%s','%s')", type, account_id, char_id, str, val); +/** + * Handles save reg data from map server and distributes accordingly. + * + * @param val either str or int, depending on type + * @param type false when int, true otherwise + **/ +void inter_savereg(int account_id, int char_id, const char *key, unsigned int index, intptr_t val, bool is_string) { + /* to login server we go! */ + if( key[0] == '#' && key[1] == '#' ) {/* global account reg */ + global_accreg_to_login_add(key,index,val,is_string); + } else if ( key[0] == '#' ) {/* local account reg */ + if( is_string ) { + if( val ) { + if( SQL_ERROR == SQL->Query(sql_handle, "REPLACE INTO `%s` (`account_id`,`key`,`index`,`value`) VALUES ('%d','%s','%u','%s')", acc_reg_str_db, account_id, key, index, (char*)val) ) + Sql_ShowDebug(sql_handle); + } else { + if( SQL_ERROR == SQL->Query(sql_handle, "DELETE FROM `%s` WHERE `account_id` = '%d' AND `key` = '%s' AND `index` = '%u' LIMIT 1", acc_reg_str_db, account_id, key, index) ) + Sql_ShowDebug(sql_handle); + } + } else { + if( val ) { + if( SQL_ERROR == SQL->Query(sql_handle, "REPLACE INTO `%s` (`account_id`,`key`,`index`,`value`) VALUES ('%d','%s','%u','%d')", acc_reg_num_db, account_id, key, index, (int)val) ) + Sql_ShowDebug(sql_handle); + } else { + if( SQL_ERROR == SQL->Query(sql_handle, "DELETE FROM `%s` WHERE `account_id` = '%d' AND `key` = '%s' AND `index` = '%u' LIMIT 1", acc_reg_num_db, account_id, key, index) ) + Sql_ShowDebug(sql_handle); + } + } + } else { /* char reg */ + if( is_string ) { + if( val ) { + if( SQL_ERROR == SQL->Query(sql_handle, "REPLACE INTO `%s` (`char_id`,`key`,`index`,`value`) VALUES ('%d','%s','%u','%s')", char_reg_str_db, char_id, key, index, (char*)val) ) + Sql_ShowDebug(sql_handle); + } else { + if( SQL_ERROR == SQL->Query(sql_handle, "DELETE FROM `%s` WHERE `char_id` = '%d' AND `key` = '%s' AND `index` = '%u' LIMIT 1", char_reg_str_db, char_id, key, index) ) + Sql_ShowDebug(sql_handle); + } + } else { + if( val ) { + if( SQL_ERROR == SQL->Query(sql_handle, "REPLACE INTO `%s` (`char_id`,`key`,`index`,`value`) VALUES ('%d','%s','%u','%d')", char_reg_num_db, char_id, key, index, (int)val) ) + Sql_ShowDebug(sql_handle); + } else { + if( SQL_ERROR == SQL->Query(sql_handle, "DELETE FROM `%s` WHERE `char_id` = '%d' AND `key` = '%s' AND `index` = '%u' LIMIT 1", char_reg_num_db, char_id, key, index) ) + Sql_ShowDebug(sql_handle); + } } } - - if( SQL_ERROR == SQL->QueryStr(sql_handle, StrBuf->Value(&buf)) ) { - Sql_ShowDebug(sql_handle); - } - - StrBuf->Destroy(&buf); - - return 1; + } // Load account_reg from sql (type=2) -int inter_accreg_fromsql(int account_id,int char_id, struct accreg *reg, int type) +int inter_accreg_fromsql(int account_id,int char_id, int fd, int type) { - struct global_reg* r; char* data; size_t len; - int i; + unsigned int plen = 0; - if( reg == NULL) - return 0; - - memset(reg, 0, sizeof(struct accreg)); - reg->account_id = account_id; - reg->char_id = char_id; + switch( type ) { + case 3: //char reg + if( SQL_ERROR == SQL->Query(sql_handle, "SELECT `key`, `index`, `value` FROM `%s` WHERE `char_id`='%d'", char_reg_str_db, char_id) ) + Sql_ShowDebug(sql_handle); + break; + case 2: //account reg + if( SQL_ERROR == SQL->Query(sql_handle, "SELECT `key`, `index`, `value` FROM `%s` WHERE `account_id`='%d'", acc_reg_str_db, account_id) ) + Sql_ShowDebug(sql_handle); + break; + case 1: //account2 reg + ShowError("inter_accreg_fromsql: Char server shouldn't handle type 1 registry values (##). That is the login server's work!\n"); + return 0; + default: + ShowError("inter_accreg_fromsql: Invalid type %d\n", type); + return 0; + } + + WFIFOHEAD(fd, 60000 + 300); + WFIFOW(fd, 0) = 0x3804; + /* 0x2 = length, set prior to being sent */ + WFIFOL(fd, 4) = account_id; + WFIFOL(fd, 8) = char_id; + WFIFOB(fd, 12) = 0;/* var type (only set when all vars have been sent, regardless of type) */ + WFIFOB(fd, 13) = 1;/* is string type */ + WFIFOW(fd, 14) = 0;/* count */ + plen = 16; + + /** + * Vessel! + * + * str type + * { keyLength(B), key(<keyLength>), index(L), valLength(B), val(<valLength>) } + **/ + while ( SQL_SUCCESS == SQL->NextRow(sql_handle) ) { + + SQL->GetData(sql_handle, 0, &data, NULL); + len = strlen(data)+1; + + WFIFOB(fd, plen) = (unsigned char)len;/* won't be higher; the column size is 32 */ + plen += 1; + + safestrncpy((char*)WFIFOP(fd,plen), data, len); + plen += len; + + SQL->GetData(sql_handle, 1, &data, NULL); + + WFIFOL(fd, plen) = (unsigned int)atol(data); + plen += 4; + + SQL->GetData(sql_handle, 2, &data, NULL); + len = strlen(data)+1; + + WFIFOB(fd, plen) = (unsigned char)len;/* won't be higher; the column size is 254 */ + plen += 1; + + safestrncpy((char*)WFIFOP(fd,plen), data, len); + plen += len; + + WFIFOW(fd, 14) += 1; + + if( plen > 60000 ) { + WFIFOW(fd, 2) = plen; + WFIFOSET(fd, plen); + + /* prepare follow up */ + WFIFOHEAD(fd, 60000 + 300); + WFIFOW(fd, 0) = 0x3804; + /* 0x2 = length, set prior to being sent */ + WFIFOL(fd, 4) = account_id; + WFIFOL(fd, 8) = char_id; + WFIFOB(fd, 12) = 0;/* var type (only set when all vars have been sent, regardless of type) */ + WFIFOB(fd, 13) = 1;/* is string type */ + WFIFOW(fd, 14) = 0;/* count */ + plen = 16; + } + } + + /* mark & go. */ + WFIFOW(fd, 2) = plen; + WFIFOSET(fd, plen); - //`global_reg_value` (`type`, `account_id`, `char_id`, `str`, `value`) - switch( type ) - { - case 3: //char reg - if( SQL_ERROR == SQL->Query(sql_handle, "SELECT `str`, `value` FROM `%s` WHERE `type`=3 AND `char_id`='%d'", reg_db, char_id) ) - Sql_ShowDebug(sql_handle); - break; - case 2: //account reg - if( SQL_ERROR == SQL->Query(sql_handle, "SELECT `str`, `value` FROM `%s` WHERE `type`=2 AND `account_id`='%d'", reg_db, account_id) ) - Sql_ShowDebug(sql_handle); - break; - case 1: //account2 reg - ShowError("inter_accreg_fromsql: Char server shouldn't handle type 1 registry values (##). That is the login server's work!\n"); - return 0; - default: - ShowError("inter_accreg_fromsql: Invalid type %d\n", type); - return 0; + SQL->FreeResult(sql_handle); + + switch( type ) { + case 3: //char reg + if( SQL_ERROR == SQL->Query(sql_handle, "SELECT `key`, `index`, `value` FROM `%s` WHERE `char_id`='%d'", char_reg_num_db, char_id) ) + Sql_ShowDebug(sql_handle); + break; + case 2: //account reg + if( SQL_ERROR == SQL->Query(sql_handle, "SELECT `key`, `index`, `value` FROM `%s` WHERE `account_id`='%d'", acc_reg_num_db, account_id) ) + Sql_ShowDebug(sql_handle); + break; + case 1: //account2 reg + ShowError("inter_accreg_fromsql: Char server shouldn't handle type 1 registry values (##). That is the login server's work!\n"); + return 0; } - for( i = 0; i < MAX_REG_NUM && SQL_SUCCESS == SQL->NextRow(sql_handle); ++i ) - { - r = ®->reg[i]; - // str - SQL->GetData(sql_handle, 0, &data, &len); - memcpy(r->str, data, min(len, sizeof(r->str))); - // value - SQL->GetData(sql_handle, 1, &data, &len); - memcpy(r->value, data, min(len, sizeof(r->value))); + + WFIFOHEAD(fd, 60000 + 300); + WFIFOW(fd, 0) = 0x3804; + /* 0x2 = length, set prior to being sent */ + WFIFOL(fd, 4) = account_id; + WFIFOL(fd, 8) = char_id; + WFIFOB(fd, 12) = 0;/* var type (only set when all vars have been sent, regardless of type) */ + WFIFOB(fd, 13) = 0;/* is int type */ + WFIFOW(fd, 14) = 0;/* count */ + plen = 16; + + /** + * Vessel! + * + * int type + * { keyLength(B), key(<keyLength>), index(L), value(L) } + **/ + while ( SQL_SUCCESS == SQL->NextRow(sql_handle) ) { + + SQL->GetData(sql_handle, 0, &data, NULL); + len = strlen(data)+1; + + WFIFOB(fd, plen) = (unsigned char)len;/* won't be higher; the column size is 32 */ + plen += 1; + + safestrncpy((char*)WFIFOP(fd,plen), data, len); + plen += len; + + SQL->GetData(sql_handle, 1, &data, NULL); + + WFIFOL(fd, plen) = (unsigned int)atol(data); + plen += 4; + + SQL->GetData(sql_handle, 2, &data, NULL); + + WFIFOL(fd, plen) = atoi(data); + plen += 4; + + WFIFOW(fd, 14) += 1; + + if( plen > 60000 ) { + WFIFOW(fd, 2) = plen; + WFIFOSET(fd, plen); + + /* prepare follow up */ + WFIFOHEAD(fd, 60000 + 300); + WFIFOW(fd, 0) = 0x3804; + /* 0x2 = length, set prior to being sent */ + WFIFOL(fd, 4) = account_id; + WFIFOL(fd, 8) = char_id; + WFIFOB(fd, 12) = 0;/* var type (only set when all vars have been sent, regardless of type) */ + WFIFOB(fd, 13) = 0;/* is int type */ + WFIFOW(fd, 14) = 0;/* count */ + plen = 16; + } } - reg->reg_num = i; + + /* mark as complete & go. */ + WFIFOB(fd, 12) = type; + WFIFOW(fd, 2) = plen; + WFIFOSET(fd, plen); + SQL->FreeResult(sql_handle); return 1; } -// Initialize -int inter_accreg_sql_init(void) -{ - CREATE(accreg_pt, struct accreg, 1); - return 0; - -} - /*========================================== * read config file *------------------------------------------*/ @@ -833,7 +930,6 @@ int inter_init_sql(const char *file) inter_homunculus_sql_init(); inter_mercenary_sql_init(); inter_elemental_sql_init(); - inter_accreg_sql_init(); inter_mail_sql_init(); inter_auction_sql_init(); @@ -857,8 +953,6 @@ void inter_final(void) inter_mail_sql_final(); inter_auction_sql_final(); - if (accreg_pt) aFree(accreg_pt); - do_final_msg(); return; } @@ -921,36 +1015,15 @@ int mapif_wis_end(struct WisData *wd, int flag) } // Account registry transfer to map-server -static void mapif_account_reg(int fd, unsigned char *src) -{ - WBUFW(src,0)=0x3804; //NOTE: writing to RFIFO - mapif_sendallwos(fd, src, WBUFW(src,2)); -} +//static void mapif_account_reg(int fd, unsigned char *src) +//{ +// WBUFW(src,0)=0x3804; //NOTE: writing to RFIFO +// mapif_sendallwos(fd, src, WBUFW(src,2)); +//} // Send the requested account_reg -int mapif_account_reg_reply(int fd,int account_id,int char_id, int type) -{ - struct accreg *reg=accreg_pt; - WFIFOHEAD(fd, 13 + 5000); - inter_accreg_fromsql(account_id,char_id,reg,type); - - WFIFOW(fd,0)=0x3804; - WFIFOL(fd,4)=account_id; - WFIFOL(fd,8)=char_id; - WFIFOB(fd,12)=type; - if(reg->reg_num==0){ - WFIFOW(fd,2)=13; - }else{ - int i,p; - for (p=13,i = 0; i < reg->reg_num && p < 5000; i++) { - p+= sprintf((char*)WFIFOP(fd,p), "%s", reg->reg[i].str)+1; //We add 1 to consider the '\0' in place. - p+= sprintf((char*)WFIFOP(fd,p), "%s", reg->reg[i].value)+1; - } - WFIFOW(fd,2)=p; - if (p>= 5000) - ShowWarning("Too many acc regs for %d:%d, not all values were loaded.\n", account_id, char_id); - } - WFIFOSET(fd,WFIFOW(fd,2)); +int mapif_account_reg_reply(int fd,int account_id,int char_id, int type) { + inter_accreg_fromsql(account_id,char_id,fd,type); return 0; } @@ -1128,34 +1201,50 @@ int mapif_parse_WisToGM(int fd) // Save account_reg into sql (type=2) int mapif_parse_Registry(int fd) { - int j,p,len, max; - struct accreg *reg=accreg_pt; - - memset(accreg_pt,0,sizeof(struct accreg)); - switch (RFIFOB(fd, 12)) { - case 3: //Character registry - max = GLOBAL_REG_NUM; - break; - case 2: //Account Registry - max = ACCOUNT_REG_NUM; - break; - case 1: //Account2 registry, must be sent over to login server. - return save_accreg2(RFIFOP(fd,4), RFIFOW(fd,2)-4); - default: - return 1; - } - for(j=0,p=13;j<max && p<RFIFOW(fd,2);j++){ - sscanf((char*)RFIFOP(fd,p), "%31c%n",reg->reg[j].str,&len); - reg->reg[j].str[len]='\0'; - p +=len+1; //+1 to skip the '\0' between strings. - sscanf((char*)RFIFOP(fd,p), "%255c%n",reg->reg[j].value,&len); - reg->reg[j].value[len]='\0'; - p +=len+1; - } - reg->reg_num=j; + int account_id = RFIFOL(fd, 4), char_id = RFIFOL(fd, 8), count = RFIFOW(fd, 12); + + if( count ) { + int cursor = 14, i; + char key[32], sval[254]; + unsigned int index; + + global_accreg_to_login_start(account_id,char_id); + + for(i = 0; i < count; i++) { + safestrncpy(key, (char*)RFIFOP(fd, cursor + 1), RFIFOB(fd, cursor)); + cursor += RFIFOB(fd, cursor) + 1; + + index = RFIFOL(fd, cursor); + cursor += 4; + + switch (RFIFOB(fd, cursor++)) { + /* int */ + case 0: + inter_savereg(account_id,char_id,key,index,RFIFOL(fd, cursor),false); + cursor += 4; + break; + case 1: + inter_savereg(account_id,char_id,key,index,0,false); + break; + /* str */ + case 2: + safestrncpy(sval, (char*)RFIFOP(fd, cursor + 1), RFIFOB(fd, cursor)); + cursor += RFIFOB(fd, cursor) + 1; + inter_savereg(account_id,char_id,key,index,(intptr_t)sval,true); + break; + case 3: + inter_savereg(account_id,char_id,key,index,0,true); + break; + + default: + ShowError("mapif_parse_Registry: unknown type %d\n",RFIFOB(fd, cursor - 1)); + return 1; + } - inter_accreg_tosql(RFIFOL(fd,4),RFIFOL(fd,8),reg, RFIFOB(fd,12)); - mapif_account_reg(fd,RFIFOP(fd,0)); // Send updated accounts to other map servers. + } + + global_accreg_to_login_send(); + } return 0; } diff --git a/src/common/db.c b/src/common/db.c index efe7ca8b2..ddfb032d4 100644 --- a/src/common/db.c +++ b/src/common/db.c @@ -47,6 +47,7 @@ * - create a db that organizes itself by splaying * * HISTORY: + * 2013/08/25 - Added int64/uint64 support for keys [Ind/Hercules] * 2013/04/27 - Added ERS to speed up iterator memory allocation [Ind/Hercules] * 2012/03/09 - Added enum for data types (int, uint, void*) * 2008/02/19 - Fixed db_obj_get not handling deleted entries correctly. @@ -236,10 +237,14 @@ static struct db_stats { uint32 db_uint_alloc; uint32 db_string_alloc; uint32 db_istring_alloc; + uint32 db_int64_alloc; + uint32 db_uint64_alloc; uint32 db_int_destroy; uint32 db_uint_destroy; uint32 db_string_destroy; uint32 db_istring_destroy; + uint32 db_int64_destroy; + uint32 db_uint64_destroy; // Function usage counters uint32 db_rotate_left; uint32 db_rotate_right; @@ -256,10 +261,14 @@ static struct db_stats { uint32 db_uint_cmp; uint32 db_string_cmp; uint32 db_istring_cmp; + uint32 db_int64_cmp; + uint32 db_uint64_cmp; uint32 db_int_hash; uint32 db_uint_hash; uint32 db_string_hash; uint32 db_istring_hash; + uint32 db_int64_hash; + uint32 db_uint64_hash; uint32 db_release_nothing; uint32 db_release_key; uint32 db_release_data; @@ -298,6 +307,8 @@ static struct db_stats { uint32 db_i2key; uint32 db_ui2key; uint32 db_str2key; + uint32 db_i642key; + uint32 db_ui642key; uint32 db_i2data; uint32 db_ui2data; uint32 db_ptr2data; @@ -830,10 +841,14 @@ static void db_free_unlock(DBMap_impl* db) * db_uint_cmp - Default comparator for DB_UINT databases. * * db_string_cmp - Default comparator for DB_STRING databases. * * db_istring_cmp - Default comparator for DB_ISTRING databases. * + * db_int64_cmp - Default comparator for DB_INT64 databases. * + * db_uint64_cmp - Default comparator for DB_UINT64 databases. * * db_int_hash - Default hasher for DB_INT databases. * * db_uint_hash - Default hasher for DB_UINT databases. * * db_string_hash - Default hasher for DB_STRING databases. * * db_istring_hash - Default hasher for DB_ISTRING databases. * + * db_int64_hash - Default hasher for DB_INT64 databases. * + * db_uint64_hash - Default hasher for DB_UINT64 databases. * * db_release_nothing - Releaser that releases nothing. * * db_release_key - Releaser that only releases the key. * * db_release_data - Releaser that only releases the data. * @@ -921,6 +936,51 @@ static int db_istring_cmp(DBKey key1, DBKey key2, unsigned short maxlen) } /** + * Default comparator for DB_INT64 databases. + * Compares key1 to key2. + * Return 0 if equal, negative if lower and positive if higher. + * <code>maxlen</code> is ignored. + * @param key1 Key to be compared + * @param key2 Key being compared to + * @param maxlen Maximum length of the key to hash + * @return 0 if equal, negative if lower and positive if higher + * @see DBType#DB_INT64 + * @see #DBComparator + * @see #db_default_cmp(DBType) + */ +static int db_int64_cmp(DBKey key1, DBKey key2, unsigned short maxlen) +{ + (void)maxlen;//not used + DB_COUNTSTAT(db_int64_cmp); + if (key1.i64 < key2.i64) return -1; + if (key1.i64 > key2.i64) return 1; + return 0; +} + +/** + * Default comparator for DB_UINT64 databases. + * Compares key1 to key2. + * Return 0 if equal, negative if lower and positive if higher. + * <code>maxlen</code> is ignored. + * @param key1 Key to be compared + * @param key2 Key being compared to + * @param maxlen Maximum length of the key to hash + * @return 0 if equal, negative if lower and positive if higher + * @see DBType#DB_UINT64 + * @see #DBComparator + * @see #db_default_cmp(DBType) + */ +static int db_uint64_cmp(DBKey key1, DBKey key2, unsigned short maxlen) +{ + (void)maxlen;//not used + DB_COUNTSTAT(db_uint64_cmp); + if (key1.ui64 < key2.ui64) return -1; + if (key1.ui64 > key2.ui64) return 1; + return 0; +} + + +/** * Default hasher for DB_INT databases. * Returns the value of the key as an unsigned int. * <code>maxlen</code> is ignored. @@ -931,11 +991,11 @@ static int db_istring_cmp(DBKey key1, DBKey key2, unsigned short maxlen) * @see #DBHasher * @see #db_default_hash(DBType) */ -static unsigned int db_int_hash(DBKey key, unsigned short maxlen) +static uint64 db_int_hash(DBKey key, unsigned short maxlen) { (void)maxlen;//not used DB_COUNTSTAT(db_int_hash); - return (unsigned int)key.i; + return (uint64)key.i; } /** @@ -949,11 +1009,11 @@ static unsigned int db_int_hash(DBKey key, unsigned short maxlen) * @see #DBHasher * @see #db_default_hash(DBType) */ -static unsigned int db_uint_hash(DBKey key, unsigned short maxlen) +static uint64 db_uint_hash(DBKey key, unsigned short maxlen) { (void)maxlen;//not used DB_COUNTSTAT(db_uint_hash); - return key.ui; + return (uint64)key.ui; } /** @@ -965,7 +1025,7 @@ static unsigned int db_uint_hash(DBKey key, unsigned short maxlen) * @see #DBHasher * @see #db_default_hash(DBType) */ -static unsigned int db_string_hash(DBKey key, unsigned short maxlen) +static uint64 db_string_hash(DBKey key, unsigned short maxlen) { const char *k = key.str; unsigned int hash = 0; @@ -980,7 +1040,7 @@ static unsigned int db_string_hash(DBKey key, unsigned short maxlen) break; } - return hash; + return (uint64)hash; } /** @@ -991,7 +1051,7 @@ static unsigned int db_string_hash(DBKey key, unsigned short maxlen) * @see DBType#DB_ISTRING * @see #db_default_hash(DBType) */ -static unsigned int db_istring_hash(DBKey key, unsigned short maxlen) +static uint64 db_istring_hash(DBKey key, unsigned short maxlen) { const char *k = key.str; unsigned int hash = 0; @@ -1006,7 +1066,43 @@ static unsigned int db_istring_hash(DBKey key, unsigned short maxlen) break; } - return hash; + return (uint64)hash; +} + +/** + * Default hasher for DB_INT64 databases. + * Returns the value of the key as an unsigned int. + * <code>maxlen</code> is ignored. + * @param key Key to be hashed + * @param maxlen Maximum length of the key to hash + * @return hash of the key + * @see DBType#DB_INT64 + * @see #DBHasher + * @see #db_default_hash(DBType) + */ +static uint64 db_int64_hash(DBKey key, unsigned short maxlen) +{ + (void)maxlen;//not used + DB_COUNTSTAT(db_int64_hash); + return (uint64)key.i64; +} + +/** + * Default hasher for DB_UINT64 databases. + * Just returns the value of the key. + * <code>maxlen</code> is ignored. + * @param key Key to be hashed + * @param maxlen Maximum length of the key to hash + * @return hash of the key + * @see DBType#DB_UINT64 + * @see #DBHasher + * @see #db_default_hash(DBType) + */ +static uint64 db_uint64_hash(DBKey key, unsigned short maxlen) +{ + (void)maxlen;//not used + DB_COUNTSTAT(db_uint64_hash); + return key.ui64; } /** @@ -2122,6 +2218,8 @@ static int db_obj_vdestroy(DBMap* self, DBApply func, va_list args) case DB_UINT: DB_COUNTSTAT(db_uint_destroy); break; case DB_STRING: DB_COUNTSTAT(db_string_destroy); break; case DB_ISTRING: DB_COUNTSTAT(db_istring_destroy); break; + case DB_INT64: DB_COUNTSTAT(db_int64_destroy); break; + case DB_UINT64: DB_COUNTSTAT(db_uint64_destroy); break; } #endif /* DB_ENABLE_STATS */ db_free_lock(db); @@ -2246,6 +2344,8 @@ static DBOptions db_obj_options(DBMap* self) * db_i2key - Manual cast from 'int' to 'DBKey'. * db_ui2key - Manual cast from 'unsigned int' to 'DBKey'. * db_str2key - Manual cast from 'unsigned char *' to 'DBKey'. + * db_i642key - Manual cast from 'int64' to 'DBKey'. + * db_ui642key - Manual cast from 'uin64' to 'DBKey'. * db_i2data - Manual cast from 'int' to 'DBData'. * db_ui2data - Manual cast from 'unsigned int' to 'DBData'. * db_ptr2data - Manual cast from 'void*' to 'DBData'. @@ -2272,7 +2372,9 @@ DBOptions db_fix_options(DBType type, DBOptions options) DB_COUNTSTAT(db_fix_options); switch (type) { case DB_INT: - case DB_UINT: // Numeric database, do nothing with the keys + case DB_UINT: + case DB_INT64: + case DB_UINT64: // Numeric database, do nothing with the keys return (DBOptions)(options&~(DB_OPT_DUP_KEY|DB_OPT_RELEASE_KEY)); default: @@ -2292,6 +2394,8 @@ DBOptions db_fix_options(DBType type, DBOptions options) * @see #db_uint_cmp(DBKey,DBKey,unsigned short) * @see #db_string_cmp(DBKey,DBKey,unsigned short) * @see #db_istring_cmp(DBKey,DBKey,unsigned short) + * @see #db_int64_cmp(DBKey,DBKey,unsigned short) + * @see #db_uint64_cmp(DBKey,DBKey,unsigned short) */ DBComparator db_default_cmp(DBType type) { @@ -2301,6 +2405,8 @@ DBComparator db_default_cmp(DBType type) case DB_UINT: return &db_uint_cmp; case DB_STRING: return &db_string_cmp; case DB_ISTRING: return &db_istring_cmp; + case DB_INT64: return &db_int64_cmp; + case DB_UINT64: return &db_uint64_cmp; default: ShowError("db_default_cmp: Unknown database type %u\n", type); return NULL; @@ -2316,6 +2422,8 @@ DBComparator db_default_cmp(DBType type) * @see #db_uint_hash(DBKey,unsigned short) * @see #db_string_hash(DBKey,unsigned short) * @see #db_istring_hash(DBKey,unsigned short) + * @see #db_int64_hash(DBKey,unsigned short) + * @see #db_uint64_hash(DBKey,unsigned short) */ DBHasher db_default_hash(DBType type) { @@ -2325,6 +2433,8 @@ DBHasher db_default_hash(DBType type) case DB_UINT: return &db_uint_hash; case DB_STRING: return &db_string_hash; case DB_ISTRING: return &db_istring_hash; + case DB_INT64: return &db_int64_hash; + case DB_UINT64: return &db_uint64_hash; default: ShowError("db_default_hash: Unknown database type %u\n", type); return NULL; @@ -2412,6 +2522,8 @@ DBMap* db_alloc(const char *file, const char *func, int line, DBType type, DBOpt case DB_UINT: DB_COUNTSTAT(db_uint_alloc); break; case DB_STRING: DB_COUNTSTAT(db_string_alloc); break; case DB_ISTRING: DB_COUNTSTAT(db_istring_alloc); break; + case DB_INT64: DB_COUNTSTAT(db_int64_alloc); break; + case DB_UINT64: DB_COUNTSTAT(db_uint64_alloc); break; } #endif /* DB_ENABLE_STATS */ db = ers_alloc(db_alloc_ers, struct DBMap_impl); @@ -2511,6 +2623,36 @@ DBKey db_str2key(const char *key) } /** + * Manual cast from 'int64' to the union DBKey. + * @param key Key to be casted + * @return The key as a DBKey union + * @public + */ +DBKey db_i642key(int64 key) +{ + DBKey ret; + + DB_COUNTSTAT(db_i642key); + ret.i64 = key; + return ret; +} + +/** + * Manual cast from 'uin64' to the union DBKey. + * @param key Key to be casted + * @return The key as a DBKey union + * @public + */ +DBKey db_ui642key(uint64 key) +{ + DBKey ret; + + DB_COUNTSTAT(db_ui642key); + ret.ui64 = key; + return ret; +} + +/** * Manual cast from 'int' to the struct DBData. * @param data Data to be casted * @return The data as a DBData struct @@ -2632,10 +2774,14 @@ void db_final(void) "DB_UINT : allocated %10u, destroyed %10u\n" "DB_STRING : allocated %10u, destroyed %10u\n" "DB_ISTRING : allocated %10u, destroyed %10u\n", + "DB_INT64 : allocated %10u, destroyed %10u\n" + "DB_UINT64 : allocated %10u, destroyed %10u\n" stats.db_int_alloc, stats.db_int_destroy, stats.db_uint_alloc, stats.db_uint_destroy, stats.db_string_alloc, stats.db_string_destroy, - stats.db_istring_alloc, stats.db_istring_destroy); + stats.db_istring_alloc, stats.db_istring_destroy, + stats.db_int64_alloc, stats.db_int64_destroy, + stats.db_uint64_alloc, stats.db_uint64_destroy,); ShowInfo(CL_WHITE"Database function counters"CL_RESET":\n" "db_rotate_left %10u, db_rotate_right %10u,\n" "db_rebalance %10u, db_rebalance_erase %10u,\n" @@ -2645,8 +2791,10 @@ void db_final(void) "db_free_lock %10u, db_free_unlock %10u,\n" "db_int_cmp %10u, db_uint_cmp %10u,\n" "db_string_cmp %10u, db_istring_cmp %10u,\n" + "db_int64_cmp %10u, db_uint64_cmp %10u,\n" "db_int_hash %10u, db_uint_hash %10u,\n" "db_string_hash %10u, db_istring_hash %10u,\n" + "db_int64_hash %10u, db_uint64_hash %10u,\n" "db_release_nothing %10u, db_release_key %10u,\n" "db_release_data %10u, db_release_both %10u,\n" "dbit_first %10u, dbit_last %10u,\n" @@ -2666,6 +2814,7 @@ void db_final(void) "db_default_release %10u, db_custom_release %10u,\n" "db_alloc %10u, db_i2key %10u,\n" "db_ui2key %10u, db_str2key %10u,\n" + "db_i642key %10u, db_ui642key %10u,\n" "db_i2data %10u, db_ui2data %10u,\n" "db_ptr2data %10u, db_data2i %10u,\n" "db_data2ui %10u, db_data2ptr %10u,\n" @@ -2678,8 +2827,10 @@ void db_final(void) stats.db_free_lock, stats.db_free_unlock, stats.db_int_cmp, stats.db_uint_cmp, stats.db_string_cmp, stats.db_istring_cmp, + stats.db_int64_cmp, stats.db_uint64_cmp, stats.db_int_hash, stats.db_uint_hash, stats.db_string_hash, stats.db_istring_hash, + stats.db_int64_hash, stats.db_uint64_hash, stats.db_release_nothing, stats.db_release_key, stats.db_release_data, stats.db_release_both, stats.dbit_first, stats.dbit_last, @@ -2699,6 +2850,7 @@ void db_final(void) stats.db_default_release, stats.db_custom_release, stats.db_alloc, stats.db_i2key, stats.db_ui2key, stats.db_str2key, + stats.db_i642key, stats.db_ui642key, stats.db_i2data, stats.db_ui2data, stats.db_ptr2data, stats.db_data2i, stats.db_data2ui, stats.db_data2ptr, @@ -2855,4 +3007,7 @@ void db_defaults(void) { DB->str2key = db_str2key; DB->ui2data = db_ui2data; DB->ui2key = db_ui2key; + DB->i642key = db_i642key; + DB->ui642key = db_ui642key; + } diff --git a/src/common/db.h b/src/common/db.h index 5f4478909..9d7bdda10 100644 --- a/src/common/db.h +++ b/src/common/db.h @@ -20,6 +20,7 @@ * - see what functions need or should be added to the database interface * * * * HISTORY: * + * 2013/08/25 - Added int64/uint64 support for keys * * 2012/03/09 - Added enum for data types (int, uint, void*) * * 2007/11/09 - Added an iterator to the database. * * 2.1 (Athena build #???#) - Portability fix * @@ -83,6 +84,8 @@ typedef enum DBRelease { * @param DB_UINT Uses unsigned int's for keys * @param DB_STRING Uses strings for keys. * @param DB_ISTRING Uses case insensitive strings for keys. + * @param DB_INT64 Uses int64's for keys + * @param DB_UINT64 Uses uint64's for keys * @public * @see #DBOptions * @see #DBKey @@ -96,7 +99,9 @@ typedef enum DBType { DB_INT, DB_UINT, DB_STRING, - DB_ISTRING + DB_ISTRING, + DB_INT64, + DB_UINT64, } DBType; /** @@ -145,6 +150,8 @@ typedef union DBKey { int i; unsigned int ui; const char *str; + int64 i64; + uint64 ui64; } DBKey; /** @@ -158,7 +165,7 @@ typedef union DBKey { typedef enum DBDataType { DB_DATA_INT, DB_DATA_UINT, - DB_DATA_PTR + DB_DATA_PTR, } DBDataType; /** @@ -245,7 +252,7 @@ typedef int (*DBComparator)(DBKey key1, DBKey key2, unsigned short maxlen); * @public * @see #db_default_hash(DBType) */ -typedef unsigned int (*DBHasher)(DBKey key, unsigned short maxlen); +typedef uint64 (*DBHasher)(DBKey key, unsigned short maxlen); /** * Format of the releaser used by the database system. @@ -598,65 +605,86 @@ struct DBMap { // For easy access to the common functions. -#define db_exists(db,k) ( (db)->exists((db),(k)) ) -#define idb_exists(db,k) ( (db)->exists((db),DB->i2key(k)) ) -#define uidb_exists(db,k) ( (db)->exists((db),DB->ui2key(k)) ) -#define strdb_exists(db,k) ( (db)->exists((db),DB->str2key(k)) ) +#define db_exists(db,k) ( (db)->exists((db),(k)) ) +#define idb_exists(db,k) ( (db)->exists((db),DB->i2key(k)) ) +#define uidb_exists(db,k) ( (db)->exists((db),DB->ui2key(k)) ) +#define strdb_exists(db,k) ( (db)->exists((db),DB->str2key(k)) ) +#define i64db_exists(db,k) ( (db)->exists((db),DB->i642key(k)) ) +#define ui64db_exists(db,k) ( (db)->exists((db),DB->ui642key(k)) ) // Get pointer-type data from DBMaps of various key types -#define db_get(db,k) ( DB->data2ptr((db)->get((db),(k))) ) -#define idb_get(db,k) ( DB->data2ptr((db)->get((db),DB->i2key(k))) ) -#define uidb_get(db,k) ( DB->data2ptr((db)->get((db),DB->ui2key(k))) ) -#define strdb_get(db,k) ( DB->data2ptr((db)->get((db),DB->str2key(k))) ) +#define db_get(db,k) ( DB->data2ptr((db)->get((db),(k))) ) +#define idb_get(db,k) ( DB->data2ptr((db)->get((db),DB->i2key(k))) ) +#define uidb_get(db,k) ( DB->data2ptr((db)->get((db),DB->ui2key(k))) ) +#define strdb_get(db,k) ( DB->data2ptr((db)->get((db),DB->str2key(k))) ) +#define i64db_get(db,k) ( DB->data2ptr((db)->get((db),DB->i642key(k))) ) +#define ui64db_get(db,k) ( DB->data2ptr((db)->get((db),DB->ui642key(k))) ) + // Get int-type data from DBMaps of various key types -#define db_iget(db,k) ( DB->data2i((db)->get((db),(k))) ) -#define idb_iget(db,k) ( DB->data2i((db)->get((db),DB->i2key(k))) ) -#define uidb_iget(db,k) ( DB->data2i((db)->get((db),DB->ui2key(k))) ) -#define strdb_iget(db,k) ( DB->data2i((db)->get((db),DB->str2key(k))) ) +#define db_iget(db,k) ( DB->data2i((db)->get((db),(k))) ) +#define idb_iget(db,k) ( DB->data2i((db)->get((db),DB->i2key(k))) ) +#define uidb_iget(db,k) ( DB->data2i((db)->get((db),DB->ui2key(k))) ) +#define strdb_iget(db,k) ( DB->data2i((db)->get((db),DB->str2key(k))) ) +#define i64db_iget(db,k) ( DB->data2i((db)->get((db),DB->i642key(k))) ) +#define ui64db_iget(db,k) ( DB->data2i((db)->get((db),DB->ui642key(k))) ) // Get uint-type data from DBMaps of various key types -#define db_uiget(db,k) ( DB->data2ui((db)->get((db),(k))) ) -#define idb_uiget(db,k) ( DB->data2ui((db)->get((db),DB->i2key(k))) ) -#define uidb_uiget(db,k) ( DB->data2ui((db)->get((db),DB->ui2key(k))) ) -#define strdb_uiget(db,k) ( DB->data2ui((db)->get((db),DB->str2key(k))) ) +#define db_uiget(db,k) ( DB->data2ui((db)->get((db),(k))) ) +#define idb_uiget(db,k) ( DB->data2ui((db)->get((db),DB->i2key(k))) ) +#define uidb_uiget(db,k) ( DB->data2ui((db)->get((db),DB->ui2key(k))) ) +#define strdb_uiget(db,k) ( DB->data2ui((db)->get((db),DB->str2key(k))) ) +#define i64db_uiget(db,k) ( DB->data2ui((db)->get((db),DB->i642key(k))) ) +#define ui64db_uiget(db,k) ( DB->data2ui((db)->get((db),DB->ui642key(k))) ) // Put pointer-type data into DBMaps of various key types -#define db_put(db,k,d) ( (db)->put((db),(k),DB->ptr2data(d),NULL) ) -#define idb_put(db,k,d) ( (db)->put((db),DB->i2key(k),DB->ptr2data(d),NULL) ) -#define uidb_put(db,k,d) ( (db)->put((db),DB->ui2key(k),DB->ptr2data(d),NULL) ) -#define strdb_put(db,k,d) ( (db)->put((db),DB->str2key(k),DB->ptr2data(d),NULL) ) +#define db_put(db,k,d) ( (db)->put((db),(k),DB->ptr2data(d),NULL) ) +#define idb_put(db,k,d) ( (db)->put((db),DB->i2key(k),DB->ptr2data(d),NULL) ) +#define uidb_put(db,k,d) ( (db)->put((db),DB->ui2key(k),DB->ptr2data(d),NULL) ) +#define strdb_put(db,k,d) ( (db)->put((db),DB->str2key(k),DB->ptr2data(d),NULL) ) +#define i64db_put(db,k,d) ( (db)->put((db),DB->i642key(k),DB->ptr2data(d),NULL) ) +#define ui64db_put(db,k,d) ( (db)->put((db),DB->ui642key(k),DB->ptr2data(d),NULL) ) // Put int-type data into DBMaps of various key types -#define db_iput(db,k,d) ( (db)->put((db),(k),DB->i2data(d),NULL) ) -#define idb_iput(db,k,d) ( (db)->put((db),DB->i2key(k),DB->i2data(d),NULL) ) -#define uidb_iput(db,k,d) ( (db)->put((db),DB->ui2key(k),DB->i2data(d),NULL) ) -#define strdb_iput(db,k,d) ( (db)->put((db),DB->str2key(k),DB->i2data(d),NULL) ) +#define db_iput(db,k,d) ( (db)->put((db),(k),DB->i2data(d),NULL) ) +#define idb_iput(db,k,d) ( (db)->put((db),DB->i2key(k),DB->i2data(d),NULL) ) +#define uidb_iput(db,k,d) ( (db)->put((db),DB->ui2key(k),DB->i2data(d),NULL) ) +#define strdb_iput(db,k,d) ( (db)->put((db),DB->str2key(k),DB->i2data(d),NULL) ) +#define i64db_iput(db,k,d) ( (db)->put((db),DB->i642key(k),DB->i2data(d),NULL) ) +#define ui64db_iput(db,k,d) ( (db)->put((db),DB->ui642key(k),DB->i2data(d),NULL) ) // Put uint-type data into DBMaps of various key types -#define db_uiput(db,k,d) ( (db)->put((db),(k),DB->ui2data(d),NULL) ) -#define idb_uiput(db,k,d) ( (db)->put((db),DB->i2key(k),DB->ui2data(d),NULL) ) -#define uidb_uiput(db,k,d) ( (db)->put((db),DB->ui2key(k),DB->ui2data(d),NULL) ) -#define strdb_uiput(db,k,d) ( (db)->put((db),DB->str2key(k),DB->ui2data(d),NULL) ) +#define db_uiput(db,k,d) ( (db)->put((db),(k),DB->ui2data(d),NULL) ) +#define idb_uiput(db,k,d) ( (db)->put((db),DB->i2key(k),DB->ui2data(d),NULL) ) +#define uidb_uiput(db,k,d) ( (db)->put((db),DB->ui2key(k),DB->ui2data(d),NULL) ) +#define strdb_uiput(db,k,d) ( (db)->put((db),DB->str2key(k),DB->ui2data(d),NULL) ) +#define i64db_uiput(db,k,d) ( (db)->put((db),DB->i642key(k),DB->ui2data(d),NULL) ) +#define ui64db_uiput(db,k,d) ( (db)->put((db),DB->ui642key(k),DB->ui2data(d),NULL) ) // Remove entry from DBMaps of various key types -#define db_remove(db,k) ( (db)->remove((db),(k),NULL) ) -#define idb_remove(db,k) ( (db)->remove((db),DB->i2key(k),NULL) ) -#define uidb_remove(db,k) ( (db)->remove((db),DB->ui2key(k),NULL) ) -#define strdb_remove(db,k) ( (db)->remove((db),DB->str2key(k),NULL) ) +#define db_remove(db,k) ( (db)->remove((db),(k),NULL) ) +#define idb_remove(db,k) ( (db)->remove((db),DB->i2key(k),NULL) ) +#define uidb_remove(db,k) ( (db)->remove((db),DB->ui2key(k),NULL) ) +#define strdb_remove(db,k) ( (db)->remove((db),DB->str2key(k),NULL) ) +#define i64db_remove(db,k) ( (db)->remove((db),DB->i642key(k),NULL) ) +#define ui64db_remove(db,k) ( (db)->remove((db),DB->ui642key(k),NULL) ) //These are discarding the possible vargs you could send to the function, so those //that require vargs must not use these defines. -#define db_ensure(db,k,f) ( DB->data2ptr((db)->ensure((db),(k),(f))) ) -#define idb_ensure(db,k,f) ( DB->data2ptr((db)->ensure((db),DB->i2key(k),(f))) ) -#define uidb_ensure(db,k,f) ( DB->data2ptr((db)->ensure((db),DB->ui2key(k),(f))) ) -#define strdb_ensure(db,k,f) ( DB->data2ptr((db)->ensure((db),DB->str2key(k),(f))) ) +#define db_ensure(db,k,f) ( DB->data2ptr((db)->ensure((db),(k),(f))) ) +#define idb_ensure(db,k,f) ( DB->data2ptr((db)->ensure((db),DB->i2key(k),(f))) ) +#define uidb_ensure(db,k,f) ( DB->data2ptr((db)->ensure((db),DB->ui2key(k),(f))) ) +#define strdb_ensure(db,k,f) ( DB->data2ptr((db)->ensure((db),DB->str2key(k),(f))) ) +#define i64db_ensure(db,k,f) ( DB->data2ptr((db)->ensure((db),DB->i642key(k),(f))) ) +#define ui64db_ensure(db,k,f) ( DB->data2ptr((db)->ensure((db),DB->ui642key(k),(f))) ) // Database creation and destruction macros #define idb_alloc(opt) DB->alloc(__FILE__,__func__,__LINE__,DB_INT,(opt),sizeof(int)) #define uidb_alloc(opt) DB->alloc(__FILE__,__func__,__LINE__,DB_UINT,(opt),sizeof(unsigned int)) #define strdb_alloc(opt,maxlen) DB->alloc(__FILE__,__func__,__LINE__,DB_STRING,(opt),(maxlen)) #define stridb_alloc(opt,maxlen) DB->alloc(__FILE__,__func__,__LINE__,DB_ISTRING,(opt),(maxlen)) +#define i64db_alloc(opt) DB->alloc(__FILE__,__func__,__LINE__,DB_INT64,(opt),sizeof(int64)) +#define ui64db_alloc(opt) DB->alloc(__FILE__,__func__,__LINE__,DB_UINT64,(opt),sizeof(uint64)) #define db_destroy(db) ( (db)->destroy((db),NULL) ) // Other macros #define db_clear(db) ( (db)->clear((db),NULL) ) @@ -682,6 +710,8 @@ struct DBMap { * db_i2key - Manual cast from 'int' to 'DBKey'. * * db_ui2key - Manual cast from 'unsigned int' to 'DBKey'. * * db_str2key - Manual cast from 'unsigned char *' to 'DBKey'. * + * db_i642key - Manual cast from 'int64' to 'DBKey'. * + * db_ui642key - Manual cast from 'uint64' to 'DBKey'. * * db_i2data - Manual cast from 'int' to 'DBData'. * * db_ui2data - Manual cast from 'unsigned int' to 'DBData'. * * db_ptr2data - Manual cast from 'void*' to 'DBData'. * @@ -803,6 +833,22 @@ DBKey (*ui2key) (unsigned int key); DBKey (*str2key) (const char *key); /** + * Manual cast from 'int64' to the union DBKey. + * @param key Key to be casted + * @return The key as a DBKey union + * @public + */ +DBKey (*i642key) (int64 key); + +/** + * Manual cast from 'uint64' to the union DBKey. + * @param key Key to be casted + * @return The key as a DBKey union + * @public + */ +DBKey (*ui642key) (uint64 key); + +/** * Manual cast from 'int' to the struct DBData. * @param data Data to be casted * @return The data as a DBData struct diff --git a/src/common/mmo.h b/src/common/mmo.h index 273edc42b..594d1cb26 100644 --- a/src/common/mmo.h +++ b/src/common/mmo.h @@ -101,11 +101,6 @@ //Update this max as necessary. 55 is the value needed for Super Baby currently //Raised to 84 since Expanded Super Novice needs it. #define MAX_SKILL_TREE 84 -#define GLOBAL_REG_NUM 256 // Max permanent character variables per char -#define ACCOUNT_REG_NUM 64 // Max permanent local account variables per account -#define ACCOUNT_REG2_NUM 16 // Max permanent global account variables per account -//Should hold the max of GLOBAL/ACCOUNT/ACCOUNT2 (needed for some arrays that hold all three) -#define MAX_REG_NUM 256 #define DEFAULT_WALK_SPEED 150 #define MIN_WALK_SPEED 20 /* below 20 clips animation */ #define MAX_WALK_SPEED 1000 @@ -269,16 +264,19 @@ struct s_skill { unsigned char flag; // See enum e_skill_flag }; -struct global_reg { - char str[32]; - char value[256]; +struct script_reg_state { + unsigned int type : 1;/* because I'm a memory hoarder and having them in the same struct would be a 8-byte/instance waste while ints outnumber str on a 10000-to-1 ratio. */ + unsigned int update : 1;/* whether it needs to be sent to char server for insertion/update/delete */ }; -// Holds array of global registries, used by the char server and converter. -struct accreg { - int account_id, char_id; - int reg_num; - struct global_reg reg[MAX_REG_NUM]; +struct script_reg_num { + struct script_reg_state flag; + int value; +}; + +struct script_reg_str { + struct script_reg_state flag; + char *value; }; // For saving status changes across sessions. [Skotlex] @@ -483,15 +481,6 @@ struct auction_data { int auction_end_timer; }; -struct registry { - int global_num; - struct global_reg global[GLOBAL_REG_NUM]; - int account_num; - struct global_reg account[ACCOUNT_REG_NUM]; - int account2_num; - struct global_reg account2[ACCOUNT_REG2_NUM]; -}; - struct party_member { int account_id; int char_id; @@ -866,6 +855,14 @@ enum e_char_server_type { CST_F2P = 4, }; +enum e_pc_reg_loading { + PRL_NONE = 0x0, + PRL_CHAR = 0x1, + PRL_ACCL = 0x2,/* local */ + PRL_ACCG = 0x4,/* global */ + PRL_ALL = 0xFF, +}; + /* packet size constant for itemlist */ #if MAX_INVENTORY > MAX_STORAGE && MAX_INVENTORY > MAX_CART #define MAX_ITEMLIST MAX_INVENTORY diff --git a/src/common/socket.c b/src/common/socket.c index 9c6938008..1ffa15708 100644 --- a/src/common/socket.c +++ b/src/common/socket.c @@ -1464,8 +1464,6 @@ void socket_datasync(int fd, bool send) { { sizeof(struct item) }, { sizeof(struct point) }, { sizeof(struct s_skill) }, - { sizeof(struct global_reg) }, - { sizeof(struct accreg) }, { sizeof(struct status_change_data) }, { sizeof(struct storage_data) }, { sizeof(struct guild_storage) }, @@ -1476,7 +1474,6 @@ void socket_datasync(int fd, bool send) { { sizeof(struct s_friend) }, { sizeof(struct mail_message) }, { sizeof(struct mail_data) }, - { sizeof(struct registry) }, { sizeof(struct party_member) }, { sizeof(struct party) }, { sizeof(struct guild_member) }, @@ -1487,6 +1484,7 @@ void socket_datasync(int fd, bool send) { { sizeof(struct guild) }, { sizeof(struct guild_castle) }, { sizeof(struct fame_list) }, + { PACKETVER }, }; unsigned short i; unsigned int alen = ARRAYLENGTH(data_list); diff --git a/src/login/account.h b/src/login/account.h index f55e38b0c..74a9e9626 100644 --- a/src/login/account.h +++ b/src/login/account.h @@ -1,5 +1,6 @@ -// Copyright (c) Athena Dev Teams - Licensed under GNU GPL -// For more information, see LICENCE in the main folder +// Copyright (c) Hercules Dev Team, licensed under GNU GPL. +// See the LICENSE file +// Portions Copyright (c) Athena Dev Teams #ifndef __ACCOUNT_H_INCLUDED__ #define __ACCOUNT_H_INCLUDED__ @@ -32,8 +33,6 @@ struct mmo_account char lastlogin[24]; // date+time of last successful login char last_ip[16]; // save of last IP of connection char birthdate[10+1]; // assigned birth date (format: YYYY-MM-DD, default: 0000-00-00) - int account_reg2_num; - struct global_reg account_reg2[ACCOUNT_REG2_NUM]; // account script variables (stored on login server) }; @@ -137,4 +136,7 @@ struct AccountDB Sql *account_db_sql_up(AccountDB* self); +void mmo_send_accreg2(AccountDB* self, int fd, int account_id, int char_id); +void mmo_save_accreg2(AccountDB* self, int fd, int account_id, int char_id); + #endif // __ACCOUNT_H_INCLUDED__ diff --git a/src/login/account_sql.c b/src/login/account_sql.c index 283eb3a0d..1483196ab 100644 --- a/src/login/account_sql.c +++ b/src/login/account_sql.c @@ -9,6 +9,7 @@ #include "../common/strlib.h" #include "../common/timer.h" #include "../common/console.h" +#include "../common/socket.h" #include "account.h" #include <stdlib.h> #include <string.h> @@ -40,7 +41,9 @@ typedef struct AccountDB_SQL // other settings bool case_sensitive; char account_db[32]; - char accreg_db[32]; + char global_acc_reg_num_db[32]; + char global_acc_reg_str_db[32]; + } AccountDB_SQL; @@ -106,7 +109,8 @@ AccountDB* account_db_sql(void) // other settings db->case_sensitive = false; safestrncpy(db->account_db, "login", sizeof(db->account_db)); - safestrncpy(db->accreg_db, "global_reg_value", sizeof(db->accreg_db)); + safestrncpy(db->global_acc_reg_num_db, "global_acc_reg_num_db", sizeof(db->global_acc_reg_num_db)); + safestrncpy(db->global_acc_reg_str_db, "global_acc_reg_str_db", sizeof(db->global_acc_reg_str_db)); return &db->vtable; } @@ -250,8 +254,11 @@ static bool account_db_sql_get_property(AccountDB* self, const char* key, char* if( strcmpi(key, "account_db") == 0 ) safesnprintf(buf, buflen, "%s", db->account_db); else - if( strcmpi(key, "accreg_db") == 0 ) - safesnprintf(buf, buflen, "%s", db->accreg_db); + if( strcmpi(key, "global_acc_reg_str_db") == 0 ) + safesnprintf(buf, buflen, "%s", db->global_acc_reg_str_db); + else + if( strcmpi(key, "global_acc_reg_num_db") == 0 ) + safesnprintf(buf, buflen, "%s", db->global_acc_reg_num_db); else return false;// not found return true; @@ -321,8 +328,11 @@ static bool account_db_sql_set_property(AccountDB* self, const char* key, const if( strcmpi(key, "account_db") == 0 ) safestrncpy(db->account_db, value, sizeof(db->account_db)); else - if( strcmpi(key, "accreg_db") == 0 ) - safestrncpy(db->accreg_db, value, sizeof(db->accreg_db)); + if( strcmpi(key, "global_acc_reg_str_db") == 0 ) + safestrncpy(db->global_acc_reg_str_db, value, sizeof(db->global_acc_reg_str_db)); + else + if( strcmpi(key, "global_acc_reg_num_db") == 0 ) + safestrncpy(db->global_acc_reg_num_db, value, sizeof(db->global_acc_reg_num_db)); else return false;// not found return true; @@ -393,7 +403,9 @@ static bool account_db_sql_remove(AccountDB* self, const int account_id) if( SQL_SUCCESS != SQL->QueryStr(sql_handle, "START TRANSACTION") || SQL_SUCCESS != SQL->Query(sql_handle, "DELETE FROM `%s` WHERE `account_id` = %d", db->account_db, account_id) - || SQL_SUCCESS != SQL->Query(sql_handle, "DELETE FROM `%s` WHERE `account_id` = %d", db->accreg_db, account_id) ) + || SQL_SUCCESS != SQL->Query(sql_handle, "DELETE FROM `%s` WHERE `account_id` = %d", db->global_acc_reg_num_db, account_id) + || SQL_SUCCESS != SQL->Query(sql_handle, "DELETE FROM `%s` WHERE `account_id` = %d", db->global_acc_reg_str_db, account_id) + ) Sql_ShowDebug(sql_handle); else result = true; @@ -520,7 +532,6 @@ static bool mmo_auth_fromsql(AccountDB_SQL* db, struct mmo_account* acc, int acc { Sql* sql_handle = db->accounts; char* data; - int i = 0; // retrieve login entry for the specified account if( SQL_ERROR == SQL->Query(sql_handle, @@ -556,27 +567,6 @@ static bool mmo_auth_fromsql(AccountDB_SQL* db, struct mmo_account* acc, int acc SQL->FreeResult(sql_handle); - - // retrieve account regs for the specified user - if( SQL_ERROR == SQL->Query(sql_handle, "SELECT `str`,`value` FROM `%s` WHERE `type`='1' AND `account_id`='%d'", db->accreg_db, acc->account_id) ) - { - Sql_ShowDebug(sql_handle); - return false; - } - - acc->account_reg2_num = (int)SQL->NumRows(sql_handle); - - while( SQL_SUCCESS == SQL->NextRow(sql_handle) ) - { - SQL->GetData(sql_handle, 0, &data, NULL); safestrncpy(acc->account_reg2[i].str, data, sizeof(acc->account_reg2[i].str)); - SQL->GetData(sql_handle, 1, &data, NULL); safestrncpy(acc->account_reg2[i].value, data, sizeof(acc->account_reg2[i].value)); - ++i; - } - SQL->FreeResult(sql_handle); - - if( i != acc->account_reg2_num ) - return false; - return true; } @@ -585,7 +575,6 @@ static bool mmo_auth_tosql(AccountDB_SQL* db, const struct mmo_account* acc, boo Sql* sql_handle = db->accounts; SqlStmt* stmt = SQL->StmtMalloc(sql_handle); bool result = false; - int i; // try do @@ -647,34 +636,6 @@ static bool mmo_auth_tosql(AccountDB_SQL* db, const struct mmo_account* acc, boo } } - // remove old account regs - if( SQL_SUCCESS != SQL->Query(sql_handle, "DELETE FROM `%s` WHERE `type`='1' AND `account_id`='%d'", db->accreg_db, acc->account_id) ) - { - Sql_ShowDebug(sql_handle); - break; - } - // insert new account regs - if( SQL_SUCCESS != SQL->StmtPrepare(stmt, "INSERT INTO `%s` (`type`, `account_id`, `str`, `value`) VALUES ( 1 , '%d' , ? , ? );", db->accreg_db, acc->account_id) ) - { - SqlStmt_ShowDebug(stmt); - break; - } - for( i = 0; i < acc->account_reg2_num; ++i ) - { - if( SQL_SUCCESS != SQL->StmtBindParam(stmt, 0, SQLDT_STRING, (void*)acc->account_reg2[i].str, strlen(acc->account_reg2[i].str)) - || SQL_SUCCESS != SQL->StmtBindParam(stmt, 1, SQLDT_STRING, (void*)acc->account_reg2[i].value, strlen(acc->account_reg2[i].value)) - || SQL_SUCCESS != SQL->StmtExecute(stmt) - ) { - SqlStmt_ShowDebug(stmt); - break; - } - } - if( i < acc->account_reg2_num ) - { - result = false; - break; - } - // if we got this far, everything was successful result = true; @@ -686,6 +647,7 @@ static bool mmo_auth_tosql(AccountDB_SQL* db, const struct mmo_account* acc, boo return result; } + Sql* account_db_sql_up(AccountDB* self) { AccountDB_SQL* db = (AccountDB_SQL*)self; Sql_HerculesUpdateCheck(db->accounts); @@ -694,3 +656,196 @@ Sql* account_db_sql_up(AccountDB* self) { #endif return db->accounts; } +void mmo_save_accreg2(AccountDB* self, int fd, int account_id, int char_id) { + Sql* sql_handle = ((AccountDB_SQL*)self)->accounts; + AccountDB_SQL* db = (AccountDB_SQL*)self; + int count = RFIFOW(fd, 12); + + if( count ) { + int cursor = 14, i; + char key[32], sval[254]; + unsigned int index; + + for(i = 0; i < count; i++) { + safestrncpy(key, (char*)RFIFOP(fd, cursor + 1), RFIFOB(fd, cursor)); + cursor += RFIFOB(fd, cursor) + 1; + + index = RFIFOL(fd, cursor); + cursor += 4; + + switch (RFIFOB(fd, cursor++)) { + /* int */ + case 0: + if( SQL_ERROR == SQL->Query(sql_handle, "REPLACE INTO `%s` (`account_id`,`key`,`index`,`value`) VALUES ('%d','%s','%u','%d')", db->global_acc_reg_num_db, account_id, key, index, RFIFOL(fd, cursor)) ) + Sql_ShowDebug(sql_handle); + cursor += 4; + break; + case 1: + if( SQL_ERROR == SQL->Query(sql_handle, "DELETE FROM `%s` WHERE `account_id` = '%d' AND `key` = '%s' AND `index` = '%u' LIMIT 1", db->global_acc_reg_num_db, account_id, key, index) ) + Sql_ShowDebug(sql_handle); + break; + /* str */ + case 2: + safestrncpy(sval, (char*)RFIFOP(fd, cursor + 1), RFIFOB(fd, cursor)); + cursor += RFIFOB(fd, cursor) + 1; + if( SQL_ERROR == SQL->Query(sql_handle, "REPLACE INTO `%s` (`account_id`,`key`,`index`,`value`) VALUES ('%d','%s','%u','%s')", db->global_acc_reg_str_db, account_id, key, index, sval) ) + Sql_ShowDebug(sql_handle); + break; + case 3: + if( SQL_ERROR == SQL->Query(sql_handle, "DELETE FROM `%s` WHERE `account_id` = '%d' AND `key` = '%s' AND `index` = '%u' LIMIT 1", db->global_acc_reg_str_db, account_id, key, index) ) + Sql_ShowDebug(sql_handle); + break; + + default: + ShowError("mmo_save_accreg2: DA HOO UNKNOWN TYPE %d\n",RFIFOB(fd, cursor - 1)); + return; + } + + } + + } + +} +void mmo_send_accreg2(AccountDB* self, int fd, int account_id, int char_id) { + Sql* sql_handle = ((AccountDB_SQL*)self)->accounts; + AccountDB_SQL* db = (AccountDB_SQL*)self; + char* data; + int plen = 0; + size_t len; + + if( SQL_ERROR == SQL->Query(sql_handle, "SELECT `key`, `index`, `value` FROM `%s` WHERE `account_id`='%d'", db->global_acc_reg_str_db, account_id) ) + Sql_ShowDebug(sql_handle); + + WFIFOHEAD(fd, 60000 + 300); + WFIFOW(fd, 0) = 0x3804; + /* 0x2 = length, set prior to being sent */ + WFIFOL(fd, 4) = account_id; + WFIFOL(fd, 8) = char_id; + WFIFOB(fd, 12) = 0;/* var type (only set when all vars have been sent, regardless of type) */ + WFIFOB(fd, 13) = 1;/* is string type */ + WFIFOW(fd, 14) = 0;/* count */ + plen = 16; + + /** + * Vessel! + * + * str type + * { keyLength(B), key(<keyLength>), index(L), valLength(B), val(<valLength>) } + **/ + while ( SQL_SUCCESS == SQL->NextRow(sql_handle) ) { + + SQL->GetData(sql_handle, 0, &data, NULL); + len = strlen(data)+1; + + WFIFOB(fd, plen) = (unsigned char)len;/* won't be higher; the column size is 32 */ + plen += 1; + + safestrncpy((char*)WFIFOP(fd,plen), data, len); + plen += len; + + SQL->GetData(sql_handle, 1, &data, NULL); + + WFIFOL(fd, plen) = (unsigned int)atol(data); + plen += 4; + + SQL->GetData(sql_handle, 2, &data, NULL); + len = strlen(data)+1; + + WFIFOB(fd, plen) = (unsigned char)len;/* won't be higher; the column size is 254 */ + plen += 1; + + safestrncpy((char*)WFIFOP(fd,plen), data, len); + plen += len; + + WFIFOW(fd, 14) += 1; + + if( plen > 60000 ) { + WFIFOW(fd, 2) = plen; + WFIFOSET(fd, plen); + + /* prepare follow up */ + WFIFOHEAD(fd, 60000 + 300); + WFIFOW(fd, 0) = 0x3804; + /* 0x2 = length, set prior to being sent */ + WFIFOL(fd, 4) = account_id; + WFIFOL(fd, 8) = char_id; + WFIFOB(fd, 12) = 0;/* var type (only set when all vars have been sent, regardless of type) */ + WFIFOB(fd, 13) = 1;/* is string type */ + WFIFOW(fd, 14) = 0;/* count */ + plen = 16; + } + } + + /* mark & go. */ + WFIFOW(fd, 2) = plen; + WFIFOSET(fd, plen); + + SQL->FreeResult(sql_handle); + + if( SQL_ERROR == SQL->Query(sql_handle, "SELECT `key`, `index`, `value` FROM `%s` WHERE `account_id`='%d'", db->global_acc_reg_num_db, account_id) ) + Sql_ShowDebug(sql_handle); + + WFIFOHEAD(fd, 60000 + 300); + WFIFOW(fd, 0) = 0x3804; + /* 0x2 = length, set prior to being sent */ + WFIFOL(fd, 4) = account_id; + WFIFOL(fd, 8) = char_id; + WFIFOB(fd, 12) = 0;/* var type (only set when all vars have been sent, regardless of type) */ + WFIFOB(fd, 13) = 0;/* is int type */ + WFIFOW(fd, 14) = 0;/* count */ + plen = 16; + + /** + * Vessel! + * + * int type + * { keyLength(B), key(<keyLength>), index(L), value(L) } + **/ + while ( SQL_SUCCESS == SQL->NextRow(sql_handle) ) { + + SQL->GetData(sql_handle, 0, &data, NULL); + len = strlen(data)+1; + + WFIFOB(fd, plen) = (unsigned char)len;/* won't be higher; the column size is 32 */ + plen += 1; + + safestrncpy((char*)WFIFOP(fd,plen), data, len); + plen += len; + + SQL->GetData(sql_handle, 1, &data, NULL); + + WFIFOL(fd, plen) = (unsigned int)atol(data); + plen += 4; + + SQL->GetData(sql_handle, 2, &data, NULL); + + WFIFOL(fd, plen) = atoi(data); + plen += 4; + + WFIFOW(fd, 14) += 1; + + if( plen > 60000 ) { + WFIFOW(fd, 2) = plen; + WFIFOSET(fd, plen); + + /* prepare follow up */ + WFIFOHEAD(fd, 60000 + 300); + WFIFOW(fd, 0) = 0x3804; + /* 0x2 = length, set prior to being sent */ + WFIFOL(fd, 4) = account_id; + WFIFOL(fd, 8) = char_id; + WFIFOB(fd, 12) = 0;/* var type (only set when all vars have been sent, regardless of type) */ + WFIFOB(fd, 13) = 0;/* is int type */ + WFIFOW(fd, 14) = 0;/* count */ + + plen = 16; + } + } + + /* mark as complete & go. */ + WFIFOB(fd, 12) = 1; + WFIFOW(fd, 2) = plen; + WFIFOSET(fd, plen); + + SQL->FreeResult(sql_handle); +} diff --git a/src/login/login.c b/src/login/login.c index 57a2d0b74..e9d0eac9d 100644 --- a/src/login/login.c +++ b/src/login/login.c @@ -704,30 +704,8 @@ int parse_fromchar(int fd) if( !accounts->load_num(accounts, &acc, account_id) ) ShowStatus("Char-server '%s': receiving (from the char-server) of account_reg2 (account: %d not found, ip: %s).\n", server[id].name, account_id, ip); - else - { - int len; - int p; - ShowNotice("char-server '%s': receiving (from the char-server) of account_reg2 (account: %d, ip: %s).\n", server[id].name, account_id, ip); - for( j = 0, p = 13; j < ACCOUNT_REG2_NUM && p < RFIFOW(fd,2); ++j ) - { - sscanf((char*)RFIFOP(fd,p), "%31c%n", acc.account_reg2[j].str, &len); - acc.account_reg2[j].str[len]='\0'; - p +=len+1; //+1 to skip the '\0' between strings. - sscanf((char*)RFIFOP(fd,p), "%255c%n", acc.account_reg2[j].value, &len); - acc.account_reg2[j].value[len]='\0'; - p +=len+1; - remove_control_chars(acc.account_reg2[j].str); - remove_control_chars(acc.account_reg2[j].value); - } - acc.account_reg2_num = j; - - // Save - accounts->save(accounts, &acc); - - // Sending information towards the other char-servers. - RFIFOW(fd,0) = 0x2729;// reusing read buffer - charif_sendallwos(fd, RFIFOP(fd,0), RFIFOW(fd,2)); + else { + mmo_save_accreg2(accounts,fd,account_id,RFIFOL(fd, 8)); } RFIFOSKIP(fd,RFIFOW(fd,2)); } @@ -797,34 +775,11 @@ int parse_fromchar(int fd) if (RFIFOREST(fd) < 10) return 0; { - struct mmo_account acc; - size_t off; - int account_id = RFIFOL(fd,2); int char_id = RFIFOL(fd,6); RFIFOSKIP(fd,10); - WFIFOHEAD(fd,ACCOUNT_REG2_NUM*sizeof(struct global_reg)); - WFIFOW(fd,0) = 0x2729; - WFIFOL(fd,4) = account_id; - WFIFOL(fd,8) = char_id; - WFIFOB(fd,12) = 1; //Type 1 for Account2 registry - - off = 13; - if( accounts->load_num(accounts, &acc, account_id) ) - { - for( j = 0; j < acc.account_reg2_num; j++ ) - { - if( acc.account_reg2[j].str[0] != '\0' ) - { - off += sprintf((char*)WFIFOP(fd,off), "%s", acc.account_reg2[j].str)+1; //We add 1 to consider the '\0' in place. - off += sprintf((char*)WFIFOP(fd,off), "%s", acc.account_reg2[j].value)+1; - } - } - } - - WFIFOW(fd,2) = (uint16)off; - WFIFOSET(fd,WFIFOW(fd,2)); + mmo_send_accreg2(accounts,fd,account_id,char_id); } break; diff --git a/src/login/login.h b/src/login/login.h index 80cbee74b..d6a021125 100644 --- a/src/login/login.h +++ b/src/login/login.h @@ -1,5 +1,6 @@ -// Copyright (c) Athena Dev Teams - Licensed under GNU GPL -// For more information, see LICENCE in the main folder +// Copyright (c) Hercules Dev Team, licensed under GNU GPL. +// See the LICENSE file +// Portions Copyright (c) Athena Dev Teams #ifndef _LOGIN_H_ #define _LOGIN_H_ diff --git a/src/map/atcommand.c b/src/map/atcommand.c index ffd6da9d5..d4b6c2382 100644 --- a/src/map/atcommand.c +++ b/src/map/atcommand.c @@ -8444,12 +8444,12 @@ ACMD(set) { break; case '#': if( reg[1] == '#' ) - data->u.str = pc_readaccountreg2str(sd, reg);// global + data->u.str = pc_readaccountreg2str(sd, script->add_str(reg));// global else - data->u.str = pc_readaccountregstr(sd, reg);// local + data->u.str = pc_readaccountregstr(sd, script->add_str(reg));// local break; default: - data->u.str = pc_readglobalreg_str(sd, reg); + data->u.str = pc_readglobalreg_str(sd, script->add_str(reg)); break; } @@ -8473,12 +8473,12 @@ ACMD(set) { break; case '#': if( reg[1] == '#' ) - data->u.num = pc_readaccountreg2(sd, reg);// global + data->u.num = pc_readaccountreg2(sd, script->add_str(reg));// global else - data->u.num = pc_readaccountreg(sd, reg);// local + data->u.num = pc_readaccountreg(sd, script->add_str(reg));// local break; default: - data->u.num = pc_readglobalreg(sd, reg); + data->u.num = pc_readglobalreg(sd, script->add_str(reg)); break; } diff --git a/src/map/battleground.c b/src/map/battleground.c index 6029a8c35..96d39aa45 100644 --- a/src/map/battleground.c +++ b/src/map/battleground.c @@ -496,7 +496,7 @@ void bg_match_over(struct bg_arena *arena, bool canceled) { if( canceled ) clif->colormes(sd->fd,COLOR_RED,"BG Match Cancelled: not enough players"); else { - pc_setglobalreg(sd, arena->delay_var, (unsigned int)time(NULL)); + pc_setglobalreg(sd, script->add_str(arena->delay_var), (unsigned int)time(NULL)); } } } @@ -693,7 +693,7 @@ enum BATTLEGROUNDS_QUEUE_ACK bg_canqueue(struct map_session_data *sd, struct bg_ tsec = (unsigned int)time(NULL); - if ( ( tick = pc_readglobalreg(sd, bg->gdelay_var) ) && tsec < tick ) { + if ( ( tick = pc_readglobalreg(sd, script->add_str(bg->gdelay_var)) ) && tsec < tick ) { char response[100]; if( (tick-tsec) > 60 ) sprintf(response, "You are a deserter! Wait %d minute(s) before you can apply again",(tick-tsec)/60); @@ -703,7 +703,7 @@ enum BATTLEGROUNDS_QUEUE_ACK bg_canqueue(struct map_session_data *sd, struct bg_ return BGQA_FAIL_DESERTER; } - if ( ( tick = pc_readglobalreg(sd, arena->delay_var) ) && tsec < tick ) { + if ( ( tick = pc_readglobalreg(sd, script->add_str(arena->delay_var)) ) && tsec < tick ) { char response[100]; if( (tick-tsec) > 60 ) sprintf(response, "You can't reapply to this arena so fast. Apply to the different arena or wait %d minute(s)",(tick-tsec)/60); diff --git a/src/map/chrif.c b/src/map/chrif.c index 7b5cbbe59..d66ec1d85 100644 --- a/src/map/chrif.c +++ b/src/map/chrif.c @@ -265,12 +265,8 @@ int chrif_save(struct map_session_data *sd, int flag) { sd->state.storage_flag = 0; //Force close it. //Saving of registry values. - if (sd->state.reg_dirty&4) - intif->saveregistry(sd, 3); //Save char regs - if (sd->state.reg_dirty&2) - intif->saveregistry(sd, 2); //Save account regs - if (sd->state.reg_dirty&1) - intif->saveregistry(sd, 1); //Save account2 regs + if (sd->vars_dirty) + intif->saveregistry(sd); WFIFOHEAD(chrif->fd, sizeof(sd->status) + 13); WFIFOW(chrif->fd,0) = 0x2b01; @@ -603,7 +599,7 @@ void chrif_authok(int fd) { //Causes problems if the currently connected player tries to quit or this data belongs to an already connected player which is trying to re-auth. if ( ( sd = map->id2sd(account_id) ) != NULL ) return; - + if ( ( node = chrif->search(account_id) ) == NULL ) return; // should not happen diff --git a/src/map/clif.c b/src/map/clif.c index 4ae6c8d34..5f61bafad 100644 --- a/src/map/clif.c +++ b/src/map/clif.c @@ -14419,7 +14419,7 @@ void clif_parse_FeelSaveOk(int fd,struct map_session_data *sd) sd->feel_map[i].index = map_id2index(sd->bl.m); sd->feel_map[i].m = sd->bl.m; - pc_setglobalreg(sd,pc->sg_info[i].feel_var,sd->feel_map[i].index); + pc_setglobalreg(sd,script->add_str(pc->sg_info[i].feel_var),sd->feel_map[i].index); //Are these really needed? Shouldn't they show up automatically from the feel save packet? // clif_misceffect2(&sd->bl, 0x1b0); diff --git a/src/map/duel.c b/src/map/duel.c index 5e305244a..30277be50 100644 --- a/src/map/duel.c +++ b/src/map/duel.c @@ -24,7 +24,7 @@ void duel_savetime(struct map_session_data* sd) { time(&clock); t = localtime(&clock); - pc_setglobalreg(sd, "PC_LAST_DUEL_TIME", t->tm_mday*24*60 + t->tm_hour*60 + t->tm_min); + pc_setglobalreg(sd, script->add_str("PC_LAST_DUEL_TIME"), t->tm_mday*24*60 + t->tm_hour*60 + t->tm_min); } int duel_checktime(struct map_session_data* sd) { @@ -35,7 +35,7 @@ int duel_checktime(struct map_session_data* sd) { time(&clock); t = localtime(&clock); - diff = t->tm_mday*24*60 + t->tm_hour*60 + t->tm_min - pc_readglobalreg(sd, "PC_LAST_DUEL_TIME"); + diff = t->tm_mday*24*60 + t->tm_hour*60 + t->tm_min - pc_readglobalreg(sd, script->add_str("PC_LAST_DUEL_TIME") ); return !(diff >= 0 && diff < battle_config.duel_time_interval); } diff --git a/src/map/instance.c b/src/map/instance.c index 924bbfd14..7c092e6cb 100644 --- a/src/map/instance.c +++ b/src/map/instance.c @@ -109,7 +109,8 @@ int instance_create(int owner_id, const char *name, enum instance_owner_type typ instance->list[i].num_map = 0; instance->list[i].owner_id = owner_id; instance->list[i].owner_type = type; - instance->list[i].vars = idb_alloc(DB_OPT_RELEASE_DATA); + instance->list[i].vars = i64db_alloc(DB_OPT_RELEASE_DATA); + instance->list[i].array_db = NULL; instance->list[i].respawn.map = 0; instance->list[i].respawn.y = 0; instance->list[i].respawn.x = 0; @@ -551,6 +552,8 @@ void instance_destroy(int instance_id) { if( instance->list[instance_id].vars ) db_destroy(instance->list[instance_id].vars); + if( instance->list[instance_id].array_db ) + instance->list[instance_id].array_db->destroy(instance->list[instance_id].array_db,script->array_free_db); if( instance->list[instance_id].progress_timer != INVALID_TIMER ) timer->delete( instance->list[instance_id].progress_timer, instance->destroy_timer); diff --git a/src/map/instance.h b/src/map/instance.h index 4f65d7db0..ddca3e36b 100644 --- a/src/map/instance.h +++ b/src/map/instance.h @@ -33,8 +33,9 @@ struct instance_data { unsigned short num_map; unsigned short users; - struct DBMap* vars; // Instance Variable for scripts - + struct DBMap *vars; // Instance Variable for scripts + struct DBMap *array_db ; + int progress_timer; unsigned int progress_timeout; diff --git a/src/map/intif.c b/src/map/intif.c index b9d4d1746..0a36fbc3d 100644 --- a/src/map/intif.c +++ b/src/map/intif.c @@ -265,60 +265,117 @@ int intif_wis_message_to_gm(char *wisp_name, int permission, char *mes) return 0; } -int intif_regtostr(char* str, struct global_reg *reg, int qty) -{ - int len =0, i; - - for (i = 0; i < qty; i++) { - len+= sprintf(str+len, "%s", reg[i].str)+1; //We add 1 to consider the '\0' in place. - len+= sprintf(str+len, "%s", reg[i].value)+1; - } - return len; -} - //Request for saving registry values. -int intif_saveregistry(struct map_session_data *sd, int type) -{ - struct global_reg *reg; - int count; - int i, p; +int intif_saveregistry(struct map_session_data *sd) { + DBIterator *iter; + DBKey key; + DBData *data; + int plen = 0; + size_t len; if (intif->CheckForCharServer()) return -1; - - switch (type) { - case 3: //Character reg - reg = sd->save_reg.global; - count = sd->save_reg.global_num; - sd->state.reg_dirty &= ~0x4; - break; - case 2: //Account reg - reg = sd->save_reg.account; - count = sd->save_reg.account_num; - sd->state.reg_dirty &= ~0x2; - break; - case 1: //Account2 reg - reg = sd->save_reg.account2; - count = sd->save_reg.account2_num; - sd->state.reg_dirty &= ~0x1; - break; - default: //Broken code? - ShowError("intif_saveregistry: Invalid type %d\n", type); - return -1; - } - WFIFOHEAD(inter_fd, 288 * MAX_REG_NUM+13); - WFIFOW(inter_fd,0)=0x3004; - WFIFOL(inter_fd,4)=sd->status.account_id; - WFIFOL(inter_fd,8)=sd->status.char_id; - WFIFOB(inter_fd,12)=type; - for( p = 13, i = 0; i < count; i++ ) { - if (reg[i].str[0] != '\0' && reg[i].value[0] != '\0') { - p+= sprintf((char*)WFIFOP(inter_fd,p), "%s", reg[i].str)+1; //We add 1 to consider the '\0' in place. - p+= sprintf((char*)WFIFOP(inter_fd,p), "%s", reg[i].value)+1; + + WFIFOHEAD(inter_fd, 60000 + 300); + WFIFOW(inter_fd,0) = 0x3004; + /* 0x2 = length (set later) */ + WFIFOL(inter_fd,4) = sd->status.account_id; + WFIFOL(inter_fd,8) = sd->status.char_id; + WFIFOW(inter_fd,12) = 0;/* count */ + + plen = 14; + + iter = db_iterator(sd->var_db); + for( data = iter->first(iter,&key); iter->exists(iter); data = iter->next(iter,&key) ) { + const char *varname = NULL; + void *src = NULL; + + if( data->type != DB_DATA_PTR ) /* its a @number */ + continue; + + varname = script->get_str(script_getvarid(key.i64)); + + if( varname[0] == '@' ) /* @string$ can get here, so we skip */ + continue; + + src = DB->data2ptr(data); + + /* no need! */ + if( !((struct script_reg_state*)src)->update ) + continue; + + ((struct script_reg_state*)src)->update = false; + + len = strlen(varname)+1; + + WFIFOB(inter_fd, plen) = (unsigned char)len;/* won't be higher; the column size is 32 */ + plen += 1; + + safestrncpy((char*)WFIFOP(inter_fd,plen), varname, len); + plen += len; + + WFIFOL(inter_fd, plen) = script_getvaridx(key.i64); + plen += 4; + + if( ((struct script_reg_state*)src)->type ) { + struct script_reg_str *p = src; + + WFIFOB(inter_fd, plen) = p->value ? 2 : 3; + plen += 1; + + if( p->value ) { + len = strlen(p->value)+1; + + WFIFOB(inter_fd, plen) = (unsigned char)len;/* won't be higher; the column size is 254 */ + plen += 1; + + safestrncpy((char*)WFIFOP(inter_fd,plen), p->value, len); + plen += len; + } else { + script->reg_destroy_single(sd,key.i64,&p->flag); + } + + } else { + struct script_reg_num *p = src; + + WFIFOB(inter_fd, plen) = p->value ? 0 : 1; + plen += 1; + + if( p->value ) { + WFIFOL(inter_fd, plen) = p->value; + plen += 4; + } else { + script->reg_destroy_single(sd,key.i64,&p->flag); + } + + } + + WFIFOW(inter_fd,12) += 1; + + if( plen > 60000 ) { + WFIFOW(inter_fd, 2) = plen; + WFIFOSET(inter_fd, plen); + + /* prepare follow up */ + WFIFOHEAD(inter_fd, 60000 + 300); + WFIFOW(inter_fd,0) = 0x3004; + /* 0x2 = length (set later) */ + WFIFOL(inter_fd,4) = sd->status.account_id; + WFIFOL(inter_fd,8) = sd->status.char_id; + WFIFOW(inter_fd,12) = 0;/* count */ + + plen = 14; } + } - WFIFOW(inter_fd,2)=p; - WFIFOSET(inter_fd,WFIFOW(inter_fd,2)); + dbi_destroy(iter); + + /* mark & go. */ + WFIFOW(inter_fd, 2) = plen; + WFIFOSET(inter_fd, plen); + + sd->vars_dirty = false; + return 0; } @@ -327,10 +384,7 @@ int intif_request_registry(struct map_session_data *sd, int flag) { nullpo_ret(sd); - sd->save_reg.account2_num = -1; - sd->save_reg.account_num = -1; - sd->save_reg.global_num = -1; - + /* if char server aint online it doesn't load, shouldn't we kill the session then? */ if (intif->CheckForCharServer()) return 0; @@ -927,54 +981,93 @@ void mapif_parse_WisToGM(int fd) // Request player registre void intif_parse_Registers(int fd) { - int j,p,len,max, flag; + int i, flag; struct map_session_data *sd; - struct global_reg *reg; - int *qty; int account_id = RFIFOL(fd,4), char_id = RFIFOL(fd,8); struct auth_node *node = chrif->auth_check(account_id, char_id, ST_LOGIN); + char type = RFIFOB(fd, 13); + if (node) sd = node->sd; else { //Normally registries should arrive for in log-in chars. sd = map->id2sd(account_id); - if (sd && RFIFOB(fd,12) == 3 && sd->status.char_id != char_id) - sd = NULL; //Character registry from another character. } - if (!sd) return; - - flag = (sd->save_reg.global_num == -1 || sd->save_reg.account_num == -1 || sd->save_reg.account2_num == -1); - + + if (!sd || sd->status.char_id != char_id) { + return; //Character registry from another character. + } + + flag = ( sd->vars_received&PRL_ACCG && sd->vars_received&PRL_ACCL && sd->vars_received&PRL_CHAR ) ? 0 : 1; + switch (RFIFOB(fd,12)) { case 3: //Character Registry - reg = sd->save_reg.global; - qty = &sd->save_reg.global_num; - max = GLOBAL_REG_NUM; - break; + sd->vars_received |= PRL_CHAR; + break; case 2: //Account Registry - reg = sd->save_reg.account; - qty = &sd->save_reg.account_num; - max = ACCOUNT_REG_NUM; - break; + sd->vars_received |= PRL_ACCL; + break; case 1: //Account2 Registry - reg = sd->save_reg.account2; - qty = &sd->save_reg.account2_num; - max = ACCOUNT_REG2_NUM; - break; + sd->vars_received |= PRL_ACCG; + break; + case 0: + break; default: ShowError("intif_parse_Registers: Unrecognized type %d\n",RFIFOB(fd,12)); return; } - for(j=0,p=13;j<max && p<RFIFOW(fd,2);j++){ - sscanf((char*)RFIFOP(fd,p), "%31c%n", reg[j].str,&len); - reg[j].str[len]='\0'; - p += len+1; //+1 to skip the '\0' between strings. - sscanf((char*)RFIFOP(fd,p), "%255c%n", reg[j].value,&len); - reg[j].value[len]='\0'; - p += len+1; + /* have it not complain about insertion of vars before loading, and not set those vars as new or modified */ + pc->reg_load = true; + + if( RFIFOW(fd, 14) ) { + char key[32], sval[254]; + unsigned int index; + int max = RFIFOW(fd, 14), cursor = 16, ival; + + /** + * Vessel!char_reg_num_db + * + * str type + * { keyLength(B), key(<keyLength>), index(L), valLength(B), val(<valLength>) } + **/ + if( type ) { + for(i = 0; i < max; i++) { + safestrncpy(key, (char*)RFIFOP(fd, cursor + 1), RFIFOB(fd, cursor)); + cursor += RFIFOB(fd, cursor) + 1; + + index = RFIFOL(fd, cursor); + cursor += 4; + + safestrncpy(sval, (char*)RFIFOP(fd, cursor + 1), RFIFOB(fd, cursor)); + cursor += RFIFOB(fd, cursor) + 1; + + script->set_reg(NULL,sd,reference_uid(script->add_str(key), index), key, (void*)sval, NULL); + } + /** + * Vessel! + * + * int type + * { keyLength(B), key(<keyLength>), index(L), value(L) } + **/ + } else { + for(i = 0; i < max; i++) { + safestrncpy(key, (char*)RFIFOP(fd, cursor + 1), RFIFOB(fd, cursor)); + cursor += RFIFOB(fd, cursor) + 1; + + index = RFIFOL(fd, cursor); + cursor += 4; + + ival = RFIFOL(fd, cursor); + cursor += 4; + + script->set_reg(NULL,sd,reference_uid(script->add_str(key), index), key, (void*)__64BPTRSIZE(ival), NULL); + } + } } - *qty = j; - - if (flag && sd->save_reg.global_num > -1 && sd->save_reg.account_num > -1 && sd->save_reg.account2_num > -1) + + /* flag it back */ + pc->reg_load = false; + + if (flag && sd->vars_received&PRL_ACCG && sd->vars_received&PRL_ACCL && sd->vars_received&PRL_CHAR) pc->reg_received(sd); //Received all registry values, execute init scripts and what-not. [Skotlex] } diff --git a/src/map/intif.h b/src/map/intif.h index bd3908ce7..f0bb5c16e 100644 --- a/src/map/intif.h +++ b/src/map/intif.h @@ -45,7 +45,7 @@ struct intif_interface { int (*main_message) (struct map_session_data* sd, const char* message); int (*wis_message) (struct map_session_data *sd,char *nick,char *mes,size_t mes_len); int (*wis_message_to_gm) (char *Wisp_name, int permission, char *mes); - int (*saveregistry) (struct map_session_data *sd, int type); + int (*saveregistry) (struct map_session_data *sd); int (*request_registry) (struct map_session_data *sd, int flag); int (*request_guild_storage) (int account_id, int guild_id); int (*send_guild_storage) (int account_id, struct guild_storage *gstor); diff --git a/src/map/mapreg.h b/src/map/mapreg.h index c8f229cef..157e634cc 100644 --- a/src/map/mapreg.h +++ b/src/map/mapreg.h @@ -9,7 +9,7 @@ #include "../common/db.h" struct mapreg_save { - int uid; + int64 uid; union { int i; char *str; @@ -19,19 +19,27 @@ struct mapreg_save { struct mapreg_interface { DBMap *db; // int var_id -> int value + /* TODO duck str_db, use same */ DBMap *str_db; // int var_id -> char* value + /* */ + DBMap *array_db; + /* */ + bool skip_insert; + /* */ struct eri *ers; //[Ind/Hercules] + /* */ char table[32]; + /* */ bool i_dirty; bool str_dirty; /* */ void (*init) (void); void (*final) (void); /* */ - int (*readreg) (int uid); - char* (*readregstr) (int uid); - bool (*setreg) (int uid, int val); - bool (*setregstr) (int uid, const char *str); + int (*readreg) (int64 uid); + char* (*readregstr) (int64 uid); + bool (*setreg) (int64 uid, int val); + bool (*setregstr) (int64 uid, const char *str); void (*load) (void); void (*save) (void); int (*save_timer) (int tid, int64 tick, int id, intptr_t data); diff --git a/src/map/mapreg_sql.c b/src/map/mapreg_sql.c index 6a13ef2a0..6df614aac 100644 --- a/src/map/mapreg_sql.c +++ b/src/map/mapreg_sql.c @@ -21,51 +21,56 @@ struct mapreg_interface mapreg_s; #define MAPREG_AUTOSAVE_INTERVAL (300*1000) /// Looks up the value of an integer variable using its uid. -int mapreg_readreg(int uid) { - struct mapreg_save *m = idb_get(mapreg->db, uid); +int mapreg_readreg(int64 uid) { + struct mapreg_save *m = i64db_get(mapreg->db, uid); return m?m->u.i:0; } /// Looks up the value of a string variable using its uid. -char* mapreg_readregstr(int uid) { - struct mapreg_save *m = idb_get(mapreg->str_db, uid); +char* mapreg_readregstr(int64 uid) { + struct mapreg_save *m = i64db_get(mapreg->str_db, uid); return m?m->u.str:NULL; } /// Modifies the value of an integer variable. -bool mapreg_setreg(int uid, int val) { +bool mapreg_setreg(int64 uid, int val) { struct mapreg_save *m; - int num = (uid & 0x00ffffff); - int i = (uid & 0xff000000) >> 24; + int num = script_getvarid(uid); + unsigned int i = script_getvaridx(uid); const char* name = script->get_str(num); if( val != 0 ) { - if( (m = idb_get(mapreg->db,uid)) ) { + if( (m = i64db_get(mapreg->db,uid)) ) { m->u.i = val; if(name[1] != '@') { m->save = true; mapreg->i_dirty = true; } } else { + if( i ) + script->array_update(&mapreg->array_db,uid,false); + m = ers_alloc(mapreg->ers, struct mapreg_save); m->u.i = val; m->uid = uid; m->save = false; - if(name[1] != '@') {// write new variable to database + if(name[1] != '@' && !mapreg->skip_insert) {// write new variable to database char tmp_str[32*2+1]; SQL->EscapeStringLen(map->mysql_handle, tmp_str, name, strnlen(name, 32)); if( SQL_ERROR == SQL->Query(map->mysql_handle, "INSERT INTO `%s`(`varname`,`index`,`value`) VALUES ('%s','%d','%d')", mapreg->table, tmp_str, i, val) ) Sql_ShowDebug(map->mysql_handle); } - idb_put(mapreg->db, uid, m); + i64db_put(mapreg->db, uid, m); } } else { // val == 0 - if( (m = idb_get(mapreg->db,uid)) ) { + if( i ) + script->array_update(&mapreg->array_db,uid,true); + if( (m = i64db_get(mapreg->db,uid)) ) { ers_free(mapreg->ers, m); } - idb_remove(mapreg->db,uid); + i64db_remove(mapreg->db,uid); if( name[1] != '@' ) {// Remove from database because it is unused. if( SQL_ERROR == SQL->Query(map->mysql_handle, "DELETE FROM `%s` WHERE `varname`='%s' AND `index`='%d'", mapreg->table, name, i) ) @@ -77,25 +82,27 @@ bool mapreg_setreg(int uid, int val) { } /// Modifies the value of a string variable. -bool mapreg_setregstr(int uid, const char* str) { +bool mapreg_setregstr(int64 uid, const char* str) { struct mapreg_save *m; - int num = (uid & 0x00ffffff); - int i = (uid & 0xff000000) >> 24; + int num = script_getvarid(uid); + unsigned int i = script_getvaridx(uid); const char* name = script->get_str(num); if( str == NULL || *str == 0 ) { + if( i ) + script->array_update(&mapreg->array_db,uid,true); if(name[1] != '@') { if( SQL_ERROR == SQL->Query(map->mysql_handle, "DELETE FROM `%s` WHERE `varname`='%s' AND `index`='%d'", mapreg->table, name, i) ) Sql_ShowDebug(map->mysql_handle); } - if( (m = idb_get(mapreg->str_db,uid)) ) { + if( (m = i64db_get(mapreg->str_db,uid)) ) { if( m->u.str != NULL ) aFree(m->u.str); ers_free(mapreg->ers, m); } - idb_remove(mapreg->str_db,uid); + i64db_remove(mapreg->str_db,uid); } else { - if( (m = idb_get(mapreg->str_db,uid)) ) { + if( (m = i64db_get(mapreg->str_db,uid)) ) { if( m->u.str != NULL ) aFree(m->u.str); m->u.str = aStrdup(str); @@ -104,13 +111,16 @@ bool mapreg_setregstr(int uid, const char* str) { m->save = true; } } else { + if( i ) + script->array_update(&mapreg->array_db,uid,false); + m = ers_alloc(mapreg->ers, struct mapreg_save); m->uid = uid; m->u.str = aStrdup(str); m->save = false; - if(name[1] != '@') { //put returned null, so we must insert. + if(name[1] != '@' && !mapreg->skip_insert) { //put returned null, so we must insert. char tmp_str[32*2+1]; char tmp_str2[255*2+1]; SQL->EscapeStringLen(map->mysql_handle, tmp_str, name, strnlen(name, 32)); @@ -118,7 +128,7 @@ bool mapreg_setregstr(int uid, const char* str) { if( SQL_ERROR == SQL->Query(map->mysql_handle, "INSERT INTO `%s`(`varname`,`index`,`value`) VALUES ('%s','%d','%s')", mapreg->table, tmp_str, i, tmp_str2) ) Sql_ShowDebug(map->mysql_handle); } - idb_put(mapreg->str_db, uid, m); + i64db_put(mapreg->str_db, uid, m); } } @@ -147,41 +157,36 @@ void script_load_mapreg(void) { return; } + mapreg->skip_insert = true; + SQL->StmtBindColumn(stmt, 0, SQLDT_STRING, &varname[0], sizeof(varname), &length, NULL); SQL->StmtBindColumn(stmt, 1, SQLDT_INT, &index, 0, NULL, NULL); SQL->StmtBindColumn(stmt, 2, SQLDT_STRING, &value[0], sizeof(value), NULL, NULL); - + while ( SQL_SUCCESS == SQL->StmtNextRow(stmt) ) { - struct mapreg_save *m = NULL; int s = script->add_str(varname); int i = index; + if( varname[length-1] == '$' ) { - if( idb_exists(mapreg->str_db, (i<<24)|s) ) { + if( i64db_exists(mapreg->str_db, reference_uid(s, i)) ) { ShowWarning("load_mapreg: duplicate! '%s' => '%s' skipping...\n",varname,value); continue; } + script->set_reg(NULL,NULL,reference_uid(s, i), varname, (void*)value, NULL); } else { - if( idb_exists(mapreg->db, (i<<24)|s) ) { + if( i64db_exists(mapreg->db, reference_uid(s, i)) ) { ShowWarning("load_mapreg: duplicate! '%s' => '%s' skipping...\n",varname,value); continue; } - } - - m = ers_alloc(mapreg->ers, struct mapreg_save); - m->uid = (i<<24)|s; - m->save = false; - if( varname[length-1] == '$' ) { - m->u.str = aStrdup(value); - idb_put(mapreg->str_db, m->uid, m); - } else { - m->u.i = atoi(value); - idb_put(mapreg->db, m->uid, m); + script->set_reg(NULL,NULL,reference_uid(s, i), varname, (void*)__64BPTRSIZE(atoi(value)), NULL); } } SQL->StmtFree(stmt); + mapreg->skip_insert = false; + mapreg->i_dirty = false; mapreg->str_dirty = false; } @@ -195,8 +200,8 @@ void script_save_mapreg(void) { iter = db_iterator(mapreg->db); for( m = dbi_first(iter); dbi_exists(iter); m = dbi_next(iter) ) { if( m->save ) { - int num = (m->uid & 0x00ffffff); - int i = (m->uid & 0xff000000) >> 24; + int num = script_getvarid(m->uid); + int i = script_getvaridx(m->uid); const char* name = script->get_str(num); if( SQL_ERROR == SQL->Query(map->mysql_handle, "UPDATE `%s` SET `value`='%d' WHERE `varname`='%s' AND `index`='%d' LIMIT 1", mapreg->table, m->u.i, name, i) ) @@ -212,8 +217,8 @@ void script_save_mapreg(void) { iter = db_iterator(mapreg->str_db); for( m = dbi_first(iter); dbi_exists(iter); m = dbi_next(iter) ) { if( m->save ) { - int num = (m->uid & 0x00ffffff); - int i = (m->uid & 0xff000000) >> 24; + int num = script_getvarid(m->uid); + int i = script_getvaridx(m->uid); const char* name = script->get_str(num); char tmp_str2[2*255+1]; @@ -257,7 +262,10 @@ void mapreg_reload(void) { db_clear(mapreg->db); db_clear(mapreg->str_db); - + + if( mapreg->array_db ) + mapreg->array_db->destroy(mapreg->array_db,script->array_free_db); + mapreg->load(); } @@ -286,11 +294,14 @@ void mapreg_final(void) { db_destroy(mapreg->str_db); ers_destroy(mapreg->ers); + + if( mapreg->array_db ) + mapreg->array_db->destroy(mapreg->array_db,script->array_free_db); } void mapreg_init(void) { - mapreg->db = idb_alloc(DB_OPT_BASE); - mapreg->str_db = idb_alloc(DB_OPT_BASE); + mapreg->db = i64db_alloc(DB_OPT_BASE); + mapreg->str_db = i64db_alloc(DB_OPT_BASE); mapreg->ers = ers_new(sizeof(struct mapreg_save), "mapreg_sql.c::mapreg_ers", ERS_OPT_CLEAN); mapreg->load(); @@ -314,12 +325,16 @@ void mapreg_defaults(void) { mapreg->db = NULL; mapreg->str_db = NULL; mapreg->ers = NULL; + mapreg->skip_insert = false; safestrncpy(mapreg->table, "mapreg", sizeof(mapreg->table)); mapreg->i_dirty = false; mapreg->str_dirty = false; /* */ + mapreg->array_db = NULL; + + /* */ mapreg->init = mapreg_init; mapreg->final = mapreg_final; diff --git a/src/map/mob.c b/src/map/mob.c index d919e7478..6a8aa5f03 100644 --- a/src/map/mob.c +++ b/src/map/mob.c @@ -2552,11 +2552,11 @@ int mob_dead(struct mob_data *md, struct block_list *src, int type) { if( ++sd->mission_count >= 100 && (temp = mob->get_random_id(0, 0xE, sd->status.base_level)) ) { pc->addfame(sd, 1); sd->mission_mobid = temp; - pc_setglobalreg(sd,"TK_MISSION_ID", temp); + pc_setglobalreg(sd,script->add_str("TK_MISSION_ID"), temp); sd->mission_count = 0; clif->mission_info(sd, temp, 0); } - pc_setglobalreg(sd,"TK_MISSION_COUNT", sd->mission_count); + pc_setglobalreg(sd,script->add_str("TK_MISSION_COUNT"), sd->mission_count); } if( sd->status.party_id ) diff --git a/src/map/pc.c b/src/map/pc.c index 9e819bcac..b090b7b17 100644 --- a/src/map/pc.c +++ b/src/map/pc.c @@ -968,7 +968,7 @@ bool pc_authok(struct map_session_data *sd, int login_id2, time_t expiration_tim uint32 ip = session[sd->fd]->client_addr; sd->login_id2 = login_id2; - + if (pc->set_group(sd, group_id) != 0) { ShowWarning("pc_authok: %s (AID:%d) logged in with unknown group id (%d)! kicking...\n", st->name, sd->status.account_id, group_id); @@ -1108,6 +1108,11 @@ bool pc_authok(struct map_session_data *sd, int login_id2, time_t expiration_tim sd->avail_quests = 0; sd->save_quest = false; + sd->var_db = i64db_alloc(DB_OPT_BASE); + sd->vars_dirty = false; + sd->vars_ok = false; + sd->vars_received = 0x0; + //warp player if ((i=pc->setpos(sd,sd->status.last_point.map, sd->status.last_point.x, sd->status.last_point.y, CLR_OUTSIGHT)) != 0) { ShowError ("Last_point_map %s - id %d not found (error code %d)\n", mapindex_id2name(sd->status.last_point.map), sd->status.last_point.map, i); @@ -1214,7 +1219,7 @@ int pc_set_hate_mob(struct map_session_data *sd, int pos, struct block_list *bl) return 0; //Wrong size } sd->hate_mob[pos] = class_; - pc_setglobalreg(sd,pc->sg_info[pos].hate_var,class_+1); + pc_setglobalreg(sd,script->add_str(pc->sg_info[pos].hate_var),class_+1); clif->hate_info(sd, pos, class_, 1); return 1; } @@ -1226,55 +1231,58 @@ int pc_reg_received(struct map_session_data *sd) { int i,j, idx = 0; - sd->change_level_2nd = pc_readglobalreg(sd,"jobchange_level"); - sd->change_level_3rd = pc_readglobalreg(sd,"jobchange_level_3rd"); - sd->die_counter = pc_readglobalreg(sd,"PC_DIE_COUNTER"); + sd->vars_ok = true; + + sd->change_level_2nd = pc_readglobalreg(sd,script->add_str("jobchange_level")); + sd->change_level_3rd = pc_readglobalreg(sd,script->add_str("jobchange_level_3rd")); + sd->die_counter = pc_readglobalreg(sd,script->add_str("PC_DIE_COUNTER")); // Cash shop - sd->cashPoints = pc_readaccountreg(sd,"#CASHPOINTS"); - sd->kafraPoints = pc_readaccountreg(sd,"#KAFRAPOINTS"); + sd->cashPoints = pc_readaccountreg(sd,script->add_str("#CASHPOINTS")); + sd->kafraPoints = pc_readaccountreg(sd,script->add_str("#KAFRAPOINTS")); // Cooking Exp - sd->cook_mastery = pc_readglobalreg(sd,"COOK_MASTERY"); + sd->cook_mastery = pc_readglobalreg(sd,script->add_str("COOK_MASTERY")); if( (sd->class_&MAPID_BASEMASK) == MAPID_TAEKWON ) { // Better check for class rather than skill to prevent "skill resets" from unsetting this - sd->mission_mobid = pc_readglobalreg(sd,"TK_MISSION_ID"); - sd->mission_count = pc_readglobalreg(sd,"TK_MISSION_COUNT"); + sd->mission_mobid = pc_readglobalreg(sd,script->add_str("TK_MISSION_ID")); + sd->mission_count = pc_readglobalreg(sd,script->add_str("TK_MISSION_COUNT")); } //SG map and mob read [Komurka] for(i=0;i<MAX_PC_FEELHATE;i++) { //for now - someone need to make reading from txt/sql - if ((j = pc_readglobalreg(sd,pc->sg_info[i].feel_var))!=0) { + if ((j = pc_readglobalreg(sd,script->add_str(pc->sg_info[i].feel_var)))!=0) { sd->feel_map[i].index = j; sd->feel_map[i].m = map->mapindex2mapid(j); } else { sd->feel_map[i].index = 0; sd->feel_map[i].m = -1; } - sd->hate_mob[i] = pc_readglobalreg(sd,pc->sg_info[i].hate_var)-1; + sd->hate_mob[i] = pc_readglobalreg(sd,script->add_str(pc->sg_info[i].hate_var))-1; } if ((i = pc->checkskill(sd,RG_PLAGIARISM)) > 0) { - sd->cloneskill_id = pc_readglobalreg(sd,"CLONE_SKILL"); + sd->cloneskill_id = pc_readglobalreg(sd,script->add_str("CLONE_SKILL")); if (sd->cloneskill_id > 0 && (idx = skill->get_index(sd->cloneskill_id))) { sd->status.skill[idx].id = sd->cloneskill_id; - sd->status.skill[idx].lv = pc_readglobalreg(sd,"CLONE_SKILL_LV"); + sd->status.skill[idx].lv = pc_readglobalreg(sd,script->add_str("CLONE_SKILL_LV")); if (sd->status.skill[idx].lv > i) sd->status.skill[idx].lv = i; sd->status.skill[idx].flag = SKILL_FLAG_PLAGIARIZED; } } if ((i = pc->checkskill(sd,SC_REPRODUCE)) > 0) { - sd->reproduceskill_id = pc_readglobalreg(sd,"REPRODUCE_SKILL"); + sd->reproduceskill_id = pc_readglobalreg(sd,script->add_str("REPRODUCE_SKILL")); if( sd->reproduceskill_id > 0 && (idx = skill->get_index(sd->reproduceskill_id))) { sd->status.skill[idx].id = sd->reproduceskill_id; - sd->status.skill[idx].lv = pc_readglobalreg(sd,"REPRODUCE_SKILL_LV"); + sd->status.skill[idx].lv = pc_readglobalreg(sd,script->add_str("REPRODUCE_SKILL_LV")); if( i < sd->status.skill[idx].lv) sd->status.skill[idx].lv = i; sd->status.skill[idx].flag = SKILL_FLAG_PLAGIARIZED; } } + //Weird... maybe registries were reloaded? if (sd->state.active) return 0; @@ -1652,7 +1660,7 @@ int pc_calc_skilltree_normalize_job(struct map_session_data *sd) } - pc_setglobalreg (sd, "jobchange_level", sd->change_level_2nd); + pc_setglobalreg (sd, script->add_str("jobchange_level"), sd->change_level_2nd); } if (skill_point < novice_skills + (sd->change_level_2nd - 1)) { @@ -1665,7 +1673,7 @@ int pc_calc_skilltree_normalize_job(struct map_session_data *sd) - (sd->status.job_level - 1) - (sd->change_level_2nd - 1) - novice_skills; - pc_setglobalreg (sd, "jobchange_level_3rd", sd->change_level_3rd); + pc_setglobalreg (sd, script->add_str("jobchange_level_3rd"), sd->change_level_3rd); } if (skill_point < novice_skills + (sd->change_level_2nd - 1) + (sd->change_level_3rd - 1)) { @@ -3777,8 +3785,8 @@ int pc_paycash(struct map_session_data *sd, int price, int points) return -1; } - pc_setaccountreg(sd, "#CASHPOINTS", sd->cashPoints-cash); - pc_setaccountreg(sd, "#KAFRAPOINTS", sd->kafraPoints-points); + pc_setaccountreg(sd, script->add_str("#CASHPOINTS"), sd->cashPoints-cash); + pc_setaccountreg(sd, script->add_str("#KAFRAPOINTS"), sd->kafraPoints-points); if( battle_config.cashshop_show_points ) { @@ -3804,7 +3812,7 @@ int pc_getcash(struct map_session_data *sd, int cash, int points) cash = MAX_ZENY-sd->cashPoints; } - pc_setaccountreg(sd, "#CASHPOINTS", sd->cashPoints+cash); + pc_setaccountreg(sd, script->add_str("#CASHPOINTS"), sd->cashPoints+cash); if( battle_config.cashshop_show_points ) { @@ -3827,7 +3835,7 @@ int pc_getcash(struct map_session_data *sd, int cash, int points) points = MAX_ZENY-sd->kafraPoints; } - pc_setaccountreg(sd, "#KAFRAPOINTS", sd->kafraPoints+points); + pc_setaccountreg(sd, script->add_str("#KAFRAPOINTS"), sd->kafraPoints+points); if( battle_config.cashshop_show_points ) { @@ -6505,7 +6513,7 @@ int pc_resetstate(struct map_session_data* sd) if( sd->mission_mobid ) { //bugreport:2200 sd->mission_mobid = 0; sd->mission_count = 0; - pc_setglobalreg(sd,"TK_MISSION_ID", 0); + pc_setglobalreg(sd,script->add_str("TK_MISSION_ID"), 0); } status_calc_pc(sd,SCO_NONE); @@ -6637,7 +6645,7 @@ int pc_resetfeel(struct map_session_data* sd) { sd->feel_map[i].m = -1; sd->feel_map[i].index = 0; - pc_setglobalreg(sd,pc->sg_info[i].feel_var,0); + pc_setglobalreg(sd,script->add_str(pc->sg_info[i].feel_var),0); } return 0; @@ -6651,7 +6659,7 @@ int pc_resethate(struct map_session_data* sd) for (i=0; i<3; i++) { sd->hate_mob[i] = -1; - pc_setglobalreg(sd,pc->sg_info[i].hate_var,0); + pc_setglobalreg(sd,script->add_str(pc->sg_info[i].hate_var),0); } return 0; } @@ -6806,7 +6814,7 @@ int pc_dead(struct map_session_data *sd,struct block_list *src) { if (sd->npc_id && sd->st && sd->st->state != RUN) npc->event_dequeue(sd); - pc_setglobalreg(sd,"PC_DIE_COUNTER",sd->die_counter+1); + pc_setglobalreg(sd,script->add_str("PC_DIE_COUNTER"),sd->die_counter+1); pc->setparam(sd, SP_KILLERRID, src?src->id:0); if( sd->bg_id ) {/* TODO: purge when bgqueue is deemed ok */ @@ -7608,12 +7616,12 @@ int pc_jobchange(struct map_session_data *sd,int job, int upper) // changing from 1st to 2nd job if ((b_class&JOBL_2) && !(sd->class_&JOBL_2) && (b_class&MAPID_UPPERMASK) != MAPID_SUPER_NOVICE) { sd->change_level_2nd = sd->status.job_level; - pc_setglobalreg (sd, "jobchange_level", sd->change_level_2nd); + pc_setglobalreg (sd, script->add_str("jobchange_level"), sd->change_level_2nd); } // changing from 2nd to 3rd job else if((b_class&JOBL_THIRD) && !(sd->class_&JOBL_THIRD)) { sd->change_level_3rd = sd->status.job_level; - pc_setglobalreg (sd, "jobchange_level_3rd", sd->change_level_3rd); + pc_setglobalreg (sd, script->add_str("jobchange_level_3rd"), sd->change_level_3rd); } if(sd->cloneskill_id) { @@ -7625,8 +7633,8 @@ int pc_jobchange(struct map_session_data *sd,int job, int upper) clif->deleteskill(sd,sd->cloneskill_id); } sd->cloneskill_id = 0; - pc_setglobalreg(sd, "CLONE_SKILL", 0); - pc_setglobalreg(sd, "CLONE_SKILL_LV", 0); + pc_setglobalreg(sd, script->add_str("CLONE_SKILL"), 0); + pc_setglobalreg(sd, script->add_str("CLONE_SKILL_LV"), 0); } if(sd->reproduceskill_id) { @@ -7638,8 +7646,8 @@ int pc_jobchange(struct map_session_data *sd,int job, int upper) clif->deleteskill(sd,sd->reproduceskill_id); } sd->reproduceskill_id = 0; - pc_setglobalreg(sd, "REPRODUCE_SKILL",0); - pc_setglobalreg(sd, "REPRODUCE_SKILL_LV",0); + pc_setglobalreg(sd, script->add_str("REPRODUCE_SKILL"),0); + pc_setglobalreg(sd, script->add_str("REPRODUCE_SKILL_LV"),0); } if ( (b_class&MAPID_UPPERMASK) != (sd->class_&MAPID_UPPERMASK) ) { //Things to remove when changing class tree. @@ -8044,341 +8052,245 @@ int pc_candrop(struct map_session_data *sd, struct item *item) return 0; return (itemdb_isdropable(item, pc_get_group_level(sd))); } - -/*========================================== - * Read ram register for player sd - * get val (int) from reg for player sd - *------------------------------------------*/ -int pc_readreg(struct map_session_data* sd, int reg) -{ - int i; - - nullpo_ret(sd); - - ARR_FIND( 0, sd->reg_num, i, sd->reg[i].index == reg ); - return ( i < sd->reg_num ) ? sd->reg[i].data : 0; +/** + * For '@type' variables (temporary numeric char reg) + **/ +int pc_readreg(struct map_session_data* sd, int64 reg) { + return i64db_iget(sd->var_db, reg); } -/*========================================== - * Set ram register for player sd - * memo val(int) at reg for player sd - *------------------------------------------*/ -int pc_setreg(struct map_session_data* sd, int reg, int val) -{ - int i; - - nullpo_ret(sd); - - ARR_FIND( 0, sd->reg_num, i, sd->reg[i].index == reg ); - if( i < sd->reg_num ) - {// overwrite existing entry - sd->reg[i].data = val; - return 1; - } - - ARR_FIND( 0, sd->reg_num, i, sd->reg[i].data == 0 ); - if( i == sd->reg_num ) - {// nothing free, increase size - sd->reg_num++; - RECREATE(sd->reg, struct script_reg, sd->reg_num); +/** + * For '@type' variables (temporary numeric char reg) + **/ +void pc_setreg(struct map_session_data* sd, int64 reg, int val) { + unsigned int index = script_getvaridx(reg); + + if( val ) { + i64db_iput(sd->var_db, reg, val); + if( index ) + script->array_update(&sd->array_db,reg,false); + } else { + i64db_remove(sd->var_db, reg); + if( index ) + script->array_update(&sd->array_db,reg,true); } - sd->reg[i].index = reg; - sd->reg[i].data = val; - - return 1; } -/*========================================== - * Read ram register for player sd - * get val (str) from reg for player sd - *------------------------------------------*/ -char* pc_readregstr(struct map_session_data* sd, int reg) -{ - int i; - - nullpo_ret(sd); +/** + * For '@type$' variables (temporary string char reg) + **/ +char* pc_readregstr(struct map_session_data* sd, int64 reg) { + struct script_reg_str *p = NULL; - ARR_FIND( 0, sd->regstr_num, i, sd->regstr[i].index == reg ); - return ( i < sd->regstr_num ) ? sd->regstr[i].data : NULL; + p = i64db_get(sd->var_db, reg); + + return p ? p->value : NULL; } -/*========================================== - * Set ram register for player sd - * memo val(str) at reg for player sd - *------------------------------------------*/ -int pc_setregstr(struct map_session_data* sd, int reg, const char* str) -{ - int i; - - nullpo_ret(sd); +/** + * For '@type$' variables (temporary string char reg) + **/ +void pc_setregstr(struct map_session_data* sd, int64 reg, const char* str) { + struct script_reg_str *p = NULL; + unsigned int index = script_getvaridx(reg); + DBData prev; - ARR_FIND( 0, sd->regstr_num, i, sd->regstr[i].index == reg ); - if( i < sd->regstr_num ) - {// found entry, update - if( str == NULL || *str == '\0' ) - {// empty string - if( sd->regstr[i].data != NULL ) - aFree(sd->regstr[i].data); - sd->regstr[i].data = NULL; - } - else if( sd->regstr[i].data ) - {// recreate - size_t len = strlen(str)+1; - RECREATE(sd->regstr[i].data, char, len); - memcpy(sd->regstr[i].data, str, len*sizeof(char)); + if( str[0] ) { + p = ers_alloc(pc->str_reg_ers, struct script_reg_str); + + p->value = aStrdup(str); + + if( sd->var_db->put(sd->var_db,DB->i642key(reg),DB->ptr2data(p),&prev) ) { + p = DB->data2ptr(&prev); + if( p->value ) + aFree(p->value); + ers_free(pc->str_reg_ers, p); + } else { + if( index ) + script->array_update(&sd->array_db,reg,false); } - else - {// create - sd->regstr[i].data = aStrdup(str); + } else { + if( sd->var_db->remove(sd->var_db,DB->i642key(reg),&prev) ) { + p = DB->data2ptr(&prev); + if( p->value ) + aFree(p->value); + ers_free(pc->str_reg_ers, p); + if( index ) + script->array_update(&sd->array_db,reg,true); } - return 1; - } - - if( str == NULL || *str == '\0' ) - return 1;// nothing to add, empty string - - ARR_FIND( 0, sd->regstr_num, i, sd->regstr[i].data == NULL ); - if( i == sd->regstr_num ) - {// nothing free, increase size - sd->regstr_num++; - RECREATE(sd->regstr, struct script_regstr, sd->regstr_num); } - sd->regstr[i].index = reg; - sd->regstr[i].data = aStrdup(str); - - return 1; } - -int pc_readregistry(struct map_session_data *sd,const char *reg,int type) -{ - struct global_reg *sd_reg; - int i,max; - - nullpo_ret(sd); - switch (type) { - case 3: //Char reg - sd_reg = sd->save_reg.global; - max = sd->save_reg.global_num; - break; - case 2: //Account reg - sd_reg = sd->save_reg.account; - max = sd->save_reg.account_num; - break; - case 1: //Account2 reg - sd_reg = sd->save_reg.account2; - max = sd->save_reg.account2_num; - break; - default: - return 0; - } - if (max == -1) { - ShowError("pc_readregistry: Trying to read reg value %s (type %d) before it's been loaded!\n", reg, type); +/** + * Serves the following variable types: + * - 'type' (permanent nuneric char reg) + * - '#type' (permanent numeric account reg) + * - '##type' (permanent numeric account reg2) + **/ +int pc_readregistry(struct map_session_data *sd, int64 reg) { + struct script_reg_num *p = NULL; + + if (!sd->vars_ok) { + ShowError("pc_readregistry: Trying to read reg %s before it's been loaded!\n", script->get_str(script_getvarid(reg))); //This really shouldn't happen, so it's possible the data was lost somewhere, we should request it again. - intif->request_registry(sd,type==3?4:type); + //intif->request_registry(sd,type==3?4:type); + set_eof(sd->fd); return 0; } + + p = i64db_get(sd->var_db, reg); - ARR_FIND( 0, max, i, strcmp(sd_reg[i].str,reg) == 0 ); - return ( i < max ) ? atoi(sd_reg[i].value) : 0; + return p ? p->value : 0; } - -char* pc_readregistry_str(struct map_session_data *sd,const char *reg,int type) -{ - struct global_reg *sd_reg; - int i,max; - - nullpo_ret(sd); - switch (type) { - case 3: //Char reg - sd_reg = sd->save_reg.global; - max = sd->save_reg.global_num; - break; - case 2: //Account reg - sd_reg = sd->save_reg.account; - max = sd->save_reg.account_num; - break; - case 1: //Account2 reg - sd_reg = sd->save_reg.account2; - max = sd->save_reg.account2_num; - break; - default: - return NULL; - } - if (max == -1) { - ShowError("pc_readregistry: Trying to read reg value %s (type %d) before it's been loaded!\n", reg, type); +/** + * Serves the following variable types: + * - 'type$' (permanent str char reg) + * - '#type$' (permanent str account reg) + * - '##type$' (permanent str account reg2) + **/ +char* pc_readregistry_str(struct map_session_data *sd, int64 reg) { + struct script_reg_str *p = NULL; + + if (!sd->vars_ok) { + ShowError("pc_readregistry_str: Trying to read reg %s before it's been loaded!\n", script->get_str(script_getvarid(reg))); //This really shouldn't happen, so it's possible the data was lost somewhere, we should request it again. - intif->request_registry(sd,type==3?4:type); + //intif->request_registry(sd,type==3?4:type); + set_eof(sd->fd); return NULL; } - ARR_FIND( 0, max, i, strcmp(sd_reg[i].str,reg) == 0 ); - return ( i < max ) ? sd_reg[i].value : NULL; + p = i64db_get(sd->var_db, reg); + + return p ? p->value : NULL; } +/** + * Serves the following variable types: + * - 'type' (permanent nuneric char reg) + * - '#type' (permanent numeric account reg) + * - '##type' (permanent numeric account reg2) + **/ +int pc_setregistry(struct map_session_data *sd, int64 reg, int val) { + struct script_reg_num *p = NULL; + int i; + const char *regname = script->get_str( script_getvarid(reg) ); + unsigned int index = script_getvaridx(reg); -int pc_setregistry(struct map_session_data *sd,const char *reg,int val,int type) -{ - struct global_reg *sd_reg; - int i,*max, regmax; - - nullpo_ret(sd); - - switch( type ) - { - case 3: //Char reg - if( !strcmp(reg,"PC_DIE_COUNTER") && sd->die_counter != val ) - { - i = (!sd->die_counter && (sd->class_&MAPID_UPPERMASK) == MAPID_SUPER_NOVICE); - sd->die_counter = val; - if( i ) - status_calc_pc(sd,SCO_NONE); // Lost the bonus. - } - else if( !strcmp(reg,"COOK_MASTERY") && sd->cook_mastery != val ) - { - val = cap_value(val, 0, 1999); - sd->cook_mastery = val; - } - sd_reg = sd->save_reg.global; - max = &sd->save_reg.global_num; - regmax = GLOBAL_REG_NUM; - break; - case 2: //Account reg - if( !strcmp(reg,"#CASHPOINTS") && sd->cashPoints != val ) - { - val = cap_value(val, 0, MAX_ZENY); - sd->cashPoints = val; - } - else if( !strcmp(reg,"#KAFRAPOINTS") && sd->kafraPoints != val ) - { - val = cap_value(val, 0, MAX_ZENY); - sd->kafraPoints = val; - } - sd_reg = sd->save_reg.account; - max = &sd->save_reg.account_num; - regmax = ACCOUNT_REG_NUM; - break; - case 1: //Account2 reg - sd_reg = sd->save_reg.account2; - max = &sd->save_reg.account2_num; - regmax = ACCOUNT_REG2_NUM; - break; - default: - return 0; + /* SAAD! those things should be stored elsewhere e.g. char ones in char table, the cash ones in account_data table! */ + switch( regname[0] ) { + default: //Char reg + if( !strcmp(regname,"PC_DIE_COUNTER") && sd->die_counter != val ) { + i = (!sd->die_counter && (sd->class_&MAPID_UPPERMASK) == MAPID_SUPER_NOVICE); + sd->die_counter = val; + if( i ) + status_calc_pc(sd,SCO_NONE); // Lost the bonus. + } else if( !strcmp(regname,"COOK_MASTERY") && sd->cook_mastery != val ) { + val = cap_value(val, 0, 1999); + sd->cook_mastery = val; + } + break; + case '#': + if( !strcmp(regname,"#CASHPOINTS") && sd->cashPoints != val ) { + val = cap_value(val, 0, MAX_ZENY); + sd->cashPoints = val; + } else if( !strcmp(regname,"#KAFRAPOINTS") && sd->kafraPoints != val ) { + val = cap_value(val, 0, MAX_ZENY); + sd->kafraPoints = val; + } + break; } - if (*max == -1) { - ShowError("pc_setregistry : refusing to set %s (type %d) until vars are received.\n", reg, type); - return 1; + + if ( !pc->reg_load && !sd->vars_ok ) { + ShowError("pc_setregistry : refusing to set %s until vars are received.\n", regname); + return 0; } - - // delete reg - if (val == 0) { - ARR_FIND( 0, *max, i, strcmp(sd_reg[i].str, reg) == 0 ); - if( i < *max ) - { - if (i != *max - 1) - memcpy(&sd_reg[i], &sd_reg[*max - 1], sizeof(struct global_reg)); - memset(&sd_reg[*max - 1], 0, sizeof(struct global_reg)); - (*max)--; - sd->state.reg_dirty |= 1<<(type-1); //Mark this registry as "need to be saved" + + if( (p = i64db_get(sd->var_db, reg) ) ) { + if( val ) { + if( !p->value && index ) /* its a entry that was deleted, so we reset array */ + script->array_update(&sd->array_db,reg,false); + p->value = val; + } else { + p->value = 0; + if( index ) + script->array_update(&sd->array_db,reg,true); + } + if( !pc->reg_load ) + p->flag.update = 1;/* either way, it will require either delete or replace */ + } else if( val ) { + DBData prev; + + if( index ) + script->array_update(&sd->array_db,reg,false); + + p = ers_alloc(pc->num_reg_ers, struct script_reg_num); + + p->value = val; + if( !pc->reg_load ) + p->flag.update = 1; + + if( sd->var_db->put(sd->var_db,DB->i642key(reg),DB->ptr2data(p),&prev) ) { + p = DB->data2ptr(&prev); + ers_free(pc->num_reg_ers, p); } - return 1; - } - // change value if found - ARR_FIND( 0, *max, i, strcmp(sd_reg[i].str, reg) == 0 ); - if( i < *max ) - { - safesnprintf(sd_reg[i].value, sizeof(sd_reg[i].value), "%d", val); - sd->state.reg_dirty |= 1<<(type-1); - return 1; - } - - // add value if not found - if (i < regmax) { - memset(&sd_reg[i], 0, sizeof(struct global_reg)); - safestrncpy(sd_reg[i].str, reg, sizeof(sd_reg[i].str)); - safesnprintf(sd_reg[i].value, sizeof(sd_reg[i].value), "%d", val); - (*max)++; - sd->state.reg_dirty |= 1<<(type-1); - return 1; } + + if( !pc->reg_load && p ) + sd->vars_dirty = true; - ShowError("pc_setregistry : couldn't set %s, limit of registries reached (%d)\n", reg, regmax); - - return 0; + return 1; } +/** + * Serves the following variable types: + * - 'type$' (permanent str char reg) + * - '#type$' (permanent str account reg) + * - '##type$' (permanent str account reg2) + **/ +int pc_setregistry_str(struct map_session_data *sd, int64 reg, const char *val) { + struct script_reg_str *p = NULL; + const char *regname = script->get_str( script_getvarid(reg) ); + unsigned int index = script_getvaridx(reg); -int pc_setregistry_str(struct map_session_data *sd,const char *reg,const char *val,int type) -{ - struct global_reg *sd_reg; - int i,*max, regmax; - - nullpo_ret(sd); - if (reg[strlen(reg)-1] != '$') { - ShowError("pc_setregistry_str : reg %s must be string (end in '$') to use this!\n", reg); - return 0; - } - - switch (type) { - case 3: //Char reg - sd_reg = sd->save_reg.global; - max = &sd->save_reg.global_num; - regmax = GLOBAL_REG_NUM; - break; - case 2: //Account reg - sd_reg = sd->save_reg.account; - max = &sd->save_reg.account_num; - regmax = ACCOUNT_REG_NUM; - break; - case 1: //Account2 reg - sd_reg = sd->save_reg.account2; - max = &sd->save_reg.account2_num; - regmax = ACCOUNT_REG2_NUM; - break; - default: - return 0; - } - if (*max == -1) { - ShowError("pc_setregistry_str : refusing to set %s (type %d) until vars are received.\n", reg, type); + if ( !pc->reg_load && !sd->vars_ok ) { + ShowError("pc_setregistry_str : refusing to set %s until vars are received.\n", regname); return 0; } - // delete reg - if (!val || strcmp(val,"")==0) - { - ARR_FIND( 0, *max, i, strcmp(sd_reg[i].str, reg) == 0 ); - if( i < *max ) - { - if (i != *max - 1) - memcpy(&sd_reg[i], &sd_reg[*max - 1], sizeof(struct global_reg)); - memset(&sd_reg[*max - 1], 0, sizeof(struct global_reg)); - (*max)--; - sd->state.reg_dirty |= 1<<(type-1); //Mark this registry as "need to be saved" - if (type!=3) intif->saveregistry(sd,type); + if( (p = i64db_get(sd->var_db, reg) ) ) { + if( val[0] ) { + if( p->value ) + aFree(p->value); + else if ( index ) /* a entry that was deleted, so we reset */ + script->array_update(&sd->array_db,reg,false); + p->value = aStrdup(val); + } else { + p->value = NULL; + if( index ) + script->array_update(&sd->array_db,reg,true); } - return 1; - } + if( !pc->reg_load ) + p->flag.update = 1;/* either way, it will require either delete or replace */ + } else if( val[0] ) { + DBData prev; - // change value if found - ARR_FIND( 0, *max, i, strcmp(sd_reg[i].str, reg) == 0 ); - if( i < *max ) - { - safestrncpy(sd_reg[i].value, val, sizeof(sd_reg[i].value)); - sd->state.reg_dirty |= 1<<(type-1); //Mark this registry as "need to be saved" - if (type!=3) intif->saveregistry(sd,type); - return 1; - } + if( index ) + script->array_update(&sd->array_db,reg,false); - // add value if not found - if (i < regmax) { - memset(&sd_reg[i], 0, sizeof(struct global_reg)); - safestrncpy(sd_reg[i].str, reg, sizeof(sd_reg[i].str)); - safestrncpy(sd_reg[i].value, val, sizeof(sd_reg[i].value)); - (*max)++; - sd->state.reg_dirty |= 1<<(type-1); //Mark this registry as "need to be saved" - if (type!=3) intif->saveregistry(sd,type); - return 1; + p = ers_alloc(pc->str_reg_ers, struct script_reg_str); + + p->value = aStrdup(val); + if( !pc->reg_load ) + p->flag.update = 1; + p->flag.type = 1; + + if( sd->var_db->put(sd->var_db,DB->i642key(reg),DB->ptr2data(p),&prev) ) { + p = DB->data2ptr(&prev); + if( p->value ) + aFree(p->value); + ers_free(pc->str_reg_ers, p); + } } - - ShowError("pc_setregistry : couldn't set %s, limit of registries reached (%d)\n", reg, regmax); - - return 0; + + if( !pc->reg_load && p ) + sd->vars_dirty = true; + + return 1; } /*========================================== @@ -10556,6 +10468,9 @@ void do_final_pc(void) { pcg->final(); ers_destroy(pc->sc_display_ers); + ers_destroy(pc->num_reg_ers); + ers_destroy(pc->str_reg_ers); + return; } @@ -10599,6 +10514,12 @@ void do_init_pc(bool minimal) { pcg->init(); pc->sc_display_ers = ers_new(sizeof(struct sc_display_entry), "pc.c:sc_display_ers", ERS_OPT_NONE); + pc->num_reg_ers = ers_new(sizeof(struct script_reg_num), "pc.c::num_reg_ers", ERS_OPT_CLEAN); + pc->str_reg_ers = ers_new(sizeof(struct script_reg_str), "pc.c::str_reg_ers", ERS_OPT_CLEAN); + + ers_chunk_size(pc->sc_display_ers, 150); + ers_chunk_size(pc->num_reg_ers, 300); + ers_chunk_size(pc->str_reg_ers, 50); } /*===================================== * Default Functions : pc.h @@ -10613,7 +10534,6 @@ void pc_defaults(void) { }; unsigned int equip_pos[EQI_MAX]={EQP_ACC_L,EQP_ACC_R,EQP_SHOES,EQP_GARMENT,EQP_HEAD_LOW,EQP_HEAD_MID,EQP_HEAD_TOP,EQP_ARMOR,EQP_HAND_L,EQP_HAND_R,EQP_COSTUME_HEAD_TOP,EQP_COSTUME_HEAD_MID,EQP_COSTUME_HEAD_LOW,EQP_COSTUME_GARMENT,EQP_AMMO, EQP_SHADOW_ARMOR, EQP_SHADOW_WEAPON, EQP_SHADOW_SHIELD, EQP_SHADOW_SHOES, EQP_SHADOW_ACC_R, EQP_SHADOW_ACC_L }; - pc = &pc_s; /* vars */ @@ -10642,6 +10562,11 @@ void pc_defaults(void) { pc->sc_display_ers = NULL; /* */ pc->expiration_tid = INVALID_TIMER; + /* */ + pc->num_reg_ers = NULL; + pc->str_reg_ers = NULL; + /* */ + pc->reg_load = false; /* funcs */ pc->init = do_init_pc; pc->final = do_final_pc; diff --git a/src/map/pc.h b/src/map/pc.h index 2f143f15c..f64b20945 100644 --- a/src/map/pc.h +++ b/src/map/pc.h @@ -139,7 +139,6 @@ struct map_session_data { unsigned int abra_flag : 2; // Abracadabra bugfix by Aru unsigned int autocast : 1; // Autospell flag [Inkfish] unsigned int autotrade : 1; //By Fantik - unsigned int reg_dirty : 4; //By Skotlex (marks whether registry variables have been saved or not yet) unsigned int showdelay :1; unsigned int showexp :1; unsigned int showzeny :1; @@ -203,7 +202,6 @@ struct map_session_data { unsigned int extra_temp_permissions; /* permissions from @addperm */ struct mmo_charstatus status; - struct registry save_reg; struct item_data* inventory_data[MAX_INVENTORY]; // direct pointers to itemdb entries (faster than doing item_id lookups) short equip_index[EQI_MAX]; unsigned int weight,max_weight; @@ -363,10 +361,6 @@ struct map_session_data { short mission_mobid; //Stores the target mob_id for TK_MISSION int die_counter; //Total number of times you've died int devotion[5]; //Stores the account IDs of chars devoted to. - int reg_num; //Number of registries (type numeric) - int regstr_num; //Number of registries (type string) - struct script_reg *reg; - struct script_regstr *regstr; int trade_partner; struct { struct { @@ -531,6 +525,16 @@ struct map_session_data { struct { unsigned int second,third; } sktree; + + /** + * Account/Char variables & array control of those variables + **/ + DBMap *var_db; + DBMap *array_db; + unsigned char vars_received;/* char loading is only complete when you get it all. */ + bool vars_ok; + bool vars_dirty; + // temporary debugging of bug #3504 const char* delunit_prevfile; int delunit_prevline; @@ -672,18 +676,18 @@ enum equip_pos { #define pc_checkoverhp(sd) ((sd)->battle_status.hp == (sd)->battle_status.max_hp) #define pc_checkoversp(sd) ((sd)->battle_status.sp == (sd)->battle_status.max_sp) -#define pc_readglobalreg(sd,reg) (pc->readregistry((sd),(reg),3)) -#define pc_setglobalreg(sd,reg,val) (pc->setregistry((sd),(reg),(val),3)) -#define pc_readglobalreg_str(sd,reg) (pc->readregistry_str((sd),(reg),3)) -#define pc_setglobalreg_str(sd,reg,val) (pc->setregistry_str((sd),(reg),(val),3)) -#define pc_readaccountreg(sd,reg) (pc->readregistry((sd),(reg),2)) -#define pc_setaccountreg(sd,reg,val) (pc->setregistry((sd),(reg),(val),2)) -#define pc_readaccountregstr(sd,reg) (pc->readregistry_str((sd),(reg),2)) -#define pc_setaccountregstr(sd,reg,val) (pc->setregistry_str((sd),(reg),(val),2)) -#define pc_readaccountreg2(sd,reg) (pc->readregistry((sd),(reg),1)) -#define pc_setaccountreg2(sd,reg,val) (pc->setregistry((sd),(reg),(val),1)) -#define pc_readaccountreg2str(sd,reg) (pc->readregistry_str((sd),(reg),1)) -#define pc_setaccountreg2str(sd,reg,val) (pc->setregistry_str((sd),(reg),(val),1)) +#define pc_readglobalreg(sd,reg) (pc->readregistry((sd),(reg))) +#define pc_setglobalreg(sd,reg,val) (pc->setregistry((sd),(reg),(val))) +#define pc_readglobalreg_str(sd,reg) (pc->readregistry_str((sd),(reg))) +#define pc_setglobalreg_str(sd,reg,val) (pc->setregistry_str((sd),(reg),(val))) +#define pc_readaccountreg(sd,reg) (pc->readregistry((sd),(reg))) +#define pc_setaccountreg(sd,reg,val) (pc->setregistry((sd),(reg),(val))) +#define pc_readaccountregstr(sd,reg) (pc->readregistry_str((sd),(reg))) +#define pc_setaccountregstr(sd,reg,val) (pc->setregistry_str((sd),(reg),(val))) +#define pc_readaccountreg2(sd,reg) (pc->readregistry((sd),(reg))) +#define pc_setaccountreg2(sd,reg,val) (pc->setregistry((sd),(reg),(val))) +#define pc_readaccountreg2str(sd,reg) (pc->readregistry_str((sd),(reg))) +#define pc_setaccountreg2str(sd,reg,val) (pc->setregistry_str((sd),(reg),(val))) /* pc_groups easy access */ #define pc_get_group_level(sd) ( (sd)->group->level ) @@ -772,6 +776,13 @@ struct pc_interface { struct eri *sc_display_ers; /* global expiration timer id */ int expiration_tid; + /** + * ERS for the bulk of pc vars + **/ + struct eri *num_reg_ers; + struct eri *str_reg_ers; + /* */ + bool reg_load; /* funcs */ void (*init) (bool minimal); void (*final) (void); @@ -908,14 +919,14 @@ struct pc_interface { int (*readparam) (struct map_session_data *sd,int type); int (*setparam) (struct map_session_data *sd,int type,int val); - int (*readreg) (struct map_session_data *sd,int reg); - int (*setreg) (struct map_session_data *sd,int reg,int val); - char * (*readregstr) (struct map_session_data *sd,int reg); - int (*setregstr) (struct map_session_data *sd,int reg,const char *str); - int (*readregistry) (struct map_session_data *sd,const char *reg,int type); - int (*setregistry) (struct map_session_data *sd,const char *reg,int val,int type); - char * (*readregistry_str) (struct map_session_data *sd,const char *reg,int type); - int (*setregistry_str) (struct map_session_data *sd,const char *reg,const char *val,int type); + int (*readreg) (struct map_session_data *sd, int64 reg); + void (*setreg) (struct map_session_data *sd, int64 reg,int val); + char * (*readregstr) (struct map_session_data *sd, int64 reg); + void (*setregstr) (struct map_session_data *sd, int64 reg, const char *str); + int (*readregistry) (struct map_session_data *sd, int64 reg); + int (*setregistry) (struct map_session_data *sd, int64 reg, int val); + char * (*readregistry_str) (struct map_session_data *sd, int64 reg); + int (*setregistry_str) (struct map_session_data *sd, int64 reg, const char *val); int (*addeventtimer) (struct map_session_data *sd,int tick,const char *name); int (*deleventtimer) (struct map_session_data *sd,const char *name); diff --git a/src/map/script.c b/src/map/script.c index 8bd4bc028..21d4d289c 100644 --- a/src/map/script.c +++ b/src/map/script.c @@ -212,10 +212,7 @@ void script_reportdata(struct script_data* data) case C_NAME:// reference if( reference_tovariable(data) ) {// variable const char* name = reference_getname(data); - if( not_array_variable(*name) ) - ShowDebug("Data: variable name='%s'\n", name); - else - ShowDebug("Data: variable name='%s' index=%d\n", name, reference_getindex(data)); + ShowDebug("Data: variable name='%s' index=%d\n", name, reference_getindex(data)); } else if( reference_toconstant(data) ) {// constant ShowDebug("Data: constant name='%s' value=%d\n", reference_getname(data), reference_getconstant(data)); } else if( reference_toparam(data) ) {// param @@ -2501,9 +2498,9 @@ struct script_data *get_val(struct script_state* st, struct script_data* data) { break; case '#': if( name[1] == '#' ) - data->u.str = pc_readaccountreg2str(sd, name);// global + data->u.str = pc_readaccountreg2str(sd, data->u.num);// global else - data->u.str = pc_readaccountregstr(sd, name);// local + data->u.str = pc_readaccountregstr(sd, data->u.num);// local break; case '.': { @@ -2512,21 +2509,21 @@ struct script_data *get_val(struct script_state* st, struct script_data* data) { st->stack->var_function : // instance/scope variable st->script->script_vars; // npc variable if( n ) - data->u.str = (char*)idb_get(n,reference_getuid(data)); + data->u.str = (char*)i64db_get(n,reference_getuid(data)); else data->u.str = NULL; } break; case '\'': if ( st->instance_id >= 0 ) { - data->u.str = (char*)idb_get(instance->list[st->instance_id].vars,reference_getuid(data)); + data->u.str = (char*)i64db_get(instance->list[st->instance_id].vars,reference_getuid(data)); } else { ShowWarning("script_get_val: cannot access instance variable '%s', defaulting to \"\"\n", name); data->u.str = NULL; } break; default: - data->u.str = pc_readglobalreg_str(sd, name); + data->u.str = pc_readglobalreg_str(sd, data->u.num); break; } @@ -2556,9 +2553,9 @@ struct script_data *get_val(struct script_state* st, struct script_data* data) { break; case '#': if( name[1] == '#' ) - data->u.num = pc_readaccountreg2(sd, name);// global + data->u.num = pc_readaccountreg2(sd, data->u.num);// global else - data->u.num = pc_readaccountreg(sd, name);// local + data->u.num = pc_readaccountreg(sd, data->u.num);// local break; case '.': { @@ -2567,21 +2564,21 @@ struct script_data *get_val(struct script_state* st, struct script_data* data) { st->stack->var_function : // instance/scope variable st->script->script_vars; // npc variable if( n ) - data->u.num = (int)idb_iget(n,reference_getuid(data)); + data->u.num = (int)i64db_iget(n,reference_getuid(data)); else data->u.num = 0; } break; case '\'': if( st->instance_id >= 0 ) - data->u.num = (int)idb_iget(instance->list[st->instance_id].vars,reference_getuid(data)); + data->u.num = (int)i64db_iget(instance->list[st->instance_id].vars,reference_getuid(data)); else { ShowWarning("script_get_val: cannot access instance variable '%s', defaulting to 0\n", name); data->u.num = 0; } break; default: - data->u.num = pc_readglobalreg(sd, name); + data->u.num = pc_readglobalreg(sd, data->u.num); break; } @@ -2592,63 +2589,245 @@ struct script_data *get_val(struct script_state* st, struct script_data* data) { /// Retrieves the value of a reference identified by uid (variable, constant, param) /// The value is left in the top of the stack and needs to be removed manually. -void* get_val2(struct script_state* st, int uid, struct DBMap** ref) { +void* get_val2(struct script_state* st, int64 uid, struct DBMap** ref) { struct script_data* data; script->push_val(st->stack, C_NAME, uid, ref); data = script_getdatatop(st, -1); script->get_val(st, data); return (data->type == C_INT ? (void*)__64BPTRSIZE(data->u.num) : (void*)__64BPTRSIZE(data->u.str)); } +/** + * Returns array size by ID + **/ +unsigned int script_array_size(struct script_state *st, struct map_session_data *sd, const char *name) { + struct script_array *sa = NULL; + struct DBMap *src = script->array_src(st, sd, name); + + if( src ) + sa = idb_get(src, script->search_str(name)); + + return sa ? sa->size : 0; +} +/** + * Returns array's highest key (for that awful getarraysize implementation that doesn't really gets the array size) + **/ +unsigned int script_array_highest_key(struct script_state *st, struct map_session_data *sd, const char *name) { + struct script_array *sa = NULL; + struct DBMap *src = script->array_src(st, sd, name); + + if( src && ( sa = idb_get(src, script->search_str(name)) ) ) { + unsigned int i, highest_key = 0; + + for(i = 0; i < sa->size; i++) { + if( sa->members[i] > highest_key ) + highest_key = sa->members[i]; + } + + return highest_key + 1; + } + + return 0; +} +int script_free_array_db(DBKey key, DBData *data, va_list ap) { + struct script_array *sa = DB->data2ptr(data); + aFree(sa->members); + ers_free(script->array_ers, sa); + return 0; +} +/** + * Clears script_array and removes it from script->array_db + **/ +void script_array_delete(struct DBMap *src, struct script_array *sa) { + aFree(sa->members); + idb_remove(src, sa->id); + ers_free(script->array_ers, sa); +} +/** + * Removes a member from a script_array list + * + * @param idx the index of the member in script_array struct list, not of the actual array member + **/ +void script_array_remove_member(struct DBMap *src,struct script_array *sa, unsigned int idx) { + unsigned int i, cursor; + + /* its the only member left, no need to do anything other than delete the array data */ + if( sa->size == 1 ) { + script->array_delete(src,sa); + return; + } + + sa->members[idx] = UINT_MAX; + + for(i = 0, cursor = 0; i < sa->size; i++) { + if( sa->members[i] == UINT_MAX ) + continue; + if( i != cursor ) + sa->members[cursor] = sa->members[i]; + cursor++; + } + + sa->size = cursor; +} +/** + * Appends a new array index to the list in script_array + * + * @param idx the index of the array member being inserted + **/ +void script_array_add_member(struct script_array *sa, unsigned int idx) { + + RECREATE(sa->members, unsigned int, ++sa->size); + + sa->members[sa->size - 1] = idx; +} +/** + * Obtains the source of the array database for this type and scenario + * Initializes such database when not yet initialised. + **/ +struct DBMap *script_array_src(struct script_state *st, struct map_session_data *sd, const char *name) { + struct DBMap **src = NULL; + + switch( name[0] ) { + /* from player */ + default: /* char reg */ + case '@':/* temp char reg */ + case '#':/* account reg */ + src = &sd->array_db; + break; + case '$':/* map reg */ + src = &mapreg->array_db; + break; + case '.':/* npc/script */ + src = (name[1] == '@') ? &st->stack->array_function_db : &st->script->script_arrays_db; + break; + case '\'':/* instance */ + if( st->instance_id >= 0 ) { + src = &instance->list[st->instance_id].array_db; + } + break; + } + + if( src ) { + if( !*src ) + *src = idb_alloc(DB_OPT_BASE); + return *src; + } + + return NULL; +} +/** + * Processes a array member modification, and update data accordingly + **/ +void script_array_update(struct DBMap **src, int64 num, bool empty) { + struct script_array *sa = NULL; + int id = script_getvarid(num); + unsigned int index = script_getvaridx(num); + + if( !*src ) { + *src = idb_alloc(DB_OPT_BASE); + } else { + sa = idb_get(*src, id); + } + + if( sa ) { + unsigned int i; + + /* search */ + for(i = 0; i < sa->size; i++) { + if( sa->members[i] == index ) + break; + } + + /* if existent */ + if( i != sa->size ) { + /* if empty, we gotta remove it */ + if( empty ) { + script->array_remove_member(*src,sa,i); + } + } else if( !empty ) { /* new entry */ + script->array_add_member(sa,index); + /* we do nothing if its empty, no point in modifying array data for a new empty member */ + } + } else if ( !empty ) {/* we only move to create if not empty */ + sa = ers_alloc(script->array_ers, struct script_array); + sa->id = id; + script->array_add_member(sa,index); + idb_put(*src, id, sa); + } +} /*========================================== * Stores the value of a script variable * Return value is 0 on fail, 1 on success. + * TODO: return values are screwed up, have been for some time (reaad: years), e.g. some functions return 1 failure and success. *------------------------------------------*/ -int set_reg(struct script_state* st, TBL_PC* sd, int num, const char* name, const void* value, struct DBMap** ref) -{ +int set_reg(struct script_state* st, TBL_PC* sd, int64 num, const char* name, const void* value, struct DBMap** ref) { char prefix = name[0]; + + if( is_string_variable(name) ) {// string variable + const char *str = (const char*)value; - if( is_string_variable(name) ) - {// string variable - const char* str = (const char*)value; switch (prefix) { - case '@': - return pc->setregstr(sd, num, str); - case '$': - return mapreg->setregstr(num, str); - case '#': - return (name[1] == '#') ? - pc_setaccountreg2str(sd, name, str) : - pc_setaccountregstr(sd, name, str); - case '.': - { - struct DBMap* n; - n = (ref) ? *ref : (name[1] == '@') ? st->stack->var_function : st->script->script_vars; - if( n ) { - idb_remove(n, num); - if (str[0]) idb_put(n, num, aStrdup(str)); + case '@': + pc->setregstr(sd, num, str); + return 1; + case '$': + return mapreg->setregstr(num, str); + case '#': + return (name[1] == '#') ? + pc_setaccountreg2str(sd, num, str) : + pc_setaccountregstr(sd, num, str); + case '.': + { + struct DBMap* n; + n = (ref) ? *ref : (name[1] == '@') ? st->stack->var_function : st->script->script_vars; + if( n ) { + if (str[0]) { + i64db_put(n, num, aStrdup(str)); + if( script_getvaridx(num) ) + script->array_update( + (name[1] == '@') ? + &st->stack->array_function_db : + &st->script->script_arrays_db, + num, + false); + } else { + i64db_remove(n, num); + if( script_getvaridx(num) ) + script->array_update( + (name[1] == '@') ? + &st->stack->array_function_db : + &st->script->script_arrays_db, + num, + true); + } + } } - } - return 1; - case '\'': - if( st->instance_id >= 0 ) { - idb_remove(instance->list[st->instance_id].vars, num); - if( str[0] ) idb_put(instance->list[st->instance_id].vars, num, aStrdup(str)); - } - return 1; - default: - return pc_setglobalreg_str(sd, name, str); + return 1; + case '\'': + if( st->instance_id >= 0 ) { + if( str[0] ) { + i64db_put(instance->list[st->instance_id].vars, num, aStrdup(str)); + if( script_getvaridx(num) ) + script->array_update(&instance->list[st->instance_id].array_db,num,false); + } else { + i64db_remove(instance->list[st->instance_id].vars, num); + if( script_getvaridx(num) ) + script->array_update(&instance->list[st->instance_id].array_db,num,true); + } + } else { + ShowError("script_set_reg: cannot write instance variable '%s', NPC not in a instance!\n", name); + script_reportsrc(st); + } + return 1; + default: + return pc_setglobalreg_str(sd, num, str); } - } - else - {// integer variable + } else {// integer variable int val = (int)__64BPTRSIZE(value); - if(script->str_data[num&0x00ffffff].type == C_PARAM) - { - if( pc->setparam(sd, script->str_data[num&0x00ffffff].val, val) == 0 ) - { - if( st != NULL ) - { + + if(script->str_data[script_getvarid(num)].type == C_PARAM) { + if( pc->setparam(sd, script->str_data[script_getvarid(num)].val, val) == 0 ) { + if( st != NULL ) { ShowError("script:set_reg: failed to set param '%s' to %d.\n", name, val); script->reportsrc(st); st->state = END; @@ -2659,34 +2838,60 @@ int set_reg(struct script_state* st, TBL_PC* sd, int num, const char* name, cons } switch (prefix) { - case '@': - return pc->setreg(sd, num, val); - case '$': - return mapreg->setreg(num, val); - case '#': - return (name[1] == '#') ? - pc_setaccountreg2(sd, name, val) : - pc_setaccountreg(sd, name, val); - case '.': - { - struct DBMap* n; - n = (ref) ? *ref : (name[1] == '@') ? st->stack->var_function : st->script->script_vars; - if( n ) { - idb_remove(n, num); - if( val != 0 ) - idb_iput(n, num, val); + case '@': + pc->setreg(sd, num, val); + return 1; + case '$': + return mapreg->setreg(num, val); + case '#': + return (name[1] == '#') ? + pc_setaccountreg2(sd, num, val) : + pc_setaccountreg(sd, num, val); + case '.': + { + struct DBMap* n; + n = (ref) ? *ref : (name[1] == '@') ? st->stack->var_function : st->script->script_vars; + if( n ) { + if( val != 0 ) { + i64db_iput(n, num, val); + if( script_getvaridx(num) ) + script->array_update( + (name[1] == '@') ? + &st->stack->array_function_db : + &st->script->script_arrays_db, + num, + false); + } else { + i64db_remove(n, num); + if( script_getvaridx(num) ) + script->array_update( + (name[1] == '@') ? + &st->stack->array_function_db : + &st->script->script_arrays_db, + num, + true); + } + } } - } - return 1; - case '\'': - if( st->instance_id >= 0 ) { - idb_remove(instance->list[st->instance_id].vars, num); - if( val != 0 ) - idb_iput(instance->list[st->instance_id].vars, num, val); - } - return 1; - default: - return pc_setglobalreg(sd, name, val); + return 1; + case '\'': + if( st->instance_id >= 0 ) { + if( val != 0 ) { + i64db_iput(instance->list[st->instance_id].vars, num, val); + if( script_getvaridx(num) ) + script->array_update(&instance->list[st->instance_id].array_db,num,false); + } else { + i64db_remove(instance->list[st->instance_id].vars, num); + if( script_getvaridx(num) ) + script->array_update(&instance->list[st->instance_id].array_db,num,true); + } + } else { + ShowError("script_set_reg: cannot write instance variable '%s', NPC not in a instance!\n", name); + script_reportsrc(st); + } + return 1; + default: + return pc_setglobalreg(sd, num, val); } } } @@ -2712,7 +2917,7 @@ const char* conv_str(struct script_state* st, struct script_data* data) else if( data_isint(data) ) {// int -> string CREATE(p, char, ITEM_NAME_LENGTH); - snprintf(p, ITEM_NAME_LENGTH, "%d", data->u.num); + snprintf(p, ITEM_NAME_LENGTH, "%lld", data->u.num); p[ITEM_NAME_LENGTH-1] = '\0'; data->type = C_STR; data->u.str = p; @@ -2786,7 +2991,7 @@ int conv_num(struct script_state* st, struct script_data* data) { data->u.num = 0; } #endif - return data->u.num; + return (int)data->u.num; } // @@ -2803,7 +3008,7 @@ void stack_expand(struct script_stack* stack) { } /// Pushes a value into the stack (with reference) -struct script_data* push_val(struct script_stack* stack, enum c_op type, int val, struct DBMap** ref) { +struct script_data* push_val(struct script_stack* stack, enum c_op type, int64 val, struct DBMap** ref) { if( stack->sp >= stack->sp_max ) script->stack_expand(stack); stack->stack_data[stack->sp].type = type; @@ -2932,6 +3137,8 @@ void script_free_vars(struct DBMap* var_storage) { void script_free_code(struct script_code* code) { script->free_vars( code->script_vars ); + if( code->script_arrays_db ) + code->script_arrays_db->destroy(code->script_arrays_db,script->array_free_db); aFree( code->script_buf ); aFree( code ); } @@ -2952,7 +3159,8 @@ struct script_state* script_alloc_state(struct script_code* rootscript, int pos, st->stack->sp_max = 64; CREATE(st->stack->stack_data, struct script_data, st->stack->sp_max); st->stack->defsp = st->stack->sp; - st->stack->var_function = idb_alloc(DB_OPT_RELEASE_DATA); + st->stack->var_function = i64db_alloc(DB_OPT_RELEASE_DATA); + st->stack->array_function_db = NULL; st->state = RUN; st->script = rootscript; st->pos = pos; @@ -2962,7 +3170,7 @@ struct script_state* script_alloc_state(struct script_code* rootscript, int pos, st->npc_item_flag = battle_config.item_enabled_npc; if( !st->script->script_vars ) - st->script->script_vars = idb_alloc(DB_OPT_RELEASE_DATA); + st->script->script_vars = i64db_alloc(DB_OPT_RELEASE_DATA); st->id = script->next_id++; script->active_scripts++; @@ -2985,14 +3193,22 @@ void script_free_state(struct script_state* st) { timer->delete(st->sleep.timer, script->run_timer); if( st->stack ) { script->free_vars(st->stack->var_function); + if( st->stack->array_function_db ) + st->stack->array_function_db->destroy(st->stack->array_function_db,script->array_free_db); script->pop_stack(st, 0, st->stack->sp); aFree(st->stack->stack_data); ers_free(script->stack_ers, st->stack); st->stack = NULL; } - if( st->script && st->script->script_vars && !db_size(st->script->script_vars) ) { - script->free_vars(st->script->script_vars); - st->script->script_vars = NULL; + if( st->script ) { + if( st->script->script_vars && !db_size(st->script->script_vars) ) { + script->free_vars(st->script->script_vars); + st->script->script_vars = NULL; + } + if( st->script->script_arrays_db && !db_size(st->script->script_arrays_db) ) { + script->free_vars(st->script->script_arrays_db); + st->script->script_arrays_db = NULL; + } } st->pos = -1; idb_remove(script->st_db, st->id); @@ -3037,20 +3253,6 @@ int get_num(unsigned char *scriptbuf,int *pos) return i+((scriptbuf[(*pos)++]&0x7f)<<j); } -/*========================================== - * Remove the value from the stack - *------------------------------------------*/ -int pop_val(struct script_state* st) -{ - if(st->stack->sp<=0) - return 0; - st->stack->sp--; - script->get_val(st,&(st->stack->stack_data[st->stack->sp])); - if(st->stack->stack_data[st->stack->sp].type==C_INT) - return st->stack->stack_data[st->stack->sp].u.num; - return 0; -} - /// Ternary operators /// test ? if_true : if_false void op_3(struct script_state* st, int op) @@ -3064,7 +3266,7 @@ void op_3(struct script_state* st, int op) if( data_isstring(data) ) flag = data->u.str[0];// "" -> false else if( data_isint(data) ) - flag = data->u.num;// 0 -> false + flag = data->u.num == 0 ? 0 : 1;// 0 -> false else { ShowError("script:op_3: invalid data for the ternary operator test\n"); @@ -3234,8 +3436,8 @@ void op_2(struct script_state *st, int op) } else if( data_isint(left) && data_isint(right) ) {// ii => op_2num - int i1 = left->u.num; - int i2 = right->u.num; + int i1 = (int)left->u.num; + int i2 = (int)right->u.num; script_removetop(st, leftref.type == C_NOP ? -2 : -1, 0); script->op_2num(st, op, i1, i2); @@ -3277,19 +3479,18 @@ void op_1(struct script_state* st, int op) return; } - i1 = data->u.num; + i1 = (int)data->u.num; script_removetop(st, -1, 0); - switch( op ) - { - case C_NEG: i1 = -i1; break; - case C_NOT: i1 = ~i1; break; - case C_LNOT: i1 = !i1; break; - default: - ShowError("script:op_1: unexpected operator %s i1=%d\n", script->op2name(op), i1); - script->reportsrc(st); - script_pushnil(st); - st->state = END; - return; + switch( op ) { + case C_NEG: i1 = -i1; break; + case C_NOT: i1 = ~i1; break; + case C_LNOT: i1 = !i1; break; + default: + ShowError("script:op_1: unexpected operator %s i1=%d\n", script->op2name(op), i1); + script->reportsrc(st); + script_pushnil(st); + st->state = END; + return; } script_pushint(st, i1); } @@ -3400,7 +3601,7 @@ int run_func(struct script_state *st) data = &st->stack->stack_data[st->start]; if( data->type == C_NAME && script->str_data[data->u.num].type == C_FUNC ) - func = data->u.num; + func = (int)data->u.num; else { ShowError("script:run_func: not a buildin command.\n"); @@ -3418,7 +3619,7 @@ int run_func(struct script_state *st) if (!(script->str_data[func].func(st))) //Report error script->reportsrc(st); } else { - ShowError("script:run_func: '%s' (id=%d type=%s) has no C function. please report this!!!\n", script->get_str(func), func, script->op2name(script->str_data[func].type)); + ShowError("script:run_func: '%s' (id=%lld type=%s) has no C function. please report this!!!\n", script->get_str(func), func, script->op2name(script->str_data[func].type)); script->reportsrc(st); st->state = END; } @@ -3723,10 +3924,8 @@ void run_script_main(struct script_state *st) { } //Restore previous script if any. script->detach_state(st, true); - if (sd->state.reg_dirty&2) - intif->saveregistry(sd,2); - if (sd->state.reg_dirty&1) - intif->saveregistry(sd,1); + if (sd->vars_dirty) + intif->saveregistry(sd); } script->free_state(st); st = NULL; @@ -3813,70 +4012,99 @@ void script_add_autobonus(const char *autobonus) /// resets a temporary character array variable to given value -void script_cleararray_pc(struct map_session_data* sd, const char* varname, void* value) -{ +void script_cleararray_pc(struct map_session_data* sd, const char* varname, void* value) { + struct script_array *sa = NULL; + struct DBMap *src = NULL; + unsigned int i, *list = NULL, size = 0; int key; - uint8 idx; - - if( not_array_variable(varname[0]) || !not_server_variable(varname[0]) ) - { - ShowError("script_cleararray_pc: Variable '%s' has invalid scope (char_id=%d).\n", varname, sd->status.char_id); - return; - } key = script->add_str(varname); - - if( is_string_variable(varname) ) - { - for( idx = 0; idx < SCRIPT_MAX_ARRAYSIZE; idx++ ) - { - pc->setregstr(sd, reference_uid(key, idx), (const char*)value); - } - } - else - { - for( idx = 0; idx < SCRIPT_MAX_ARRAYSIZE; idx++ ) - { - pc->setreg(sd, reference_uid(key, idx), (int)__64BPTRSIZE(value)); - } + + if( !(src = script->array_src(NULL,sd,varname) ) ) + return; + + if( !(sa = idb_get(src, key)) ) /* non-existent array, nothing to empty */ + return; + + size = sa->size; + list = script->array_cpy_list(sa); + + for(i = 0; i < size; i++) { + script->set_reg(NULL,sd,reference_uid(key, list[i]),varname,value,NULL); } } /// sets a temporary character array variable element idx to given value /// @param refcache Pointer to an int variable, which keeps a copy of the reference to varname and must be initialized to 0. Can be NULL if only one element is set. -void script_setarray_pc(struct map_session_data* sd, const char* varname, uint8 idx, void* value, int* refcache) -{ +void script_setarray_pc(struct map_session_data* sd, const char* varname, uint32 idx, void* value, int* refcache) { int key; - - if( not_array_variable(varname[0]) || !not_server_variable(varname[0]) ) - { - ShowError("script_setarray_pc: Variable '%s' has invalid scope (char_id=%d).\n", varname, sd->status.char_id); - return; - } - - if( idx >= SCRIPT_MAX_ARRAYSIZE ) - { - ShowError("script_setarray_pc: Variable '%s' has invalid index '%d' (char_id=%d).\n", varname, (int)idx, sd->status.char_id); + + if( idx >= SCRIPT_MAX_ARRAYSIZE ) { + ShowError("script_setarray_pc: Variable '%s' has invalid index '%u' (char_id=%d).\n", varname, idx, sd->status.char_id); return; } key = ( refcache && refcache[0] ) ? refcache[0] : script->add_str(varname); - - if( is_string_variable(varname) ) - { - pc->setregstr(sd, reference_uid(key, idx), (const char*)value); - } - else - { - pc->setreg(sd, reference_uid(key, idx), (int)__64BPTRSIZE(value)); - } - + + script->set_reg(NULL,sd,reference_uid(key, idx),varname,value,NULL); + if( refcache ) {// save to avoid repeated script->add_str calls refcache[0] = key; } } +/** + * Clears persistent variables from memory + **/ +int script_reg_destroy(DBKey key, DBData *data, va_list ap) { + void *src; + + if( data->type != DB_DATA_PTR )/* got no need for those! */ + return 0; + + src = DB->data2ptr(data); + + if( ((struct script_reg_state*)src)->type ) { + struct script_reg_str *p = src; + + if( p->value ) + aFree(p->value); + + ers_free(pc->str_reg_ers,p); + } else { + ers_free(pc->num_reg_ers,(struct script_reg_num*)src); + } + + return 0; +} +/** + * Clears a single persistent variable + **/ +void script_reg_destroy_single(struct map_session_data *sd, int64 reg, struct script_reg_state *data) { + i64db_remove(sd->var_db, reg); + + if( data->type ) { + struct script_reg_str *p = (struct script_reg_str*)data; + + if( p->value ) + aFree(p->value); + + ers_free(pc->str_reg_ers,p); + } else { + ers_free(pc->num_reg_ers,(struct script_reg_num*)data); + } +} +unsigned int *script_array_cpy_list(struct script_array *sa) { + if( sa->size > script->generic_ui_array_size ) + script->generic_ui_array_expand(sa->size); + memcpy(script->generic_ui_array, sa->members, sizeof(unsigned int)*sa->size); + return script->generic_ui_array; +} +void script_generic_ui_array_expand (unsigned int plus) { + script->generic_ui_array_size += plus + 100; + RECREATE(script->generic_ui_array, unsigned int, script->generic_ui_array_size); +} /*========================================== * Destructor *------------------------------------------*/ @@ -4005,6 +4233,11 @@ void do_final_script(void) { if( script->labels != NULL ) aFree(script->labels); + + ers_destroy(script->array_ers); + + if( script->generic_ui_array ) + aFree(script->generic_ui_array); } /*========================================== * Initialization @@ -4016,6 +4249,7 @@ void do_init_script(bool minimal) { script->st_ers = ers_new(sizeof(struct script_state), "script.c::st_ers", ERS_OPT_CLEAN); script->stack_ers = ers_new(sizeof(struct script_stack), "script.c::script_stack", ERS_OPT_NONE); + script->array_ers = ers_new(sizeof(struct script_array), "script.c::array_ers", ERS_OPT_CLEAN|ERS_OPT_CLEAR); ers_chunk_size(script->st_ers, 10); ers_chunk_size(script->stack_ers, 10); @@ -4073,7 +4307,7 @@ const char *script_getfuncname(struct script_state *st) { data = &st->stack->stack_data[st->start]; if( data->type == C_NAME && script->str_data[data->u.num].type == C_FUNC ) - return script->get_str(data->u.num); + return script->get_str(script_getvarid(data->u.num)); return NULL; } @@ -4568,7 +4802,7 @@ BUILDIN(callfunc) st->script = scr; st->stack->defsp = st->stack->sp; st->state = GOTO; - st->stack->var_function = idb_alloc(DB_OPT_RELEASE_DATA); + st->stack->var_function = i64db_alloc(DB_OPT_RELEASE_DATA); return true; } @@ -4617,7 +4851,7 @@ BUILDIN(callsub) st->pos = pos; st->stack->defsp = st->stack->sp; st->state = GOTO; - st->stack->var_function = idb_alloc(DB_OPT_RELEASE_DATA); + st->stack->var_function = i64db_alloc(DB_OPT_RELEASE_DATA); return true; } @@ -5150,7 +5384,7 @@ BUILDIN(input) { TBL_PC* sd; struct script_data* data; - int uid; + int64 uid; const char* name; int min; int max; @@ -5214,7 +5448,7 @@ BUILDIN(setr) { TBL_PC* sd = NULL; struct script_data* data; //struct script_data* datavalue; - int num; + int64 num; const char* name; char prefix; @@ -5292,34 +5526,6 @@ BUILDIN(setr) { /// Array variables /// -/// Returns the size of the specified array -int32 getarraysize(struct script_state* st, int32 id, int32 idx, int isstring, struct DBMap** ref) -{ - int32 ret = idx; - - if( isstring ) - { - for( ; idx < SCRIPT_MAX_ARRAYSIZE; ++idx ) - { - char* str = (char*)script->get_val2(st, reference_uid(id, idx), ref); - if( str && *str ) - ret = idx + 1; - script_removetop(st, -1, 0); - } - } - else - { - for( ; idx < SCRIPT_MAX_ARRAYSIZE; ++idx ) - { - int32 num = (int32)__64BPTRSIZE(script->get_val2(st, reference_uid(id, idx), ref)); - if( num ) - ret = idx + 1; - script_removetop(st, -1, 0); - } - } - return ret; -} - /// Sets values of an array, from the starting index. /// ex: setarray arr[1],1,2,3; /// @@ -5346,13 +5552,6 @@ BUILDIN(setarray) id = reference_getid(data); start = reference_getindex(data); name = reference_getname(data); - if( not_array_variable(*name) ) - { - ShowError("script:setarray: illegal scope\n"); - script->reportdata(data); - st->state = END; - return false;// not supported - } if( not_server_variable(*name) ) { @@ -5404,13 +5603,6 @@ BUILDIN(cleararray) id = reference_getid(data); start = reference_getindex(data); name = reference_getname(data); - if( not_array_variable(*name) ) - { - ShowError("script:cleararray: illegal scope\n"); - script->reportdata(data); - st->state = END; - return false;// not supported - } if( not_server_variable(*name) ) { @@ -5469,14 +5661,6 @@ BUILDIN(copyarray) idx2 = reference_getindex(data2); name1 = reference_getname(data1); name2 = reference_getname(data2); - if( not_array_variable(*name1) || not_array_variable(*name2) ) - { - ShowError("script:copyarray: illegal scope\n"); - script->reportdata(data1); - script->reportdata(data2); - st->state = END; - return false;// not supported - } if( is_string_variable(name1) != is_string_variable(name2) ) { @@ -5534,7 +5718,6 @@ BUILDIN(copyarray) BUILDIN(getarraysize) { struct script_data* data; - const char* name; data = script_getdata(st, 2); if( !data_isreference(data) ) @@ -5546,19 +5729,12 @@ BUILDIN(getarraysize) return false;// not a variable } - name = reference_getname(data); - if( not_array_variable(*name) ) - { - ShowError("script:getarraysize: illegal scope\n"); - script->reportdata(data); - script_pushnil(st); - st->state = END; - return false;// not supported - } - - script_pushint(st, script->getarraysize(st, reference_getid(data), reference_getindex(data), is_string_variable(name), reference_getref(data))); + script_pushint(st, script->array_highest_key(st,st->rid ? script->rid2sd(st) : NULL,reference_getname(data))); return true; } +int script_array_index_cmp(const void *a, const void *b) { + return ( *(unsigned int*)a - *(unsigned int*)b ); +} /// Deletes count or all the elements in an array, from the starting index. /// ex: deletearray arr[4],2; @@ -5569,10 +5745,12 @@ BUILDIN(deletearray) { struct script_data* data; const char* name; - int start; - int end; + unsigned int start, end, i; int id; TBL_PC *sd = NULL; + struct script_array *sa = NULL; + struct DBMap *src = NULL; + void *value; data = script_getdata(st, 2); if( !data_isreference(data) ) @@ -5586,13 +5764,6 @@ BUILDIN(deletearray) id = reference_getid(data); start = reference_getindex(data); name = reference_getname(data); - if( not_array_variable(*name) ) - { - ShowError("script:deletearray: illegal scope\n"); - script->reportdata(data); - st->state = END; - return false;// not supported - } if( not_server_variable(*name) ) { @@ -5601,39 +5772,81 @@ BUILDIN(deletearray) return true;// no player attached } - end = SCRIPT_MAX_ARRAYSIZE; + if( !(src = script->array_src(st,sd,name) ) ) { + ShowError("script:deletearray: not a array\n"); + script->reportdata(data); + st->state = END; + return false;// not a variable + } else if ( !(sa = idb_get(src, id)) ) { /* non-existent array, nothing to empty */ + ShowError("script:deletearray: unknown array\n"); + script->reportdata(data); + st->state = END; + return false;// not a variable + } + end = script->array_highest_key(st,sd,name); + if( start >= end ) return true;// nothing to free - if( script_hasdata(st,3) ) - { - int count = script_getnum(st, 3); + if( is_string_variable(name) ) + value = (void *)""; + else + value = (void *)0; + + if( script_hasdata(st,3) ) { + unsigned int count = script_getnum(st, 3); if( count > end - start ) count = end - start; if( count <= 0 ) return true;// nothing to free - // move rest of the elements backward - for( ; start + count < end; ++start ) - { - void* v = script->get_val2(st, reference_uid(id, start + count), reference_getref(data)); - script->set_reg(st, sd, reference_uid(id, start), name, v, reference_getref(data)); - script_removetop(st, -1, 0); + if( end - start < sa->size ) { + // Better to iterate directly on the array, no speed-up from using sa + for( ; start + count < end; ++start ) { + // Compact and overwrite + void* v = script->get_val2(st, reference_uid(id, start + count), reference_getref(data)); + script->set_reg(st, sd, reference_uid(id, start), name, v, reference_getref(data)); + script_removetop(st, -1, 0); + } + for( ; start < end; start++ ) { + // Clean up any leftovers that weren't overwritten + script->set_reg(st, sd, reference_uid(id, start), name, value, reference_getref(data)); + } + } else { + // using sa to speed up + unsigned int *list = NULL, size = 0; + list = script->array_cpy_list(sa); + size = sa->size; + qsort(list, size, sizeof(unsigned int), script_array_index_cmp); + + ARR_FIND(0, size, i, list[i] >= start); + + for( ; i < size && list[i] < start + count; i++ ) { + // Clear any entries between start and start+count, if they exist + script->set_reg(st, sd, reference_uid(id, list[i]), name, value, reference_getref(data)); + } + + for( ; i < size && list[i] < end; i++ ) { + // Move back count positions any entries between start+count to fill the gaps + void* v = script->get_val2(st, reference_uid(id, list[i]), reference_getref(data)); + script->set_reg(st, sd, reference_uid(id, list[i]-count), name, v, reference_getref(data)); + script_removetop(st, -1, 0); + // Clear their originals + script->set_reg(st, sd, reference_uid(id, list[i]), name, value, reference_getref(data)); + } + } + } else { + unsigned int *list = NULL, size = 0; + list = script->array_cpy_list(sa); + size = sa->size; + + for(i = 0; i < size; i++) { + if( list[i] >= start ) // Less expensive than sorting it, most likely + script->set_reg(st, sd, reference_uid(id, list[i]), name, value, reference_getref(data)); } } - // clear the rest of the array - if( is_string_variable(name) ) - { - for( ; start < end; ++start ) - script->set_reg(st, sd, reference_uid(id, start), name, (void *)"", reference_getref(data)); - } - else - { - for( ; start < end; ++start ) - script->set_reg(st, sd, reference_uid(id, start), name, (void*)0, reference_getref(data)); - } return true; } @@ -5646,7 +5859,7 @@ BUILDIN(getelementofarray) struct script_data* data; const char* name; int32 id; - int i; + int64 i; data = script_getdata(st, 2); if( !data_isreference(data) ) @@ -5659,27 +5872,18 @@ BUILDIN(getelementofarray) } id = reference_getid(data); - name = reference_getname(data); - if( not_array_variable(*name) ) - { - ShowError("script:getelementofarray: illegal scope\n"); - script->reportdata(data); - script_pushnil(st); - st->state = END; - return false;// not supported - } i = script_getnum(st, 3); if( i < 0 || i >= SCRIPT_MAX_ARRAYSIZE ) { - ShowWarning("script:getelementofarray: index out of range (%d)\n", i); + ShowWarning("script:getelementofarray: index out of range (%lld)\n", i); script->reportdata(data); script_pushnil(st); st->state = END; return false;// out of range } - script->push_val(st->stack, C_NAME, reference_uid(id, i), reference_getref(data)); + script->push_val(st->stack, C_NAME, reference_uid(id, (unsigned int)i), reference_getref(data)); return true; } @@ -5976,19 +6180,13 @@ BUILDIN(checkweight2) name_it = reference_getname(data_it); name_nb = reference_getname(data_nb); - if( not_array_variable(*name_it) || not_array_variable(*name_nb)) - { - ShowError("script:checkweight2: illegal scope\n"); - script_pushint(st,0); - return false;// not supported - } if(is_string_variable(name_it) || is_string_variable(name_nb)) { ShowError("script:checkweight2: illegal type, need int\n"); script_pushint(st,0); return false;// not supported } - nb_it = script->getarraysize(st, id_it, idx_it, 0, reference_getref(data_it)); - nb_nb = script->getarraysize(st, id_nb, idx_nb, 0, reference_getref(data_nb)); + nb_it = script->array_highest_key(st,sd,reference_getname(data_it)); + nb_nb = script->array_highest_key(st,sd,reference_getname(data_nb)); if(nb_it != nb_nb) { ShowError("Size mistmatch: nb_it=%d, nb_nb=%d\n",nb_it,nb_nb); fail = 1; @@ -12810,7 +13008,7 @@ BUILDIN(getmapxy) struct block_list *bl = NULL; TBL_PC *sd=NULL; - int num; + int64 num; const char *name; char prefix; @@ -12910,7 +13108,7 @@ BUILDIN(getmapxy) //Set MapName$ num=st->stack->stack_data[st->start+2].u.num; - name=script->get_str(num&0x00ffffff); + name=script->get_str(script_getvarid(num)); prefix=*name; if(not_server_variable(prefix)) @@ -12921,7 +13119,7 @@ BUILDIN(getmapxy) //Set MapX num=st->stack->stack_data[st->start+3].u.num; - name=script->get_str(num&0x00ffffff); + name=script->get_str(script_getvarid(num)); prefix=*name; if(not_server_variable(prefix)) @@ -12932,7 +13130,7 @@ BUILDIN(getmapxy) //Set MapY num=st->stack->stack_data[st->start+4].u.num; - name=script->get_str(num&0x00ffffff); + name=script->get_str(script_getvarid(num)); prefix=*name; if(not_server_variable(prefix)) @@ -13537,14 +13735,6 @@ BUILDIN(explode) start = reference_getindex(data); name = reference_getname(data); - if( not_array_variable(*name) ) - { - ShowError("script:explode: illegal scope\n"); - script->reportdata(data); - st->state = END; - return false;// not supported - } - if( !is_string_variable(name) ) { ShowError("script:explode: not string array\n"); @@ -13586,7 +13776,7 @@ BUILDIN(implode) { struct script_data* data = script_getdata(st, 2); const char *glue = NULL, *name, *temp; - int32 array_size, id; + uint32 array_size, id; size_t len = 0, glue_len = 0, k = 0; int i; @@ -13605,14 +13795,6 @@ BUILDIN(implode) id = reference_getid(data); name = reference_getname(data); - if( not_array_variable(*name) ) - { - ShowError("script:implode: illegal scope\n"); - script->reportdata(data); - st->state = END; - return false;// not supported - } - if( !is_string_variable(name) ) { ShowError("script:implode: not string array\n"); @@ -13629,7 +13811,7 @@ BUILDIN(implode) } //count chars - array_size = script->getarraysize(st, id, reference_getindex(data), is_string_variable(name), reference_getref(data)) - 1; + array_size = script->array_highest_key(st,sd,name) - 1; if(array_size == -1) { //empty array check (AmsTaff) @@ -14270,8 +14452,6 @@ int buildin_query_sql_sub(struct script_state* st, Sql* handle) return false; } } - if( not_array_variable(*name) ) - max_rows = 1;// not an array, limit to one row } else { ShowError("script:query_sql: not a variable\n"); script->reportdata(data); @@ -14857,13 +15037,6 @@ BUILDIN(searchitem) id = reference_getid(data); start = reference_getindex(data); name = reference_getname(data); - if( not_array_variable(*name) ) - { - ShowError("script:searchitem: illegal scope\n"); - script->reportdata(data); - st->state = END; - return false;// not supported - } if( not_server_variable(*name) ) { @@ -18811,11 +18984,11 @@ void script_defaults(void) { script->next_id = 0; script->st_ers = NULL; script->stack_ers = NULL; - + script->array_ers = NULL; + script->hq = NULL; script->hqi = NULL; script->hqs = script->hqis = 0; - memset(&script->hqe, 0, sizeof(script->hqe)); script->buildin = NULL; script->buildin_count = 0; @@ -18868,6 +19041,9 @@ void script_defaults(void) { script->potion_flag = script->potion_hp = script->potion_per_hp = script->potion_sp = script->potion_per_sp = script->potion_target = 0; + script->generic_ui_array = NULL; + script->generic_ui_array_size = 0; + /* */ script->init = do_init_script; script->final = do_final_script; script->reload = script_reload; @@ -18955,7 +19131,6 @@ void script_defaults(void) { script->set_reg = set_reg; script->stack_expand = stack_expand; script->push_retinfo = push_retinfo; - script->pop_val = pop_val; script->op_3 = op_3; script->op_2str = op_2str; script->op_2num = op_2num; @@ -18968,7 +19143,6 @@ void script_defaults(void) { script->menu_countoptions = menu_countoptions; script->buildin_areawarp_sub = buildin_areawarp_sub; script->buildin_areapercentheal_sub = buildin_areapercentheal_sub; - script->getarraysize = getarraysize; script->buildin_delitem_delete = buildin_delitem_delete; script->buildin_delitem_search = buildin_delitem_search; script->buildin_killmonster_sub_strip = buildin_killmonster_sub_strip; @@ -19033,4 +19207,23 @@ void script_defaults(void) { script->global_casecheck.str_pos = 0; memset(script->global_casecheck.str_hash, 0, sizeof(script->global_casecheck.str_hash)); // end ENABLE_CASE_CHECK + + /** + * Array Handling + **/ + script->array_src = script_array_src; + script->array_update = script_array_update; + script->array_add_member = script_array_add_member; + script->array_remove_member = script_array_remove_member; + script->array_delete = script_array_delete; + script->array_size = script_array_size; + script->array_free_db = script_free_array_db; + script->array_highest_key = script_array_highest_key; + /* */ + script->reg_destroy_single = script_reg_destroy_single; + script->reg_destroy = script_reg_destroy; + /* */ + script->generic_ui_array_expand = script_generic_ui_array_expand; + script->array_cpy_list = script_array_cpy_list; + } diff --git a/src/map/script.h b/src/map/script.h index 2f7499569..65e11d3ca 100644 --- a/src/map/script.h +++ b/src/map/script.h @@ -6,6 +6,7 @@ #define _SCRIPT_H_ #include "../common/strlib.h" //StringBuf +#include "../common/cbasetypes.h" #include "map.h" //EVENT_NAME_LENGTH #include <setjmp.h> @@ -28,8 +29,8 @@ struct eri; #define NUM_WHISPER_VAR 10 -/// Maximum amount of elements in script arrays (soon getting ducked) -#define SCRIPT_MAX_ARRAYSIZE 128 +/// Maximum amount of elements in script arrays +#define SCRIPT_MAX_ARRAYSIZE (UINT_MAX - 1) #define SCRIPT_BLOCK_SIZE 512 @@ -127,9 +128,9 @@ struct eri; /// Returns the unique id of the reference (id and index) #define reference_getuid(data) ( (data)->u.num ) /// Returns the id of the reference -#define reference_getid(data) ( (int32)(reference_getuid(data) & 0x00ffffff) ) +#define reference_getid(data) ( (int32)(int64)(reference_getuid(data) & 0xFFFFFFFF) ) /// Returns the array index of the reference -#define reference_getindex(data) ( (int32)(((uint32)(reference_getuid(data) & 0xff000000)) >> 24) ) +#define reference_getindex(data) ( (uint32)(int64)((reference_getuid(data) >> 32) & 0xFFFFFFFF) ) /// Returns the name of the reference #define reference_getname(data) ( script->str_buf + script->str_data[reference_getid(data)].str ) /// Returns the linked list of uid-value pairs of the reference (can be NULL) @@ -140,10 +141,12 @@ struct eri; #define reference_getparamtype(data) ( script->str_data[reference_getid(data)].val ) /// Composes the uid of a reference from the id and the index -#define reference_uid(id,idx) ( (int32)((((uint32)(id)) & 0x00ffffff) | (((uint32)(idx)) << 24)) ) +#define reference_uid(id,idx) ( (int64) ((uint64)(id) & 0xFFFFFFFF) | ((uint64)(idx) << 32) ) + +#define script_getvarid(var) ( (int32)(int64)(var & 0xFFFFFFFF) ) +#define script_getvaridx(var) ( (uint32)(int64)((var >> 32) & 0xFFFFFFFF) ) #define not_server_variable(prefix) ( (prefix) != '$' && (prefix) != '.' && (prefix) != '\'') -#define not_array_variable(prefix) ( (prefix) != '$' && (prefix) != '@' && (prefix) != '.' && (prefix) != '\'' ) #define is_string_variable(name) ( (name)[strlen(name) - 1] == '$' ) #define BUILDIN(x) bool buildin_ ## x (struct script_state* st) @@ -339,7 +342,7 @@ struct script_retinfo { struct script_data { enum c_op type; union script_data_val { - int num; + int64 num; char *str; struct script_retinfo* ri; } u; @@ -350,8 +353,9 @@ struct script_data { // it must be saved when script state is RERUNLINE. [Eoe / jA 1094] struct script_code { int script_size; - unsigned char* script_buf; - struct DBMap* script_vars; + unsigned char *script_buf; + struct DBMap *script_vars; + struct DBMap *script_arrays_db; }; struct script_stack { @@ -359,7 +363,8 @@ struct script_stack { int sp_max;// capacity of the stack int defsp; struct script_data *stack_data;// stack - struct DBMap* var_function;// scope variables + struct DBMap *var_function;// scope variables + struct DBMap *array_function_db; }; /* [Ind/Hercules] */ @@ -401,13 +406,14 @@ struct script_state { unsigned int id; }; +/* TODO: HELLO DUCK THIS */ struct script_reg { - int index; + int64 index; int data; }; - +/* TODO: HELLO DUCK THIS */ struct script_regstr { - int index; + int64 index; char* data; }; @@ -458,6 +464,12 @@ struct casecheck_data { void (*clear) (void); }; +struct script_array { + unsigned int id;/* the first 32b of the 64b uid, aka the id */ + unsigned int size;/* how many members */ + unsigned int *members;/* member list */ +}; + /** * Interface **/ @@ -472,10 +484,13 @@ struct script_interface { struct hQueue *hq; struct hQueueIterator *hqi; int hqs, hqis; - int hqe[HQO_MAX]; /* */ char **buildin; unsigned int buildin_count; + /** + * used to generate quick script_array entries + **/ + struct eri *array_ers; /* */ struct str_data_struct *str_data; int str_data_size; // size of the data @@ -531,6 +546,9 @@ struct script_interface { int potion_flag; //For use on Alchemist improved potions/Potion Pitcher. [Skotlex] int potion_hp, potion_per_hp, potion_sp, potion_per_sp; int potion_target; + /* */ + unsigned int *generic_ui_array; + unsigned int generic_ui_array_size; /* */ void (*init) (bool minimal); void (*final) (void); @@ -549,9 +567,9 @@ struct script_interface { const char* (*conv_str) (struct script_state *st,struct script_data *data); TBL_PC *(*rid2sd) (struct script_state *st); void (*detach_rid) (struct script_state* st); - struct script_data* (*push_val)(struct script_stack* stack, enum c_op type, int val, struct DBMap** ref); + struct script_data* (*push_val)(struct script_stack* stack, enum c_op type, int64 val, struct DBMap** ref); struct script_data *(*get_val) (struct script_state* st, struct script_data* data); - void* (*get_val2) (struct script_state* st, int uid, struct DBMap** ref); + void* (*get_val2) (struct script_state* st, int64 uid, struct DBMap** ref); struct script_data* (*push_str) (struct script_stack* stack, enum c_op type, char* str); struct script_data* (*push_copy) (struct script_stack* stack, int pos); void (*pop_stack) (struct script_state* st, int start, int end); @@ -571,7 +589,7 @@ struct script_interface { void (*free_state) (struct script_state* st); void (*run_autobonus) (const char *autobonus,int id, int pos); void (*cleararray_pc) (struct map_session_data* sd, const char* varname, void* value); - void (*setarray_pc) (struct map_session_data* sd, const char* varname, uint8 idx, void* value, int* refcache); + void (*setarray_pc) (struct map_session_data* sd, const char* varname, uint32 idx, void* value, int* refcache); int (*config_read) (char *cfgName); int (*add_str) (const char* p); const char* (*get_str) (int id); @@ -615,10 +633,9 @@ struct script_interface { void (*read_constdb) (void); const char* (*print_line) (StringBuf *buf, const char *p, const char *mark, int line); void (*errorwarning_sub) (StringBuf *buf, const char *src, const char *file, int start_line, const char *error_msg, const char *error_pos); - int (*set_reg) (struct script_state *st, TBL_PC *sd, int num, const char *name, const void *value, struct DBMap **ref); + int (*set_reg) (struct script_state *st, TBL_PC *sd, int64 num, const char *name, const void *value, struct DBMap **ref); void (*stack_expand) (struct script_stack *stack); struct script_data* (*push_retinfo) (struct script_stack *stack, struct script_retinfo *ri, DBMap **ref); - int (*pop_val) (struct script_state *st); void (*op_3) (struct script_state *st, int op); void (*op_2str) (struct script_state *st, int op, const char *s1, const char *s2); void (*op_2num) (struct script_state *st, int op, int i1, int i2); @@ -631,7 +648,6 @@ struct script_interface { int (*menu_countoptions) (const char *str, int max_count, int *total); int (*buildin_areawarp_sub) (struct block_list *bl, va_list ap); int (*buildin_areapercentheal_sub) (struct block_list *bl, va_list ap); - int32 (*getarraysize) (struct script_state *st, int32 id, int32 idx, int isstring, struct DBMap **ref); void (*buildin_delitem_delete) (struct map_session_data *sd, int idx, int *amount, bool delete_items); bool (*buildin_delitem_search) (struct map_session_data *sd, struct item *it, bool exact_match); int (*buildin_killmonster_sub_strip) (struct block_list *bl, va_list ap); @@ -661,6 +677,23 @@ struct script_interface { struct casecheck_data local_casecheck; struct casecheck_data global_casecheck; // end ENABLE_CASE_CHECK + /** + * Array Handling + **/ + struct DBMap *(*array_src) (struct script_state *st, struct map_session_data *sd, const char *name); + void (*array_update) (struct DBMap **src, int64 num, bool empty); + void (*array_delete) (struct DBMap *src, struct script_array *sa); + void (*array_remove_member) (struct DBMap *src, struct script_array *sa, unsigned int idx); + void (*array_add_member) (struct script_array *sa, unsigned int idx); + unsigned int (*array_size) (struct script_state *st, struct map_session_data *sd, const char *name); + unsigned int (*array_highest_key) (struct script_state *st, struct map_session_data *sd, const char *name); + int (*array_free_db) (DBKey key, DBData *data, va_list ap); + /* */ + void (*reg_destroy_single) (struct map_session_data *sd, int64 reg, struct script_reg_state *data); + int (*reg_destroy) (DBKey key, DBData *data, va_list ap); + /* */ + void (*generic_ui_array_expand) (unsigned int plus); + unsigned int *(*array_cpy_list) (struct script_array *sa); }; struct script_interface *script; diff --git a/src/map/skill.c b/src/map/skill.c index 28937048e..0804adf90 100644 --- a/src/map/skill.c +++ b/src/map/skill.c @@ -2539,8 +2539,8 @@ int skill_attack(int attack_type, struct block_list* src, struct block_list *dsr } tsd->reproduceskill_id = copy_skill; - pc_setglobalreg(tsd, "REPRODUCE_SKILL", copy_skill); - pc_setglobalreg(tsd, "REPRODUCE_SKILL_LV", lv); + pc_setglobalreg(tsd, script->add_str("REPRODUCE_SKILL"), copy_skill); + pc_setglobalreg(tsd, script->add_str("REPRODUCE_SKILL_LV"), lv); tsd->status.skill[cidx].id = copy_skill; tsd->status.skill[cidx].lv = lv; @@ -2562,8 +2562,8 @@ int skill_attack(int attack_type, struct block_list* src, struct block_list *dsr lv = type; tsd->cloneskill_id = copy_skill; - pc_setglobalreg(tsd, "CLONE_SKILL", copy_skill); - pc_setglobalreg(tsd, "CLONE_SKILL_LV", lv); + pc_setglobalreg(tsd, script->add_str("CLONE_SKILL"), copy_skill); + pc_setglobalreg(tsd, script->add_str("CLONE_SKILL_LV"), lv); tsd->status.skill[cidx].id = copy_skill; tsd->status.skill[cidx].lv = lv; @@ -5660,7 +5660,7 @@ int skill_castend_nodamage_id(struct block_list *src, struct block_list *bl, uin } sd->mission_mobid = id; sd->mission_count = 0; - pc_setglobalreg(sd,"TK_MISSION_ID", id); + pc_setglobalreg(sd,script->add_str("TK_MISSION_ID"), id); clif->mission_info(sd, id, 0); clif->skill_nodamage(src,bl,skill_id,skill_lv,1); } @@ -7414,7 +7414,7 @@ int skill_castend_nodamage_id(struct block_list *src, struct block_list *bl, uin if (skill_id == SL_SUPERNOVICE && dstsd && dstsd->die_counter && !(rnd()%100)) { //Erase death count 1% of the casts dstsd->die_counter = 0; - pc_setglobalreg(dstsd,"PC_DIE_COUNTER", 0); + pc_setglobalreg(dstsd,script->add_str("PC_DIE_COUNTER"), 0); clif->specialeffect(bl, 0x152, AREA); //SC_SOULLINK invokes status_calc_pc for us. } @@ -16597,7 +16597,7 @@ int skill_produce_mix(struct map_session_data *sd, uint16 skill_id, int nameid, { //Cooking items. clif->specialeffect(&sd->bl, 608, AREA); if( sd->cook_mastery < 1999 ) - pc_setglobalreg(sd, "COOK_MASTERY",sd->cook_mastery + ( 1 << ( (skill->produce_db[idx].itemlv - 11) / 2 ) ) * 5); + pc_setglobalreg(sd, script->add_str("COOK_MASTERY"),sd->cook_mastery + ( 1 << ( (skill->produce_db[idx].itemlv - 11) / 2 ) ) * 5); } break; } @@ -16698,7 +16698,7 @@ int skill_produce_mix(struct map_session_data *sd, uint16 skill_id, int nameid, { //Cooking items. clif->specialeffect(&sd->bl, 609, AREA); if( sd->cook_mastery > 0 ) - pc_setglobalreg(sd, "COOK_MASTERY", sd->cook_mastery - ( 1 << ((skill->produce_db[idx].itemlv - 11) / 2) ) - ( ( ( 1 << ((skill->produce_db[idx].itemlv - 11) / 2) ) >> 1 ) * 3 )); + pc_setglobalreg(sd, script->add_str("COOK_MASTERY"), sd->cook_mastery - ( 1 << ((skill->produce_db[idx].itemlv - 11) / 2) ) - ( ( ( 1 << ((skill->produce_db[idx].itemlv - 11) / 2) ) >> 1 ) * 3 )); } } } diff --git a/src/map/trade.c b/src/map/trade.c index 6f079bdd3..44b669ebd 100644 --- a/src/map/trade.c +++ b/src/map/trade.c @@ -178,7 +178,7 @@ int impossible_trade_check(struct map_session_data *sd) nullpo_retr(1, sd); if(sd->deal.zeny > sd->status.zeny) { - pc_setglobalreg(sd,"ZENY_HACKER",1); + pc_setglobalreg(sd,script->add_str("ZENY_HACKER"),1); return -1; } diff --git a/src/map/unit.c b/src/map/unit.c index 7f722684d..d801a72f0 100644 --- a/src/map/unit.c +++ b/src/map/unit.c @@ -2352,20 +2352,13 @@ int unit_free(struct block_list *bl, clr_type clrtype) { pc->delspiritball(sd,sd->spiritball,1); for(i = 1; i < 5; i++) pc->del_charm(sd, sd->charm[i], i); - - if( sd->reg ) { //Double logout already freed pointer fix... [Skotlex] - aFree(sd->reg); - sd->reg = NULL; - sd->reg_num = 0; - } - if( sd->regstr ) { - for( i = 0; i < sd->regstr_num; ++i ) - if( sd->regstr[i].data ) - aFree(sd->regstr[i].data); - aFree(sd->regstr); - sd->regstr = NULL; - sd->regstr_num = 0; - } + + if( sd->var_db ) + sd->var_db->destroy(sd->var_db,script->reg_destroy); + + if( sd->array_db ) + sd->array_db->destroy(sd->array_db,script->array_free_db); + if( sd->st && sd->st->state != RUN ) {// free attached scripts that are waiting script->free_state(sd->st); sd->st = NULL; |