소스 검색

Script Engine Upgrade
* Upgraded the script engine variables to no longer be limited.
* Increased array limit from 127 to 2 billion.
* Improvement in overall speed and storage size.
* All variable types now support arrays.
- Merge from HerculesWS/Hercules@82b583b. Thanks to those who worked on it.

aleos89 10 년 전
부모
커밋
0696125b82

+ 9 - 3
conf/inter_athena.conf

@@ -80,9 +80,12 @@ mysql_reconnect_count: 1
 
 
 // Login Database Tables
 // Login Database Tables
 login_server_account_db: login
 login_server_account_db: login
-login_server_accreg_db: global_reg_value
 ipban_table: ipbanlist
 ipban_table: ipbanlist
 
 
+// Shared
+global_acc_reg_num_table: global_acc_reg_num
+global_acc_reg_str_table: global_acc_reg_str
+
 // Char Database Tables
 // Char Database Tables
 char_db: char
 char_db: char
 hotkey_db: hotkey
 hotkey_db: hotkey
@@ -91,7 +94,6 @@ cart_db: cart_inventory
 inventory_db: inventory
 inventory_db: inventory
 charlog_db: charlog
 charlog_db: charlog
 storage_db: storage
 storage_db: storage
-reg_db: global_reg_value
 skill_db: skill
 skill_db: skill
 interlog_db: interlog
 interlog_db: interlog
 memo_db: memo
 memo_db: memo
@@ -117,6 +119,10 @@ elemental_db: elemental
 ragsrvinfo_db: ragsrvinfo
 ragsrvinfo_db: ragsrvinfo
 skillcooldown_db: skillcooldown
 skillcooldown_db: skillcooldown
 bonus_script_db: bonus_script
 bonus_script_db: bonus_script
+acc_reg_num_table: acc_reg_num
+acc_reg_str_table: acc_reg_str
+char_reg_str_table: char_reg_str
+char_reg_num_table: char_reg_num
 
 
 // Map Database Tables
 // Map Database Tables
 buyingstore_db: buyingstores
 buyingstore_db: buyingstores
@@ -135,7 +141,7 @@ mob_skill_db_db: mob_skill_db
 mob_skill_db_re_db: mob_skill_db_re
 mob_skill_db_re_db: mob_skill_db_re
 mob_skill_db2_db: mob_skill_db2
 mob_skill_db2_db: mob_skill_db2
 //mob_skill_db2_db: mob_skill_db2_re
 //mob_skill_db2_db: mob_skill_db2_re
-mapreg_db: mapreg
+mapreg_table: mapreg
 vending_db: vendings
 vending_db: vendings
 vending_items_db: vending_items
 vending_items_db: vending_items
 market_table: market
 market_table: market

+ 14 - 45
doc/script_commands.txt

@@ -3,7 +3,7 @@
 //===== By: ==================================================
 //===== By: ==================================================
 //= rAthena Dev Team
 //= rAthena Dev Team
 //===== Last Updated: ========================================
 //===== Last Updated: ========================================
-//= 20140313
+//= 20150610
 //===== Description: =========================================
 //===== Description: =========================================
 //= A reference manual for the rAthena scripting language.
 //= A reference manual for the rAthena scripting language.
 //= Commands are sorted depending on their functionality.
 //= Commands are sorted depending on their functionality.
@@ -616,6 +616,8 @@ same name. You can tell between the specific variables of an array with an
 
 
 <variable name>[<array index>]
 <variable name>[<array index>]
 
 
+All variable types can be used as arrays.
+
 Variables stored in this way, inside an array, are also called 'array elements'. 
 Variables stored in this way, inside an array, are also called 'array elements'. 
 Arrays are specifically useful for storing a set of similar data (like several 
 Arrays are specifically useful for storing a set of similar data (like several 
 item IDs for example) and then looping through it. You can address any array 
 item IDs for example) and then looping through it. You can address any array 
@@ -631,8 +633,9 @@ value from an another array) to get at an array value:
     
     
 This will make @arrayofnumbers[100] equal to 10.
 This will make @arrayofnumbers[100] equal to 10.
 
 
-Notice that index numbering always starts with 0. Arrays cannot hold more than 
-128 variables. (So the last one can't have a number higher than 127)
+Index numbering always starts with 0 and arrays can hold over 2 billion
+variables. As such, the (guaranteed) allowed values for indices are in the
+range 0 ~ 2147483647.
 
 
 And array indexes probably can't be negative. Nobody tested what happens when 
 And array indexes probably can't be negative. Nobody tested what happens when 
 you try to get a negatively numbered variable from an array, but it's not going 
 you try to get a negatively numbered variable from an array, but it's not going 
@@ -644,41 +647,6 @@ Arrays can naturally store strings:
 the '$', normally denoting a string variable, before the square brackets that 
 the '$', normally denoting a string variable, before the square brackets that 
 denotes an array index.
 denotes an array index.
 
 
-Resume of the allowed variable and array scopes
------------------------------------------------
-
-+==========+======+=======+
-|VarType   | Norm | Array |
-+==========+======+=======+
-|$Str$     | OK!  | OK!   |
-+----------+------+-------+
-|$@Str$    | OK!  | OK!   |
-+----------+------+-------+
-|@Str$     | OK!  | OK!   |
-+----------+------+-------+
-|#Str$     | OK!  | FAIL! |
-+----------+------+-------+
-|Str$      | OK!  | FAIL! |
-+----------+------+-------+
-|$Int      | OK!  | OK!   |
-+----------+------+-------+
-|$@Int     | OK!  | OK!   |
-+----------+------+-------+
-|@Int      | OK!  | OK!   |
-+----------+------+-------+
-|#Int      | OK!  | FAIL! |
-+----------+------+-------+
-|Int       | OK!  | FAIL! |
-+----------+------+-------+
-|.Str$     | OK!  | OK!   |
-+----------+------+-------+
-|.Int      | OK!  | OK!   |
-+----------+------+-------+
-|.@Str$    | OK!  | OK!   |
-+----------+------+-------+
-|.@Int     | OK!  | OK!   |
-+----------+------+-------+
-
 Variable References
 Variable References
 -------------------
 -------------------
 
 
@@ -991,6 +959,7 @@ On<label name>:
 These special labels are used with Mob scripts mostly, and script commands
 These special labels are used with Mob scripts mostly, and script commands
 that requires you to point/link a command to a mob or another NPC, giving a label
 that requires you to point/link a command to a mob or another NPC, giving a label
 name to start from. The label name can be any of your liking, but must be
 name to start from. The label name can be any of your liking, but must be
+started with "On".
 
 
 Example:
 Example:
 
 
@@ -2167,7 +2136,7 @@ example:
 
 
     setarray @array[0],200,200,200;
     setarray @array[0],200,200,200;
     setarray @array[1],300,150;
     setarray @array[1],300,150;
-    
+
 will produce:
 will produce:
 
 
  @array[0]=200
  @array[0]=200
@@ -2200,7 +2169,7 @@ some cases invaluable.
     setarray @array[0], 100, 200, 300, 400, 500, 600;
     setarray @array[0], 100, 200, 300, 400, 500, 600;
     // So we have made @array[]
     // So we have made @array[]
     copyarray @array2[0],@array[2],2;
     copyarray @array2[0],@array[2],2;
-    
+
     // Now, @array2[0] will be equal to @array[2] (300) and 
     // Now, @array2[0] will be equal to @array[2] (300) and 
     // @array2[1] will be equal to @array[3].
     // @array2[1] will be equal to @array[3].
 
 
@@ -2274,9 +2243,9 @@ Whatever it returns is determined by type.
 
 
 *getarraysize(<array name>)
 *getarraysize(<array name>)
 
 
-This function returns the number of values that are contained inside the 
-specified array. Notice that zeros and empty strings at the end of this array 
-are not counted towards this number.
+This function returns highest index of the array that is filled.
+Notice that zeros and empty strings at the end of this array are not
+counted towards this number.
 
 
 For example:
 For example:
 
 
@@ -2287,8 +2256,8 @@ This will make @arraysize == 6. But if you try this:
 
 
     setarray @array[0], 100, 200, 300, 400, 500, 600, 0;
     setarray @array[0], 100, 200, 300, 400, 500, 600, 0;
     set @arraysize,getarraysize(@array);
     set @arraysize,getarraysize(@array);
-    
-@arraysize will still equal 6, even though you've set 7 values.    
+
+@arraysize will still equal 6, even though you've set 7 values.
 
 
 ---------------------------------------
 ---------------------------------------
 
 

+ 4 - 4
npc/custom/quests/hunting_missions.txt

@@ -55,7 +55,7 @@ function Chk;
 			mes "You've started a mission";
 			mes "You've started a mission";
 			mes "on another character.";
 			mes "on another character.";
 			if (!@hm_char_del_check) {  // check for deleted character
 			if (!@hm_char_del_check) {  // check for deleted character
-				query_sql("SELECT 1 FROM `global_reg_value` WHERE `str` = 'Mission0' AND `char_id` IN(SELECT `char_id` FROM `char` WHERE `account_id` = " + getcharid(3) + ")", .@i);
+				query_sql("SELECT 1 FROM `char_reg_num` WHERE `key` = 'Mission0' AND `char_id` IN(SELECT `char_id` FROM `char` WHERE `account_id` = " + getcharid(3) + ")", .@i);
 				if (!.@i) {
 				if (!.@i) {
 					next;
 					next;
 					mes "[Hunting Missions]";
 					mes "[Hunting Missions]";
@@ -146,7 +146,7 @@ function Chk;
 	case 6:
 	case 6:
 		mes "[Hunting Missions]";
 		mes "[Hunting Missions]";
 		mes "The top hunters are:";
 		mes "The top hunters are:";
-		query_sql("SELECT char_id AS id, (SELECT `name` FROM `char` WHERE char_id = id),`value` FROM `global_reg_value` WHERE str = 'Mission_Total' ORDER BY CAST(`value` AS SIGNED) DESC LIMIT 5", .@id, .@name$, .@val);
+		query_sql("SELECT char_id AS id, (SELECT `name` FROM `char` WHERE char_id = id),`value` FROM `char_reg_num` WHERE `key` = 'Mission_Total' ORDER BY CAST(`value` AS SIGNED) DESC LIMIT 5", .@id, .@name$, .@val);
 		for (.@i = 0; .@i < 5; .@i++)
 		for (.@i = 0; .@i < 5; .@i++)
 			mes "  [Rank " + (.@i+1) + "]  " + ((.@name$[.@i] == "") ? "^777777none" : "^0055FF" + .@name$[.@i]+"^000000 : ^FF0000" + .@val[.@i] + " pt.") + "^000000";
 			mes "  [Rank " + (.@i+1) + "]  " + ((.@name$[.@i] == "") ? "^777777none" : "^0055FF" + .@name$[.@i]+"^000000 : ^FF0000" + .@val[.@i] + " pt.") + "^000000";
 		close;
 		close;
@@ -207,9 +207,9 @@ Mission_Status:
 		#Mission_Delay = gettimetick(2) + (.Delay * 3600);
 		#Mission_Delay = gettimetick(2) + (.Delay * 3600);
 	Mission_Total++;
 	Mission_Total++;
 	if (Mission_Total == 1)
 	if (Mission_Total == 1)
-		query_sql("INSERT INTO `global_reg_value` (`char_id`,`str`,`value`,`type`,`account_id`) VALUES (" + getcharid(0) + ",'Mission_Total','1',3,0)");
+		query_sql("INSERT INTO `char_reg_num` (`char_id`,`key`,`index`,`value`) VALUES (" + getcharid(0) + ",'Mission_Total','0',1)");
 	else
 	else
-		query_sql("UPDATE `global_reg_value` SET `value` = " + Mission_Total + " WHERE char_id = " + getcharid(0) + " AND `str` = 'Mission_Total'");
+		query_sql("UPDATE `char_reg_num` SET `value` = " + Mission_Total + " WHERE `char_id` = " + getcharid(0) + " AND `key` = 'Mission_Total'");
 	close;
 	close;
 
 
 Mission_Info:
 Mission_Info:

+ 72 - 10
sql-files/main.sql

@@ -1,3 +1,28 @@
+--
+-- Table structure for table `acc_reg_num`
+--
+
+CREATE TABLE IF NOT EXISTS `acc_reg_num` (
+  `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`
+--
+
+CREATE TABLE IF NOT EXISTS `acc_reg_str` (
+  `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 `skillcooldown`
 -- Table structure for table `skillcooldown`
@@ -133,6 +158,32 @@ CREATE TABLE IF NOT EXISTS `char` (
   KEY `online` (`online`)
   KEY `online` (`online`)
 ) ENGINE=MyISAM AUTO_INCREMENT=150000; 
 ) ENGINE=MyISAM AUTO_INCREMENT=150000; 
 
 
+--
+-- Table structure for table `char_reg_num`
+--
+
+CREATE TABLE IF NOT EXISTS `char_reg_num` (
+  `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`
+--
+
+CREATE TABLE IF NOT EXISTS `char_reg_str` (
+  `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 `charlog`
 -- Table structure for table `charlog`
 --
 --
@@ -203,16 +254,28 @@ CREATE TABLE IF NOT EXISTS `hotkey` (
 ) ENGINE=MyISAM;
 ) ENGINE=MyISAM;
 
 
 --
 --
--- Table structure for table `global_reg_value`
+-- Table structure for table `global_acc_reg_num`
 --
 --
 
 
-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',
+CREATE TABLE IF NOT EXISTS `global_acc_reg_num` (
+  `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`
+--
+
+CREATE TABLE IF NOT EXISTS `global_acc_reg_str` (
   `account_id` int(11) unsigned NOT NULL default '0',
   `account_id` int(11) unsigned NOT NULL default '0',
-  PRIMARY KEY  (`char_id`,`str`,`account_id`),
+  `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`)
   KEY `account_id` (`account_id`)
 ) ENGINE=MyISAM;
 ) ENGINE=MyISAM;
 
 
@@ -478,11 +541,10 @@ INSERT INTO `login` (`account_id`, `userid`, `user_pass`, `sex`, `email`) VALUES
 --
 --
 
 
 CREATE TABLE IF NOT EXISTS `mapreg` (
 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',
   `index` int(11) unsigned NOT NULL default '0',
   `value` varchar(255) NOT NULL,
   `value` varchar(255) NOT NULL,
-  KEY `varname` (`varname`),
-  KEY `index` (`index`)
+  PRIMARY KEY (`varname`,`index`)
 ) ENGINE=MyISAM;
 ) ENGINE=MyISAM;
 
 
 --
 --

+ 66 - 0
sql-files/upgrades/upgrade_20150610.sql

@@ -0,0 +1,66 @@
+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` (
+  `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` (
+  `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` (
+  `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` (
+  `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` (
+  `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` (
+  `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` (`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` (`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` (`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` (`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` (`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` (`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`;

+ 34 - 8
src/char/char.c

@@ -1575,7 +1575,9 @@ int char_delete_char_sql(uint32 char_id){
 		Sql_ShowDebug(sql_handle);
 		Sql_ShowDebug(sql_handle);
 
 
 	/* delete character registry */
 	/* delete character registry */
-	if( SQL_ERROR == Sql_Query(sql_handle, "DELETE FROM `%s` WHERE `type`=3 AND `char_id`='%d'", schema_config.reg_db, char_id) )
+	if( SQL_ERROR == Sql_Query(sql_handle, "DELETE FROM `%s` WHERE `char_id`='%d'", schema_config.char_reg_str_table, char_id) )
+		Sql_ShowDebug(sql_handle);
+	if( SQL_ERROR == Sql_Query(sql_handle, "DELETE FROM `%s` WHERE `char_id`='%d'", schema_config.char_reg_num_table, char_id) )
 		Sql_ShowDebug(sql_handle);
 		Sql_ShowDebug(sql_handle);
 
 
 	/* delete skills */
 	/* delete skills */
@@ -2129,7 +2131,8 @@ bool char_checkdb(void){
 	const char* sqltable[] = {
 	const char* sqltable[] = {
 		schema_config.char_db, schema_config.hotkey_db, schema_config.scdata_db, schema_config.cart_db, 
 		schema_config.char_db, schema_config.hotkey_db, schema_config.scdata_db, schema_config.cart_db, 
                 schema_config.inventory_db, schema_config.charlog_db, schema_config.storage_db, 
                 schema_config.inventory_db, schema_config.charlog_db, schema_config.storage_db, 
-                schema_config.reg_db, schema_config.skill_db, schema_config.interlog_db, schema_config.memo_db,
+                schema_config.char_reg_str_table, schema_config.char_reg_num_table, schema_config.acc_reg_str_table,
+                schema_config.acc_reg_num_table, schema_config.skill_db, schema_config.interlog_db, schema_config.memo_db,
 		schema_config.guild_db, schema_config.guild_alliance_db, schema_config.guild_castle_db, 
 		schema_config.guild_db, schema_config.guild_alliance_db, schema_config.guild_castle_db, 
                 schema_config.guild_expulsion_db, schema_config.guild_member_db, 
                 schema_config.guild_expulsion_db, schema_config.guild_member_db, 
                 schema_config.guild_skill_db, schema_config.guild_position_db, schema_config.guild_storage_db,
                 schema_config.guild_skill_db, schema_config.guild_position_db, schema_config.guild_storage_db,
@@ -2162,8 +2165,23 @@ bool char_checkdb(void){
 		Sql_ShowDebug(sql_handle);
 		Sql_ShowDebug(sql_handle);
 		return false;
 		return false;
 	}
 	}
-	//checking reg_db
-	if( SQL_ERROR == Sql_Query(sql_handle, "SELECT `char_id`,`str`,`value`,`type`,`account_id` FROM `%s` LIMIT 1;", schema_config.reg_db) ){
+	//checking char_reg_str_table
+	if( SQL_ERROR == Sql_Query(sql_handle, "SELECT `char_id`,`key`,`index`,`value` from `%s` LIMIT 1;", schema_config.char_reg_str_table) ) {
+		Sql_ShowDebug(sql_handle);
+		return false;
+	}
+	//checking char_reg_num_table
+	if( SQL_ERROR == Sql_Query(sql_handle, "SELECT `char_id`,`key`,`index`,`value` from `%s` LIMIT 1;", schema_config.char_reg_num_table) ) {
+		Sql_ShowDebug(sql_handle);
+		return false;
+	}
+	//checking global_acc_reg_str_table
+	if( SQL_ERROR == Sql_Query(sql_handle, "SELECT `account_id`,`key`,`index`,`value` from `%s` LIMIT 1;", schema_config.acc_reg_str_table) ) {
+		Sql_ShowDebug(sql_handle);
+		return false;
+	}
+	//checking global_acc_reg_num_table
+	if( SQL_ERROR == Sql_Query(sql_handle, "SELECT `account_id`,`key`,`index`,`value` from `%s` LIMIT 1;", schema_config.acc_reg_num_table) ) {
 		Sql_ShowDebug(sql_handle);
 		Sql_ShowDebug(sql_handle);
 		return false;
 		return false;
 	}
 	}
@@ -2321,7 +2339,6 @@ bool char_checkdb(void){
 		Sql_ShowDebug(sql_handle);
 		Sql_ShowDebug(sql_handle);
 		return false;
 		return false;
 	}
 	}
-	
 	//checking cart_db
 	//checking cart_db
 	if( SQL_ERROR == Sql_Query(sql_handle, "SELECT  `id`,`char_id`,`nameid`,`amount`,`equip`,`identify`,`refine`,"
 	if( SQL_ERROR == Sql_Query(sql_handle, "SELECT  `id`,`char_id`,`nameid`,`amount`,`equip`,`identify`,`refine`,"
 		"`attribute`,`card0`,`card1`,`card2`,`card3`,`expire_time`,`bound`,`unique_id`"
 		"`attribute`,`card0`,`card1`,`card2`,`card3`,`expire_time`,`bound`,`unique_id`"
@@ -2383,8 +2400,6 @@ void char_sql_config_read(const char* cfgName) {
 			safestrncpy(schema_config.charlog_db, w2, sizeof(schema_config.charlog_db));
 			safestrncpy(schema_config.charlog_db, w2, sizeof(schema_config.charlog_db));
 		else if(!strcmpi(w1,"storage_db"))
 		else if(!strcmpi(w1,"storage_db"))
 			safestrncpy(schema_config.storage_db, w2, sizeof(schema_config.storage_db));
 			safestrncpy(schema_config.storage_db, w2, sizeof(schema_config.storage_db));
-		else if(!strcmpi(w1,"reg_db"))
-			safestrncpy(schema_config.reg_db, w2, sizeof(schema_config.reg_db));
 		else if(!strcmpi(w1,"skill_db"))
 		else if(!strcmpi(w1,"skill_db"))
 			safestrncpy(schema_config.skill_db, w2, sizeof(schema_config.skill_db));
 			safestrncpy(schema_config.skill_db, w2, sizeof(schema_config.skill_db));
 		else if(!strcmpi(w1,"interlog_db"))
 		else if(!strcmpi(w1,"interlog_db"))
@@ -2435,6 +2450,14 @@ void char_sql_config_read(const char* cfgName) {
 			safestrncpy(schema_config.skillcooldown_db, w2, sizeof(schema_config.skillcooldown_db));
 			safestrncpy(schema_config.skillcooldown_db, w2, sizeof(schema_config.skillcooldown_db));
 		else if(!strcmpi(w1,"bonus_script_db"))
 		else if(!strcmpi(w1,"bonus_script_db"))
 			safestrncpy(schema_config.bonus_script_db, w2, sizeof(schema_config.bonus_script_db));
 			safestrncpy(schema_config.bonus_script_db, w2, sizeof(schema_config.bonus_script_db));
+		else if(!strcmpi(w1,"char_reg_num_table"))
+			safestrncpy(schema_config.char_reg_num_table, w2, sizeof(schema_config.char_reg_num_table));
+		else if(!strcmpi(w1,"char_reg_str_table"))
+			safestrncpy(schema_config.char_reg_str_table, w2, sizeof(schema_config.char_reg_str_table));
+		else if(!strcmpi(w1,"acc_reg_str_table"))
+			safestrncpy(schema_config.acc_reg_str_table, w2, sizeof(schema_config.acc_reg_str_table));
+		else if(!strcmpi(w1,"acc_reg_num_table"))
+			safestrncpy(schema_config.acc_reg_num_table, w2, sizeof(schema_config.acc_reg_num_table));
 		//support the import command, just like any other config
 		//support the import command, just like any other config
 		else if(!strcmpi(w1,"import"))
 		else if(!strcmpi(w1,"import"))
 			char_sql_config_read(w2);
 			char_sql_config_read(w2);
@@ -2454,7 +2477,6 @@ void char_set_default_sql(){
 	safestrncpy(schema_config.charlog_db,"charlog",sizeof(schema_config.charlog_db));
 	safestrncpy(schema_config.charlog_db,"charlog",sizeof(schema_config.charlog_db));
 	safestrncpy(schema_config.storage_db,"storage",sizeof(schema_config.storage_db));
 	safestrncpy(schema_config.storage_db,"storage",sizeof(schema_config.storage_db));
 	safestrncpy(schema_config.interlog_db,"interlog",sizeof(schema_config.interlog_db));
 	safestrncpy(schema_config.interlog_db,"interlog",sizeof(schema_config.interlog_db));
-	safestrncpy(schema_config.reg_db,"global_reg_value",sizeof(schema_config.reg_db));
 	safestrncpy(schema_config.skill_db,"skill",sizeof(schema_config.skill_db));
 	safestrncpy(schema_config.skill_db,"skill",sizeof(schema_config.skill_db));
 	safestrncpy(schema_config.memo_db,"memo",sizeof(schema_config.memo_db));
 	safestrncpy(schema_config.memo_db,"memo",sizeof(schema_config.memo_db));
 	safestrncpy(schema_config.guild_db,"guild",sizeof(schema_config.guild_db));
 	safestrncpy(schema_config.guild_db,"guild",sizeof(schema_config.guild_db));
@@ -2479,6 +2501,10 @@ void char_set_default_sql(){
 	safestrncpy(schema_config.ragsrvinfo_db,"ragsrvinfo",sizeof(schema_config.ragsrvinfo_db));
 	safestrncpy(schema_config.ragsrvinfo_db,"ragsrvinfo",sizeof(schema_config.ragsrvinfo_db));
 	safestrncpy(schema_config.skillcooldown_db,"skillcooldown",sizeof(schema_config.skillcooldown_db));
 	safestrncpy(schema_config.skillcooldown_db,"skillcooldown",sizeof(schema_config.skillcooldown_db));
 	safestrncpy(schema_config.bonus_script_db,"bonus_script",sizeof(schema_config.bonus_script_db));
 	safestrncpy(schema_config.bonus_script_db,"bonus_script",sizeof(schema_config.bonus_script_db));
+	safestrncpy(schema_config.char_reg_num_table,"char_reg_num",sizeof(schema_config.char_reg_num_table));
+	safestrncpy(schema_config.char_reg_str_table,"char_reg_str",sizeof(schema_config.char_reg_str_table));
+	safestrncpy(schema_config.acc_reg_str_table,"acc_reg_str",sizeof(schema_config.acc_reg_str_table));
+	safestrncpy(schema_config.acc_reg_num_table,"acc_reg_num",sizeof(schema_config.acc_reg_num_table));
 }
 }
 
 
 //set default config
 //set default config

+ 4 - 2
src/char/char.h

@@ -40,7 +40,6 @@ struct Schema_Config {
 	char charlog_db[DB_NAME_LEN];
 	char charlog_db[DB_NAME_LEN];
 	char storage_db[DB_NAME_LEN];
 	char storage_db[DB_NAME_LEN];
 	char interlog_db[DB_NAME_LEN];
 	char interlog_db[DB_NAME_LEN];
-	char reg_db[DB_NAME_LEN];
 	char skill_db[DB_NAME_LEN];
 	char skill_db[DB_NAME_LEN];
 	char memo_db[DB_NAME_LEN];
 	char memo_db[DB_NAME_LEN];
 	char guild_db[DB_NAME_LEN];
 	char guild_db[DB_NAME_LEN];
@@ -65,6 +64,10 @@ struct Schema_Config {
 	char ragsrvinfo_db[DB_NAME_LEN];
 	char ragsrvinfo_db[DB_NAME_LEN];
 	char elemental_db[DB_NAME_LEN];
 	char elemental_db[DB_NAME_LEN];
 	char bonus_script_db[DB_NAME_LEN];
 	char bonus_script_db[DB_NAME_LEN];
+	char acc_reg_num_table[DB_NAME_LEN];
+	char acc_reg_str_table[DB_NAME_LEN];
+	char char_reg_str_table[DB_NAME_LEN];
+	char char_reg_num_table[DB_NAME_LEN];
 };
 };
 extern struct Schema_Config schema_config;
 extern struct Schema_Config schema_config;
 
 
@@ -249,7 +252,6 @@ int char_child(int parent_id, int child_id);
 int char_family(int pl1,int pl2,int pl3);
 int char_family(int pl1,int pl2,int pl3);
 
 
 int char_request_accreg2(uint32 account_id, uint32 char_id);
 int char_request_accreg2(uint32 account_id, uint32 char_id);
-int char_save_accreg2(unsigned char* buf, int len);
 
 
 //extern bool char_gm_read;
 //extern bool char_gm_read;
 int char_loadName(uint32 char_id, char* name);
 int char_loadName(uint32 char_id, char* name);

+ 62 - 15
src/char/char_logif.c

@@ -164,17 +164,67 @@ int chlogif_broadcast_user_count(int tid, unsigned int tick, int id, intptr_t da
 	return 0;
 	return 0;
 }
 }
 
 
-//Send packet forward to login-server for account saving
-int chlogif_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;
+void chlogif_request_global_accreg(uint32 account_id, uint32 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
+}
+
+void chlogif_prepsend_global_accreg(void) {
+	WFIFOSET(login_fd, WFIFOW(login_fd,2));
+}
+
+void chlogif_send_global_accreg(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);
+
+		chlogif_prepsend_global_accreg();
+		chlogif_request_global_accreg(account_id, char_id); // prepare next
 	}
 	}
-	return 0;
 }
 }
 
 
 int chlogif_request_accreg2(uint32 account_id, uint32 char_id){
 int chlogif_request_accreg2(uint32 account_id, uint32 char_id){
@@ -426,10 +476,7 @@ int chlogif_parse_ackacc2req(int fd, struct char_session_data* sd){
 		return 0;
 		return 0;
 
 
 	{	//Receive account_reg2 registry, forward to map servers.
 	{	//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]
-		chmapif_sendall(buf, WBUFW(buf,2));
+		chmapif_sendall(RFIFOP(fd,0), RFIFOW(fd,2));
 		RFIFOSKIP(fd, RFIFOW(fd,2));
 		RFIFOSKIP(fd, RFIFOW(fd,2));
 	}
 	}
 	return 1;
 	return 1;
@@ -651,7 +698,7 @@ int chlogif_parse(int fd) {
 			// changesex reply
 			// changesex reply
 			case 0x2723: next = chlogif_parse_ackchangesex(fd, sd); break;
 			case 0x2723: next = chlogif_parse_ackchangesex(fd, sd); break;
 			// reply to an account_reg2 registry request
 			// reply to an account_reg2 registry request
-			case 0x2729: next = chlogif_parse_ackacc2req(fd, sd); break;
+			case 0x3804: next = chlogif_parse_ackacc2req(fd, sd); break;
 			// State change of account/ban notification (from login-server)
 			// State change of account/ban notification (from login-server)
 			case 0x2731: next = chlogif_parse_accbannotification(fd, sd); break;
 			case 0x2731: next = chlogif_parse_accbannotification(fd, sd); break;
 			// Login server request to kick a character out. [Skotlex]
 			// Login server request to kick a character out. [Skotlex]

+ 3 - 1
src/char/char_logif.h

@@ -20,7 +20,9 @@ void chlogif_pincode_start(int fd, struct char_session_data* sd);
 int chlogif_send_acc_tologin(int tid, unsigned int tick, int id, intptr_t data);
 int chlogif_send_acc_tologin(int tid, unsigned int tick, int id, intptr_t data);
 int chlogif_broadcast_user_count(int tid, unsigned int tick, int id, intptr_t data);
 int chlogif_broadcast_user_count(int tid, unsigned int tick, int id, intptr_t data);
 int chlogif_send_usercount(int users);
 int chlogif_send_usercount(int users);
-int chlogif_save_accreg2(unsigned char* buf, int len);
+void chlogif_request_global_accreg(uint32 account_id, uint32 char_id);
+void chlogif_prepsend_global_accreg(void);
+void chlogif_send_global_accreg(const char *key, unsigned int index, intptr_t val, bool is_string);
 int chlogif_request_accreg2(uint32 account_id, uint32 char_id);
 int chlogif_request_accreg2(uint32 account_id, uint32 char_id);
 int chlogif_send_reqaccdata(int fd, struct char_session_data *sd);
 int chlogif_send_reqaccdata(int fd, struct char_session_data *sd);
 int chlogif_send_setacconline(int aid);
 int chlogif_send_setacconline(int aid);

+ 233 - 134
src/char/inter.c

@@ -40,7 +40,6 @@ char char_server_pw[32] = ""; // Allow user to send empty password (bugreport:77
 char char_server_db[32] = "ragnarok";
 char char_server_db[32] = "ragnarok";
 char default_codepage[32] = ""; //Feature by irmin.
 char default_codepage[32] = ""; //Feature by irmin.
 
 
-static struct accreg *accreg_pt;
 unsigned int party_share_level = 10;
 unsigned int party_share_level = 10;
 
 
 // recv. packet list
 // recv. packet list
@@ -512,120 +511,224 @@ void mapif_accinfo_ack(bool success, int map_fd, int u_fd, int u_aid, int accoun
 	Sql_FreeResult(sql_handle);
 	Sql_FreeResult(sql_handle);
 }
 }
 
 
-//--------------------------------------------------------
-// Save registry to sql
-int inter_accreg_tosql(uint32 account_id, uint32 char_id, struct accreg* reg, int type)
+/**
+ * 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(uint32 account_id, uint32 char_id, const char *key, unsigned int index, intptr_t val, bool is_string)
 {
 {
-	StringBuf buf;
-	int i;
+	if( key[0] == '#' && key[1] == '#' ) { // global account reg
+		if( session_isValid(login_fd) )
+			chlogif_send_global_accreg(key,index,val,is_string);
+		else {
+			ShowError("Login server unavailable, can't perform update on '%s' variable for AID:%d CID:%d\n",key,account_id,char_id);
+		}
+	} 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')", schema_config.acc_reg_str_table, 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", schema_config.acc_reg_str_table, 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')", schema_config.acc_reg_num_table, 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", schema_config.acc_reg_num_table, 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')", schema_config.char_reg_str_table, 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", schema_config.char_reg_str_table, 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')", schema_config.char_reg_num_table, 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", schema_config.char_reg_num_table, char_id, key, index) )
+					Sql_ShowDebug(sql_handle);
+			}
+		}
+	}
+}
 
 
-	if( account_id <= 0 )
-		return 0;
-	reg->account_id = account_id;
-	reg->char_id = char_id;
+// Load account_reg from sql (type=2)
+int inter_accreg_fromsql(uint32 account_id, uint32 char_id, int fd, int type)
+{
+	char* data;
+	size_t len;
+	unsigned int plen = 0;
 
 
-	//`global_reg_value` (`type`, `account_id`, `char_id`, `str`, `value`)
 	switch( type ) {
 	switch( type ) {
-		case 3: //Char Reg
-			if( SQL_ERROR == Sql_Query(sql_handle, "DELETE FROM `%s` WHERE `type`=3 AND `char_id`='%d'", schema_config.reg_db, char_id) )
+		case 3: //char reg
+			if( SQL_ERROR == Sql_Query(sql_handle, "SELECT `key`, `index`, `value` FROM `%s` WHERE `char_id`='%d'", schema_config.char_reg_str_table, char_id) )
 				Sql_ShowDebug(sql_handle);
 				Sql_ShowDebug(sql_handle);
-			account_id = 0;
 			break;
 			break;
-		case 2: //Account Reg
-			if( SQL_ERROR == Sql_Query(sql_handle, "DELETE FROM `%s` WHERE `type`=2 AND `account_id`='%d'", schema_config.reg_db, account_id) )
+		case 2: //account reg
+			if( SQL_ERROR == Sql_Query(sql_handle, "SELECT `key`, `index`, `value` FROM `%s` WHERE `account_id`='%d'", schema_config.acc_reg_str_table, account_id) )
 				Sql_ShowDebug(sql_handle);
 				Sql_ShowDebug(sql_handle);
-			char_id = 0;
 			break;
 			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");
+		case 1: //account2 reg
+			ShowError("inter_accreg_fromsql: Char server shouldn't handle type 1 registry values (##). That is the login server's job!\n");
 			return 0;
 			return 0;
 		default:
 		default:
-			ShowError("inter_accreg_tosql: Invalid type %d\n", type);
+			ShowError("inter_accreg_fromsql: Invalid type %d\n", type);
 			return 0;
 			return 0;
 	}
 	}
 
 
-	if( reg->reg_num <= 0 )
-		return 0;
-
-	StringBuf_Init(&buf);
-	StringBuf_Printf(&buf, "INSERT INTO `%s` (`type`,`account_id`,`char_id`,`str`,`value`) VALUES ", schema_config.reg_db);
-
-	for( i = 0; i < reg->reg_num; ++i ) {
-		struct global_reg* r = &reg->reg[i];
-		if( r->str[0] != '\0' && r->value[0] != '\0' ) {
-			char str[32];
-			char val[256];
-
-			if( i > 0 )
-				StringBuf_AppendStr(&buf, ",");
-
-			Sql_EscapeString(sql_handle, str, r->str);
-			Sql_EscapeString(sql_handle, val, r->value);
-
-			StringBuf_Printf(&buf, "('%d','%d','%d','%s','%s')", type, account_id, char_id, str, val);
+	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;
 		}
 		}
 	}
 	}
 
 
-	if( SQL_ERROR == Sql_QueryStr(sql_handle, StringBuf_Value(&buf)) ) {
-		Sql_ShowDebug(sql_handle);
-	}
-
-	StringBuf_Destroy(&buf);
-
-	return 1;
-}
-
-// Load account_reg from sql (type=2)
-int inter_accreg_fromsql(uint32 account_id,uint32 char_id, struct accreg *reg, int type)
-{
-	char* data;
-	size_t len;
-	int i;
-
-	if( reg == NULL)
-		return 0;
+	WFIFOW(fd, 2) = plen;
+	WFIFOSET(fd, plen);
 
 
-	memset(reg, 0, sizeof(struct accreg));
-	reg->account_id = account_id;
-	reg->char_id = char_id;
+	Sql_FreeResult(sql_handle);
 
 
-	//`global_reg_value` (`type`, `account_id`, `char_id`, `str`, `value`)
 	switch( type ) {
 	switch( type ) {
 		case 3: //char reg
 		case 3: //char reg
-			if( SQL_ERROR == Sql_Query(sql_handle, "SELECT `str`, `value` FROM `%s` WHERE `type`=3 AND `char_id`='%d'", schema_config.reg_db, char_id) )
+			if (SQL_ERROR == Sql_Query(sql_handle, "SELECT `key`, `index`, `value` FROM `%s` WHERE `char_id`='%d'", schema_config.char_reg_num_table, char_id))
 				Sql_ShowDebug(sql_handle);
 				Sql_ShowDebug(sql_handle);
 			break;
 			break;
 		case 2: //account reg
 		case 2: //account reg
-			if( SQL_ERROR == Sql_Query(sql_handle, "SELECT `str`, `value` FROM `%s` WHERE `type`=2 AND `account_id`='%d'", schema_config.reg_db, account_id) )
+			if (SQL_ERROR == Sql_Query(sql_handle, "SELECT `key`, `index`, `value` FROM `%s` WHERE `account_id`='%d'", schema_config.acc_reg_num_table, account_id))
 				Sql_ShowDebug(sql_handle);
 				Sql_ShowDebug(sql_handle);
 			break;
 			break;
+#if 0 // This is already checked above.
 		case 1: //account2 reg
 		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");
 			ShowError("inter_accreg_fromsql: Char server shouldn't handle type 1 registry values (##). That is the login server's work!\n");
 			return 0;
 			return 0;
-		default:
-			ShowError("inter_accreg_fromsql: Invalid type %d\n", type);
-			return 0;
+#endif // 0
 	}
 	}
-	for( i = 0; i < MAX_REG_NUM && SQL_SUCCESS == Sql_NextRow(sql_handle); ++i ) {
-		struct global_reg* r = &reg->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;
-	Sql_FreeResult(sql_handle);
-	return 1;
-}
 
 
-// Initialize
-int inter_accreg_sql_init(void)
-{
-	CREATE(accreg_pt, struct accreg, 1);
-	return 0;
+	WFIFOB(fd, 12) = type;
+	WFIFOW(fd, 2) = plen;
+	WFIFOSET(fd, plen);
 
 
+	Sql_FreeResult(sql_handle);
+	return 1;
 }
 }
 
 
 /*==========================================
 /*==========================================
@@ -727,7 +830,6 @@ int inter_init_sql(const char *file)
 	inter_homunculus_sql_init();
 	inter_homunculus_sql_init();
 	inter_mercenary_sql_init();
 	inter_mercenary_sql_init();
 	inter_elemental_sql_init();
 	inter_elemental_sql_init();
-	inter_accreg_sql_init();
 	inter_mail_sql_init();
 	inter_mail_sql_init();
 	inter_auction_sql_init();
 	inter_auction_sql_init();
 
 
@@ -750,7 +852,6 @@ void inter_final(void)
 	inter_mail_sql_final();
 	inter_mail_sql_final();
 	inter_auction_sql_final();
 	inter_auction_sql_final();
 
 
-	if (accreg_pt) aFree(accreg_pt);
 	if(geoip_cache) aFree(geoip_cache);
 	if(geoip_cache) aFree(geoip_cache);
 	
 	
 	return;
 	return;
@@ -820,29 +921,9 @@ static void mapif_account_reg(int fd, unsigned char *src)
 }
 }
 
 
 // Send the requested account_reg
 // Send the requested account_reg
-int mapif_account_reg_reply(int fd,uint32 account_id,uint32 char_id, int type)
+int mapif_account_reg_reply(int fd, uint32 account_id, uint32 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));
+	inter_accreg_fromsql(account_id,char_id,fd,type);
 	return 0;
 	return 0;
 }
 }
 
 
@@ -1020,34 +1101,52 @@ int mapif_parse_WisToGM(int fd)
 // Save account_reg into sql (type=2)
 // Save account_reg into sql (type=2)
 int mapif_parse_Registry(int fd)
 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 chlogif_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];
+		bool isLoginActive = session_isActive(login_fd);
+
+		if( isLoginActive )
+			chlogif_request_global_accreg(account_id,char_id);
+
+		for(i = 0; i < count; i++) {
+			unsigned int index;
+			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.
+		if (isLoginActive)
+			chlogif_prepsend_global_accreg();
+	}
 	return 0;
 	return 0;
 }
 }
 
 

+ 2 - 2
src/char/inter.h

@@ -4,7 +4,6 @@
 #ifndef _INTER_SQL_H_
 #ifndef _INTER_SQL_H_
 #define _INTER_SQL_H_
 #define _INTER_SQL_H_
 
 
-struct accreg;
 #include "../common/sql.h"
 #include "../common/sql.h"
 
 
 int inter_init_sql(const char *file);
 int inter_init_sql(const char *file);
@@ -26,6 +25,7 @@ extern unsigned int party_share_level;
 extern Sql* sql_handle;
 extern Sql* sql_handle;
 extern Sql* lsql_handle;
 extern Sql* lsql_handle;
 
 
-int inter_accreg_tosql(uint32 account_id, uint32 char_id, struct accreg *reg, int type);
+void inter_savereg(uint32 account_id, uint32 char_id, const char *key, unsigned int index, intptr_t val, bool is_string);
+int inter_accreg_fromsql(uint32 account_id, uint32 char_id, int fd, int type);
 
 
 #endif /* _INTER_SQL_H_ */
 #endif /* _INTER_SQL_H_ */

+ 192 - 41
src/common/db.c

@@ -3,7 +3,7 @@
  *  For more information, see LICENCE in the main folder
  *  For more information, see LICENCE in the main folder
  *
  *
  *  This file is separated in five sections:
  *  This file is separated in five sections:
- *  (1) Private typedefs, enums, structures, defines and gblobal variables
+ *  (1) Private typedefs, enums, structures, defines and global variables
  *  (2) Private functions
  *  (2) Private functions
  *  (3) Protected functions used internally
  *  (3) Protected functions used internally
  *  (4) Protected functions used in the interface of the database
  *  (4) Protected functions used in the interface of the database
@@ -47,6 +47,7 @@
  *  - create a db that organizes itself by splaying
  *  - create a db that organizes itself by splaying
  *
  *
  *  HISTORY:
  *  HISTORY:
+ *    2013/08/25 - Added int64/uint64 support for keys [Ind/Hercules]
  *    2012/03/09 - Added enum for data types (int, uint, void*)
  *    2012/03/09 - Added enum for data types (int, uint, void*)
  *    2008/02/19 - Fixed db_obj_get not handling deleted entries correctly.
  *    2008/02/19 - Fixed db_obj_get not handling deleted entries correctly.
  *    2007/11/09 - Added an iterator to the database.
  *    2007/11/09 - Added an iterator to the database.
@@ -82,15 +83,15 @@
  *  DBNColor        - Enumeration of colors of the nodes.                    *
  *  DBNColor        - Enumeration of colors of the nodes.                    *
  *  DBNode          - Structure of a node in RED-BLACK trees.                *
  *  DBNode          - Structure of a node in RED-BLACK trees.                *
  *  struct db_free  - Structure that holds a deleted node to be freed.       *
  *  struct db_free  - Structure that holds a deleted node to be freed.       *
- *  DBMap_impl      - Struture of the database.                              *
+ *  DBMap_impl      - Structure of the database.                             *
  *  stats           - Statistics about the database system.                  *
  *  stats           - Statistics about the database system.                  *
 \*****************************************************************************/
 \*****************************************************************************/
 
 
 /**
 /**
- * If defined statistics about database nodes, database creating/destruction 
- * and function usage are keept and displayed when finalizing the database
+ * If defined statistics about database nodes, database creating/destruction
+ * and function usage are kept and displayed when finalizing the database
  * system.
  * system.
- * WARNING: This adds overhead to every database operation (not shure how much).
+ * WARNING: This adds overhead to every database operation (not sure how much).
  * @private
  * @private
  * @see #DBStats
  * @see #DBStats
  * @see #stats
  * @see #stats
@@ -234,10 +235,14 @@ static struct db_stats {
 	uint32 db_uint_alloc;
 	uint32 db_uint_alloc;
 	uint32 db_string_alloc;
 	uint32 db_string_alloc;
 	uint32 db_istring_alloc;
 	uint32 db_istring_alloc;
+	uint32 db_int64_alloc;
+	uint32 db_uint64_alloc;
 	uint32 db_int_destroy;
 	uint32 db_int_destroy;
 	uint32 db_uint_destroy;
 	uint32 db_uint_destroy;
 	uint32 db_string_destroy;
 	uint32 db_string_destroy;
 	uint32 db_istring_destroy;
 	uint32 db_istring_destroy;
+	uint32 db_int64_destroy;
+	uint32 db_uint64_destroy;
 	// Function usage counters
 	// Function usage counters
 	uint32 db_rotate_left;
 	uint32 db_rotate_left;
 	uint32 db_rotate_right;
 	uint32 db_rotate_right;
@@ -254,10 +259,14 @@ static struct db_stats {
 	uint32 db_uint_cmp;
 	uint32 db_uint_cmp;
 	uint32 db_string_cmp;
 	uint32 db_string_cmp;
 	uint32 db_istring_cmp;
 	uint32 db_istring_cmp;
+	uint32 db_int64_cmp;
+	uint32 db_uint64_cmp;
 	uint32 db_int_hash;
 	uint32 db_int_hash;
 	uint32 db_uint_hash;
 	uint32 db_uint_hash;
 	uint32 db_string_hash;
 	uint32 db_string_hash;
 	uint32 db_istring_hash;
 	uint32 db_istring_hash;
+	uint32 db_int64_hash;
+	uint32 db_uint64_hash;
 	uint32 db_release_nothing;
 	uint32 db_release_nothing;
 	uint32 db_release_key;
 	uint32 db_release_key;
 	uint32 db_release_data;
 	uint32 db_release_data;
@@ -296,6 +305,8 @@ static struct db_stats {
 	uint32 db_i2key;
 	uint32 db_i2key;
 	uint32 db_ui2key;
 	uint32 db_ui2key;
 	uint32 db_str2key;
 	uint32 db_str2key;
+	uint32 db_i642key;
+	uint32 db_ui642key;
 	uint32 db_i2data;
 	uint32 db_i2data;
 	uint32 db_ui2data;
 	uint32 db_ui2data;
 	uint32 db_ptr2data;
 	uint32 db_ptr2data;
@@ -489,7 +500,7 @@ static void db_rebalance_erase(DBNode node, DBNode *root)
 	}
 	}
 
 
 	// Remove the node from the tree
 	// Remove the node from the tree
-	if (y != node) { // both childs existed
+	if (y != node) { // both child existed
 		// put the left of 'node' in the left of 'y'
 		// put the left of 'node' in the left of 'y'
 		node->left->parent = y;
 		node->left->parent = y;
 		y->left = node->left;
 		y->left = node->left;
@@ -500,10 +511,10 @@ static void db_rebalance_erase(DBNode node, DBNode *root)
 			x_parent = y->parent;
 			x_parent = y->parent;
 			if (x) x->parent = y->parent;
 			if (x) x->parent = y->parent;
 			y->parent->left = x;
 			y->parent->left = x;
-			// put the right of 'node' in 'y' 
+			// put the right of 'node' in 'y'
 			y->right = node->right;
 			y->right = node->right;
 			node->right->parent = y;
 			node->right->parent = y;
-		// 'y' is a direct child of 'node'
+			// 'y' is a direct child of 'node'
 		} else {
 		} else {
 			x_parent = y;
 			x_parent = y;
 		}
 		}
@@ -556,7 +567,7 @@ static void db_rebalance_erase(DBNode node, DBNode *root)
 					x = x_parent;
 					x = x_parent;
 					x_parent = x_parent->parent;
 					x_parent = x_parent->parent;
 				} else {
 				} else {
-					if (w->right == NULL ||	w->right->color == BLACK) {
+					if (w->right == NULL || w->right->color == BLACK) {
 						if (w->left) w->left->color = BLACK;
 						if (w->left) w->left->color = BLACK;
 						w->color = RED;
 						w->color = RED;
 						db_rotate_right(w, root);
 						db_rotate_right(w, root);
@@ -824,10 +835,14 @@ static void db_free_unlock(DBMap_impl* db)
  *  db_uint_cmp        - Default comparator for DB_UINT databases.           *
  *  db_uint_cmp        - Default comparator for DB_UINT databases.           *
  *  db_string_cmp      - Default comparator for DB_STRING databases.         *
  *  db_string_cmp      - Default comparator for DB_STRING databases.         *
  *  db_istring_cmp     - Default comparator for DB_ISTRING 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_int_hash        - Default hasher for DB_INT databases.                *
  *  db_uint_hash       - Default hasher for DB_UINT databases.               *
  *  db_uint_hash       - Default hasher for DB_UINT databases.               *
  *  db_string_hash     - Default hasher for DB_STRING databases.             *
  *  db_string_hash     - Default hasher for DB_STRING databases.             *
  *  db_istring_hash    - Default hasher for DB_ISTRING 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_nothing - Releaser that releases nothing.                     *
  *  db_release_key     - Releaser that only releases the key.                *
  *  db_release_key     - Releaser that only releases the key.                *
  *  db_release_data    - Releaser that only releases the data.               *
  *  db_release_data    - Releaser that only releases the data.               *
@@ -914,6 +929,51 @@ static int db_istring_cmp(DBKey key1, DBKey key2, unsigned short maxlen)
 	return strncasecmp((const char *)key1.str, (const char *)key2.str, maxlen);
 	return strncasecmp((const char *)key1.str, (const char *)key2.str, 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.
  * Default hasher for DB_INT databases.
  * Returns the value of the key as an unsigned int.
  * Returns the value of the key as an unsigned int.
@@ -925,11 +985,11 @@ static int db_istring_cmp(DBKey key1, DBKey key2, unsigned short maxlen)
  * @see #DBHasher
  * @see #DBHasher
  * @see #db_default_hash(DBType)
  * @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
 	(void)maxlen;//not used
 	DB_COUNTSTAT(db_int_hash);
 	DB_COUNTSTAT(db_int_hash);
-	return (unsigned int)key.i;
+	return (uint64)key.i;
 }
 }
 
 
 /**
 /**
@@ -943,11 +1003,11 @@ static unsigned int db_int_hash(DBKey key, unsigned short maxlen)
  * @see #DBHasher
  * @see #DBHasher
  * @see #db_default_hash(DBType)
  * @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
 	(void)maxlen;//not used
 	DB_COUNTSTAT(db_uint_hash);
 	DB_COUNTSTAT(db_uint_hash);
-	return key.ui;
+	return (uint64)key.ui;
 }
 }
 
 
 /**
 /**
@@ -959,7 +1019,7 @@ static unsigned int db_uint_hash(DBKey key, unsigned short maxlen)
  * @see #DBHasher
  * @see #DBHasher
  * @see #db_default_hash(DBType)
  * @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;
 	const char *k = key.str;
 	unsigned int hash = 0;
 	unsigned int hash = 0;
@@ -974,7 +1034,7 @@ static unsigned int db_string_hash(DBKey key, unsigned short maxlen)
 			break;
 			break;
 	}
 	}
 
 
-	return hash;
+	return (uint64)hash;
 }
 }
 
 
 /**
 /**
@@ -985,7 +1045,7 @@ static unsigned int db_string_hash(DBKey key, unsigned short maxlen)
  * @see DBType#DB_ISTRING
  * @see DBType#DB_ISTRING
  * @see #db_default_hash(DBType)
  * @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;
 	const char *k = key.str;
 	unsigned int hash = 0;
 	unsigned int hash = 0;
@@ -1000,7 +1060,43 @@ static unsigned int db_istring_hash(DBKey key, unsigned short maxlen)
 			break;
 			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;
 }
 }
 
 
 /**
 /**
@@ -1117,7 +1213,7 @@ static void db_release_both(DBKey key, DBData data, DBRelease which)
 DBData* dbit_obj_first(DBIterator* self, DBKey* out_key)
 DBData* dbit_obj_first(DBIterator* self, DBKey* out_key)
 {
 {
 	DBIterator_impl* it = (DBIterator_impl*)self;
 	DBIterator_impl* it = (DBIterator_impl*)self;
-	
+
 	DB_COUNTSTAT(dbit_first);
 	DB_COUNTSTAT(dbit_first);
 	// position before the first entry
 	// position before the first entry
 	it->ht_index = -1;
 	it->ht_index = -1;
@@ -1139,7 +1235,7 @@ DBData* dbit_obj_first(DBIterator* self, DBKey* out_key)
 DBData* dbit_obj_last(DBIterator* self, DBKey* out_key)
 DBData* dbit_obj_last(DBIterator* self, DBKey* out_key)
 {
 {
 	DBIterator_impl* it = (DBIterator_impl*)self;
 	DBIterator_impl* it = (DBIterator_impl*)self;
-	
+
 	DB_COUNTSTAT(dbit_last);
 	DB_COUNTSTAT(dbit_last);
 	// position after the last entry
 	// position after the last entry
 	it->ht_index = HASH_SIZE;
 	it->ht_index = HASH_SIZE;
@@ -1261,7 +1357,6 @@ DBData* dbit_obj_prev(DBIterator* self, DBKey* out_key)
 			node = &fake;
 			node = &fake;
 		}
 		}
 
 
-		
 		while( node )
 		while( node )
 		{// next node
 		{// next node
 			if( node->left )
 			if( node->left )
@@ -1303,7 +1398,7 @@ DBData* dbit_obj_prev(DBIterator* self, DBKey* out_key)
 
 
 /**
 /**
  * Returns true if the fetched entry exists.
  * Returns true if the fetched entry exists.
- * The databases entries might have NULL data, so use this to to test if 
+ * The databases entries might have NULL data, so use this to to test if
  * the iterator is done.
  * the iterator is done.
  * @param self Iterator
  * @param self Iterator
  * @return true if the entry exists
  * @return true if the entry exists
@@ -1320,7 +1415,7 @@ bool dbit_obj_exists(DBIterator* self)
 
 
 /**
 /**
  * Removes the current entry from the database.
  * Removes the current entry from the database.
- * NOTE: {@link DBIterator#exists} will return false until another entry 
+ * NOTE: {@link DBIterator#exists} will return false until another entry
  *       is fetched
  *       is fetched
  * Puts data of the removed entry in out_data, if out_data is not NULL.
  * Puts data of the removed entry in out_data, if out_data is not NULL.
  * @param self Iterator
  * @param self Iterator
@@ -1371,7 +1466,7 @@ void dbit_obj_destroy(DBIterator* self)
 /**
 /**
  * Returns a new iterator for this database.
  * Returns a new iterator for this database.
  * The iterator keeps the database locked until it is destroyed.
  * The iterator keeps the database locked until it is destroyed.
- * The database will keep functioning normally but will only free internal 
+ * The database will keep functioning normally but will only free internal
  * memory when unlocked, so destroy the iterator as soon as possible.
  * memory when unlocked, so destroy the iterator as soon as possible.
  * @param self Database
  * @param self Database
  * @return New iterator
  * @return New iterator
@@ -1507,7 +1602,7 @@ static DBData* db_obj_get(DBMap* self, DBKey key)
  * It puts a maximum of <code>max</code> entries into <code>buf</code>.
  * It puts a maximum of <code>max</code> entries into <code>buf</code>.
  * If <code>buf</code> is NULL, it only counts the matches.
  * If <code>buf</code> is NULL, it only counts the matches.
  * Returns the number of entries that matched.
  * Returns the number of entries that matched.
- * NOTE: if the value returned is greater than <code>max</code>, only the 
+ * NOTE: if the value returned is greater than <code>max</code>, only the
  * first <code>max</code> entries found are put into the buffer.
  * first <code>max</code> entries found are put into the buffer.
  * @param self Interface of the database
  * @param self Interface of the database
  * @param buf Buffer to put the data of the matched entries
  * @param buf Buffer to put the data of the matched entries
@@ -1546,17 +1641,17 @@ static unsigned int db_obj_vgetall(DBMap* self, DBData **buf, unsigned int max,
 				}
 				}
 				va_end(argscopy);
 				va_end(argscopy);
 			}
 			}
-			
+
 			if (node->left) {
 			if (node->left) {
 				node = node->left;
 				node = node->left;
 				continue;
 				continue;
 			}
 			}
-			
+
 			if (node->right) {
 			if (node->right) {
 				node = node->right;
 				node = node->right;
 				continue;
 				continue;
 			}
 			}
-			
+
 			while (node) {
 			while (node) {
 				parent = node->parent;
 				parent = node->parent;
 				if (parent && parent->right && parent->left == node) {
 				if (parent && parent->right && parent->left == node) {
@@ -1578,7 +1673,7 @@ static unsigned int db_obj_vgetall(DBMap* self, DBData **buf, unsigned int max,
  * It puts a maximum of <code>max</code> entries into <code>buf</code>.
  * It puts a maximum of <code>max</code> entries into <code>buf</code>.
  * If <code>buf</code> is NULL, it only counts the matches.
  * If <code>buf</code> is NULL, it only counts the matches.
  * Returns the number of entries that matched.
  * Returns the number of entries that matched.
- * NOTE: if the value returned is greater than <code>max</code>, only the 
+ * NOTE: if the value returned is greater than <code>max</code>, only the
  * first <code>max</code> entries found are put into the buffer.
  * first <code>max</code> entries found are put into the buffer.
  * @param self Interface of the database
  * @param self Interface of the database
  * @param buf Buffer to put the data of the matched entries
  * @param buf Buffer to put the data of the matched entries
@@ -1606,7 +1701,7 @@ static unsigned int db_obj_getall(DBMap* self, DBData **buf, unsigned int max, D
 
 
 /**
 /**
  * Get the data of the entry identified by the key.
  * Get the data of the entry identified by the key.
- * If the entry does not exist, an entry is added with the data returned by 
+ * If the entry does not exist, an entry is added with the data returned by
  * <code>create</code>.
  * <code>create</code>.
  * @param self Interface of the database
  * @param self Interface of the database
  * @param key Key that identifies the entry
  * @param key Key that identifies the entry
@@ -1705,7 +1800,7 @@ static DBData* db_obj_vensure(DBMap* self, DBKey key, DBCreateData create, va_li
 /**
 /**
  * Just calls {@link DBMap#vensure}.
  * Just calls {@link DBMap#vensure}.
  * Get the data of the entry identified by the key.
  * Get the data of the entry identified by the key.
- * If the entry does not exist, an entry is added with the data returned by 
+ * If the entry does not exist, an entry is added with the data returned by
  * <code>create</code>.
  * <code>create</code>.
  * @param self Interface of the database
  * @param self Interface of the database
  * @param key Key that identifies the entry
  * @param key Key that identifies the entry
@@ -1863,7 +1958,7 @@ static int db_obj_remove(DBMap* self, DBKey key, DBData *out_data)
 				db->alloc_file, db->alloc_line);
 				db->alloc_file, db->alloc_line);
 		return 0; // nullpo candidate
 		return 0; // nullpo candidate
 	}
 	}
-	if (!(db->options&DB_OPT_ALLOW_NULL_KEY) && db_is_key_null(db->type, key))	{
+	if (!(db->options&DB_OPT_ALLOW_NULL_KEY) && db_is_key_null(db->type, key)) {
 		ShowError("db_remove: Attempted to use non-allowed NULL key for db allocated at %s:%d\n",db->alloc_file, db->alloc_line);
 		ShowError("db_remove: Attempted to use non-allowed NULL key for db allocated at %s:%d\n",db->alloc_file, db->alloc_line);
 		return 0; // nullpo candidate
 		return 0; // nullpo candidate
 	}
 	}
@@ -1956,7 +2051,7 @@ static int db_obj_vforeach(DBMap* self, DBApply func, va_list args)
  * Apply <code>func</code> to every entry in the database.
  * Apply <code>func</code> to every entry in the database.
  * Returns the sum of values returned by func.
  * Returns the sum of values returned by func.
  * @param self Interface of the database
  * @param self Interface of the database
- * @param func Function to be applyed
+ * @param func Function to be applied
  * @param ... Extra arguments for func
  * @param ... Extra arguments for func
  * @return Sum of the values returned by func
  * @return Sum of the values returned by func
  * @protected
  * @protected
@@ -2053,7 +2148,7 @@ static int db_obj_vclear(DBMap* self, DBApply func, va_list args)
  * Before deleting an entry, func is applied to it.
  * Before deleting an entry, func is applied to it.
  * Releases the key and the data.
  * Releases the key and the data.
  * Returns the sum of values returned by func, if it exists.
  * Returns the sum of values returned by func, if it exists.
- * NOTE: This locks the database globally. Any attempt to insert or remove 
+ * NOTE: This locks the database globally. Any attempt to insert or remove
  * a database entry will give an error and be aborted (except for clearing).
  * a database entry will give an error and be aborted (except for clearing).
  * @param self Interface of the database
  * @param self Interface of the database
  * @param func Function to be applied to every entry before deleting
  * @param func Function to be applied to every entry before deleting
@@ -2081,7 +2176,7 @@ static int db_obj_clear(DBMap* self, DBApply func, ...)
  * Finalize the database, feeing all the memory it uses.
  * Finalize the database, feeing all the memory it uses.
  * Before deleting an entry, func is applied to it.
  * Before deleting an entry, func is applied to it.
  * Returns the sum of values returned by func, if it exists.
  * Returns the sum of values returned by func, if it exists.
- * NOTE: This locks the database globally. Any attempt to insert or remove 
+ * NOTE: This locks the database globally. Any attempt to insert or remove
  * a database entry will give an error and be aborted (except for clearing).
  * a database entry will give an error and be aborted (except for clearing).
  * @param self Interface of the database
  * @param self Interface of the database
  * @param func Function to be applied to every entry before deleting
  * @param func Function to be applied to every entry before deleting
@@ -2114,6 +2209,8 @@ static int db_obj_vdestroy(DBMap* self, DBApply func, va_list args)
 		case DB_UINT: DB_COUNTSTAT(db_uint_destroy); break;
 		case DB_UINT: DB_COUNTSTAT(db_uint_destroy); break;
 		case DB_STRING: DB_COUNTSTAT(db_string_destroy); break;
 		case DB_STRING: DB_COUNTSTAT(db_string_destroy); break;
 		case DB_ISTRING: DB_COUNTSTAT(db_istring_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 */
 #endif /* DB_ENABLE_STATS */
 	db_free_lock(db);
 	db_free_lock(db);
@@ -2134,7 +2231,7 @@ static int db_obj_vdestroy(DBMap* self, DBApply func, va_list args)
  * Before deleting an entry, func is applied to it.
  * Before deleting an entry, func is applied to it.
  * Releases the key and the data.
  * Releases the key and the data.
  * Returns the sum of values returned by func, if it exists.
  * Returns the sum of values returned by func, if it exists.
- * NOTE: This locks the database globally. Any attempt to insert or remove 
+ * NOTE: This locks the database globally. Any attempt to insert or remove
  * a database entry will give an error and be aborted.
  * a database entry will give an error and be aborted.
  * @param self Database
  * @param self Database
  * @param func Function to be applied to every entry before deleting
  * @param func Function to be applied to every entry before deleting
@@ -2233,11 +2330,13 @@ static DBOptions db_obj_options(DBMap* self)
  *  db_default_cmp     - Get the default comparator for a type of database.
  *  db_default_cmp     - Get the default comparator for a type of database.
  *  db_default_hash    - Get the default hasher for a type of database.
  *  db_default_hash    - Get the default hasher for a type of database.
  *  db_default_release - Get the default releaser for a type of database with the specified options.
  *  db_default_release - Get the default releaser for a type of database with the specified options.
- *  db_custom_release  - Get a releaser that behaves a certains way.
+ *  db_custom_release  - Get a releaser that behaves a certain way.
  *  db_alloc           - Allocate a new database.
  *  db_alloc           - Allocate a new database.
  *  db_i2key           - Manual cast from 'int' to 'DBKey'.
  *  db_i2key           - Manual cast from 'int' to 'DBKey'.
  *  db_ui2key          - Manual cast from 'unsigned int' to 'DBKey'.
  *  db_ui2key          - Manual cast from 'unsigned int' to 'DBKey'.
  *  db_str2key         - Manual cast from 'unsigned char *' 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_i2data          - Manual cast from 'int' to 'DBData'.
  *  db_ui2data         - Manual cast from 'unsigned int' to 'DBData'.
  *  db_ui2data         - Manual cast from 'unsigned int' to 'DBData'.
  *  db_ptr2data        - Manual cast from 'void*' to 'DBData'.
  *  db_ptr2data        - Manual cast from 'void*' to 'DBData'.
@@ -2264,7 +2363,9 @@ DBOptions db_fix_options(DBType type, DBOptions options)
 	DB_COUNTSTAT(db_fix_options);
 	DB_COUNTSTAT(db_fix_options);
 	switch (type) {
 	switch (type) {
 		case DB_INT:
 		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));
 			return (DBOptions)(options&~(DB_OPT_DUP_KEY|DB_OPT_RELEASE_KEY));
 
 
 		default:
 		default:
@@ -2284,6 +2385,8 @@ DBOptions db_fix_options(DBType type, DBOptions options)
  * @see #db_uint_cmp(DBKey,DBKey,unsigned short)
  * @see #db_uint_cmp(DBKey,DBKey,unsigned short)
  * @see #db_string_cmp(DBKey,DBKey,unsigned short)
  * @see #db_string_cmp(DBKey,DBKey,unsigned short)
  * @see #db_istring_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)
 DBComparator db_default_cmp(DBType type)
 {
 {
@@ -2293,6 +2396,8 @@ DBComparator db_default_cmp(DBType type)
 		case DB_UINT:    return &db_uint_cmp;
 		case DB_UINT:    return &db_uint_cmp;
 		case DB_STRING:  return &db_string_cmp;
 		case DB_STRING:  return &db_string_cmp;
 		case DB_ISTRING: return &db_istring_cmp;
 		case DB_ISTRING: return &db_istring_cmp;
+		case DB_INT64:   return &db_int64_cmp;
+		case DB_UINT64:  return &db_uint64_cmp;
 		default:
 		default:
 			ShowError("db_default_cmp: Unknown database type %u\n", type);
 			ShowError("db_default_cmp: Unknown database type %u\n", type);
 			return NULL;
 			return NULL;
@@ -2308,6 +2413,8 @@ DBComparator db_default_cmp(DBType type)
  * @see #db_uint_hash(DBKey,unsigned short)
  * @see #db_uint_hash(DBKey,unsigned short)
  * @see #db_string_hash(DBKey,unsigned short)
  * @see #db_string_hash(DBKey,unsigned short)
  * @see #db_istring_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)
 DBHasher db_default_hash(DBType type)
 {
 {
@@ -2317,6 +2424,8 @@ DBHasher db_default_hash(DBType type)
 		case DB_UINT:    return &db_uint_hash;
 		case DB_UINT:    return &db_uint_hash;
 		case DB_STRING:  return &db_string_hash;
 		case DB_STRING:  return &db_string_hash;
 		case DB_ISTRING: return &db_istring_hash;
 		case DB_ISTRING: return &db_istring_hash;
+		case DB_INT64:   return &db_int64_hash;
+		case DB_UINT64:  return &db_uint64_hash;
 		default:
 		default:
 			ShowError("db_default_hash: Unknown database type %u\n", type);
 			ShowError("db_default_hash: Unknown database type %u\n", type);
 			return NULL;
 			return NULL;
@@ -2324,7 +2433,7 @@ DBHasher db_default_hash(DBType type)
 }
 }
 
 
 /**
 /**
- * Returns the default releaser for the specified type of database with the 
+ * Returns the default releaser for the specified type of database with the
  * specified options.
  * specified options.
  * NOTE: the options are fixed with {@link #db_fix_options(DBType,DBOptions)}
  * NOTE: the options are fixed with {@link #db_fix_options(DBType,DBOptions)}
  * before choosing the releaser.
  * before choosing the releaser.
@@ -2385,7 +2494,7 @@ DBReleaser db_custom_release(DBRelease which)
  * @param line Line of the file where the database is being allocated
  * @param line Line of the file where the database is being allocated
  * @param type Type of database
  * @param type Type of database
  * @param options Options of the database
  * @param options Options of the database
- * @param maxlen Maximum length of the string to be used as key in string 
+ * @param maxlen Maximum length of the string to be used as key in string
  *          databases. If 0, the maximum number of maxlen is used (64K).
  *          databases. If 0, the maximum number of maxlen is used (64K).
  * @return The interface of the database
  * @return The interface of the database
  * @public
  * @public
@@ -2404,6 +2513,8 @@ DBMap* db_alloc(const char *file, int line, DBType type, DBOptions options, unsi
 		case DB_UINT: DB_COUNTSTAT(db_uint_alloc); break;
 		case DB_UINT: DB_COUNTSTAT(db_uint_alloc); break;
 		case DB_STRING: DB_COUNTSTAT(db_string_alloc); break;
 		case DB_STRING: DB_COUNTSTAT(db_string_alloc); break;
 		case DB_ISTRING: DB_COUNTSTAT(db_istring_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 */
 #endif /* DB_ENABLE_STATS */
 	CREATE(db, struct DBMap_impl, 1);
 	CREATE(db, struct DBMap_impl, 1);
@@ -2501,6 +2612,36 @@ DBKey db_str2key(const char *key)
 	return ret;
 	return ret;
 }
 }
 
 
+/**
+ * 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.
  * Manual cast from 'int' to the struct DBData.
  * @param data Data to be casted
  * @param data Data to be casted
@@ -2620,11 +2761,15 @@ void db_final(void)
 			"DB_INT     : allocated %10u, destroyed %10u\n"
 			"DB_INT     : allocated %10u, destroyed %10u\n"
 			"DB_UINT    : allocated %10u, destroyed %10u\n"
 			"DB_UINT    : allocated %10u, destroyed %10u\n"
 			"DB_STRING  : allocated %10u, destroyed %10u\n"
 			"DB_STRING  : allocated %10u, destroyed %10u\n"
-			"DB_ISTRING : 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_int_alloc,     stats.db_int_destroy,
 			stats.db_uint_alloc,    stats.db_uint_destroy,
 			stats.db_uint_alloc,    stats.db_uint_destroy,
 			stats.db_string_alloc,  stats.db_string_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"
 	ShowInfo(CL_WHITE"Database function counters"CL_RESET":\n"
 			"db_rotate_left     %10u, db_rotate_right    %10u,\n"
 			"db_rotate_left     %10u, db_rotate_right    %10u,\n"
 			"db_rebalance       %10u, db_rebalance_erase %10u,\n"
 			"db_rebalance       %10u, db_rebalance_erase %10u,\n"
@@ -2634,8 +2779,10 @@ void db_final(void)
 			"db_free_lock       %10u, db_free_unlock     %10u,\n"
 			"db_free_lock       %10u, db_free_unlock     %10u,\n"
 			"db_int_cmp         %10u, db_uint_cmp        %10u,\n"
 			"db_int_cmp         %10u, db_uint_cmp        %10u,\n"
 			"db_string_cmp      %10u, db_istring_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_int_hash        %10u, db_uint_hash       %10u,\n"
 			"db_string_hash     %10u, db_istring_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_nothing %10u, db_release_key     %10u,\n"
 			"db_release_data    %10u, db_release_both    %10u,\n"
 			"db_release_data    %10u, db_release_both    %10u,\n"
 			"dbit_first         %10u, dbit_last          %10u,\n"
 			"dbit_first         %10u, dbit_last          %10u,\n"
@@ -2655,6 +2802,7 @@ void db_final(void)
 			"db_default_release %10u, db_custom_release  %10u,\n"
 			"db_default_release %10u, db_custom_release  %10u,\n"
 			"db_alloc           %10u, db_i2key           %10u,\n"
 			"db_alloc           %10u, db_i2key           %10u,\n"
 			"db_ui2key          %10u, db_str2key         %10u,\n"
 			"db_ui2key          %10u, db_str2key         %10u,\n"
+			"db_i642key         %10u, db_ui642key        %10u,\n"
 			"db_i2data          %10u, db_ui2data         %10u,\n"
 			"db_i2data          %10u, db_ui2data         %10u,\n"
 			"db_ptr2data        %10u, db_data2i          %10u,\n"
 			"db_ptr2data        %10u, db_data2i          %10u,\n"
 			"db_data2ui         %10u, db_data2ptr        %10u,\n"
 			"db_data2ui         %10u, db_data2ptr        %10u,\n"
@@ -2667,8 +2815,10 @@ void db_final(void)
 			stats.db_free_lock,       stats.db_free_unlock,
 			stats.db_free_lock,       stats.db_free_unlock,
 			stats.db_int_cmp,         stats.db_uint_cmp,
 			stats.db_int_cmp,         stats.db_uint_cmp,
 			stats.db_string_cmp,      stats.db_istring_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_int_hash,        stats.db_uint_hash,
 			stats.db_string_hash,     stats.db_istring_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_nothing, stats.db_release_key,
 			stats.db_release_data,    stats.db_release_both,
 			stats.db_release_data,    stats.db_release_both,
 			stats.dbit_first,         stats.dbit_last,
 			stats.dbit_first,         stats.dbit_last,
@@ -2688,6 +2838,7 @@ void db_final(void)
 			stats.db_default_release, stats.db_custom_release,
 			stats.db_default_release, stats.db_custom_release,
 			stats.db_alloc,           stats.db_i2key,
 			stats.db_alloc,           stats.db_i2key,
 			stats.db_ui2key,          stats.db_str2key,
 			stats.db_ui2key,          stats.db_str2key,
+			stats.db_i642key,         stats.db_ui642key,
 			stats.db_i2data,          stats.db_ui2data,
 			stats.db_i2data,          stats.db_ui2data,
 			stats.db_ptr2data,        stats.db_data2i,
 			stats.db_ptr2data,        stats.db_data2i,
 			stats.db_data2ui,         stats.db_data2ptr,
 			stats.db_data2ui,         stats.db_data2ptr,

+ 106 - 60
src/common/db.h

@@ -20,6 +20,7 @@
  *  - see what functions need or should be added to the database interface   *
  *  - see what functions need or should be added to the database interface   *
  *                                                                           *
  *                                                                           *
  *  HISTORY:                                                                 *
  *  HISTORY:                                                                 *
+ *    2013/08/25 - Added int64/uint64 support for keys                       *
  *    2012/03/09 - Added enum for data types (int, uint, void*)              *
  *    2012/03/09 - Added enum for data types (int, uint, void*)              *
  *    2007/11/09 - Added an iterator to the database.                        *
  *    2007/11/09 - Added an iterator to the database.                        *
  *    2.1 (Athena build #???#) - Portability fix                             *
  *    2.1 (Athena build #???#) - Portability fix                             *
@@ -77,12 +78,14 @@ typedef enum DBRelease {
 
 
 /**
 /**
  * Supported types of database.
  * Supported types of database.
- * See {@link #db_fix_options(DBType,DBOptions)} for restrictions of the 
+ * See {@link #db_fix_options(DBType,DBOptions)} for restrictions of the
  * types of databases.
  * types of databases.
  * @param DB_INT Uses int's for keys
  * @param DB_INT Uses int's for keys
  * @param DB_UINT Uses unsigned int's for keys
  * @param DB_UINT Uses unsigned int's for keys
  * @param DB_STRING Uses strings for keys.
  * @param DB_STRING Uses strings for keys.
  * @param DB_ISTRING Uses case insensitive 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
  * @public
  * @see #DBOptions
  * @see #DBOptions
  * @see #DBKey
  * @see #DBKey
@@ -96,21 +99,23 @@ typedef enum DBType {
 	DB_INT,
 	DB_INT,
 	DB_UINT,
 	DB_UINT,
 	DB_STRING,
 	DB_STRING,
-	DB_ISTRING
+	DB_ISTRING,
+	DB_INT64,
+	DB_UINT64,
 } DBType;
 } DBType;
 
 
 /**
 /**
- * Bitfield of options that define the behaviour of the database.
- * See {@link #db_fix_options(DBType,DBOptions)} for restrictions of the 
+ * Bitfield of options that define the behavior of the database.
+ * See {@link #db_fix_options(DBType,DBOptions)} for restrictions of the
  * types of databases.
  * types of databases.
  * @param DB_OPT_BASE Base options: does not duplicate keys, releases nothing
  * @param DB_OPT_BASE Base options: does not duplicate keys, releases nothing
  *          and does not allow NULL keys or NULL data.
  *          and does not allow NULL keys or NULL data.
- * @param DB_OPT_DUP_KEY Duplicates the keys internally. If DB_OPT_RELEASE_KEY 
+ * @param DB_OPT_DUP_KEY Duplicates the keys internally. If DB_OPT_RELEASE_KEY
  *          is defined, the real key is freed as soon as the entry is added.
  *          is defined, the real key is freed as soon as the entry is added.
  * @param DB_OPT_RELEASE_KEY Releases the key.
  * @param DB_OPT_RELEASE_KEY Releases the key.
- * @param DB_OPT_RELEASE_DATA Releases the data whenever an entry is removed 
+ * @param DB_OPT_RELEASE_DATA Releases the data whenever an entry is removed
  *          from the database.
  *          from the database.
- *          WARNING: for funtions that return the data (like DBMap::remove),
+ *          WARNING: for functions that return the data (like DBMap::remove),
  *          a dangling pointer will be returned.
  *          a dangling pointer will be returned.
  * @param DB_OPT_RELEASE_BOTH Releases both key and data.
  * @param DB_OPT_RELEASE_BOTH Releases both key and data.
  * @param DB_OPT_ALLOW_NULL_KEY Allow NULL keys in the database.
  * @param DB_OPT_ALLOW_NULL_KEY Allow NULL keys in the database.
@@ -145,6 +150,8 @@ typedef union DBKey {
 	int i;
 	int i;
 	unsigned int ui;
 	unsigned int ui;
 	const char *str;
 	const char *str;
+	int64 i64;
+	uint64 ui64;
 } DBKey;
 } DBKey;
 
 
 /**
 /**
@@ -180,7 +187,7 @@ typedef struct DBData {
 } DBData;
 } DBData;
 
 
 /**
 /**
- * Format of functions that create the data for the key when the entry doesn't 
+ * Format of functions that create the data for the key when the entry doesn't
  * exist in the database yet.
  * exist in the database yet.
  * @param key Key of the database entry
  * @param key Key of the database entry
  * @param args Extra arguments of the function
  * @param args Extra arguments of the function
@@ -192,9 +199,9 @@ typedef struct DBData {
 typedef DBData (*DBCreateData)(DBKey key, va_list args);
 typedef DBData (*DBCreateData)(DBKey key, va_list args);
 
 
 /**
 /**
- * Format of functions to be applied to an unspecified quantity of entries of 
+ * Format of functions to be applied to an unspecified quantity of entries of
  * a database.
  * a database.
- * Any function that applies this function to the database will return the sum 
+ * Any function that applies this function to the database will return the sum
  * of values returned by this function.
  * of values returned by this function.
  * @param key Key of the database entry
  * @param key Key of the database entry
  * @param data Data of the database entry
  * @param data Data of the database entry
@@ -245,7 +252,7 @@ typedef int (*DBComparator)(DBKey key1, DBKey key2, unsigned short maxlen);
  * @public
  * @public
  * @see #db_default_hash(DBType)
  * @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.
  * Format of the releaser used by the database system.
@@ -272,7 +279,7 @@ typedef struct DBMap DBMap;
  * Database iterator.
  * Database iterator.
  * Supports forward iteration, backward iteration and removing entries from the database.
  * Supports forward iteration, backward iteration and removing entries from the database.
  * The iterator is initially positioned before the first entry of the database.
  * The iterator is initially positioned before the first entry of the database.
- * While the iterator exists the database is locked internally, so invoke 
+ * While the iterator exists the database is locked internally, so invoke
  * {@link DBIterator#destroy} as soon as possible.
  * {@link DBIterator#destroy} as soon as possible.
  * @public
  * @public
  * @see #DBMap
  * @see #DBMap
@@ -326,7 +333,7 @@ struct DBIterator
 
 
 	/**
 	/**
 	 * Returns true if the fetched entry exists.
 	 * Returns true if the fetched entry exists.
-	 * The databases entries might have NULL data, so use this to to test if 
+	 * The databases entries might have NULL data, so use this to to test if
 	 * the iterator is done.
 	 * the iterator is done.
 	 * @param self Iterator
 	 * @param self Iterator
 	 * @return true is the entry exists
 	 * @return true is the entry exists
@@ -336,7 +343,7 @@ struct DBIterator
 
 
 	/**
 	/**
 	 * Removes the current entry from the database.
 	 * Removes the current entry from the database.
-	 * NOTE: {@link DBIterator#exists} will return false until another entry 
+	 * NOTE: {@link DBIterator#exists} will return false until another entry
 	 *       is fetched
 	 *       is fetched
 	 * Puts data of the removed entry in out_data, if out_data is not NULL.
 	 * Puts data of the removed entry in out_data, if out_data is not NULL.
 	 * @param self Iterator
 	 * @param self Iterator
@@ -357,7 +364,7 @@ struct DBIterator
 };
 };
 
 
 /**
 /**
- * Public interface of a database. Only contains funtions.
+ * Public interface of a database. Only contains functions.
  * All the functions take the interface as the first argument.
  * All the functions take the interface as the first argument.
  * @public
  * @public
  * @see #db_alloc(const char*,int,DBType,DBOptions,unsigned short)
  * @see #db_alloc(const char*,int,DBType,DBOptions,unsigned short)
@@ -367,7 +374,7 @@ struct DBMap {
 	/**
 	/**
 	 * Returns a new iterator for this database.
 	 * Returns a new iterator for this database.
 	 * The iterator keeps the database locked until it is destroyed.
 	 * The iterator keeps the database locked until it is destroyed.
-	 * The database will keep functioning normally but will only free internal 
+	 * The database will keep functioning normally but will only free internal
 	 * memory when unlocked, so destroy the iterator as soon as possible.
 	 * memory when unlocked, so destroy the iterator as soon as possible.
 	 * @param self Database
 	 * @param self Database
 	 * @return New iterator
 	 * @return New iterator
@@ -399,7 +406,7 @@ struct DBMap {
 	 * It puts a maximum of <code>max</code> entries into <code>buf</code>.
 	 * It puts a maximum of <code>max</code> entries into <code>buf</code>.
 	 * If <code>buf</code> is NULL, it only counts the matches.
 	 * If <code>buf</code> is NULL, it only counts the matches.
 	 * Returns the number of entries that matched.
 	 * Returns the number of entries that matched.
-	 * NOTE: if the value returned is greater than <code>max</code>, only the 
+	 * NOTE: if the value returned is greater than <code>max</code>, only the
 	 * first <code>max</code> entries found are put into the buffer.
 	 * first <code>max</code> entries found are put into the buffer.
 	 * @param self Database
 	 * @param self Database
 	 * @param buf Buffer to put the data of the matched entries
 	 * @param buf Buffer to put the data of the matched entries
@@ -417,7 +424,7 @@ struct DBMap {
 	 * It puts a maximum of <code>max</code> entries into <code>buf</code>.
 	 * It puts a maximum of <code>max</code> entries into <code>buf</code>.
 	 * If <code>buf</code> is NULL, it only counts the matches.
 	 * If <code>buf</code> is NULL, it only counts the matches.
 	 * Returns the number of entries that matched.
 	 * Returns the number of entries that matched.
-	 * NOTE: if the value returned is greater than <code>max</code>, only the 
+	 * NOTE: if the value returned is greater than <code>max</code>, only the
 	 * first <code>max</code> entries found are put into the buffer.
 	 * first <code>max</code> entries found are put into the buffer.
 	 * @param self Database
 	 * @param self Database
 	 * @param buf Buffer to put the data of the matched entries
 	 * @param buf Buffer to put the data of the matched entries
@@ -433,7 +440,7 @@ struct DBMap {
 	/**
 	/**
 	 * Just calls {@link DBMap#vensure}.
 	 * Just calls {@link DBMap#vensure}.
 	 * Get the data of the entry identified by the key.
 	 * Get the data of the entry identified by the key.
-	 * If the entry does not exist, an entry is added with the data returned by 
+	 * If the entry does not exist, an entry is added with the data returned by
 	 * <code>create</code>.
 	 * <code>create</code>.
 	 * @param self Database
 	 * @param self Database
 	 * @param key Key that identifies the entry
 	 * @param key Key that identifies the entry
@@ -447,7 +454,7 @@ struct DBMap {
 
 
 	/**
 	/**
 	 * Get the data of the entry identified by the key.
 	 * Get the data of the entry identified by the key.
-	 * If the entry does not exist, an entry is added with the data returned by 
+	 * If the entry does not exist, an entry is added with the data returned by
 	 * <code>create</code>.
 	 * <code>create</code>.
 	 * @param self Database
 	 * @param self Database
 	 * @param key Key that identifies the entry
 	 * @param key Key that identifies the entry
@@ -544,7 +551,7 @@ struct DBMap {
 	 * Before deleting an entry, func is applied to it.
 	 * Before deleting an entry, func is applied to it.
 	 * Releases the key and the data.
 	 * Releases the key and the data.
 	 * Returns the sum of values returned by func, if it exists.
 	 * Returns the sum of values returned by func, if it exists.
-	 * NOTE: This locks the database globally. Any attempt to insert or remove 
+	 * NOTE: This locks the database globally. Any attempt to insert or remove
 	 * a database entry will give an error and be aborted (except for clearing).
 	 * a database entry will give an error and be aborted (except for clearing).
 	 * @param self Database
 	 * @param self Database
 	 * @param func Function to be applied to every entry before deleting
 	 * @param func Function to be applied to every entry before deleting
@@ -559,7 +566,7 @@ struct DBMap {
 	 * Finalize the database, feeing all the memory it uses.
 	 * Finalize the database, feeing all the memory it uses.
 	 * Before deleting an entry, func is applied to it.
 	 * Before deleting an entry, func is applied to it.
 	 * Returns the sum of values returned by func, if it exists.
 	 * Returns the sum of values returned by func, if it exists.
-	 * NOTE: This locks the database globally. Any attempt to insert or remove 
+	 * NOTE: This locks the database globally. Any attempt to insert or remove
 	 * a database entry will give an error and be aborted (except for clearing).
 	 * a database entry will give an error and be aborted (except for clearing).
 	 * @param self Database
 	 * @param self Database
 	 * @param func Function to be applied to every entry before deleting
 	 * @param func Function to be applied to every entry before deleting
@@ -598,65 +605,86 @@ struct DBMap {
 
 
 // For easy access to the common functions.
 // 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
 // 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
 // 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
 // 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
 // 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
 // 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
 // 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
 // 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
 //These are discarding the possible vargs you could send to the function, so those
 //that require vargs must not use these defines.
 //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
 // Database creation and destruction macros
 #define idb_alloc(opt)            db_alloc(__FILE__,__LINE__,DB_INT,(opt),sizeof(int))
 #define idb_alloc(opt)            db_alloc(__FILE__,__LINE__,DB_INT,(opt),sizeof(int))
 #define uidb_alloc(opt)           db_alloc(__FILE__,__LINE__,DB_UINT,(opt),sizeof(unsigned int))
 #define uidb_alloc(opt)           db_alloc(__FILE__,__LINE__,DB_UINT,(opt),sizeof(unsigned int))
 #define strdb_alloc(opt,maxlen)   db_alloc(__FILE__,__LINE__,DB_STRING,(opt),(maxlen))
 #define strdb_alloc(opt,maxlen)   db_alloc(__FILE__,__LINE__,DB_STRING,(opt),(maxlen))
 #define stridb_alloc(opt,maxlen)  db_alloc(__FILE__,__LINE__,DB_ISTRING,(opt),(maxlen))
 #define stridb_alloc(opt,maxlen)  db_alloc(__FILE__,__LINE__,DB_ISTRING,(opt),(maxlen))
+#define i64db_alloc(opt)          db_alloc(__FILE__,__LINE__,DB_INT64,(opt),sizeof(int64))
+#define ui64db_alloc(opt)         db_alloc(__FILE__,__LINE__,DB_UINT64,(opt),sizeof(uint64))
 #define db_destroy(db)            ( (db)->destroy((db),NULL) )
 #define db_destroy(db)            ( (db)->destroy((db),NULL) )
 // Other macros
 // Other macros
 #define db_clear(db)        ( (db)->clear(db,NULL) )
 #define db_clear(db)        ( (db)->clear(db,NULL) )
@@ -682,6 +710,8 @@ struct DBMap {
  *  db_i2key           - Manual cast from 'int' to 'DBKey'.                  *
  *  db_i2key           - Manual cast from 'int' to 'DBKey'.                  *
  *  db_ui2key          - Manual cast from 'unsigned int' to 'DBKey'.         *
  *  db_ui2key          - Manual cast from 'unsigned int' to 'DBKey'.         *
  *  db_str2key         - Manual cast from 'unsigned char *' 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_i2data          - Manual cast from 'int' to 'DBData'.                 *
  *  db_ui2data         - Manual cast from 'unsigned int' to 'DBData'.        *
  *  db_ui2data         - Manual cast from 'unsigned int' to 'DBData'.        *
  *  db_ptr2data        - Manual cast from 'void*' to 'DBData'.               *
  *  db_ptr2data        - Manual cast from 'void*' to 'DBData'.               *
@@ -801,6 +831,22 @@ DBKey db_ui2key(unsigned int key);
  */
  */
 DBKey db_str2key(const char *key);
 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);
+
+/**
+ * Manual cast from 'uint64' to the union DBKey.
+ * @param key Key to be casted
+ * @return The key as a DBKey union
+ * @public
+ */
+DBKey db_ui642key(uint64 key);
+
 /**
 /**
  * Manual cast from 'int' to the struct DBData.
  * Manual cast from 'int' to the struct DBData.
  * @param data Data to be casted
  * @param data Data to be casted
@@ -1446,7 +1492,7 @@ int   linkdb_foreach( struct linkdb_node** head, LinkDBFunc func, ...  );
 		VECTOR_INDEX(__heap,0) = VECTOR_POP(__heap); /* put last at index */ \
 		VECTOR_INDEX(__heap,0) = VECTOR_POP(__heap); /* put last at index */ \
 		if( !VECTOR_LENGTH(__heap) ) /* removed last, nothing to do */ \
 		if( !VECTOR_LENGTH(__heap) ) /* removed last, nothing to do */ \
 			break; \
 			break; \
-		BHEAP_SIFTUP(__heap,0,__topcmp,__swp);	\
+		BHEAP_SIFTUP(__heap,0,__topcmp,__swp); \
 	}while(0)
 	}while(0)
 
 
 
 
@@ -1508,7 +1554,7 @@ int   linkdb_foreach( struct linkdb_node** head, LinkDBFunc func, ...  );
 /// @param __idx Index of an inserted element
 /// @param __idx Index of an inserted element
 /// @param __topcmp Comparator
 /// @param __topcmp Comparator
 /// @param __swp Swapper
 /// @param __swp Swapper
-#define BHEAP_SIFTDOWN(__heap,__startidx,__idx,__topcmp,__swp)	\
+#define BHEAP_SIFTDOWN(__heap,__startidx,__idx,__topcmp,__swp) \
 	do{ \
 	do{ \
 		size_t _i2_ = __idx; \
 		size_t _i2_ = __idx; \
 		while( _i2_ > __startidx ) \
 		while( _i2_ > __startidx ) \

+ 5 - 5
src/common/ers.c

@@ -13,16 +13,16 @@
  *  If it has reusable entries (freed entry), it uses one.                   *
  *  If it has reusable entries (freed entry), it uses one.                   *
  *  So no assumption should be made about the data of the entry.             *
  *  So no assumption should be made about the data of the entry.             *
  *  Entries should be freed in the manager they where allocated from.        *
  *  Entries should be freed in the manager they where allocated from.        *
- *  Failure to do so can lead to unexpected behaviours.                      *
+ *  Failure to do so can lead to unexpected behaviors.                      *
  *                                                                           *
  *                                                                           *
  *  <H2>Advantages:</H2>                                                     *
  *  <H2>Advantages:</H2>                                                     *
  *  - The same manager is used for entries of the same size.                 *
  *  - The same manager is used for entries of the same size.                 *
  *    So entries freed in one instance of the manager can be used by other   *
  *    So entries freed in one instance of the manager can be used by other   *
  *    instances of the manager.                                              *
  *    instances of the manager.                                              *
  *  - Much less memory allocation/deallocation - program will be faster.     *
  *  - Much less memory allocation/deallocation - program will be faster.     *
- *  - Avoids memory fragmentaion - program will run better for longer.       *
+ *  - Avoids memory fragmentation - program will run better for longer.       *
  *                                                                           *
  *                                                                           *
- *  <H2>Disavantages:</H2>                                                   *
+ *  <H2>Disadvantages:</H2>                                                   *
  *  - Unused entries are almost inevitable - memory being wasted.            *
  *  - Unused entries are almost inevitable - memory being wasted.            *
  *  - A  manager will only auto-destroy when all of its instances are        *
  *  - A  manager will only auto-destroy when all of its instances are        *
  *    destroyed so memory will usually only be recovered near the end.       *
  *    destroyed so memory will usually only be recovered near the end.       *
@@ -70,12 +70,12 @@ typedef struct ers_cache
 	// Memory blocks array
 	// Memory blocks array
 	unsigned char **Blocks;
 	unsigned char **Blocks;
 
 
-	// Max number of blocks 
+	// Max number of blocks
 	unsigned int Max;
 	unsigned int Max;
 
 
 	// Free objects count
 	// Free objects count
 	unsigned int Free;
 	unsigned int Free;
-	
+
 	// Used objects count
 	// Used objects count
 	unsigned int Used;
 	unsigned int Used;
 
 

+ 12 - 12
src/common/ers.h

@@ -13,16 +13,16 @@
  *  If it has reusable entries (freed entry), it uses one.                   *
  *  If it has reusable entries (freed entry), it uses one.                   *
  *  So no assumption should be made about the data of the entry.             *
  *  So no assumption should be made about the data of the entry.             *
  *  Entries should be freed in the manager they where allocated from.        *
  *  Entries should be freed in the manager they where allocated from.        *
- *  Failure to do so can lead to unexpected behaviours.                      *
+ *  Failure to do so can lead to unexpected behaviors.                      *
  *                                                                           *
  *                                                                           *
  *  <H2>Advantages:</H2>                                                     *
  *  <H2>Advantages:</H2>                                                     *
  *  - The same manager is used for entries of the same size.                 *
  *  - The same manager is used for entries of the same size.                 *
  *    So entries freed in one instance of the manager can be used by other   *
  *    So entries freed in one instance of the manager can be used by other   *
  *    instances of the manager.                                              *
  *    instances of the manager.                                              *
  *  - Much less memory allocation/deallocation - program will be faster.     *
  *  - Much less memory allocation/deallocation - program will be faster.     *
- *  - Avoids memory fragmentaion - program will run better for longer.       *
+ *  - Avoids memory fragmentation - program will run better for longer.       *
  *                                                                           *
  *                                                                           *
- *  <H2>Disavantages:</H2>                                                   *
+ *  <H2>Disadvantages:</H2>                                                   *
  *  - Unused entries are almost inevitable - memory being wasted.            *
  *  - Unused entries are almost inevitable - memory being wasted.            *
  *  - A  manager will only auto-destroy when all of its instances are        *
  *  - A  manager will only auto-destroy when all of its instances are        *
  *    destroyed so memory will usually only be recovered near the end.       *
  *    destroyed so memory will usually only be recovered near the end.       *
@@ -55,7 +55,7 @@
 /**
 /**
  * Define this to disable the Entry Reusage System.
  * Define this to disable the Entry Reusage System.
  * All code except the typedef of ERInterface will be disabled.
  * All code except the typedef of ERInterface will be disabled.
- * To allow a smooth transition, 
+ * To allow a smooth transition,
  */
  */
 //#define DISABLE_ERS
 //#define DISABLE_ERS
 
 
@@ -63,8 +63,8 @@
  * Entries are aligned to ERS_ALIGNED bytes in the blocks of entries.
  * Entries are aligned to ERS_ALIGNED bytes in the blocks of entries.
  * By default it aligns to one byte, using the "natural order" of the entries.
  * By default it aligns to one byte, using the "natural order" of the entries.
  * This should NEVER be set to zero or less.
  * This should NEVER be set to zero or less.
- * If greater than one, some memory can be wasted. This should never be needed 
- * but is here just in case some aligment issues arise.
+ * If greater than one, some memory can be wasted. This should never be needed
+ * but is here just in case some alignment issues arise.
  */
  */
 #ifndef ERS_ALIGNED
 #ifndef ERS_ALIGNED
 #	define ERS_ALIGNED 1
 #	define ERS_ALIGNED 1
@@ -95,7 +95,7 @@ typedef struct eri {
 	/**
 	/**
 	 * Free an entry allocated from this manager.
 	 * Free an entry allocated from this manager.
 	 * WARNING: Does not check if the entry was allocated by this manager.
 	 * WARNING: Does not check if the entry was allocated by this manager.
-	 * Freeing such an entry can lead to unexpected behaviour.
+	 * Freeing such an entry can lead to unexpected behavior.
 	 * @param self Interface of the entry manager
 	 * @param self Interface of the entry manager
 	 * @param entry Entry to be freed
 	 * @param entry Entry to be freed
 	 */
 	 */
@@ -111,7 +111,7 @@ typedef struct eri {
 	/**
 	/**
 	 * Destroy this instance of the manager.
 	 * Destroy this instance of the manager.
 	 * The manager is actually only destroyed when all the instances are destroyed.
 	 * The manager is actually only destroyed when all the instances are destroyed.
-	 * When destroying the manager a warning is shown if the manager has 
+	 * When destroying the manager a warning is shown if the manager has
 	 * missing/extra entries.
 	 * missing/extra entries.
 	 * @param self Interface of the entry manager
 	 * @param self Interface of the entry manager
 	 */
 	 */
@@ -130,7 +130,7 @@ typedef struct eri {
 #	define ers_report()
 #	define ers_report()
 #	define ers_force_destroy_all()
 #	define ers_force_destroy_all()
 #else /* not DISABLE_ERS */
 #else /* not DISABLE_ERS */
-// These defines should be used to allow the code to keep working whenever 
+// These defines should be used to allow the code to keep working whenever
 // the system is disabled
 // the system is disabled
 #	define ers_alloc(obj,type) (type *)(obj)->alloc(obj)
 #	define ers_alloc(obj,type) (type *)(obj)->alloc(obj)
 #	define ers_free(obj,entry) (obj)->free((obj),(entry))
 #	define ers_free(obj,entry) (obj)->free((obj),(entry))
@@ -140,9 +140,9 @@ typedef struct eri {
 /**
 /**
  * Get a new instance of the manager that handles the specified entry size.
  * Get a new instance of the manager that handles the specified entry size.
  * Size has to greater than 0.
  * Size has to greater than 0.
- * If the specified size is smaller than a pointer, the size of a pointer is 
+ * If the specified size is smaller than a pointer, the size of a pointer is
  * used instead.
  * used instead.
- * It's also aligned to ERS_ALIGNED bytes, so the smallest multiple of 
+ * It's also aligned to ERS_ALIGNED bytes, so the smallest multiple of
  * ERS_ALIGNED that is greater or equal to size is what's actually used.
  * ERS_ALIGNED that is greater or equal to size is what's actually used.
  * @param The requested size of the entry in bytes
  * @param The requested size of the entry in bytes
  * @return Interface of the object
  * @return Interface of the object
@@ -152,7 +152,7 @@ ERS ers_new(uint32 size, char *name, enum ERSOptions options);
 /**
 /**
  * Print a report about the current state of the Entry Reusage System.
  * Print a report about the current state of the Entry Reusage System.
  * Shows information about the global system and each entry manager.
  * Shows information about the global system and each entry manager.
- * The number of entries are checked and a warning is shown if extra reusable 
+ * The number of entries are checked and a warning is shown if extra reusable
  * entries are found.
  * entries are found.
  * The extra entries are included in the count of reusable entries.
  * The extra entries are included in the count of reusable entries.
  */
  */

+ 19 - 21
src/common/mmo.h

@@ -55,10 +55,6 @@
 #define MAX_FAME 1000000000 ///Max fame points
 #define MAX_FAME 1000000000 ///Max fame points
 #define MAX_CART 100 ///Maximum item in cart
 #define MAX_CART 100 ///Maximum item in cart
 #define MAX_SKILL 1200 ///Maximum skill can be hold by Player, Homunculus, & Mercenary (skill list) AND skill_db limit
 #define MAX_SKILL 1200 ///Maximum skill can be hold by Player, Homunculus, & Mercenary (skill list) AND skill_db limit
-#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
-#define MAX_REG_NUM 256 ///Should hold the max of GLOBAL/ACCOUNT/ACCOUNT2 (needed for some arrays that hold all three)
 #define DEFAULT_WALK_SPEED 150 ///Default walk speed
 #define DEFAULT_WALK_SPEED 150 ///Default walk speed
 #define MIN_WALK_SPEED 20 ///Min walk speed
 #define MIN_WALK_SPEED 20 ///Min walk speed
 #define MAX_WALK_SPEED 1000 ///Max walk speed
 #define MAX_WALK_SPEED 1000 ///Max walk speed
@@ -242,16 +238,19 @@ struct s_skill {
 	uint8 flag; // see enum e_skill_flag
 	uint8 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 {
-	uint32 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]
 //For saving status changes across sessions. [Skotlex]
@@ -480,15 +479,6 @@ struct auction_data {
 	int auction_end_timer;
 	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 {
 struct party_member {
 	uint32 account_id;
 	uint32 account_id;
 	uint32 char_id;
 	uint32 char_id;
@@ -812,6 +802,14 @@ enum bound_type {
 	BOUND_DISPYELLOW = 2, /// Shows the item name in yellow color
 	BOUND_DISPYELLOW = 2, /// Shows the item name in yellow color
 };
 };
 
 
+enum e_pc_reg_loading {
+	PRL_NONE = 0x0,
+	PRL_CHAR = 0x1,
+	PRL_ACCL = 0x2, // local
+	PRL_ACCG = 0x4, // global
+	PRL_ALL = 0xFF,
+};
+
 // sanity checks...
 // sanity checks...
 #if MAX_ZENY > INT_MAX
 #if MAX_ZENY > INT_MAX
 #error MAX_ZENY is too big
 #error MAX_ZENY is too big

+ 204 - 58
src/login/account.c

@@ -10,6 +10,7 @@
 #include "../common/malloc.h"
 #include "../common/malloc.h"
 #include "../common/mmo.h"
 #include "../common/mmo.h"
 #include "../common/showmsg.h"
 #include "../common/showmsg.h"
+#include "../common/socket.h"
 #include "../common/sql.h"
 #include "../common/sql.h"
 #include "../common/strlib.h"
 #include "../common/strlib.h"
 #include "account.h"
 #include "account.h"
@@ -32,7 +33,8 @@ typedef struct AccountDB_SQL {
 	bool case_sensitive;
 	bool case_sensitive;
 	//table name
 	//table name
 	char account_db[32];
 	char account_db[32];
-	char accreg_db[32];
+	char global_acc_reg_num_table[32];
+	char global_acc_reg_str_table[32];
 
 
 } AccountDB_SQL;
 } AccountDB_SQL;
 
 
@@ -88,7 +90,8 @@ AccountDB* account_db_sql(void) {
 	// other settings
 	// other settings
 	db->case_sensitive = false;
 	db->case_sensitive = false;
 	safestrncpy(db->account_db, "login", sizeof(db->account_db));
 	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_table, "global_acc_reg_num", sizeof(db->global_acc_reg_num_table));
+	safestrncpy(db->global_acc_reg_str_table, "global_acc_reg_str", sizeof(db->global_acc_reg_str_table));
 
 
 	return &db->vtable;
 	return &db->vtable;
 }
 }
@@ -187,8 +190,11 @@ static bool account_db_sql_get_property(AccountDB* self, const char* key, char*
 		if( strcmpi(key, "account_db") == 0 )
 		if( strcmpi(key, "account_db") == 0 )
 			safesnprintf(buf, buflen, "%s", db->account_db);
 			safesnprintf(buf, buflen, "%s", db->account_db);
 		else
 		else
-		if( strcmpi(key, "accreg_db") == 0 )
-			safesnprintf(buf, buflen, "%s", db->accreg_db);
+		if( strcmpi(key, "global_acc_reg_str_table") == 0 )
+			safesnprintf(buf, buflen, "%s", db->global_acc_reg_str_table);
+		else
+		if( strcmpi(key, "global_acc_reg_num_table") == 0 )
+			safesnprintf(buf, buflen, "%s", db->global_acc_reg_num_table);
 		else
 		else
 			return false;// not found
 			return false;// not found
 		return true;
 		return true;
@@ -243,8 +249,11 @@ static bool account_db_sql_set_property(AccountDB* self, const char* key, const
 		if( strcmpi(key, "account_db") == 0 )
 		if( strcmpi(key, "account_db") == 0 )
 			safestrncpy(db->account_db, value, sizeof(db->account_db));
 			safestrncpy(db->account_db, value, sizeof(db->account_db));
 		else
 		else
-		if( strcmpi(key, "accreg_db") == 0 )
-			safestrncpy(db->accreg_db, value, sizeof(db->accreg_db));
+		if( strcmpi(key, "global_acc_reg_str_table") == 0 )
+			safestrncpy(db->global_acc_reg_str_table, value, sizeof(db->global_acc_reg_str_table));
+		else
+		if( strcmpi(key, "global_acc_reg_num_table") == 0 )
+			safestrncpy(db->global_acc_reg_num_table, value, sizeof(db->global_acc_reg_num_table));
 		else
 		else
 			return false;// not found
 			return false;// not found
 		return true;
 		return true;
@@ -336,7 +345,8 @@ static bool account_db_sql_remove(AccountDB* self, const uint32 account_id) {
 
 
 	if( SQL_SUCCESS != Sql_QueryStr(sql_handle, "START TRANSACTION")
 	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->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_table, account_id)
+	||  SQL_SUCCESS != Sql_Query(sql_handle, "DELETE FROM `%s` WHERE `account_id` = %d", db->global_acc_reg_str_table, account_id) )
 		Sql_ShowDebug(sql_handle);
 		Sql_ShowDebug(sql_handle);
 	else
 	else
 		result = true;
 		result = true;
@@ -491,7 +501,6 @@ static bool account_db_sql_iter_next(AccountDBIterator* self, struct mmo_account
 static bool mmo_auth_fromsql(AccountDB_SQL* db, struct mmo_account* acc, uint32 account_id) {
 static bool mmo_auth_fromsql(AccountDB_SQL* db, struct mmo_account* acc, uint32 account_id) {
 	Sql* sql_handle = db->accounts;
 	Sql* sql_handle = db->accounts;
 	char* data;
 	char* data;
-	int i = 0;
 
 
 	// retrieve login entry for the specified account
 	// retrieve login entry for the specified account
 	if( SQL_ERROR == Sql_Query(sql_handle,
 	if( SQL_ERROR == Sql_Query(sql_handle,
@@ -533,27 +542,6 @@ static bool mmo_auth_fromsql(AccountDB_SQL* db, struct mmo_account* acc, uint32
 	Sql_GetData(sql_handle, 17, &data, NULL); acc->old_group = atoi(data);
 	Sql_GetData(sql_handle, 17, &data, NULL); acc->old_group = atoi(data);
 #endif
 #endif
 	Sql_FreeResult(sql_handle);
 	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) )
-	{
-		char* data_tmp;
-		Sql_GetData(sql_handle, 0, &data_tmp, NULL); safestrncpy(acc->account_reg2[i].str, data_tmp, sizeof(acc->account_reg2[i].str));
-		Sql_GetData(sql_handle, 1, &data_tmp, NULL); safestrncpy(acc->account_reg2[i].value, data_tmp, sizeof(acc->account_reg2[i].value));
-		++i;
-	}
-	Sql_FreeResult(sql_handle);
-
-	if( i != acc->account_reg2_num )
-		return false;
 
 
 	return true;
 	return true;
 }
 }
@@ -569,7 +557,6 @@ static bool mmo_auth_tosql(AccountDB_SQL* db, const struct mmo_account* acc, boo
 	Sql* sql_handle = db->accounts;
 	Sql* sql_handle = db->accounts;
 	SqlStmt* stmt = SqlStmt_Malloc(sql_handle);
 	SqlStmt* stmt = SqlStmt_Malloc(sql_handle);
 	bool result = false;
 	bool result = false;
-	int i;
 
 
 	// try
 	// try
 	do
 	do
@@ -651,34 +638,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 != SqlStmt_Prepare(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 != SqlStmt_BindParam(stmt, 0, SQLDT_STRING, (void*)acc->account_reg2[i].str, strlen(acc->account_reg2[i].str))
-		||  SQL_SUCCESS != SqlStmt_BindParam(stmt, 1, SQLDT_STRING, (void*)acc->account_reg2[i].value, strlen(acc->account_reg2[i].value))
-		||  SQL_SUCCESS != SqlStmt_Execute(stmt)
-		) {
-			SqlStmt_ShowDebug(stmt);
-			break;
-		}
-	}
-	if( i < acc->account_reg2_num )
-	{
-		result = false;
-		break;
-	}
-
 	// if we got this far, everything was successful
 	// if we got this far, everything was successful
 	result = true;
 	result = true;
 
 
@@ -690,3 +649,190 @@ static bool mmo_auth_tosql(AccountDB_SQL* db, const struct mmo_account* acc, boo
 
 
 	return result;
 	return result;
 }
 }
+
+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];
+
+		for (i = 0; i < count; i++) {
+			unsigned int index;
+			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_table, 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_table, 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_table, 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_table, account_id, key, index) )
+						Sql_ShowDebug(sql_handle);
+					break;
+				default:
+					ShowError("mmo_save_accreg2: 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_table, 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;
+		}
+	}
+
+	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_table, 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;
+		}
+	}
+
+	WFIFOB(fd, 12) = 1;
+	WFIFOW(fd, 2) = plen;
+	WFIFOSET(fd, plen);
+
+	Sql_FreeResult(sql_handle);
+}

+ 3 - 2
src/login/account.h

@@ -38,12 +38,10 @@ struct mmo_account {
 	char birthdate[10+1];   // assigned birth date (format: YYYY-MM-DD, default: 0000-00-00)
 	char birthdate[10+1];   // assigned birth date (format: YYYY-MM-DD, default: 0000-00-00)
 	char pincode[PINCODE_LENGTH+1];		// pincode system
 	char pincode[PINCODE_LENGTH+1];		// pincode system
 	time_t pincode_change;	// (timestamp): last time of pincode change
 	time_t pincode_change;	// (timestamp): last time of pincode change
-	int account_reg2_num;
 #ifdef VIP_ENABLE
 #ifdef VIP_ENABLE
 	int old_group;
 	int old_group;
 	time_t vip_time;
 	time_t vip_time;
 #endif
 #endif
-	struct global_reg account_reg2[ACCOUNT_REG2_NUM]; // account script variables (stored on login server)
 };
 };
 
 
 
 
@@ -140,5 +138,8 @@ struct AccountDB {
 	AccountDBIterator* (*iterator)(AccountDB* self);
 	AccountDBIterator* (*iterator)(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__
 #endif // __ACCOUNT_H_INCLUDED__

+ 3 - 44
src/login/loginchrif.c

@@ -486,28 +486,8 @@ int logchrif_parse_updreg2(int fd, int id, char* ip){
 
 
 		if( !accounts->load_num(accounts, &acc, account_id) )
 		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", ch_server[id].name, account_id, ip);
 			ShowStatus("Char-server '%s': receiving (from the char-server) of account_reg2 (account: %d not found, ip: %s).\n", ch_server[id].name, account_id, ip);
-		else{
-			int len;
-			int p;
-			uint8 j;
-			ShowNotice("char-server '%s': receiving (from the char-server) of account_reg2 (account: %d, ip: %s).\n", ch_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
-			logchrif_sendallwos(fd, RFIFOP(fd,0), RFIFOW(fd,2));
-		}
+		else
+			mmo_save_accreg2(accounts,fd,account_id,RFIFOL(fd, 8));
 		RFIFOSKIP(fd,RFIFOW(fd,2));
 		RFIFOSKIP(fd,RFIFOW(fd,2));
 	}
 	}
 	return 1;
 	return 1;
@@ -609,33 +589,12 @@ int logchrif_parse_reqacc2reg(int fd){
 	if (RFIFOREST(fd) < 10)
 	if (RFIFOREST(fd) < 10)
 		return 0;
 		return 0;
 	else{
 	else{
-		struct mmo_account acc;
 		AccountDB* accounts = login_get_accounts_db();
 		AccountDB* accounts = login_get_accounts_db();
-		size_t off;
-
 		uint32 account_id = RFIFOL(fd,2);
 		uint32 account_id = RFIFOL(fd,2);
 		uint32 char_id = RFIFOL(fd,6);
 		uint32 char_id = RFIFOL(fd,6);
 		RFIFOSKIP(fd,10);
 		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) ){
-			uint8 j;
-			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);
 	}
 	}
 	return 1;
 	return 1;
 }
 }

+ 8 - 8
src/map/atcommand.c

@@ -28,6 +28,7 @@
 #include "mercenary.h"
 #include "mercenary.h"
 #include "elemental.h"
 #include "elemental.h"
 #include "party.h"
 #include "party.h"
+#include "script.h"
 #include "storage.h"
 #include "storage.h"
 #include "trade.h"
 #include "trade.h"
 #include "mapreg.h"
 #include "mapreg.h"
@@ -8978,12 +8979,12 @@ ACMD_FUNC(set) {
 				break;
 				break;
 			case '#':
 			case '#':
 				if( reg[1] == '#' )
 				if( reg[1] == '#' )
-					data->u.str = pc_readaccountreg2str(sd, reg);// global
+					data->u.str = pc_readaccountreg2str(sd, add_str(reg));// global
 				else
 				else
-					data->u.str = pc_readaccountregstr(sd, reg);// local
+					data->u.str = pc_readaccountregstr(sd, add_str(reg));// local
 				break;
 				break;
 			default:
 			default:
-				data->u.str = pc_readglobalreg_str(sd, reg);
+				data->u.str = pc_readglobalreg_str(sd, add_str(reg));
 				break;
 				break;
 		}
 		}
 
 
@@ -9007,18 +9008,17 @@ ACMD_FUNC(set) {
 				break;
 				break;
 			case '#':
 			case '#':
 				if( reg[1] == '#' )
 				if( reg[1] == '#' )
-					data->u.num = pc_readaccountreg2(sd, reg);// global
+					data->u.num = pc_readaccountreg2(sd, add_str(reg));// global
 				else
 				else
-					data->u.num = pc_readaccountreg(sd, reg);// local
+					data->u.num = pc_readaccountreg(sd, add_str(reg));// local
 				break;
 				break;
 			default:
 			default:
-				data->u.num = pc_readglobalreg(sd, reg);
+				data->u.num = pc_readglobalreg(sd, add_str(reg));
 				break;
 				break;
 		}
 		}
 
 
 	}
 	}
 
 
-
 	switch( data->type ) {
 	switch( data->type ) {
 		case C_INT:
 		case C_INT:
 			sprintf(atcmd_output,msg_txt(sd,1373),reg,data->u.num); // %s value is now :%d
 			sprintf(atcmd_output,msg_txt(sd,1373),reg,data->u.num); // %s value is now :%d
@@ -9353,7 +9353,7 @@ ACMD_FUNC(langtype)
 		lang = msg_langstr2langtype(langstr); //Switch langstr to associated langtype
 		lang = msg_langstr2langtype(langstr); //Switch langstr to associated langtype
 		if( msg_checklangtype(lang,false) == 1 ){ //Verify it's enabled and set it
 		if( msg_checklangtype(lang,false) == 1 ){ //Verify it's enabled and set it
 			char output[100];
 			char output[100];
-			pc_setaccountreg(sd, "#langtype", lang); //For login/char
+			pc_setaccountreg(sd, add_str("#langtype"), lang); //For login/char
 			sd->langtype = lang;
 			sd->langtype = lang;
 			sprintf(output,msg_txt(sd,461),msg_langtype2langstr(lang)); // Language is now set to %s.
 			sprintf(output,msg_txt(sd,461),msg_langtype2langstr(lang)); // Language is now set to %s.
 			clif_displaymessage(fd,output);
 			clif_displaymessage(fd,output);

+ 18 - 8
src/map/chrif.c

@@ -157,8 +157,15 @@ bool chrif_auth_delete(uint32 account_id, uint32 char_id, enum sd_state state) {
 		if ( node->char_dat )
 		if ( node->char_dat )
 			aFree(node->char_dat);
 			aFree(node->char_dat);
 
 
-		if ( node->sd )
+		if ( node->sd ) {
+			if (node->sd->regs.vars)
+				node->sd->regs.vars->destroy(node->sd->regs.vars, script_reg_destroy);
+
+			if (node->sd->regs.arrays)
+				node->sd->regs.arrays->destroy(node->sd->regs.arrays, script_free_array_db);
+
 			aFree(node->sd);
 			aFree(node->sd);
+		}
 
 
 		ers_free(auth_db_ers, node);
 		ers_free(auth_db_ers, node);
 		idb_remove(auth_db,account_id);
 		idb_remove(auth_db,account_id);
@@ -298,12 +305,8 @@ int chrif_save(struct map_session_data *sd, int flag) {
 		sd->state.storage_flag = 0; //Force close it.
 		sd->state.storage_flag = 0; //Force close it.
 
 
 	//Saving of registry values.
 	//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);
 
 
 	mmo_charstatus_len = sizeof(sd->status) + 13;
 	mmo_charstatus_len = sizeof(sd->status) + 13;
 	WFIFOHEAD(char_fd, mmo_charstatus_len);
 	WFIFOHEAD(char_fd, mmo_charstatus_len);
@@ -1927,8 +1930,15 @@ int auth_db_final(DBKey key, DBData *data, va_list ap) {
 	if (node->char_dat)
 	if (node->char_dat)
 		aFree(node->char_dat);
 		aFree(node->char_dat);
 
 
-	if (node->sd)
+	if (node->sd) {
+		if (node->sd->regs.vars)
+			node->sd->regs.vars->destroy(node->sd->regs.vars, script_reg_destroy);
+
+		if (node->sd->regs.arrays)
+			node->sd->regs.arrays->destroy(node->sd->regs.arrays, script_free_array_db);
+
 		aFree(node->sd);
 		aFree(node->sd);
+	}
 
 
 	ers_free(auth_db_ers, node);
 	ers_free(auth_db_ers, node);
 
 

+ 1 - 1
src/map/clif.c

@@ -14223,7 +14223,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].index = map_id2index(sd->bl.m);
 	sd->feel_map[i].m = sd->bl.m;
 	sd->feel_map[i].m = sd->bl.m;
-	pc_setglobalreg(sd,sg_info[i].feel_var,sd->feel_map[i].index);
+	pc_setglobalreg(sd, add_str(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?
 //Are these really needed? Shouldn't they show up automatically from the feel save packet?
 //	clif_misceffect2(&sd->bl, 0x1b0);
 //	clif_misceffect2(&sd->bl, 0x1b0);

+ 2 - 2
src/map/duel.c

@@ -27,7 +27,7 @@ void duel_savetime(struct map_session_data* sd)
 	time(&timer);
 	time(&timer);
 	t = localtime(&timer);
 	t = localtime(&timer);
 
 
-	pc_setglobalreg(sd, "PC_LAST_DUEL_TIME", t->tm_mday*24*60 + t->tm_hour*60 + t->tm_min);
+	pc_setglobalreg(sd, add_str("PC_LAST_DUEL_TIME"), t->tm_mday*24*60 + t->tm_hour*60 + t->tm_min);
 }
 }
 
 
 /*
 /*
@@ -42,7 +42,7 @@ int duel_checktime(struct map_session_data* sd)
 	time(&timer);
 	time(&timer);
 	t = localtime(&timer);
 	t = localtime(&timer);
 
 
-	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, add_str("PC_LAST_DUEL_TIME"));
 
 
 	return !(diff >= 0 && diff < battle_config.duel_time_interval);
 	return !(diff >= 0 && diff < battle_config.duel_time_interval);
 }
 }

+ 8 - 4
src/map/instance.c

@@ -278,7 +278,8 @@ int instance_create(int party_id, const char *name)
 	instance_data[i].keep_timer = INVALID_TIMER;
 	instance_data[i].keep_timer = INVALID_TIMER;
 	instance_data[i].idle_limit = 0;
 	instance_data[i].idle_limit = 0;
 	instance_data[i].idle_timer = INVALID_TIMER;
 	instance_data[i].idle_timer = INVALID_TIMER;
-	instance_data[i].vars = idb_alloc(DB_OPT_RELEASE_DATA);
+	instance_data[i].regs.vars = i64db_alloc(DB_OPT_RELEASE_DATA);
+	instance_data[i].regs.arrays = NULL;
 	memset(instance_data[i].map, 0, sizeof(instance_data[i].map));
 	memset(instance_data[i].map, 0, sizeof(instance_data[i].map));
 
 
 	p->instance_id = i;
 	p->instance_id = i;
@@ -459,11 +460,14 @@ int instance_destroy(short instance_id)
 			clif_instance_changewait( party_getavailablesd( p ), 0xffff, 1 );
 			clif_instance_changewait( party_getavailablesd( p ), 0xffff, 1 );
 	}
 	}
 
 
-	if( im->vars ) {
-		db_destroy(im->vars);
-		im->vars = NULL;
+	if( im->regs.vars ) {
+		db_destroy(im->regs.vars);
+		im->regs.vars = NULL;
 	}
 	}
 
 
+	if( im->regs.arrays )
+		instance_data[instance_id].regs.arrays->destroy(instance_data[instance_id].regs.arrays, script_free_array_db);
+
 	ShowInfo("[Instance] Destroyed %d.\n", instance_id);
 	ShowInfo("[Instance] Destroyed %d.\n", instance_id);
 
 
 	memset(&instance_data[instance_id], 0, sizeof(instance_data[instance_id]));
 	memset(&instance_data[instance_id], 0, sizeof(instance_data[instance_id]));

+ 7 - 1
src/map/instance.h

@@ -4,6 +4,11 @@
 #ifndef _INSTANCE_H_
 #ifndef _INSTANCE_H_
 #define _INSTANCE_H_
 #define _INSTANCE_H_
 
 
+#include "../common/mmo.h" // struct point
+#include "script.h" // struct reg_db
+
+struct block_list;
+
 #define MAX_INSTANCE_DATA	300	// Essentially how many instances we can create, but instance creation is primarily decided by MAX_MAP_PER_SERVER	
 #define MAX_INSTANCE_DATA	300	// Essentially how many instances we can create, but instance creation is primarily decided by MAX_MAP_PER_SERVER	
 #define MAX_MAP_PER_INSTANCE 	10	// Max number of maps per instance
 #define MAX_MAP_PER_INSTANCE 	10	// Max number of maps per instance
 
 
@@ -21,7 +26,8 @@ struct instance_data {
 	unsigned int idle_limit;
 	unsigned int idle_limit;
 	int idle_timer;
 	int idle_timer;
 
 
-	struct DBMap* vars; // Instance Variable for scripts
+	struct reg_db regs; ///< Instance variables for scripts
+
 	struct {
 	struct {
 		int m;
 		int m;
 		int src_m;
 		int src_m;

+ 174 - 91
src/map/intif.c

@@ -350,73 +350,120 @@ int intif_wis_message_to_gm(char *wisp_name, int permission, char *mes)
 	return 1;
 	return 1;
 }
 }
 
 
-/**
- * 
- * @param str
- * @param reg
- * @param qty
- * @return 
- */
-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.
  * Request for saving registry values.
  * @param sd : Player to save registry
  * @param sd : Player to save registry
- * @param type : Type of registry to save, 1=login save, 2=acc on char, 3=char
  * @return 1=msg sent, -1=error
  * @return 1=msg sent, -1=error
  */
  */
-int intif_saveregistry(struct map_session_data *sd, int type)
+int intif_saveregistry(struct map_session_data *sd)
 {
 {
-	struct global_reg *reg;
-	int count;
-	int i, p;
+	DBIterator *iter;
+	DBKey key;
+	DBData *data;
+	int plen = 0;
+	size_t len;
 
 
-	if (CheckForCharServer())
+	if (CheckForCharServer() || !sd->regs.vars)
 		return -1;
 		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->regs.vars);
+	for( data = iter->first(iter,&key); iter->exists(iter); data = iter->next(iter,&key) ) {
+		const char *varname = NULL;
+		struct script_reg_state *src = NULL;
+
+		if( data->type != DB_DATA_PTR ) // it's a @number
+			continue;
+
+		varname = get_str(script_getvarid(key.i64));
+
+		if( varname[0] == '@' ) // @string$ can get here, so we skip
+			continue;
+
+		src = db_data2ptr(data);
+
+		if( !src->update )
+			continue;
+
+		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( src->type ) {
+			struct script_reg_str *p = (struct script_reg_str *)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 = (struct script_reg_num *)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));
-	return 1;
+	dbi_destroy(iter);
+
+	WFIFOW(inter_fd, 2) = plen;
+	WFIFOSET(inter_fd, plen);
+
+	sd->vars_dirty = false;
+
+	return 0;
 }
 }
 
 
 /**
 /**
@@ -429,10 +476,6 @@ int intif_request_registry(struct map_session_data *sd, int flag)
 {
 {
 	nullpo_ret(sd);
 	nullpo_ret(sd);
 
 
-	sd->save_reg.account2_num = -1;
-	sd->save_reg.account_num = -1;
-	sd->save_reg.global_num = -1;
-
 	if (CheckForCharServer())
 	if (CheckForCharServer())
 		return 0;
 		return 0;
 
 
@@ -1266,58 +1309,98 @@ int mapif_parse_WisToGM(int fd)
  * @param fd : char-serv link
  * @param fd : char-serv link
  * @return 0=error, 1=sucess
  * @return 0=error, 1=sucess
  */
  */
-int intif_parse_Registers(int fd)
+void intif_parse_Registers(int fd)
 {
 {
-	int j,p,len,max, flag;
+	int flag;
 	struct map_session_data *sd;
 	struct map_session_data *sd;
-	struct global_reg *reg;
-	int *qty;
 	uint32 account_id = RFIFOL(fd,4), char_id = RFIFOL(fd,8);
 	uint32 account_id = RFIFOL(fd,4), char_id = RFIFOL(fd,8);
 	struct auth_node *node = chrif_auth_check(account_id, char_id, ST_LOGIN);
 	struct auth_node *node = chrif_auth_check(account_id, char_id, ST_LOGIN);
+	char type = RFIFOB(fd, 13);
+
 	if (node)
 	if (node)
 		sd = node->sd;
 		sd = node->sd;
 	else { //Normally registries should arrive for in log-in chars.
 	else { //Normally registries should arrive for in log-in chars.
 		sd = map_id2sd(account_id);
 		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 0;
 
 
-	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)) {
 	switch (RFIFOB(fd,12)) {
 		case 3: //Character Registry
 		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
 		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
 		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:
 		default:
 			ShowError("intif_parse_Registers: Unrecognized type %d\n",RFIFOB(fd,12));
 			ShowError("intif_parse_Registers: Unrecognized type %d\n",RFIFOB(fd,12));
-			return 0;
+			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
+	reg_load = true;
+	
+	if( RFIFOW(fd, 14) ) {
+		char key[32];
+		unsigned int index;
+		int max = RFIFOW(fd, 14), cursor = 16, i;
+
+		/**
+		 * 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++) {
+				char sval[254];
+				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;
+
+				set_reg(NULL,sd,reference_uid(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++) {
+				int ival;
+				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;
+
+				set_reg(NULL,sd,reference_uid(add_str(key), index), key, (void*)__64BPRTSIZE(ival), NULL);
+			}
+		}
 	}
 	}
-	*qty = j;
 
 
-	if (flag && sd->save_reg.global_num > -1 && sd->save_reg.account_num > -1 && sd->save_reg.account2_num > -1)
+	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]
 		pc_reg_received(sd); //Received all registry values, execute init scripts and what-not. [Skotlex]
-	return 1;
 }
 }
 
 
 /**
 /**

+ 1 - 1
src/map/intif.h

@@ -24,7 +24,7 @@ int intif_main_message(struct map_session_data* sd, const char* message);
 int intif_wis_message(struct map_session_data *sd,char *nick,char *mes,int mes_len);
 int intif_wis_message(struct map_session_data *sd,char *nick,char *mes,int mes_len);
 int intif_wis_message_to_gm(char *Wisp_name, int permission, char *mes);
 int intif_wis_message_to_gm(char *Wisp_name, int permission, char *mes);
 
 
-int intif_saveregistry(struct map_session_data *sd, int type);
+int intif_saveregistry(struct map_session_data *sd);
 int intif_request_registry(struct map_session_data *sd, int flag);
 int intif_request_registry(struct map_session_data *sd, int flag);
 
 
 int intif_request_guild_storage(uint32 account_id, int guild_id);
 int intif_request_guild_storage(uint32 account_id, int guild_id);

+ 230 - 111
src/map/mapreg.c

@@ -3,62 +3,103 @@
 
 
 #include "../common/cbasetypes.h"
 #include "../common/cbasetypes.h"
 #include "../common/db.h"
 #include "../common/db.h"
+#include "../common/ers.h"
 #include "../common/malloc.h"
 #include "../common/malloc.h"
+#include "../common/showmsg.h"
 #include "../common/sql.h"
 #include "../common/sql.h"
 #include "../common/strlib.h"
 #include "../common/strlib.h"
 #include "../common/timer.h"
 #include "../common/timer.h"
 
 
 #include "map.h" // mmysql_handle
 #include "map.h" // mmysql_handle
+#include "mapreg.h"
 #include "script.h"
 #include "script.h"
 
 
 #include <stdlib.h>
 #include <stdlib.h>
 
 
-static DBMap* mapreg_db = NULL; // int var_id -> int value
-static DBMap* mapregstr_db = NULL; // int var_id -> char* value
+static struct eri *mapreg_ers;
+
+bool skip_insert = false;
 
 
 static char mapreg_table[32] = "mapreg";
 static char mapreg_table[32] = "mapreg";
-static bool mapreg_dirty = false;
+static bool mapreg_dirty = false; // Whether there are modified regs to be saved
+
 #define MAPREG_AUTOSAVE_INTERVAL (300*1000)
 #define MAPREG_AUTOSAVE_INTERVAL (300*1000)
 
 
 
 
-/// Looks up the value of an integer variable using its uid.
-int mapreg_readreg(int uid)
+/**
+ * Looks up the value of an integer variable using its uid.
+ *
+ * @param uid: variable's unique identifier.
+ * @return: variable's integer value
+ */
+int mapreg_readreg(int64 uid)
 {
 {
-	return idb_iget(mapreg_db, uid);
+	struct mapreg_save *m = i64db_get(regs.vars, uid);
+	return m ? m->u.i : 0;
 }
 }
 
 
-/// Looks up the value of a string variable using its uid.
-char* mapreg_readregstr(int uid)
+/**
+ * Looks up the value of a string variable using its uid.
+ *
+ * @param uid: variable's unique identifier
+ * @return: variable's string value
+ */
+char* mapreg_readregstr(int64 uid)
 {
 {
-	return idb_get(mapregstr_db, uid);
+	struct mapreg_save *m = i64db_get(regs.vars, uid);
+	return m ? m->u.str : NULL;
 }
 }
 
 
-/// Modifies the value of an integer variable.
-bool mapreg_setreg(int uid, int val)
+/**
+ * Modifies the value of an integer variable.
+ *
+ * @param uid: variable's unique identifier
+ * @param val new value
+ * @return: true value was successfully set
+ */
+bool mapreg_setreg(int64 uid, int val)
 {
 {
-	int num = (uid & 0x00ffffff);
-	int i   = (uid & 0xff000000) >> 24;
+	struct mapreg_save *m;
+	int num = script_getvarid(uid);
+	unsigned int i = script_getvaridx(uid);
 	const char* name = get_str(num);
 	const char* name = get_str(num);
 
 
-	if( val != 0 )
-	{
-		if( idb_iput(mapreg_db,uid,val) )
-			mapreg_dirty = true; // already exists, delay write
-		else if(name[1] != '@')
-		{// write new variable to database
-			char tmp_str[32*2+1];
-			Sql_EscapeStringLen(mmysql_handle, tmp_str, name, strnlen(name, 32));
-			if( SQL_ERROR == Sql_Query(mmysql_handle, "INSERT INTO `%s`(`varname`,`index`,`value`) VALUES ('%s','%d','%d')", mapreg_table, tmp_str, i, val) )
-				Sql_ShowDebug(mmysql_handle);
+	if (val != 0) {
+		if ((m = i64db_get(regs.vars, uid))) {
+			m->u.i = val;
+			if (name[1] != '@') {
+				m->save = true;
+				mapreg_dirty = true;
+			}
+		} else {
+			if (i)
+				script_array_update(&regs, uid, false);
+
+			m = ers_alloc(mapreg_ers, struct mapreg_save);
+
+			m->u.i = val;
+			m->uid = uid;
+			m->save = false;
+			m->is_string = false;
+
+			if (name[1] != '@' && !skip_insert) {// write new variable to database
+				char tmp_str[32 * 2 + 1];
+				Sql_EscapeStringLen(mmysql_handle, tmp_str, name, strnlen(name, 32));
+				if (SQL_ERROR == Sql_Query(mmysql_handle, "INSERT INTO `%s`(`varname`,`index`,`value`) VALUES ('%s','%d','%d')", mapreg_table, tmp_str, i, val))
+					Sql_ShowDebug(mmysql_handle);
+			}
+			i64db_put(regs.vars, uid, m);
 		}
 		}
-	}
-	else // val == 0
-	{
-		idb_remove(mapreg_db,uid);
+	} else { // val == 0
+		if (i)
+			script_array_update(&regs, uid, true);
+		if ((m = i64db_get(regs.vars, uid))) {
+			ers_free(mapreg_ers, m);
+		}
+		i64db_remove(regs.vars, uid);
 
 
-		if( name[1] != '@' )
-		{// Remove from database because it is unused.
-			if( SQL_ERROR == Sql_Query(mmysql_handle, "DELETE FROM `%s` WHERE `varname`='%s' AND `index`='%d'", mapreg_table, name, i) )
+		if (name[1] != '@') {// Remove from database because it is unused.
+			if (SQL_ERROR == Sql_Query(mmysql_handle, "DELETE FROM `%s` WHERE `varname`='%s' AND `index`='%d'", mapreg_table, name, i))
 				Sql_ShowDebug(mmysql_handle);
 				Sql_ShowDebug(mmysql_handle);
 		}
 		}
 	}
 	}
@@ -66,40 +107,71 @@ bool mapreg_setreg(int uid, int val)
 	return true;
 	return true;
 }
 }
 
 
-/// Modifies the value of a string variable.
-bool mapreg_setregstr(int uid, const char* str)
+/**
+ * Modifies the value of a string variable.
+ *
+ * @param uid: variable's unique identifier
+ * @param str: new value
+ * @return: true value was successfully set
+ */
+bool mapreg_setregstr(int64 uid, const char* str)
 {
 {
-	int num = (uid & 0x00ffffff);
-	int i   = (uid & 0xff000000) >> 24;
+	struct mapreg_save *m;
+	int num = script_getvarid(uid);
+	unsigned int i = script_getvaridx(uid);
 	const char* name = get_str(num);
 	const char* name = get_str(num);
 
 
-	if( str == NULL || *str == 0 )
-	{
-		if(name[1] != '@') {
-			if( SQL_ERROR == Sql_Query(mmysql_handle, "DELETE FROM `%s` WHERE `varname`='%s' AND `index`='%d'", mapreg_table, name, i) )
+	if (str == NULL || *str == 0) {
+		if (i)
+			script_array_update(&regs, uid, true);
+		if (name[1] != '@') {
+			if (SQL_ERROR == Sql_Query(mmysql_handle, "DELETE FROM `%s` WHERE `varname`='%s' AND `index`='%d'", mapreg_table, name, i))
 				Sql_ShowDebug(mmysql_handle);
 				Sql_ShowDebug(mmysql_handle);
 		}
 		}
-		idb_remove(mapregstr_db,uid);
-	}
-	else
-	{
-		if (idb_put(mapregstr_db,uid, aStrdup(str)))
-			mapreg_dirty = true;
-		else if(name[1] != '@') { //put returned null, so we must insert.
-			// Someone is causing a database size infinite increase here without name[1] != '@' [Lance]
-			char tmp_str[32*2+1];
-			char tmp_str2[255*2+1];
-			Sql_EscapeStringLen(mmysql_handle, tmp_str, name, strnlen(name, 32));
-			Sql_EscapeStringLen(mmysql_handle, tmp_str2, str, strnlen(str, 255));
-			if( SQL_ERROR == Sql_Query(mmysql_handle, "INSERT INTO `%s`(`varname`,`index`,`value`) VALUES ('%s','%d','%s')", mapreg_table, tmp_str, i, tmp_str2) )
-				Sql_ShowDebug(mmysql_handle);
+		if ((m = i64db_get(regs.vars, uid))) {
+			if (m->u.str != NULL)
+				aFree(m->u.str);
+			ers_free(mapreg_ers, m);
+		}
+		i64db_remove(regs.vars, uid);
+	} else {
+		if ((m = i64db_get(regs.vars, uid))) {
+			if (m->u.str != NULL)
+				aFree(m->u.str);
+			m->u.str = aStrdup(str);
+			if (name[1] != '@') {
+				mapreg_dirty = true;
+				m->save = true;
+			}
+		} else {
+			if (i)
+				script_array_update(&regs, uid, false);
+
+			m = ers_alloc(mapreg_ers, struct mapreg_save);
+
+			m->uid = uid;
+			m->u.str = aStrdup(str);
+			m->save = false;
+			m->is_string = true;
+
+			if (name[1] != '@' && !skip_insert) { //put returned null, so we must insert.
+				char tmp_str[32 * 2 + 1];
+				char tmp_str2[255 * 2 + 1];
+				Sql_EscapeStringLen(mmysql_handle, tmp_str, name, strnlen(name, 32));
+				Sql_EscapeStringLen(mmysql_handle, tmp_str2, str, strnlen(str, 255));
+				if (SQL_ERROR == Sql_Query(mmysql_handle, "INSERT INTO `%s`(`varname`,`index`,`value`) VALUES ('%s','%d','%s')", mapreg_table, tmp_str, i, tmp_str2))
+					Sql_ShowDebug(mmysql_handle);
+			}
+			i64db_put(regs.vars, uid, m);
 		}
 		}
 	}
 	}
 
 
 	return true;
 	return true;
 }
 }
 
 
-/// Loads permanent variables from database
+/**
+ * Loads permanent variables from database.
+ */
 static void script_load_mapreg(void)
 static void script_load_mapreg(void)
 {
 {
 	/*
 	/*
@@ -122,100 +194,144 @@ static void script_load_mapreg(void)
 		return;
 		return;
 	}
 	}
 
 
+	skip_insert = true;
+
 	SqlStmt_BindColumn(stmt, 0, SQLDT_STRING, &varname[0], sizeof(varname), &length, NULL);
 	SqlStmt_BindColumn(stmt, 0, SQLDT_STRING, &varname[0], sizeof(varname), &length, NULL);
 	SqlStmt_BindColumn(stmt, 1, SQLDT_INT, &index, 0, NULL, NULL);
 	SqlStmt_BindColumn(stmt, 1, SQLDT_INT, &index, 0, NULL, NULL);
 	SqlStmt_BindColumn(stmt, 2, SQLDT_STRING, &value[0], sizeof(value), NULL, NULL);
 	SqlStmt_BindColumn(stmt, 2, SQLDT_STRING, &value[0], sizeof(value), NULL, NULL);
-	
-	while ( SQL_SUCCESS == SqlStmt_NextRow(stmt) )
-	{
+
+	while ( SQL_SUCCESS == SqlStmt_NextRow(stmt) ) {
 		int s = add_str(varname);
 		int s = add_str(varname);
 		int i = index;
 		int i = index;
 
 
-		if( varname[length-1] == '$' )
-			idb_put(mapregstr_db, (i<<24)|s, aStrdup(value));
-		else
-			idb_iput(mapreg_db, (i<<24)|s, atoi(value));
+		if( varname[length-1] == '$' ) {
+			if( i64db_exists(regs.vars, reference_uid(s, i)) ) {
+				ShowWarning("load_mapreg: Duplicate! '%s' => '%s' skipping...\n",varname,value);
+				continue;
+			}
+
+			mapreg_setregstr(reference_uid(s, i), value);
+		} else {
+			if( i64db_exists(regs.vars, reference_uid(s, i)) ) {
+				ShowWarning("load_mapreg: Duplicate! '%s' => '%s' skipping...\n",varname,value);
+				continue;
+			}
+
+			mapreg_setreg(reference_uid(s, i), atoi(value));
+		}
 	}
 	}
+
 	SqlStmt_Free(stmt);
 	SqlStmt_Free(stmt);
 
 
+	skip_insert = false;
 	mapreg_dirty = false;
 	mapreg_dirty = false;
 }
 }
 
 
-/// Saves permanent variables to database
+/**
+ * Saves permanent variables to database.
+ */
 static void script_save_mapreg(void)
 static void script_save_mapreg(void)
 {
 {
-	DBIterator* iter;
-	DBData *data;
-	DBKey key;
-
-	iter = db_iterator(mapreg_db);
-	for( data = iter->first(iter,&key); iter->exists(iter); data = iter->next(iter,&key) )
-	{
-		int num = (key.i & 0x00ffffff);
-		int i   = (key.i & 0xff000000) >> 24;
-		const char* name = get_str(num);
-
-		if( name[1] == '@' )
-			continue;
-
-		if( SQL_ERROR == Sql_Query(mmysql_handle, "UPDATE `%s` SET `value`='%d' WHERE `varname`='%s' AND `index`='%d'", mapreg_table, db_data2i(data), name, i) )
-			Sql_ShowDebug(mmysql_handle);
-	}
-	dbi_destroy(iter);
-
-	iter = db_iterator(mapregstr_db);
-	for( data = iter->first(iter,&key); iter->exists(iter); data = iter->next(iter,&key) )
-	{
-		int num = (key.i & 0x00ffffff);
-		int i   = (key.i & 0xff000000) >> 24;
-		const char* name = get_str(num);
-		char tmp_str2[2*255+1];
-
-		if( name[1] == '@' )
-			continue;
-
-		Sql_EscapeStringLen(mmysql_handle, tmp_str2, db_data2ptr(data), safestrnlen(db_data2ptr(data), 255));
-		if( SQL_ERROR == Sql_Query(mmysql_handle, "UPDATE `%s` SET `value`='%s' WHERE `varname`='%s' AND `index`='%d'", mapreg_table, tmp_str2, name, i) )
-			Sql_ShowDebug(mmysql_handle);
+	if (mapreg_dirty) {
+		DBIterator *iter = db_iterator(regs.vars);
+		struct mapreg_save *m;
+		for (m = dbi_first(iter); dbi_exists(iter); m = dbi_next(iter)) {
+			if (m->save) {
+				int num = script_getvarid(m->uid);
+				int i = script_getvaridx(m->uid);
+				const char* name = get_str(num);
+				if (!m->is_string) {
+					if (SQL_ERROR == Sql_Query(mmysql_handle, "UPDATE `%s` SET `value`='%d' WHERE `varname`='%s' AND `index`='%d' LIMIT 1", mapreg_table, m->u.i, name, i))
+						Sql_ShowDebug(mmysql_handle);
+				} else {
+					char tmp_str2[2 * 255 + 1];
+					Sql_EscapeStringLen(mmysql_handle, tmp_str2, m->u.str, safestrnlen(m->u.str, 255));
+					if (SQL_ERROR == Sql_Query(mmysql_handle, "UPDATE `%s` SET `value`='%s' WHERE `varname`='%s' AND `index`='%d' LIMIT 1", mapreg_table, tmp_str2, name, i))
+						Sql_ShowDebug(mmysql_handle);
+				}
+				m->save = false;
+			}
+		}
+		dbi_destroy(iter);
+		mapreg_dirty = false;
 	}
 	}
-	dbi_destroy(iter);
-
-	mapreg_dirty = false;
 }
 }
 
 
+/**
+ * Timer event to auto-save permanent variables.
+ */
 static int script_autosave_mapreg(int tid, unsigned int tick, int id, intptr_t data)
 static int script_autosave_mapreg(int tid, unsigned int tick, int id, intptr_t data)
 {
 {
-	if( mapreg_dirty )
-		script_save_mapreg();
-
+	script_save_mapreg();
 	return 0;
 	return 0;
 }
 }
 
 
+/**
+ * Destroys a mapreg_save structure, freeing the contained string, if any.
+ *
+ * @see DBApply
+ */
+int mapreg_destroyreg(DBKey key, DBData *data, va_list ap)
+{
+	struct mapreg_save *m = NULL;
+
+	if (data->type != DB_DATA_PTR) // Sanity check
+		return 0;
+
+	m = db_data2ptr(data);
+
+	if (m->is_string) {
+		if (m->u.str)
+			aFree(m->u.str);
+	}
+	ers_free(mapreg_ers, m);
+
+	return 0;
+}
 
 
+/**
+ * Reloads mapregs, saving to database beforehand.
+ *
+ * This has the effect of clearing the temporary variables, and
+ * reloading the permanent ones.
+ */
 void mapreg_reload(void)
 void mapreg_reload(void)
 {
 {
-	if( mapreg_dirty )
-		script_save_mapreg();
+	script_save_mapreg();
 
 
-	db_clear(mapreg_db);
-	db_clear(mapregstr_db);
+	regs.vars->clear(regs.vars, mapreg_destroyreg);
+
+	if (regs.arrays)
+		regs.arrays->destroy(regs.arrays, script_free_array_db);
 
 
 	script_load_mapreg();
 	script_load_mapreg();
 }
 }
 
 
+/**
+ * Finalizer.
+ */
 void mapreg_final(void)
 void mapreg_final(void)
 {
 {
-	if( mapreg_dirty )
-		script_save_mapreg();
+	script_save_mapreg();
+
+	regs.vars->clear(regs.vars, mapreg_destroyreg);
 
 
-	db_destroy(mapreg_db);
-	db_destroy(mapregstr_db);
+	ers_destroy(mapreg_ers);
+
+	if (regs.arrays)
+		regs.arrays->destroy(regs.arrays, script_free_array_db);
 }
 }
 
 
+/**
+ * Initializer.
+ */
 void mapreg_init(void)
 void mapreg_init(void)
 {
 {
-	mapreg_db = idb_alloc(DB_OPT_BASE);
-	mapregstr_db = idb_alloc(DB_OPT_RELEASE_DATA);
+	regs.vars = i64db_alloc(DB_OPT_BASE);
+	mapreg_ers = ers_new(sizeof(struct mapreg_save), "mapreg.c:mapreg_ers", ERS_OPT_NONE);
+
+	skip_insert = false;
+	regs.arrays = NULL;
 
 
 	script_load_mapreg();
 	script_load_mapreg();
 
 
@@ -223,9 +339,12 @@ void mapreg_init(void)
 	add_timer_interval(gettick() + MAPREG_AUTOSAVE_INTERVAL, script_autosave_mapreg, 0, 0, MAPREG_AUTOSAVE_INTERVAL);
 	add_timer_interval(gettick() + MAPREG_AUTOSAVE_INTERVAL, script_autosave_mapreg, 0, 0, MAPREG_AUTOSAVE_INTERVAL);
 }
 }
 
 
+/**
+ * Loads the mapreg configuration file.
+ */
 bool mapreg_config_read(const char* w1, const char* w2)
 bool mapreg_config_read(const char* w1, const char* w2)
 {
 {
-	if(!strcmpi(w1, "mapreg_db"))
+	if(!strcmpi(w1, "mapreg_table"))
 		safestrncpy(mapreg_table, w2, sizeof(mapreg_table));
 		safestrncpy(mapreg_table, w2, sizeof(mapreg_table));
 	else
 	else
 		return false;
 		return false;

+ 20 - 4
src/map/mapreg.h

@@ -4,14 +4,30 @@
 #ifndef _MAPREG_H_
 #ifndef _MAPREG_H_
 #define _MAPREG_H_
 #define _MAPREG_H_
 
 
+#include "script.h"
+
+struct mapreg_save {
+	int64 uid;         ///< Unique ID
+	union {
+		int i;         ///< Numeric value
+		char *str;     ///< String value
+	} u;
+	bool is_string;    ///< true if it's a string, false if it's a number
+	bool save;         ///< Whether a save operation is pending
+};
+
+struct reg_db regs;
+extern bool skip_insert;
+
 void mapreg_reload(void);
 void mapreg_reload(void);
 void mapreg_final(void);
 void mapreg_final(void);
 void mapreg_init(void);
 void mapreg_init(void);
 bool mapreg_config_read(const char* w1, const char* w2);
 bool mapreg_config_read(const char* w1, const char* w2);
 
 
-int mapreg_readreg(int uid);
-char* mapreg_readregstr(int uid);
-bool mapreg_setreg(int uid, int val);
-bool mapreg_setregstr(int uid, const char* str);
+int mapreg_readreg(int64 uid);
+char* mapreg_readregstr(int64 uid);
+bool mapreg_setreg(int64 uid, int val);
+bool mapreg_setregstr(int64 uid, const char* str);
+int mapreg_destroyreg(DBKey key, DBData *data, va_list ap);
 
 
 #endif /* _MAPREG_H_ */
 #endif /* _MAPREG_H_ */

+ 2 - 2
src/map/mob.c

@@ -2662,11 +2662,11 @@ int mob_dead(struct mob_data *md, struct block_list *src, int type)
 				{
 				{
 					pc_addfame(sd, battle_config.fame_taekwon_mission);
 					pc_addfame(sd, battle_config.fame_taekwon_mission);
 					sd->mission_mobid = temp;
 					sd->mission_mobid = temp;
-					pc_setglobalreg(sd,"TK_MISSION_ID", temp);
+					pc_setglobalreg(sd, add_str("TK_MISSION_ID"), temp);
 					sd->mission_count = 0;
 					sd->mission_count = 0;
 					clif_mission_info(sd, temp, 0);
 					clif_mission_info(sd, temp, 0);
 				}
 				}
-				pc_setglobalreg(sd,"TK_MISSION_COUNT", sd->mission_count);
+				pc_setglobalreg(sd, add_str("TK_MISSION_COUNT"), sd->mission_count);
 			}
 			}
 
 
 			if( sd->status.party_id )
 			if( sd->status.party_id )

+ 4 - 5
src/map/npc.c

@@ -3523,8 +3523,7 @@ static const char* npc_parse_function(char* w1, char* w2, char* w3, char* w4, co
 
 
 	script_start = strstr(start,"\t{");
 	script_start = strstr(start,"\t{");
 	end = strchr(start,'\n');
 	end = strchr(start,'\n');
-	if( *w4 != '{' || script_start == NULL || (end != NULL && script_start > end) )
-	{
+	if (*w4 != '{' || script_start == NULL || (end != NULL && script_start > end)) {
 		ShowError("npc_parse_function: Missing left curly '%%TAB%%{' in file '%s', line '%d'. Skipping the rest of the file.\n * w1=%s\n * w2=%s\n * w3=%s\n * w4=%s\n", filepath, strline(buffer,start-buffer), w1, w2, w3, w4);
 		ShowError("npc_parse_function: Missing left curly '%%TAB%%{' in file '%s', line '%d'. Skipping the rest of the file.\n * w1=%s\n * w2=%s\n * w3=%s\n * w4=%s\n", filepath, strline(buffer,start-buffer), w1, w2, w3, w4);
 		return NULL;// can't continue
 		return NULL;// can't continue
 	}
 	}
@@ -3539,11 +3538,11 @@ static const char* npc_parse_function(char* w1, char* w2, char* w3, char* w4, co
 		return end;
 		return end;
 
 
 	func_db = script_get_userfunc_db();
 	func_db = script_get_userfunc_db();
-	if (func_db->put(func_db, db_str2key(w3), db_ptr2data(script), &old_data))
-	{
+	if (func_db->put(func_db, db_str2key(w3), db_ptr2data(script), &old_data)) {
 		struct script_code *oldscript = (struct script_code*)db_data2ptr(&old_data);
 		struct script_code *oldscript = (struct script_code*)db_data2ptr(&old_data);
+
 		ShowInfo("npc_parse_function: Overwriting user function [%s] (%s:%d)\n", w3, filepath, strline(buffer,start-buffer));
 		ShowInfo("npc_parse_function: Overwriting user function [%s] (%s:%d)\n", w3, filepath, strline(buffer,start-buffer));
-		script_free_vars(oldscript->script_vars);
+		script_free_vars(oldscript->local.vars);
 		aFree(oldscript->script_buf);
 		aFree(oldscript->script_buf);
 		aFree(oldscript);
 		aFree(oldscript);
 	}
 	}

+ 248 - 301
src/map/pc.c

@@ -1193,6 +1193,12 @@ bool pc_authok(struct map_session_data *sd, uint32 login_id2, time_t expiration_
 	sd->save_quest = false;
 	sd->save_quest = false;
 	sd->count_rewarp = 0;
 	sd->count_rewarp = 0;
 
 
+	sd->regs.vars = i64db_alloc(DB_OPT_BASE);
+	sd->regs.arrays = NULL;
+	sd->vars_dirty = false;
+	sd->vars_ok = false;
+	sd->vars_received = 0x0;
+
 	//warp player
 	//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) {
 	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);
 		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);
@@ -1309,7 +1315,7 @@ bool pc_set_hate_mob(struct map_session_data *sd, int pos, struct block_list *bl
 			return false; //Wrong size
 			return false; //Wrong size
 	}
 	}
 	sd->hate_mob[pos] = class_;
 	sd->hate_mob[pos] = class_;
-	pc_setglobalreg(sd,sg_info[pos].hate_var,class_+1);
+	pc_setglobalreg(sd, add_str(sg_info[pos].hate_var), class_+1);
 	clif_hate_info(sd, pos, class_, 1);
 	clif_hate_info(sd, pos, class_, 1);
 	return true;
 	return true;
 }
 }
@@ -1321,24 +1327,27 @@ void pc_reg_received(struct map_session_data *sd)
 {
 {
 	uint8 i;
 	uint8 i;
 
 
-	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, add_str("jobchange_level"));
+	sd->change_level_3rd = pc_readglobalreg(sd, add_str("jobchange_level_3rd"));
+	sd->die_counter = pc_readglobalreg(sd, add_str("PC_DIE_COUNTER"));
 
 
-	sd->langtype = pc_readaccountreg(sd,"#langtype");
-	if(msg_checklangtype(sd->langtype,true)<0) sd->langtype=0; //invalid langtype reset to default
+	sd->langtype = pc_readaccountreg(sd, add_str("#langtype"));
+	if (msg_checklangtype(sd->langtype,true) < 0)
+		sd->langtype = 0; //invalid langtype reset to default
 
 
 	// Cash shop
 	// Cash shop
-	sd->cashPoints = pc_readaccountreg(sd,"#CASHPOINTS");
-	sd->kafraPoints = pc_readaccountreg(sd,"#KAFRAPOINTS");
+	sd->cashPoints = pc_readaccountreg(sd, add_str("#CASHPOINTS"));
+	sd->kafraPoints = pc_readaccountreg(sd, add_str("#KAFRAPOINTS"));
 
 
 	// Cooking Exp
 	// Cooking Exp
-	sd->cook_mastery = pc_readglobalreg(sd,"COOK_MASTERY");
+	sd->cook_mastery = pc_readglobalreg(sd, add_str("COOK_MASTERY"));
 
 
 	if( (sd->class_&MAPID_BASEMASK) == MAPID_TAEKWON )
 	if( (sd->class_&MAPID_BASEMASK) == MAPID_TAEKWON )
 	{ // Better check for class rather than skill to prevent "skill resets" from unsetting this
 	{ // 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, add_str("TK_MISSION_ID"));
+		sd->mission_count = pc_readglobalreg(sd, add_str("TK_MISSION_COUNT"));
 	}
 	}
 
 
 	if (battle_config.feature_banking)
 	if (battle_config.feature_banking)
@@ -1348,31 +1357,31 @@ void pc_reg_received(struct map_session_data *sd)
 	for(i=0;i<MAX_PC_FEELHATE;i++) { //for now - someone need to make reading from txt/sql
 	for(i=0;i<MAX_PC_FEELHATE;i++) { //for now - someone need to make reading from txt/sql
 		uint16 j;
 		uint16 j;
 
 
-		if ((j = pc_readglobalreg(sd,sg_info[i].feel_var))!=0) {
+		if ((j = pc_readglobalreg(sd, add_str(sg_info[i].feel_var))) != 0) {
 			sd->feel_map[i].index = j;
 			sd->feel_map[i].index = j;
 			sd->feel_map[i].m = map_mapindex2mapid(j);
 			sd->feel_map[i].m = map_mapindex2mapid(j);
 		} else {
 		} else {
 			sd->feel_map[i].index = 0;
 			sd->feel_map[i].index = 0;
 			sd->feel_map[i].m = -1;
 			sd->feel_map[i].m = -1;
 		}
 		}
-		sd->hate_mob[i] = pc_readglobalreg(sd,sg_info[i].hate_var)-1;
+		sd->hate_mob[i] = pc_readglobalreg(sd, add_str(sg_info[i].hate_var))-1;
 	}
 	}
 
 
 	if ((i = pc_checkskill(sd,RG_PLAGIARISM)) > 0) {
 	if ((i = pc_checkskill(sd,RG_PLAGIARISM)) > 0) {
-		sd->cloneskill_idx = skill_get_index(pc_readglobalreg(sd,SKILL_VAR_PLAGIARISM));
+		sd->cloneskill_idx = skill_get_index(pc_readglobalreg(sd, add_str(SKILL_VAR_PLAGIARISM)));
 		if (sd->cloneskill_idx > 0) {
 		if (sd->cloneskill_idx > 0) {
-			sd->status.skill[sd->cloneskill_idx].id = pc_readglobalreg(sd,SKILL_VAR_PLAGIARISM);
-			sd->status.skill[sd->cloneskill_idx].lv = pc_readglobalreg(sd,SKILL_VAR_PLAGIARISM_LV);
+			sd->status.skill[sd->cloneskill_idx].id = pc_readglobalreg(sd, add_str(SKILL_VAR_PLAGIARISM));
+			sd->status.skill[sd->cloneskill_idx].lv = pc_readglobalreg(sd, add_str(SKILL_VAR_PLAGIARISM_LV));
 			if (sd->status.skill[sd->cloneskill_idx].lv > i)
 			if (sd->status.skill[sd->cloneskill_idx].lv > i)
 				sd->status.skill[sd->cloneskill_idx].lv = i;
 				sd->status.skill[sd->cloneskill_idx].lv = i;
 			sd->status.skill[sd->cloneskill_idx].flag = SKILL_FLAG_PLAGIARIZED;
 			sd->status.skill[sd->cloneskill_idx].flag = SKILL_FLAG_PLAGIARIZED;
 		}
 		}
 	}
 	}
 	if ((i = pc_checkskill(sd,SC_REPRODUCE)) > 0) {
 	if ((i = pc_checkskill(sd,SC_REPRODUCE)) > 0) {
-		sd->reproduceskill_idx = skill_get_index(pc_readglobalreg(sd,SKILL_VAR_REPRODUCE));
+		sd->reproduceskill_idx = skill_get_index(pc_readglobalreg(sd, add_str(SKILL_VAR_REPRODUCE)));
 		if (sd->reproduceskill_idx > 0) {
 		if (sd->reproduceskill_idx > 0) {
-			sd->status.skill[sd->reproduceskill_idx].id = pc_readglobalreg(sd,SKILL_VAR_REPRODUCE);
-			sd->status.skill[sd->reproduceskill_idx].lv = pc_readglobalreg(sd,SKILL_VAR_REPRODUCE_LV);
+			sd->status.skill[sd->reproduceskill_idx].id = pc_readglobalreg(sd, add_str(SKILL_VAR_REPRODUCE));
+			sd->status.skill[sd->reproduceskill_idx].lv = pc_readglobalreg(sd, add_str(SKILL_VAR_REPRODUCE_LV));
 			if (i < sd->status.skill[sd->reproduceskill_idx].lv)
 			if (i < sd->status.skill[sd->reproduceskill_idx].lv)
 				sd->status.skill[sd->reproduceskill_idx].lv = i;
 				sd->status.skill[sd->reproduceskill_idx].lv = i;
 			sd->status.skill[sd->reproduceskill_idx].flag = SKILL_FLAG_PLAGIARIZED;
 			sd->status.skill[sd->reproduceskill_idx].flag = SKILL_FLAG_PLAGIARIZED;
@@ -1842,7 +1851,7 @@ int pc_calc_skilltree_normalize_job(struct map_session_data *sd)
 
 
 			}
 			}
 
 
-			pc_setglobalreg (sd, "jobchange_level", sd->change_level_2nd);
+			pc_setglobalreg(sd, add_str("jobchange_level"), sd->change_level_2nd);
 		}
 		}
 
 
 		if (skill_point < novice_skills + (sd->change_level_2nd - 1))
 		if (skill_point < novice_skills + (sd->change_level_2nd - 1))
@@ -1859,7 +1868,7 @@ int pc_calc_skilltree_normalize_job(struct map_session_data *sd)
 						- (sd->status.job_level - 1)
 						- (sd->status.job_level - 1)
 						- (sd->change_level_2nd - 1)
 						- (sd->change_level_2nd - 1)
 						- novice_skills;
 						- novice_skills;
-					pc_setglobalreg (sd, "jobchange_level_3rd", sd->change_level_3rd);
+					pc_setglobalreg(sd, add_str("jobchange_level_3rd"), sd->change_level_3rd);
 			}
 			}
 
 
 			if (skill_point < novice_skills + (sd->change_level_2nd - 1) + (sd->change_level_3rd - 1))
 			if (skill_point < novice_skills + (sd->change_level_2nd - 1) + (sd->change_level_3rd - 1))
@@ -4173,11 +4182,11 @@ int pc_paycash(struct map_session_data *sd, int price, int points, e_log_pick_ty
 		return -1;
 		return -1;
 	}
 	}
 
 
-	pc_setaccountreg(sd, "#CASHPOINTS", sd->cashPoints-cash);
+	pc_setaccountreg(sd, add_str("#CASHPOINTS"), sd->cashPoints-cash);
 	if( cash ){
 	if( cash ){
 		log_cash( sd, type, LOG_CASH_TYPE_CASH, -cash );
 		log_cash( sd, type, LOG_CASH_TYPE_CASH, -cash );
 	}
 	}
-	pc_setaccountreg(sd, "#KAFRAPOINTS", sd->kafraPoints-points);
+	pc_setaccountreg(sd, add_str("#KAFRAPOINTS"), sd->kafraPoints-points);
 	if( points ){
 	if( points ){
 		log_cash( sd, type, LOG_CASH_TYPE_KAFRA, -points );
 		log_cash( sd, type, LOG_CASH_TYPE_KAFRA, -points );
 	}
 	}
@@ -4205,7 +4214,7 @@ int pc_getcash( struct map_session_data *sd, int cash, int points, e_log_pick_ty
 			cash = MAX_ZENY-sd->cashPoints;
 			cash = MAX_ZENY-sd->cashPoints;
 		}
 		}
 
 
-		pc_setaccountreg(sd, "#CASHPOINTS", sd->cashPoints+cash);
+		pc_setaccountreg(sd, add_str("#CASHPOINTS"), sd->cashPoints+cash);
 		if( cash ){
 		if( cash ){
 			log_cash( sd, type, LOG_CASH_TYPE_CASH, cash );
 			log_cash( sd, type, LOG_CASH_TYPE_CASH, cash );
 		}
 		}
@@ -4231,7 +4240,7 @@ int pc_getcash( struct map_session_data *sd, int cash, int points, e_log_pick_ty
 			points = MAX_ZENY-sd->kafraPoints;
 			points = MAX_ZENY-sd->kafraPoints;
 		}
 		}
 
 
-		pc_setaccountreg(sd, "#KAFRAPOINTS", sd->kafraPoints+points);
+		pc_setaccountreg(sd, add_str("#KAFRAPOINTS"), sd->kafraPoints+points);
 		if( points ){
 		if( points ){
 			log_cash( sd, type, LOG_CASH_TYPE_KAFRA, points );
 			log_cash( sd, type, LOG_CASH_TYPE_KAFRA, points );
 		}
 		}
@@ -6940,7 +6949,7 @@ int pc_resetstate(struct map_session_data* sd)
 	if( sd->mission_mobid ) { //bugreport:2200
 	if( sd->mission_mobid ) { //bugreport:2200
 		sd->mission_mobid = 0;
 		sd->mission_mobid = 0;
 		sd->mission_count = 0;
 		sd->mission_count = 0;
-		pc_setglobalreg(sd,"TK_MISSION_ID", 0);
+		pc_setglobalreg(sd, add_str("TK_MISSION_ID"), 0);
 	}
 	}
 
 
 	status_calc_pc(sd, SCO_NONE);
 	status_calc_pc(sd, SCO_NONE);
@@ -7076,7 +7085,7 @@ int pc_resetfeel(struct map_session_data* sd)
 	{
 	{
 		sd->feel_map[i].m = -1;
 		sd->feel_map[i].m = -1;
 		sd->feel_map[i].index = 0;
 		sd->feel_map[i].index = 0;
-		pc_setglobalreg(sd,sg_info[i].feel_var,0);
+		pc_setglobalreg(sd, add_str(sg_info[i].feel_var), 0);
 	}
 	}
 
 
 	return 0;
 	return 0;
@@ -7090,7 +7099,7 @@ int pc_resethate(struct map_session_data* sd)
 	for (i=0; i<3; i++)
 	for (i=0; i<3; i++)
 	{
 	{
 		sd->hate_mob[i] = -1;
 		sd->hate_mob[i] = -1;
-		pc_setglobalreg(sd,sg_info[i].hate_var,0);
+		pc_setglobalreg(sd, add_str(sg_info[i].hate_var), 0);
 	}
 	}
 	return 0;
 	return 0;
 }
 }
@@ -7331,7 +7340,7 @@ int pc_dead(struct map_session_data *sd,struct block_list *src)
 
 
 	pc_setdead(sd);
 	pc_setdead(sd);
 
 
-	pc_setglobalreg(sd,"PC_DIE_COUNTER",sd->die_counter+1);
+	pc_setglobalreg(sd, add_str("PC_DIE_COUNTER"), sd->die_counter+1);
 	pc_setparam(sd, SP_KILLERRID, src?src->id:0);
 	pc_setparam(sd, SP_KILLERRID, src?src->id:0);
 
 
 	//Reset menu skills/item skills
 	//Reset menu skills/item skills
@@ -8090,12 +8099,12 @@ bool pc_jobchange(struct map_session_data *sd,int job, char upper)
 	// changing from 1st to 2nd job
 	// changing from 1st to 2nd job
 	if ((b_class&JOBL_2) && !(sd->class_&JOBL_2) && (sd->class_&MAPID_UPPERMASK) != MAPID_SUPER_NOVICE) {
 	if ((b_class&JOBL_2) && !(sd->class_&JOBL_2) && (sd->class_&MAPID_UPPERMASK) != MAPID_SUPER_NOVICE) {
 		sd->change_level_2nd = sd->status.job_level;
 		sd->change_level_2nd = sd->status.job_level;
-		pc_setglobalreg (sd, "jobchange_level", sd->change_level_2nd);
+		pc_setglobalreg(sd, add_str("jobchange_level"), sd->change_level_2nd);
 	}
 	}
 	// changing from 2nd to 3rd job
 	// changing from 2nd to 3rd job
 	else if((b_class&JOBL_THIRD) && !(sd->class_&JOBL_THIRD)) {
 	else if((b_class&JOBL_THIRD) && !(sd->class_&JOBL_THIRD)) {
 		sd->change_level_3rd = sd->status.job_level;
 		sd->change_level_3rd = sd->status.job_level;
-		pc_setglobalreg (sd, "jobchange_level_3rd", sd->change_level_3rd);
+		pc_setglobalreg(sd, add_str("jobchange_level_3rd"), sd->change_level_3rd);
 	}
 	}
 
 
 	if(sd->cloneskill_idx > 0) {
 	if(sd->cloneskill_idx > 0) {
@@ -8103,11 +8112,11 @@ bool pc_jobchange(struct map_session_data *sd,int job, char upper)
 			sd->status.skill[sd->cloneskill_idx].id = 0;
 			sd->status.skill[sd->cloneskill_idx].id = 0;
 			sd->status.skill[sd->cloneskill_idx].lv = 0;
 			sd->status.skill[sd->cloneskill_idx].lv = 0;
 			sd->status.skill[sd->cloneskill_idx].flag = SKILL_FLAG_PERMANENT;
 			sd->status.skill[sd->cloneskill_idx].flag = SKILL_FLAG_PERMANENT;
-			clif_deleteskill(sd,pc_readglobalreg(sd,SKILL_VAR_PLAGIARISM));
+			clif_deleteskill(sd,pc_readglobalreg(sd, add_str(SKILL_VAR_PLAGIARISM)));
 		}
 		}
 		sd->cloneskill_idx = 0;
 		sd->cloneskill_idx = 0;
-		pc_setglobalreg(sd,SKILL_VAR_PLAGIARISM, 0);
-		pc_setglobalreg(sd,SKILL_VAR_PLAGIARISM_LV, 0);
+		pc_setglobalreg(sd, add_str(SKILL_VAR_PLAGIARISM), 0);
+		pc_setglobalreg(sd, add_str(SKILL_VAR_PLAGIARISM_LV), 0);
 	}
 	}
 
 
 	if(sd->reproduceskill_idx > 0) {
 	if(sd->reproduceskill_idx > 0) {
@@ -8115,11 +8124,11 @@ bool pc_jobchange(struct map_session_data *sd,int job, char upper)
 			sd->status.skill[sd->reproduceskill_idx].id = 0;
 			sd->status.skill[sd->reproduceskill_idx].id = 0;
 			sd->status.skill[sd->reproduceskill_idx].lv = 0;
 			sd->status.skill[sd->reproduceskill_idx].lv = 0;
 			sd->status.skill[sd->reproduceskill_idx].flag = SKILL_FLAG_PERMANENT;
 			sd->status.skill[sd->reproduceskill_idx].flag = SKILL_FLAG_PERMANENT;
-			clif_deleteskill(sd,pc_readglobalreg(sd,SKILL_VAR_REPRODUCE));
+			clif_deleteskill(sd,pc_readglobalreg(sd, add_str(SKILL_VAR_REPRODUCE)));
 		}
 		}
 		sd->reproduceskill_idx = 0;
 		sd->reproduceskill_idx = 0;
-		pc_setglobalreg(sd,SKILL_VAR_REPRODUCE,0);
-		pc_setglobalreg(sd,SKILL_VAR_REPRODUCE_LV,0);
+		pc_setglobalreg(sd, add_str(SKILL_VAR_REPRODUCE), 0);
+		pc_setglobalreg(sd, add_str(SKILL_VAR_REPRODUCE_LV), 0);
 	}
 	}
 
 
 	// Give or reduce transcendent status points
 	// Give or reduce transcendent status points
@@ -8554,332 +8563,266 @@ bool pc_can_attack( struct map_session_data *sd, int target_id ) {
 }
 }
 
 
 /*==========================================
 /*==========================================
- * Read ram register for player sd
- * get val (int) from reg for player sd
+ * Read '@type' variables (temporary numeric char reg)
  *------------------------------------------*/
  *------------------------------------------*/
-int pc_readreg(struct map_session_data* sd, int reg)
+int pc_readreg(struct map_session_data* sd, int64 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;
+	return i64db_iget(sd->regs.vars, reg);
 }
 }
+
 /*==========================================
 /*==========================================
- * Set ram register for player sd
- * memo val(int) at reg for player sd
+ * Set '@type' variables (temporary numeric char reg)
  *------------------------------------------*/
  *------------------------------------------*/
-bool pc_setreg(struct map_session_data* sd, int reg, int val)
+bool pc_setreg(struct map_session_data* sd, int64 reg, int val)
 {
 {
-	int i;
-
-	nullpo_retr(false,sd);
+	unsigned int index = script_getvaridx(reg);
 
 
-	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 true;
-	}
+	nullpo_retr(false, sd);
 
 
-	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);
+	if( val ) {
+		i64db_iput(sd->regs.vars, reg, val);
+		if( index )
+			script_array_update(&sd->regs, reg, false);
+	} else {
+		i64db_remove(sd->regs.vars, reg);
+		if( index )
+			script_array_update(&sd->regs, reg, true);
 	}
 	}
-	sd->reg[i].index = reg;
-	sd->reg[i].data = val;
 
 
 	return true;
 	return true;
 }
 }
 
 
 /*==========================================
 /*==========================================
- * Read ram register for player sd
- * get val (str) from reg for player sd
+ * Read '@type$' variables (temporary string char reg)
  *------------------------------------------*/
  *------------------------------------------*/
-char* pc_readregstr(struct map_session_data* sd, int reg)
+char* pc_readregstr(struct map_session_data* sd, int64 reg)
 {
 {
-	int i;
+	struct script_reg_str *p = NULL;
 
 
-	nullpo_ret(sd);
+	p = i64db_get(sd->regs.vars, reg);
 
 
-	ARR_FIND( 0, sd->regstr_num, i,  sd->regstr[i].index == reg );
-	return ( i < sd->regstr_num ) ? sd->regstr[i].data : NULL;
+	return p ? p->value : NULL;
 }
 }
+
 /*==========================================
 /*==========================================
- * Set ram register for player sd
- * memo val(str) at reg for player sd
+ * Set '@type$' variables (temporary string char reg)
  *------------------------------------------*/
  *------------------------------------------*/
-bool pc_setregstr(struct map_session_data* sd, int reg, const char* str)
+bool pc_setregstr(struct map_session_data* sd, int64 reg, const char* str)
 {
 {
-	int i;
+	struct script_reg_str *p = NULL;
+	unsigned int index = script_getvaridx(reg);
+	DBData prev;
 
 
-	nullpo_retr(false,sd);
+	nullpo_retr(false, sd);
 
 
-	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));
-		}
-		else
-		{// create
-			sd->regstr[i].data = aStrdup(str);
-		}
-		return true;
-	}
+	if( str[0] ) {
+		p = ers_alloc(str_reg_ers, struct script_reg_str);
 
 
-	if( str == NULL || *str == '\0' )
-		return true;// nothing to add, empty string
+		p->value = aStrdup(str);
+		p->flag.type = 1;
 
 
-	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);
+		if (sd->regs.vars->put(sd->regs.vars, db_i642key(reg), db_ptr2data(p), &prev)) {
+			p = db_data2ptr(&prev);
+			if( p->value )
+				aFree(p->value);
+			ers_free(str_reg_ers, p);
+		} else {
+			if( index )
+				script_array_update(&sd->regs, reg, false);
+		}
+	} else {
+		if (sd->regs.vars->remove(sd->regs.vars, db_i642key(reg), &prev)) {
+			p = db_data2ptr(&prev);
+			if( p->value )
+				aFree(p->value);
+			ers_free(str_reg_ers, p);
+			if( index )
+				script_array_update(&sd->regs, reg, true);
+		}
 	}
 	}
-	sd->regstr[i].index = reg;
-	sd->regstr[i].data = aStrdup(str);
 
 
 	return true;
 	return true;
 }
 }
 
 
-int pc_readregistry(struct map_session_data *sd,const char *reg,int type)
+/**
+ * Serves the following variable types:
+ * - 'type' (permanent numeric char reg)
+ * - '#type' (permanent numeric account reg)
+ * - '##type' (permanent numeric account reg2)
+ **/
+int pc_readregistry(struct map_session_data *sd, int64 reg)
 {
 {
-	struct global_reg *sd_reg;
-	int i,max;
+	struct script_reg_num *p = NULL;
 
 
-	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);
+	if (!sd->vars_ok) {
+		ShowError("pc_readregistry: Trying to read reg %s before it's been loaded!\n", get_str(script_getvarid(reg)));
 		//This really shouldn't happen, so it's possible the data was lost somewhere, we should request it again.
 		//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;
 		return 0;
 	}
 	}
 
 
-	ARR_FIND( 0, max, i, strcmp(sd_reg[i].str,reg) == 0 );
-	return ( i < max ) ? atoi(sd_reg[i].value) : 0;
+	p = i64db_get(sd->regs.vars, reg);
+
+	return p ? p->value : 0;
 }
 }
 
 
-char* pc_readregistry_str(struct map_session_data *sd,const char *reg,int 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 global_reg *sd_reg;
-	int i,max;
+	struct script_reg_str *p = NULL;
 
 
-	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);
+	if (!sd->vars_ok) {
+		ShowError("pc_readregistry_str: Trying to read reg %s before it's been loaded!\n", get_str(script_getvarid(reg)));
 		//This really shouldn't happen, so it's possible the data was lost somewhere, we should request it again.
 		//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;
 		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->regs.vars, reg);
 
 
-bool pc_setregistry(struct map_session_data *sd,const char *reg,int val,int type)
-{
-	struct global_reg *sd_reg;
-	int i,*max, regmax;
-
-	nullpo_retr(false,sd);
+	return p ? p->value : NULL;
+}
 
 
-	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 false;
+/**
+ * Serves the following variable types:
+ * - 'type' (permanent numeric 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;
+	const char *regname = get_str(script_getvarid(reg));
+	unsigned int index = script_getvaridx(reg);
+
+	// These 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 ) {
+				int 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 true;
+	
+	if ( !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->regs.vars, reg))) {
+		if( val ) {
+			if( !p->value && index ) /* its a entry that was deleted, so we reset array */
+				script_array_update(&sd->regs, reg, false);
+			p->value = val;
+		} else {
+			p->value = 0;
+			if( index )
+				script_array_update(&sd->regs, reg, true);
 		}
 		}
-		return true;
-	}
-	// 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 true;
-	}
+		if (!reg_load)
+			p->flag.update = 1;/* either way, it will require either delete or replace */
+	} else if( val ) {
+		DBData prev;
 
 
-	// 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 true;
+		if( index )
+			script_array_update(&sd->regs, reg, false);
+
+		p = ers_alloc(num_reg_ers, struct script_reg_num);
+
+		p->value = val;
+		if (!reg_load)
+			p->flag.update = 1;
+
+		if (sd->regs.vars->put(sd->regs.vars, db_i642key(reg), db_ptr2data(p), &prev)) {
+			p = db_data2ptr(&prev);
+			ers_free(num_reg_ers, p);
+		}
 	}
 	}
 
 
-	ShowError("pc_setregistry : couldn't set %s, limit of registries reached (%d)\n", reg, regmax);
+	if (!reg_load && p)
+		sd->vars_dirty = true;
 
 
-	return false;
+	return 1;
 }
 }
 
 
-bool pc_setregistry_str(struct map_session_data *sd,const char *reg,const char *val,int type)
+/**
+ * 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 global_reg *sd_reg;
-	int i,*max, regmax;
+	struct script_reg_str *p = NULL;
+	const char *regname = get_str(script_getvarid(reg));
+	unsigned int index = script_getvaridx(reg);
 
 
-	nullpo_retr(false,sd);
-	if (reg[strlen(reg)-1] != '$') {
-		ShowError("pc_setregistry_str : reg %s must be string (end in '$') to use this!\n", reg);
-		return false;
-	}
-
-	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 false;
-	}
-	if (*max == -1) {
-		ShowError("pc_setregistry_str : refusing to set %s (type %d) until vars are received.\n", reg, type);
-		return false;
+	if (!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->regs.vars, reg) ) ) {
+		if( val[0] ) {
+			if( p->value )
+				aFree(p->value);
+			else if ( index ) // an entry that was deleted, so we reset
+				script_array_update(&sd->regs, reg, false);
+			p->value = aStrdup(val);
+		} else {
+			p->value = NULL;
+			if( index )
+				script_array_update(&sd->regs, reg, true);
 		}
 		}
-		return true;
-	}
+		if( !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 true;
-	}
+		if( index )
+			script_array_update(&sd->regs, 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 true;
+		p = ers_alloc(str_reg_ers, struct script_reg_str);
+
+		p->value = aStrdup(val);
+		if( !reg_load )
+			p->flag.update = 1;
+		p->flag.type = 1;
+
+		if( sd->regs.vars->put(sd->regs.vars, db_i642key(reg), db_ptr2data(p), &prev) ) {
+			p = db_data2ptr(&prev);
+			if( p->value )
+				aFree(p->value);
+			ers_free(str_reg_ers, p);
+		}
 	}
 	}
 
 
-	ShowError("pc_setregistry : couldn't set %s, limit of registries reached (%d)\n", reg, regmax);
-	return false;
+	if( !reg_load && p )
+		sd->vars_dirty = true;
+
+	return 1;
 }
 }
 
 
 /**
 /**
@@ -8910,9 +8853,9 @@ bool pc_setreg2(struct map_session_data *sd, const char *reg, int val) {
 		case '@':
 		case '@':
 			return pc_setreg(sd, add_str(reg), val);
 			return pc_setreg(sd, add_str(reg), val);
 		case '#':
 		case '#':
-			return (reg[1] == '#') ? pc_setaccountreg2(sd, reg, val) : pc_setaccountreg(sd, reg, val);
+			return (reg[1] == '#') ? pc_setaccountreg2(sd, add_str(reg), val) : pc_setaccountreg(sd, add_str(reg), val);
 		default:
 		default:
-			return pc_setglobalreg(sd, reg, val);
+			return pc_setglobalreg(sd, add_str(reg), val);
 	}
 	}
 
 
 	return false;
 	return false;
@@ -8943,9 +8886,9 @@ int pc_readreg2(struct map_session_data *sd, const char *reg) {
 		case '@':
 		case '@':
 			return pc_readreg(sd, add_str(reg));
 			return pc_readreg(sd, add_str(reg));
 		case '#':
 		case '#':
-			return (reg[1] == '#') ? pc_readaccountreg2(sd, reg) : pc_readaccountreg(sd, reg);
+			return (reg[1] == '#') ? pc_readaccountreg2(sd, add_str(reg)) : pc_readaccountreg(sd, add_str(reg));
 		default:
 		default:
-			return pc_readglobalreg(sd, reg);
+			return pc_readglobalreg(sd, add_str(reg));
 	}
 	}
 
 
 	return 0;
 	return 0;
@@ -11612,6 +11555,8 @@ void do_final_pc(void) {
 
 
 	ers_destroy(pc_sc_display_ers);
 	ers_destroy(pc_sc_display_ers);
 	ers_destroy(pc_itemgrouphealrate_ers);
 	ers_destroy(pc_itemgrouphealrate_ers);
+	ers_destroy(num_reg_ers);
+	ers_destroy(str_reg_ers);
 }
 }
 
 
 void do_init_pc(void) {
 void do_init_pc(void) {
@@ -11654,4 +11599,6 @@ void do_init_pc(void) {
 
 
 	pc_sc_display_ers = ers_new(sizeof(struct sc_display_entry), "pc.c:pc_sc_display_ers", ERS_OPT_NONE);
 	pc_sc_display_ers = ers_new(sizeof(struct sc_display_entry), "pc.c:pc_sc_display_ers", ERS_OPT_NONE);
 	pc_itemgrouphealrate_ers = ers_new(sizeof(struct s_pc_itemgrouphealrate), "pc.c:pc_itemgrouphealrate_ers", ERS_OPT_NONE);
 	pc_itemgrouphealrate_ers = ers_new(sizeof(struct s_pc_itemgrouphealrate), "pc.c:pc_itemgrouphealrate_ers", ERS_OPT_NONE);
+	num_reg_ers = ers_new(sizeof(struct script_reg_num), "pc.c:num_reg_ers", ERS_OPT_NONE);
+	str_reg_ers = ers_new(sizeof(struct script_reg_str), "pc.c:str_reg_ers", ERS_OPT_NONE);
 }
 }

+ 39 - 30
src/map/pc.h

@@ -196,7 +196,6 @@ struct map_session_data {
 		unsigned int abra_flag : 2; // Abracadabra bugfix by Aru
 		unsigned int abra_flag : 2; // Abracadabra bugfix by Aru
 		unsigned int autocast : 1; // Autospell flag [Inkfish]
 		unsigned int autocast : 1; // Autospell flag [Inkfish]
 		unsigned int autotrade : 3;	//By Fantik. &2 Requested by vending autotrade; &4 Requested by buyingstore autotrade
 		unsigned int autotrade : 3;	//By Fantik. &2 Requested by vending autotrade; &4 Requested by buyingstore autotrade
-		unsigned int reg_dirty : 4; //By Skotlex (marks whether registry variables have been saved or not yet)
 		unsigned int showdelay :1;
 		unsigned int showdelay :1;
 		unsigned int showexp :1;
 		unsigned int showexp :1;
 		unsigned int showzeny :1;
 		unsigned int showzeny :1;
@@ -257,7 +256,6 @@ struct map_session_data {
 	int langtype;
 	int langtype;
 	uint32 packet_ver;  // 5: old, 6: 7july04, 7: 13july04, 8: 26july04, 9: 9aug04/16aug04/17aug04, 10: 6sept04, 11: 21sept04, 12: 18oct04, 13: 25oct04 ... 18
 	uint32 packet_ver;  // 5: old, 6: 7july04, 7: 13july04, 8: 26july04, 9: 9aug04/16aug04/17aug04, 10: 6sept04, 11: 21sept04, 12: 18oct04, 13: 25oct04 ... 18
 	struct mmo_charstatus status;
 	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)
 	struct item_data* inventory_data[MAX_INVENTORY]; // direct pointers to itemdb entries (faster than doing item_id lookups)
 	short equip_index[EQI_MAX];
 	short equip_index[EQI_MAX];
@@ -464,11 +462,6 @@ struct map_session_data {
 	short mission_mobid; //Stores the target mob_id for TK_MISSION
 	short mission_mobid; //Stores the target mob_id for TK_MISSION
 	int die_counter; //Total number of times you've died
 	int die_counter; //Total number of times you've died
 	int devotion[MAX_DEVOTION]; //Stores the account IDs of chars devoted to.
 	int devotion[MAX_DEVOTION]; //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;
 	int trade_partner;
 	struct s_deal {
 	struct s_deal {
@@ -606,6 +599,14 @@ struct map_session_data {
 
 
 	unsigned char delayed_damage; //[Ind]
 	unsigned char delayed_damage; //[Ind]
 
 
+	/**
+	 * Account/Char variables & array control of those variables
+	 **/
+	struct reg_db regs;
+	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
 	// temporary debugging of bug #3504
 	const char* delunit_prevfile;
 	const char* delunit_prevfile;
 	int delunit_prevline;
 	int delunit_prevline;
@@ -647,6 +648,14 @@ struct map_session_data {
 struct eri *pc_sc_display_ers; /// Player's SC display table
 struct eri *pc_sc_display_ers; /// Player's SC display table
 struct eri *pc_itemgrouphealrate_ers; /// Player's Item Group Heal Rate table
 struct eri *pc_itemgrouphealrate_ers; /// Player's Item Group Heal Rate table
 
 
+/**
+ * ERS for the bulk of pc vars
+ **/
+struct eri *num_reg_ers;
+struct eri *str_reg_ers;
+/* */
+bool reg_load;
+
 /* Global Expiration Timer ID */
 /* Global Expiration Timer ID */
 extern int pc_expiration_tid;
 extern int pc_expiration_tid;
 
 
@@ -1012,29 +1021,29 @@ void pc_setmadogear(struct map_session_data* sd, int flag);
 void pc_changelook(struct map_session_data *,int,int);
 void pc_changelook(struct map_session_data *,int,int);
 void pc_equiplookall(struct map_session_data *sd);
 void pc_equiplookall(struct map_session_data *sd);
 
 
-int pc_readparam(struct map_session_data*,int);
-bool pc_setparam(struct map_session_data*,int,int);
-int pc_readreg(struct map_session_data*,int);
-bool pc_setreg(struct map_session_data*,int,int);
-char *pc_readregstr(struct map_session_data *sd,int reg);
-bool pc_setregstr(struct map_session_data *sd,int reg,const char *str);
-
-#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)
-int pc_readregistry(struct map_session_data*,const char*,int);
-bool pc_setregistry(struct map_session_data*,const char*,int,int);
-char *pc_readregistry_str(struct map_session_data*,const char*,int);
-bool pc_setregistry_str(struct map_session_data*,const char*,const char*,int);
+int pc_readparam(struct map_session_data *sd, int type);
+bool pc_setparam(struct map_session_data *sd, int type, int val);
+int pc_readreg(struct map_session_data *sd, int64 reg);
+bool pc_setreg(struct map_session_data *sd, int64 reg, int val);
+char *pc_readregstr(struct map_session_data *sd, int64 reg);
+bool pc_setregstr(struct map_session_data *sd, int64 reg, const char *str);
+int pc_readregistry(struct map_session_data *sd, int64 reg);
+int pc_setregistry(struct map_session_data *sd, int64 reg, int val);
+char *pc_readregistry_str(struct map_session_data *sd, int64 reg);
+int pc_setregistry_str(struct map_session_data *sd, int64 reg, const char *val);
+
+#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)
 
 
 bool pc_setreg2(struct map_session_data *sd, const char *reg, int val);
 bool pc_setreg2(struct map_session_data *sd, const char *reg, int val);
 int pc_readreg2(struct map_session_data *sd, const char *reg);
 int pc_readreg2(struct map_session_data *sd, const char *reg);

파일 크기가 너무 크기때문에 변경 상태를 표시하지 않습니다.
+ 494 - 352
src/map/script.c


+ 180 - 16
src/map/script.h

@@ -4,13 +4,137 @@
 #ifndef _SCRIPT_H_
 #ifndef _SCRIPT_H_
 #define _SCRIPT_H_
 #define _SCRIPT_H_
 
 
+#include "../common/cbasetypes.h"
+#include "map.h"
+
 #define NUM_WHISPER_VAR 10
 #define NUM_WHISPER_VAR 10
 
 
+///////////////////////////////////////////////////////////////////////////////
+//## TODO possible enhancements: [FlavioJS]
+// - 'callfunc' supporting labels in the current npc "::LabelName"
+// - 'callfunc' supporting labels in other npcs "NpcName::LabelName"
+// - 'function FuncName;' function declarations reverting to global functions
+//   if local label isn't found
+// - join callfunc and callsub's functionality
+// - remove dynamic allocation in add_word()
+// - remove GETVALUE / SETVALUE
+// - clean up the set_reg / set_val / setd_sub mess
+// - detect invalid label references at parse-time
+
+//
+// struct script_state* st;
+//
+
+/// Returns the script_data at the target index
+#define script_getdata(st,i) ( &((st)->stack->stack_data[(st)->start + (i)]) )
+/// Returns if the stack contains data at the target index
+#define script_hasdata(st,i) ( (st)->end > (st)->start + (i) )
+/// Returns the index of the last data in the stack
+#define script_lastdata(st) ( (st)->end - (st)->start - 1 )
+/// Pushes an int into the stack
+#define script_pushint(st,val) push_val((st)->stack, C_INT, (val))
+/// Pushes a string into the stack (script engine frees it automatically)
+#define script_pushstr(st,val) push_str((st)->stack, C_STR, (val))
+/// Pushes a copy of a string into the stack
+#define script_pushstrcopy(st,val) push_str((st)->stack, C_STR, aStrdup(val))
+/// Pushes a constant string into the stack (must never change or be freed)
+#define script_pushconststr(st,val) push_str((st)->stack, C_CONSTSTR, (val))
+/// Pushes a nil into the stack
+#define script_pushnil(st) push_val((st)->stack, C_NOP, 0)
+/// Pushes a copy of the data in the target index
+#define script_pushcopy(st,i) push_copy((st)->stack, (st)->start + (i))
+
+#define script_isstring(st,i) data_isstring(script_getdata(st,i))
+#define script_isint(st,i) data_isint(script_getdata(st,i))
+
+#define script_getnum(st,val) conv_num(st, script_getdata(st,val))
+#define script_getstr(st,val) conv_str(st, script_getdata(st,val))
+#define script_getref(st,val) ( script_getdata(st,val)->ref )
+// Returns name of currently running function
+#define script_getfuncname(st) ( st->funcname )
+
+// Note: "top" functions/defines use indexes relative to the top of the stack
+//       -1 is the index of the data at the top
+
+/// Returns the script_data at the target index relative to the top of the stack
+#define script_getdatatop(st,i) ( &((st)->stack->stack_data[(st)->stack->sp + (i)]) )
+/// Pushes a copy of the data in the target index relative to the top of the stack
+#define script_pushcopytop(st,i) push_copy((st)->stack, (st)->stack->sp + (i))
+/// Removes the range of values [start,end[ relative to the top of the stack
+#define script_removetop(st,start,end) ( pop_stack((st), ((st)->stack->sp + (start)), (st)->stack->sp + (end)) )
+
+//
+// struct script_data* data;
+//
+
+/// Returns if the script data is a string
+#define data_isstring(data) ( (data)->type == C_STR || (data)->type == C_CONSTSTR )
+/// Returns if the script data is an int
+#define data_isint(data) ( (data)->type == C_INT )
+/// Returns if the script data is a reference
+#define data_isreference(data) ( (data)->type == C_NAME )
+/// Returns if the script data is a label
+#define data_islabel(data) ( (data)->type == C_POS )
+/// Returns if the script data is an internal script function label
+#define data_isfunclabel(data) ( (data)->type == C_USERFUNC_POS )
+
+/// Returns if this is a reference to a constant
+#define reference_toconstant(data) ( str_data[reference_getid(data)].type == C_INT )
+/// Returns if this a reference to a param
+#define reference_toparam(data) ( str_data[reference_getid(data)].type == C_PARAM )
+/// Returns if this a reference to a variable
+//##TODO confirm it's C_NAME [FlavioJS]
+#define reference_tovariable(data) ( str_data[reference_getid(data)].type == C_NAME )
+/// 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)(int64)(reference_getuid(data) & 0xffffffff) )
+/// Returns the array index of the reference
+#define reference_getindex(data) ( (uint32)(int64)((reference_getuid(data) >> 32) & 0xffffffff) )
+/// Returns the name of the reference
+#define reference_getname(data) ( str_buf + str_data[reference_getid(data)].str )
+/// Returns the linked list of uid-value pairs of the reference (can be NULL)
+#define reference_getref(data) ( (data)->ref )
+/// Returns the value of the constant
+#define reference_getconstant(data) ( str_data[reference_getid(data)].val )
+/// Returns the type of param
+#define reference_getparamtype(data) ( str_data[reference_getid(data)].val )
+
+/// Composes the uid of a reference from the id and the index
+#define reference_uid(id,idx) ( (int64) ((uint64)(id) & 0xFFFFFFFF) | ((uint64)(idx) << 32) )
+
+/// Checks whether two references point to the same variable (or array)
+#define is_same_reference(data1, data2) \
+	(  reference_getid(data1) == reference_getid(data2) \
+	&& ( (data1->ref == data2->ref && data1->ref == NULL) \
+	  || (data1->ref != NULL && data2->ref != NULL && data1->ref->vars == data2->ref->vars \
+	     ) ) )
+
+#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 is_string_variable(name) ( (name)[strlen(name) - 1] == '$' )
+
+#define FETCH(n, t) \
+		if( script_hasdata(st,n) ) \
+			(t)=script_getnum(st,n);
+
+/// Maximum amount of elements in script arrays
+#define SCRIPT_MAX_ARRAYSIZE (UINT_MAX - 1)
+#define SCRIPT_CMD_SUCCESS 0 ///when a buildin cmd was correctly done
+#define SCRIPT_CMD_FAILURE 1 ///when an errors appear in cmd, show_debug will follow
+
+#define SCRIPT_BLOCK_SIZE 512
+enum { LABEL_NEXTLINE = 1, LABEL_START };
+
 struct map_session_data;
 struct map_session_data;
 
 
 extern int potion_flag; //For use on Alchemist improved potions/Potion Pitcher. [Skotlex]
 extern int potion_flag; //For use on Alchemist improved potions/Potion Pitcher. [Skotlex]
 extern int potion_hp, potion_per_hp, potion_sp, potion_per_sp;
 extern int potion_hp, potion_per_hp, potion_sp, potion_per_sp;
 extern int potion_target;
 extern int potion_target;
+extern unsigned int *generic_ui_array;
+extern unsigned int generic_ui_array_size;
 
 
 extern struct Script_Config {
 extern struct Script_Config {
 	unsigned warn_func_mismatch_argtypes : 1;
 	unsigned warn_func_mismatch_argtypes : 1;
@@ -77,22 +201,30 @@ typedef enum c_op {
 	C_SUB_PP, // --a
 	C_SUB_PP, // --a
 } c_op;
 } c_op;
 
 
+/**
+ * Generic reg database abstraction to be used with various types of regs/script variables.
+ */
+struct reg_db {
+	struct DBMap *vars;
+	struct DBMap *arrays;
+};
+
 struct script_retinfo {
 struct script_retinfo {
-	struct DBMap* var_function;// scope variables
-	struct script_code* script;// script code
-	int pos;// script location
-	int nargs;// argument count
-	int defsp;// default stack pointer
+	struct reg_db scope;        ///< scope variables
+	struct script_code* script; ///< script code
+	int pos;                    ///< script location
+	int nargs;                  ///< argument count
+	int defsp;                  ///< default stack pointer
 };
 };
 
 
 struct script_data {
 struct script_data {
 	enum c_op type;
 	enum c_op type;
 	union script_data_val {
 	union script_data_val {
-		int num;
+		int64 num;
 		char *str;
 		char *str;
 		struct script_retinfo* ri;
 		struct script_retinfo* ri;
 	} u;
 	} u;
-	struct DBMap** ref;
+	struct reg_db *ref;
 };
 };
 
 
 // Moved defsp from script_state to script_stack since
 // Moved defsp from script_state to script_stack since
@@ -100,15 +232,15 @@ struct script_data {
 struct script_code {
 struct script_code {
 	int script_size;
 	int script_size;
 	unsigned char* script_buf;
 	unsigned char* script_buf;
-	struct DBMap* script_vars;
+	struct reg_db local;
 };
 };
 
 
 struct script_stack {
 struct script_stack {
-	int sp;// number of entries in the stack
-	int sp_max;// capacity of the stack
+	int sp;                         ///< number of entries in the stack
+	int sp_max;                     ///< capacity of the stack
 	int defsp;
 	int defsp;
-	struct script_data *stack_data;// stack
-	struct DBMap* var_function;// scope variables
+	struct script_data *stack_data; ///< stack
+	struct reg_db scope;            ///< scope variables
 };
 };
 
 
 
 
@@ -138,21 +270,32 @@ struct script_state {
 };
 };
 
 
 struct script_reg {
 struct script_reg {
-	int index;
+	int64 index;
 	int data;
 	int data;
 };
 };
 
 
 struct script_regstr {
 struct script_regstr {
-	int index;
+	int64 index;
 	char* data;
 	char* data;
 };
 };
 
 
+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
+};
+
 enum script_parse_options {
 enum script_parse_options {
 	SCRIPT_USE_LABEL_DB = 0x1,// records labels in scriptlabel_db
 	SCRIPT_USE_LABEL_DB = 0x1,// records labels in scriptlabel_db
 	SCRIPT_IGNORE_EXTERNAL_BRACKETS = 0x2,// ignores the check for {} brackets around the script
 	SCRIPT_IGNORE_EXTERNAL_BRACKETS = 0x2,// ignores the check for {} brackets around the script
 	SCRIPT_RETURN_EMPTY_SCRIPT = 0x4// returns the script object instead of NULL for empty scripts
 	SCRIPT_RETURN_EMPTY_SCRIPT = 0x4// returns the script object instead of NULL for empty scripts
 };
 };
 
 
+/**
+ * used to generate quick script_array entries
+ **/
+struct eri *array_ers;
+
 const char* skip_space(const char* p);
 const char* skip_space(const char* p);
 void script_error(const char* src, const char* file, int start_line, const char* error_msg, const char* error_pos);
 void script_error(const char* src, const char* file, int start_line, const char* error_msg, const char* error_pos);
 void script_warning(const char* src, const char* file, int start_line, const char* error_msg, const char* error_pos);
 void script_warning(const char* src, const char* file, int start_line, const char* error_msg, const char* error_pos);
@@ -161,9 +304,11 @@ struct script_code* parse_script(const char* src,const char* file,int line,int o
 void run_script_sub(struct script_code *rootscript,int pos,int rid,int oid, char* file, int lineno);
 void run_script_sub(struct script_code *rootscript,int pos,int rid,int oid, char* file, int lineno);
 void run_script(struct script_code *rootscript,int pos,int rid,int oid);
 void run_script(struct script_code *rootscript,int pos,int rid,int oid);
 
 
+int set_reg(struct script_state* st, TBL_PC* sd, int64 num, const char* name, const void* value, struct reg_db *ref);
 int set_var(struct map_session_data *sd, char *name, void *val);
 int set_var(struct map_session_data *sd, char *name, void *val);
 int conv_num(struct script_state *st,struct script_data *data);
 int conv_num(struct script_state *st,struct script_data *data);
 const char* conv_str(struct script_state *st,struct script_data *data);
 const char* conv_str(struct script_state *st,struct script_data *data);
+void pop_stack(struct script_state* st, int start, int end);
 int run_script_timer(int tid, unsigned int tick, int id, intptr_t data);
 int run_script_timer(int tid, unsigned int tick, int id, intptr_t data);
 void run_script_main(struct script_state *st);
 void run_script_main(struct script_state *st);
 
 
@@ -183,7 +328,7 @@ void script_set_constant(const char* name, int value, bool isparameter);
 void script_hardcoded_constants(void);
 void script_hardcoded_constants(void);
 
 
 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);
-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 script_config_read(char *cfgName);
 int script_config_read(char *cfgName);
 void do_init_script(void);
 void do_init_script(void);
@@ -193,7 +338,26 @@ const char* get_str(int id);
 void script_reload(void);
 void script_reload(void);
 
 
 // @commands (script based)
 // @commands (script based)
-void setd_sub(struct script_state *st, TBL_PC *sd, const char *varname, int elem, void *value, struct DBMap **ref);
+void setd_sub(struct script_state *st, TBL_PC *sd, const char *varname, int elem, void *value, struct reg_db *ref);
+
+/**
+ * Array Handling
+ **/
+struct reg_db *script_array_src(struct script_state *st, struct map_session_data *sd, const char *name, struct reg_db *ref);
+void script_array_update(struct reg_db *src, int64 num, bool empty);
+void script_array_delete(struct reg_db *src, struct script_array *sa);
+void script_array_remove_member(struct reg_db *src, struct script_array *sa, unsigned int idx);
+void script_array_add_member(struct script_array *sa, unsigned int idx);
+unsigned int script_array_size(struct script_state *st, struct map_session_data *sd, const char *name, struct reg_db *ref);
+unsigned int script_array_highest_key(struct script_state *st, struct map_session_data *sd, const char *name, struct reg_db *ref);
+void script_array_ensure_zero(struct script_state *st, struct map_session_data *sd, int64 uid, struct reg_db *ref);
+int script_free_array_db(DBKey key, DBData *data, va_list ap);
+/* */
+void script_reg_destroy_single(struct map_session_data *sd, int64 reg, struct script_reg_state *data);
+int script_reg_destroy(DBKey key, DBData *data, va_list ap);
+/* */
+void script_generic_ui_array_expand(unsigned int plus);
+unsigned int *script_array_cpy_list(struct script_array *sa);
 
 
 #ifdef BETA_THREAD_TEST
 #ifdef BETA_THREAD_TEST
 void queryThread_log(char * entry, int length);
 void queryThread_log(char * entry, int length);

+ 8 - 8
src/map/skill.c

@@ -2747,8 +2747,8 @@ static void skill_do_copy(struct block_list* src,struct block_list *bl, uint16 s
 					lv = min(skill_lv,pc_checkskill(tsd,RG_PLAGIARISM)); //Copied level never be > player's RG_PLAGIARISM level
 					lv = min(skill_lv,pc_checkskill(tsd,RG_PLAGIARISM)); //Copied level never be > player's RG_PLAGIARISM level
 
 
 					tsd->cloneskill_idx = idx;
 					tsd->cloneskill_idx = idx;
-					pc_setglobalreg(tsd,SKILL_VAR_PLAGIARISM,skill_id);
-					pc_setglobalreg(tsd,SKILL_VAR_PLAGIARISM_LV,lv);
+					pc_setglobalreg(tsd, add_str(SKILL_VAR_PLAGIARISM), skill_id);
+					pc_setglobalreg(tsd, add_str(SKILL_VAR_PLAGIARISM_LV), lv);
 				}
 				}
 				break;
 				break;
 			case 2: //Copied by Reproduce
 			case 2: //Copied by Reproduce
@@ -2771,8 +2771,8 @@ static void skill_do_copy(struct block_list* src,struct block_list *bl, uint16 s
 						lv = min(lv,skill_lv);
 						lv = min(lv,skill_lv);
 
 
 					tsd->reproduceskill_idx = idx;
 					tsd->reproduceskill_idx = idx;
-					pc_setglobalreg(tsd,SKILL_VAR_REPRODUCE,skill_id);
-					pc_setglobalreg(tsd,SKILL_VAR_REPRODUCE_LV,lv);
+					pc_setglobalreg(tsd, add_str(SKILL_VAR_REPRODUCE), skill_id);
+					pc_setglobalreg(tsd, add_str(SKILL_VAR_REPRODUCE_LV), lv);
 				}
 				}
 				break;
 				break;
 			default: return;
 			default: return;
@@ -6338,7 +6338,7 @@ int skill_castend_nodamage_id (struct block_list *src, struct block_list *bl, ui
 			}
 			}
 			sd->mission_mobid = id;
 			sd->mission_mobid = id;
 			sd->mission_count = 0;
 			sd->mission_count = 0;
-			pc_setglobalreg(sd,"TK_MISSION_ID", id);
+			pc_setglobalreg(sd, add_str("TK_MISSION_ID"), id);
 			clif_mission_info(sd, id, 0);
 			clif_mission_info(sd, id, 0);
 			clif_skill_nodamage(src,bl,skill_id,skill_lv,1);
 			clif_skill_nodamage(src,bl,skill_id,skill_lv,1);
 		}
 		}
@@ -8159,7 +8159,7 @@ int skill_castend_nodamage_id (struct block_list *src, struct block_list *bl, ui
 		if (skill_id == SL_SUPERNOVICE && dstsd && dstsd->die_counter && !(rnd()%100))
 		if (skill_id == SL_SUPERNOVICE && dstsd && dstsd->die_counter && !(rnd()%100))
 		{	//Erase death count 1% of the casts
 		{	//Erase death count 1% of the casts
 			dstsd->die_counter = 0;
 			dstsd->die_counter = 0;
-			pc_setglobalreg(dstsd,"PC_DIE_COUNTER", 0);
+			pc_setglobalreg(dstsd, add_str("PC_DIE_COUNTER"), 0);
 			clif_specialeffect(bl, 0x152, AREA);
 			clif_specialeffect(bl, 0x152, AREA);
 			//SC_SPIRIT invokes status_calc_pc for us.
 			//SC_SPIRIT invokes status_calc_pc for us.
 		}
 		}
@@ -18600,7 +18600,7 @@ bool skill_produce_mix(struct map_session_data *sd, uint16 skill_id, unsigned sh
 					if (skill_produce_db[idx].itemlv > 10 && skill_produce_db[idx].itemlv <= 20) { //Cooking items.
 					if (skill_produce_db[idx].itemlv > 10 && skill_produce_db[idx].itemlv <= 20) { //Cooking items.
 						clif_specialeffect(&sd->bl, 608, AREA);
 						clif_specialeffect(&sd->bl, 608, AREA);
 						if (sd->cook_mastery < 1999)
 						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, add_str("COOK_MASTERY"), sd->cook_mastery + ( 1 << ( (skill_produce_db[idx].itemlv - 11) / 2 ) ) * 5);
 					}
 					}
 					break;
 					break;
 			}
 			}
@@ -18707,7 +18707,7 @@ bool skill_produce_mix(struct map_session_data *sd, uint16 skill_id, unsigned sh
 				if (skill_produce_db[idx].itemlv > 10 && skill_produce_db[idx].itemlv <= 20 ) { //Cooking items.
 				if (skill_produce_db[idx].itemlv > 10 && skill_produce_db[idx].itemlv <= 20 ) { //Cooking items.
 					clif_specialeffect(&sd->bl, 609, AREA);
 					clif_specialeffect(&sd->bl, 609, AREA);
 					if (sd->cook_mastery > 0)
 					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, 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 ));
 				}
 				}
 				break;
 				break;
 		}
 		}

+ 1 - 1
src/map/trade.c

@@ -177,7 +177,7 @@ int impossible_trade_check(struct map_session_data *sd)
 	nullpo_retr(1, sd);
 	nullpo_retr(1, sd);
 
 
 	if(sd->deal.zeny > sd->status.zeny) {
 	if(sd->deal.zeny > sd->status.zeny) {
-		pc_setglobalreg(sd,"ZENY_HACKER",1);
+		pc_setglobalreg(sd, add_str("ZENY_HACKER"), 1);
 		return -1;
 		return -1;
 	}
 	}
 
 

+ 0 - 18
src/map/unit.c

@@ -3168,24 +3168,6 @@ int unit_free(struct block_list *bl, clr_type clrtype)
 			for(i = 1; i < 5; i++)
 			for(i = 1; i < 5; i++)
 				pc_del_talisman(sd, sd->talisman[i], i);
 				pc_del_talisman(sd, sd->talisman[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 ) {
-				int j;
-
-				for( j = 0; j < sd->regstr_num; ++j )
-					if( sd->regstr[j].data )
-						aFree(sd->regstr[j].data);
-
-				aFree(sd->regstr);
-				sd->regstr = NULL;
-				sd->regstr_num = 0;
-			}
-
 			if( sd->st && sd->st->state != RUN ) {// free attached scripts that are waiting
 			if( sd->st && sd->st->state != RUN ) {// free attached scripts that are waiting
 				script_free_state(sd->st);
 				script_free_state(sd->st);
 				sd->st = NULL;
 				sd->st = NULL;

이 변경점에서 너무 많은 파일들이 변경되어 몇몇 파일들은 표시되지 않았습니다.