Browse Source

Merge pull request #457 from rathena/feature/script_engine_upgrade

Script Engine Upgrade
* More information here: https://rathena.org/board/topic/102946-script-engine-upgrade/
Aleos 9 years ago
parent
commit
6295c77d39

+ 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`
@@ -135,6 +160,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`
 --
 --
@@ -205,16 +256,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;
 
 
@@ -480,11 +543,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_20150831.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

@@ -1637,7 +1637,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 */
@@ -2201,7 +2203,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,
@@ -2237,8 +2240,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;
 	}
 	}
@@ -2396,7 +2414,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`"
@@ -2458,8 +2475,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"))
@@ -2510,6 +2525,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);
@@ -2529,7 +2552,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));
@@ -2554,6 +2576,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;
 
 
@@ -250,7 +253,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){
@@ -477,10 +527,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;
@@ -702,7 +749,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 - 141
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);
+	WFIFOW(fd, 2) = plen;
+	WFIFOSET(fd, plen);
 
 
-	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;
-
-	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;
@@ -812,37 +913,10 @@ int mapif_wis_end(struct WisData *wd, int flag)
 	return 0;
 	return 0;
 }
 }
 
 
-// Account registry transfer to map-server
-static void mapif_account_reg(int fd, unsigned char *src)
-{
-	WBUFW(src,0)=0x3804; //NOTE: writing to RFIFO
-	chmapif_sendallwos(fd, src, WBUFW(src,2));
-}
-
 // 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;
 }
 }
 
 
@@ -1037,34 +1111,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_ */

+ 2 - 0
src/common/core.c

@@ -7,6 +7,7 @@
 #include "core.h"
 #include "core.h"
 #include "strlib.h"
 #include "strlib.h"
 #ifndef MINICORE
 #ifndef MINICORE
+#include "ers.h"
 #include "socket.h"
 #include "socket.h"
 #include "timer.h"
 #include "timer.h"
 #include "thread.h"
 #include "thread.h"
@@ -371,6 +372,7 @@ int main (int argc, char **argv)
 	db_final();
 	db_final();
 	mempool_final();
 	mempool_final();
 	rathread_final();
 	rathread_final();
+	ers_final();
 #endif
 #endif
 
 
 	malloc_final();
 	malloc_final();

File diff suppressed because it is too large
+ 269 - 111
src/common/db.c


+ 148 - 99
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                             *
@@ -42,6 +43,7 @@
 #define _DB_H_
 #define _DB_H_
 
 
 #include "../common/cbasetypes.h"
 #include "../common/cbasetypes.h"
+
 #include <stdarg.h>
 #include <stdarg.h>
 
 
 /*****************************************************************************\
 /*****************************************************************************\
@@ -69,20 +71,22 @@
  * @see #db_custom_release(DBRelease)
  * @see #db_custom_release(DBRelease)
  */
  */
 typedef enum DBRelease {
 typedef enum DBRelease {
-	DB_RELEASE_NOTHING = 0,
-	DB_RELEASE_KEY     = 1,
-	DB_RELEASE_DATA    = 2,
-	DB_RELEASE_BOTH    = 3
+	DB_RELEASE_NOTHING = 0x0,
+	DB_RELEASE_KEY     = 0x1,
+	DB_RELEASE_DATA    = 0x2,
+	DB_RELEASE_BOTH    = DB_RELEASE_KEY|DB_RELEASE_DATA,
 } DBRelease;
 } 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 +100,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.
@@ -121,13 +127,13 @@ typedef enum DBType {
  * @see #db_alloc(const char *,int,DBType,DBOptions,unsigned short)
  * @see #db_alloc(const char *,int,DBType,DBOptions,unsigned short)
  */
  */
 typedef enum DBOptions {
 typedef enum DBOptions {
-	DB_OPT_BASE            = 0,
-	DB_OPT_DUP_KEY         = 1,
-	DB_OPT_RELEASE_KEY     = 2,
-	DB_OPT_RELEASE_DATA    = 4,
-	DB_OPT_RELEASE_BOTH    = 6,
-	DB_OPT_ALLOW_NULL_KEY  = 8,
-	DB_OPT_ALLOW_NULL_DATA = 16,
+	DB_OPT_BASE            = 0x00,
+	DB_OPT_DUP_KEY         = 0x01,
+	DB_OPT_RELEASE_KEY     = 0x02,
+	DB_OPT_RELEASE_DATA    = 0x04,
+	DB_OPT_RELEASE_BOTH    = DB_OPT_RELEASE_KEY|DB_OPT_RELEASE_DATA,
+	DB_OPT_ALLOW_NULL_KEY  = 0x08,
+	DB_OPT_ALLOW_NULL_DATA = 0x10,
 } DBOptions;
 } DBOptions;
 
 
 /**
 /**
@@ -145,6 +151,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;
 
 
 /**
 /**
@@ -158,7 +166,7 @@ typedef union DBKey {
 typedef enum DBDataType {
 typedef enum DBDataType {
 	DB_DATA_INT,
 	DB_DATA_INT,
 	DB_DATA_UINT,
 	DB_DATA_UINT,
-	DB_DATA_PTR
+	DB_DATA_PTR,
 } DBDataType;
 } DBDataType;
 
 
 /**
 /**
@@ -180,7 +188,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 +200,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 +253,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 +280,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 +334,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 +344,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 +365,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 +375,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 +407,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 +425,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 +441,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 +455,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 +552,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 +567,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,75 +606,96 @@ 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 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 stridb_alloc(opt,maxlen)  db_alloc(__FILE__,__LINE__,DB_ISTRING,(opt),(maxlen))
+#define idb_alloc(opt)            db_alloc(__FILE__,__func__,__LINE__,DB_INT,(opt),sizeof(int))
+#define uidb_alloc(opt)           db_alloc(__FILE__,__func__,__LINE__,DB_UINT,(opt),sizeof(unsigned int))
+#define strdb_alloc(opt,maxlen)   db_alloc(__FILE__,__func__,__LINE__,DB_STRING,(opt),(maxlen))
+#define stridb_alloc(opt,maxlen)  db_alloc(__FILE__,__func__,__LINE__,DB_ISTRING,(opt),(maxlen))
+#define i64db_alloc(opt)          db_alloc(__FILE__,__func__,__LINE__,DB_INT64,(opt),sizeof(int64))
+#define ui64db_alloc(opt)         db_alloc(__FILE__,__func__,__LINE__,DB_UINT64,(opt),sizeof(uint64))
 #define db_destroy(db)            ( (db)->destroy((db),NULL) )
 #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) )
 #define db_size(db)         ( (db)->size(db) )
 #define db_size(db)         ( (db)->size(db) )
 #define db_iterator(db)     ( (db)->iterator(db) )
 #define db_iterator(db)     ( (db)->iterator(db) )
-#define dbi_first(dbi)      ( db_data2ptr((dbi)->first(dbi,NULL)) )
-#define dbi_last(dbi)       ( db_data2ptr((dbi)->last(dbi,NULL)) )
-#define dbi_next(dbi)       ( db_data2ptr((dbi)->next(dbi,NULL)) )
-#define dbi_prev(dbi)       ( db_data2ptr((dbi)->prev(dbi,NULL)) )
-#define dbi_remove(dbi)     ( (dbi)->remove(dbi,NULL) )
+#define dbi_first(dbi)      ( db_data2ptr((dbi)->first((dbi),NULL)) )
+#define dbi_last(dbi)       ( db_data2ptr((dbi)->last((dbi),NULL)) )
+#define dbi_next(dbi)       ( db_data2ptr((dbi)->next((dbi),NULL)) )
+#define dbi_prev(dbi)       ( db_data2ptr((dbi)->prev((dbi),NULL)) )
+#define dbi_remove(dbi)     ( (dbi)->remove((dbi),NULL) )
 #define dbi_exists(dbi)     ( (dbi)->exists(dbi) )
 #define dbi_exists(dbi)     ( (dbi)->exists(dbi) )
 #define dbi_destroy(dbi)    ( (dbi)->destroy(dbi) )
 #define dbi_destroy(dbi)    ( (dbi)->destroy(dbi) )
 
 
@@ -682,6 +711,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'.               *
@@ -727,7 +758,7 @@ DBComparator db_default_cmp(DBType type);
 DBHasher db_default_hash(DBType type);
 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 by {@link #db_fix_options(DBType,DBOptions)}
  * NOTE: the options are fixed by {@link #db_fix_options(DBType,DBOptions)}
  * before choosing the releaser
  * before choosing the releaser
@@ -756,7 +787,7 @@ DBReleaser db_custom_release(DBRelease which);
 
 
 /**
 /**
  * Allocate a new database of the specified type.
  * Allocate a new database of the specified type.
- * It uses the default comparator, hasher and releaser of the specified 
+ * It uses the default comparator, hasher and releaser of the specified
  * database type and fixed options.
  * database type and fixed options.
  * NOTE: the options are fixed by {@link #db_fix_options(DBType,DBOptions)}
  * NOTE: the options are fixed by {@link #db_fix_options(DBType,DBOptions)}
  * before creating the database.
  * before creating the database.
@@ -764,7 +795,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
@@ -775,7 +806,7 @@ DBReleaser db_custom_release(DBRelease which);
  * @see #db_default_release(DBType,DBOptions)
  * @see #db_default_release(DBType,DBOptions)
  * @see #db_fix_options(DBType,DBOptions)
  * @see #db_fix_options(DBType,DBOptions)
  */
  */
-DBMap* db_alloc(const char *file, int line, DBType type, DBOptions options, unsigned short maxlen);
+DBMap* db_alloc(const char *file, const char *func, int line, DBType type, DBOptions options, unsigned short maxlen);
 
 
 /**
 /**
  * Manual cast from 'int' to the union DBKey.
  * Manual cast from 'int' to the union DBKey.
@@ -801,6 +832,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
@@ -875,15 +922,15 @@ struct linkdb_node {
 	void               *data;
 	void               *data;
 };
 };
 
 
-typedef int (*LinkDBFunc)(void* key, void* data, va_list args);
+typedef void (*LinkDBFunc)(void* key, void* data, va_list args);
 
 
-void  linkdb_insert ( struct linkdb_node** head, void *key, void* data); // 重複を考慮しない
-void  linkdb_replace( struct linkdb_node** head, void *key, void* data); // 重複を考慮する
-void* linkdb_search ( struct linkdb_node** head, void *key);
-void* linkdb_erase  ( struct linkdb_node** head, void *key);
-void  linkdb_final  ( struct linkdb_node** head );
-int   linkdb_vforeach(struct linkdb_node** head, LinkDBFunc func, va_list ap);
-int   linkdb_foreach( struct linkdb_node** head, LinkDBFunc func, ...  );
+void  linkdb_insert  (struct linkdb_node** head, void *key, void* data); // Doesn't take into account duplicate keys
+void  linkdb_replace (struct linkdb_node** head, void *key, void* data); // Takes into account duplicate keys
+void* linkdb_search  (struct linkdb_node** head, void *key);
+void* linkdb_erase   (struct linkdb_node** head, void *key);
+void  linkdb_final   (struct linkdb_node** head);
+void  linkdb_vforeach(struct linkdb_node** head, LinkDBFunc func, va_list ap);
+void  linkdb_foreach (struct linkdb_node** head, LinkDBFunc func, ...);
 
 
 
 
 
 
@@ -1086,8 +1133,8 @@ int   linkdb_foreach( struct linkdb_node** head, LinkDBFunc func, ...  );
 	do{ \
 	do{ \
 		if( (__n) > VECTOR_CAPACITY(__vec) ) \
 		if( (__n) > VECTOR_CAPACITY(__vec) ) \
 		{ /* increase size */ \
 		{ /* increase size */ \
-			if( VECTOR_CAPACITY(__vec) == 0 ) SET_POINTER(VECTOR_DATA(__vec), aMalloc((__n)*sizeof(VECTOR_FIRST(__vec)))); /* allocate new */ \
-			else SET_POINTER(VECTOR_DATA(__vec), aRealloc(VECTOR_DATA(__vec),(__n)*sizeof(VECTOR_FIRST(__vec)))); /* reallocate */ \
+			if( VECTOR_CAPACITY(__vec) == 0 ) VECTOR_DATA(__vec) = aMalloc((__n)*sizeof(VECTOR_FIRST(__vec))); /* allocate new */ \
+			else VECTOR_DATA(__vec) = aRealloc(VECTOR_DATA(__vec),(__n)*sizeof(VECTOR_FIRST(__vec))); /* reallocate */ \
 			memset(VECTOR_DATA(__vec)+VECTOR_LENGTH(__vec), 0, (VECTOR_CAPACITY(__vec)-VECTOR_LENGTH(__vec))*sizeof(VECTOR_FIRST(__vec))); /* clear new data */ \
 			memset(VECTOR_DATA(__vec)+VECTOR_LENGTH(__vec), 0, (VECTOR_CAPACITY(__vec)-VECTOR_LENGTH(__vec))*sizeof(VECTOR_FIRST(__vec))); /* clear new data */ \
 			VECTOR_CAPACITY(__vec) = (__n); /* update capacity */ \
 			VECTOR_CAPACITY(__vec) = (__n); /* update capacity */ \
 		} \
 		} \
@@ -1099,7 +1146,7 @@ int   linkdb_foreach( struct linkdb_node** head, LinkDBFunc func, ...  );
 		} \
 		} \
 		else if( (__n) < VECTOR_CAPACITY(__vec) ) \
 		else if( (__n) < VECTOR_CAPACITY(__vec) ) \
 		{ /* reduce size */ \
 		{ /* reduce size */ \
-			SET_POINTER(VECTOR_DATA(__vec), aRealloc(VECTOR_DATA(__vec),(__n)*sizeof(VECTOR_FIRST(__vec)))); /* reallocate */ \
+			VECTOR_DATA(__vec) = aRealloc(VECTOR_DATA(__vec),(__n)*sizeof(VECTOR_FIRST(__vec))); /* reallocate */ \
 			VECTOR_CAPACITY(__vec) = (__n); /* update capacity */ \
 			VECTOR_CAPACITY(__vec) = (__n); /* update capacity */ \
 			if( VECTOR_LENGTH(__vec) > (__n) ) VECTOR_LENGTH(__vec) = (__n); /* update length */ \
 			if( VECTOR_LENGTH(__vec) > (__n) ) VECTOR_LENGTH(__vec) = (__n); /* update length */ \
 		} \
 		} \
@@ -1116,8 +1163,10 @@ int   linkdb_foreach( struct linkdb_node** head, LinkDBFunc func, ...  );
 #define VECTOR_ENSURE(__vec,__n,__step) \
 #define VECTOR_ENSURE(__vec,__n,__step) \
 	do{ \
 	do{ \
 		size_t _empty_ = VECTOR_CAPACITY(__vec)-VECTOR_LENGTH(__vec); \
 		size_t _empty_ = VECTOR_CAPACITY(__vec)-VECTOR_LENGTH(__vec); \
-		while( (__n) > _empty_ ) _empty_ += (__step); \
-		if( _empty_ != VECTOR_CAPACITY(__vec)-VECTOR_LENGTH(__vec) ) VECTOR_RESIZE(__vec,_empty_+VECTOR_LENGTH(__vec)); \
+		if( (__n) > _empty_ ) { \
+			while( (__n) > _empty_ ) _empty_ += (__step); \
+			VECTOR_RESIZE(__vec,_empty_+VECTOR_LENGTH(__vec)); \
+		} \
 	}while(0)
 	}while(0)
 
 
 
 
@@ -1446,7 +1495,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 +1557,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 ) \

+ 137 - 71
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.       *
@@ -39,23 +39,28 @@
  * @encoding US-ASCII                                                        *
  * @encoding US-ASCII                                                        *
  * @see common#ers.h                                                         *
  * @see common#ers.h                                                         *
 \*****************************************************************************/
 \*****************************************************************************/
-#include <stdlib.h>
+
+#include "ers.h"
 
 
 #include "../common/cbasetypes.h"
 #include "../common/cbasetypes.h"
 #include "../common/malloc.h" // CREATE, RECREATE, aMalloc, aFree
 #include "../common/malloc.h" // CREATE, RECREATE, aMalloc, aFree
+#include "../common/nullpo.h"
 #include "../common/showmsg.h" // ShowMessage, ShowError, ShowFatalError, CL_BOLD, CL_NORMAL
 #include "../common/showmsg.h" // ShowMessage, ShowError, ShowFatalError, CL_BOLD, CL_NORMAL
-#include "ers.h"
+
+#include <stdlib.h>
+#include <string.h>
 
 
 #ifndef DISABLE_ERS
 #ifndef DISABLE_ERS
 
 
-#define ERS_ROOT_SIZE 256
-#define ERS_BLOCK_ENTRIES 4096
+#define ERS_BLOCK_ENTRIES 2048
 
 
 struct ers_list
 struct ers_list
 {
 {
 	struct ers_list *Next;
 	struct ers_list *Next;
 };
 };
 
 
+struct ers_instance_t;
+
 typedef struct ers_cache
 typedef struct ers_cache
 {
 {
 	// Allocated object size, including ers_list size
 	// Allocated object size, including ers_list size
@@ -70,25 +75,33 @@ 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 blocks count
 	unsigned int Used;
 	unsigned int Used;
 
 
+	// Objects in-use count
+	unsigned int UsedObjs;
+
+	// Default = ERS_BLOCK_ENTRIES, can be adjusted for performance for individual cache sizes.
+	unsigned int ChunkSize;
+
+	// Misc options, some options are shared from the instance
+	enum ERSOptions Options;
+
 	// Linked list
 	// Linked list
 	struct ers_cache *Next, *Prev;
 	struct ers_cache *Next, *Prev;
 } ers_cache_t;
 } ers_cache_t;
 
 
-typedef struct
-{
+struct ers_instance_t {
 	// Interface to ERS
 	// Interface to ERS
 	struct eri VTable;
 	struct eri VTable;
-	
-	// Name, used for debbuging purpouses
+
+	// Name, used for debugging purposes
 	char *Name;
 	char *Name;
 
 
 	// Misc options
 	// Misc options
@@ -99,18 +112,23 @@ typedef struct
 
 
 	// Count of objects in use, used for detecting memory leaks
 	// Count of objects in use, used for detecting memory leaks
 	unsigned int Count;
 	unsigned int Count;
-} ers_instance_t;
+
+	struct ers_instance_t *Next, *Prev;
+};
 
 
 
 
 // Array containing a pointer for all ers_cache structures
 // Array containing a pointer for all ers_cache structures
-static ers_cache_t *CacheList;
+static ers_cache_t *CacheList = NULL;
+static struct ers_instance_t *InstanceList = NULL;
 
 
-static ers_cache_t *ers_find_cache(unsigned int size)
-{
+/**
+ * @param Options the options from the instance seeking a cache, we use it to give it a cache with matching configuration
+ **/
+static ers_cache_t *ers_find_cache(unsigned int size, enum ERSOptions Options) {
 	ers_cache_t *cache;
 	ers_cache_t *cache;
 
 
 	for (cache = CacheList; cache; cache = cache->Next)
 	for (cache = CacheList; cache; cache = cache->Next)
-		if (cache->ObjectSize == size)
+		if ( cache->ObjectSize == size && cache->Options == ( Options & ERS_CACHE_OPTIONS ) )
 			return cache;
 			return cache;
 
 
 	CREATE(cache, ers_cache_t, 1);
 	CREATE(cache, ers_cache_t, 1);
@@ -120,8 +138,11 @@ static ers_cache_t *ers_find_cache(unsigned int size)
 	cache->Blocks = NULL;
 	cache->Blocks = NULL;
 	cache->Free = 0;
 	cache->Free = 0;
 	cache->Used = 0;
 	cache->Used = 0;
+	cache->UsedObjs = 0;
 	cache->Max = 0;
 	cache->Max = 0;
-	
+	cache->ChunkSize = ERS_BLOCK_ENTRIES;
+	cache->Options = (Options & ERS_CACHE_OPTIONS);
+
 	if (CacheList == NULL)
 	if (CacheList == NULL)
 	{
 	{
 		CacheList = cache;
 		CacheList = cache;
@@ -153,90 +174,84 @@ static void ers_free_cache(ers_cache_t *cache, bool remove)
 		CacheList = cache->Next;
 		CacheList = cache->Next;
 
 
 	aFree(cache->Blocks);
 	aFree(cache->Blocks);
+
 	aFree(cache);
 	aFree(cache);
 }
 }
 
 
-static void *ers_obj_alloc_entry(ERS self)
+static void *ers_obj_alloc_entry(ERS *self)
 {
 {
-	ers_instance_t *instance = (ers_instance_t *)self;
+	struct ers_instance_t *instance = (struct ers_instance_t *)self;
 	void *ret;
 	void *ret;
 
 
-	if (instance == NULL) 
-	{
+	if (instance == NULL) {
 		ShowError("ers_obj_alloc_entry: NULL object, aborting entry freeing.\n");
 		ShowError("ers_obj_alloc_entry: NULL object, aborting entry freeing.\n");
 		return NULL;
 		return NULL;
 	}
 	}
 
 
-	if (instance->Cache->ReuseList != NULL)
-	{
+	if (instance->Cache->ReuseList != NULL) {
 		ret = (void *)((unsigned char *)instance->Cache->ReuseList + sizeof(struct ers_list));
 		ret = (void *)((unsigned char *)instance->Cache->ReuseList + sizeof(struct ers_list));
 		instance->Cache->ReuseList = instance->Cache->ReuseList->Next;
 		instance->Cache->ReuseList = instance->Cache->ReuseList->Next;
-	} 
-	else if (instance->Cache->Free > 0) 
-	{
+	} else if (instance->Cache->Free > 0) {
 		instance->Cache->Free--;
 		instance->Cache->Free--;
 		ret = &instance->Cache->Blocks[instance->Cache->Used - 1][instance->Cache->Free * instance->Cache->ObjectSize + sizeof(struct ers_list)];
 		ret = &instance->Cache->Blocks[instance->Cache->Used - 1][instance->Cache->Free * instance->Cache->ObjectSize + sizeof(struct ers_list)];
-	} 
-	else 
-	{
-		if (instance->Cache->Used == instance->Cache->Max) 
-		{
+	} else {
+		if (instance->Cache->Used == instance->Cache->Max) {
 			instance->Cache->Max = (instance->Cache->Max * 4) + 3;
 			instance->Cache->Max = (instance->Cache->Max * 4) + 3;
 			RECREATE(instance->Cache->Blocks, unsigned char *, instance->Cache->Max);
 			RECREATE(instance->Cache->Blocks, unsigned char *, instance->Cache->Max);
 		}
 		}
 
 
-		CREATE(instance->Cache->Blocks[instance->Cache->Used], unsigned char, instance->Cache->ObjectSize * ERS_BLOCK_ENTRIES);
+		CREATE(instance->Cache->Blocks[instance->Cache->Used], unsigned char, instance->Cache->ObjectSize * instance->Cache->ChunkSize);
 		instance->Cache->Used++;
 		instance->Cache->Used++;
 
 
-		instance->Cache->Free = ERS_BLOCK_ENTRIES -1;
+		instance->Cache->Free = instance->Cache->ChunkSize -1;
 		ret = &instance->Cache->Blocks[instance->Cache->Used - 1][instance->Cache->Free * instance->Cache->ObjectSize + sizeof(struct ers_list)];
 		ret = &instance->Cache->Blocks[instance->Cache->Used - 1][instance->Cache->Free * instance->Cache->ObjectSize + sizeof(struct ers_list)];
 	}
 	}
 
 
 	instance->Count++;
 	instance->Count++;
+	instance->Cache->UsedObjs++;
 
 
 	return ret;
 	return ret;
 }
 }
 
 
-static void ers_obj_free_entry(ERS self, void *entry)
+static void ers_obj_free_entry(ERS *self, void *entry)
 {
 {
-	ers_instance_t *instance = (ers_instance_t *)self;
+	struct ers_instance_t *instance = (struct ers_instance_t *)self;
 	struct ers_list *reuse = (struct ers_list *)((unsigned char *)entry - sizeof(struct ers_list));
 	struct ers_list *reuse = (struct ers_list *)((unsigned char *)entry - sizeof(struct ers_list));
 
 
-	if (instance == NULL) 
-	{
+	if (instance == NULL) {
 		ShowError("ers_obj_free_entry: NULL object, aborting entry freeing.\n");
 		ShowError("ers_obj_free_entry: NULL object, aborting entry freeing.\n");
 		return;
 		return;
-	} 
-	else if (entry == NULL) 
-	{
+	} else if (entry == NULL) {
 		ShowError("ers_obj_free_entry: NULL entry, nothing to free.\n");
 		ShowError("ers_obj_free_entry: NULL entry, nothing to free.\n");
 		return;
 		return;
 	}
 	}
 
 
+	if( instance->Cache->Options & ERS_OPT_CLEAN )
+		memset((unsigned char*)reuse + sizeof(struct ers_list), 0, instance->Cache->ObjectSize - sizeof(struct ers_list));
+
 	reuse->Next = instance->Cache->ReuseList;
 	reuse->Next = instance->Cache->ReuseList;
 	instance->Cache->ReuseList = reuse;
 	instance->Cache->ReuseList = reuse;
 	instance->Count--;
 	instance->Count--;
+	instance->Cache->UsedObjs--;
 }
 }
 
 
-static size_t ers_obj_entry_size(ERS self)
+static size_t ers_obj_entry_size(ERS *self)
 {
 {
-	ers_instance_t *instance = (ers_instance_t *)self;
+	struct ers_instance_t *instance = (struct ers_instance_t *)self;
 
 
-	if (instance == NULL) 
-	{
+	if (instance == NULL) {
 		ShowError("ers_obj_entry_size: NULL object, aborting entry freeing.\n");
 		ShowError("ers_obj_entry_size: NULL object, aborting entry freeing.\n");
 		return 0;
 		return 0;
-	} 
+	}
 
 
 	return instance->Cache->ObjectSize;
 	return instance->Cache->ObjectSize;
 }
 }
 
 
-static void ers_obj_destroy(ERS self)
+static void ers_obj_destroy(ERS *self)
 {
 {
-	ers_instance_t *instance = (ers_instance_t *)self;
+	struct ers_instance_t *instance = (struct ers_instance_t *)self;
 
 
-	if (instance == NULL) 
-	{
+	if (instance == NULL) {
 		ShowError("ers_obj_destroy: NULL object, aborting entry freeing.\n");
 		ShowError("ers_obj_destroy: NULL object, aborting entry freeing.\n");
 		return;
 		return;
 	}
 	}
@@ -248,54 +263,105 @@ static void ers_obj_destroy(ERS self)
 	if (--instance->Cache->ReferenceCount <= 0)
 	if (--instance->Cache->ReferenceCount <= 0)
 		ers_free_cache(instance->Cache, true);
 		ers_free_cache(instance->Cache, true);
 
 
+	if (instance->Next)
+		instance->Next->Prev = instance->Prev;
+
+	if (instance->Prev)
+		instance->Prev->Next = instance->Next;
+	else
+		InstanceList = instance->Next;
+
+	if( instance->Options & ERS_OPT_FREE_NAME )
+		aFree(instance->Name);
+
 	aFree(instance);
 	aFree(instance);
 }
 }
 
 
-ERS ers_new(uint32 size, char *name, enum ERSOptions options)
+void ers_cache_size(ERS *self, unsigned int new_size) {
+	struct ers_instance_t *instance = (struct ers_instance_t *)self;
+
+	nullpo_retv(instance);
+
+	if( !(instance->Cache->Options&ERS_OPT_FLEX_CHUNK) ) {
+		ShowWarning("ers_cache_size: '%s' has adjusted its chunk size to '%d', however ERS_OPT_FLEX_CHUNK is missing!\n",instance->Name,new_size);
+	}
+
+	instance->Cache->ChunkSize = new_size;
+}
+
+
+ERS *ers_new(uint32 size, char *name, enum ERSOptions options)
 {
 {
-	ers_instance_t *instance;
-	CREATE(instance, ers_instance_t, 1);
+	struct ers_instance_t *instance;
+	CREATE(instance,struct ers_instance_t, 1);
 
 
 	size += sizeof(struct ers_list);
 	size += sizeof(struct ers_list);
+
+#if ERS_ALIGNED > 1 // If it's aligned to 1-byte boundaries, no need to bother.
 	if (size % ERS_ALIGNED)
 	if (size % ERS_ALIGNED)
 		size += ERS_ALIGNED - size % ERS_ALIGNED;
 		size += ERS_ALIGNED - size % ERS_ALIGNED;
+#endif
 
 
 	instance->VTable.alloc = ers_obj_alloc_entry;
 	instance->VTable.alloc = ers_obj_alloc_entry;
 	instance->VTable.free = ers_obj_free_entry;
 	instance->VTable.free = ers_obj_free_entry;
 	instance->VTable.entry_size = ers_obj_entry_size;
 	instance->VTable.entry_size = ers_obj_entry_size;
 	instance->VTable.destroy = ers_obj_destroy;
 	instance->VTable.destroy = ers_obj_destroy;
+	instance->VTable.chunk_size = ers_cache_size;
 
 
-	instance->Name = name;
+	instance->Name = ( options & ERS_OPT_FREE_NAME ) ? aStrdup(name) : name;
 	instance->Options = options;
 	instance->Options = options;
 
 
-	instance->Cache = ers_find_cache(size);
+	instance->Cache = ers_find_cache(size,instance->Options);
+
 	instance->Cache->ReferenceCount++;
 	instance->Cache->ReferenceCount++;
 
 
+	if (InstanceList == NULL) {
+		InstanceList = instance;
+	} else {
+		instance->Next = InstanceList;
+		instance->Next->Prev = instance;
+		InstanceList = instance;
+		InstanceList->Prev = NULL;
+	}
+
 	instance->Count = 0;
 	instance->Count = 0;
 
 
 	return &instance->VTable;
 	return &instance->VTable;
 }
 }
 
 
-void ers_report(void)
-{
+void ers_report(void) {
 	ers_cache_t *cache;
 	ers_cache_t *cache;
-	int i = 0;
+	unsigned int cache_c = 0, blocks_u = 0, blocks_a = 0, memory_b = 0, memory_t = 0;
+
 	for (cache = CacheList; cache; cache = cache->Next) {
 	for (cache = CacheList; cache; cache = cache->Next) {
-		ShowMessage(CL_BOLD"[Entry manager #%u report]\n"CL_NORMAL, ++i);
+		cache_c++;
+		ShowMessage(CL_BOLD"[ERS Cache of size '"CL_NORMAL""CL_WHITE"%u"CL_NORMAL""CL_BOLD"' report]\n"CL_NORMAL, cache->ObjectSize);
 		ShowMessage("\tinstances          : %u\n", cache->ReferenceCount);
 		ShowMessage("\tinstances          : %u\n", cache->ReferenceCount);
-		ShowMessage("\tblock array size   : %u\n", cache->ObjectSize);
-		ShowMessage("\tallocated blocks   : %u\n", cache->Free+cache->Used);
-		ShowMessage("\tentries being used : %u\n", cache->Used);
-		ShowMessage("\tunused entries     : %u\n", cache->Free);
+		ShowMessage("\tblocks in use      : %u/%u\n", cache->UsedObjs, cache->UsedObjs+cache->Free);
+		ShowMessage("\tblocks unused      : %u\n", cache->Free);
+		ShowMessage("\tmemory in use      : %.2f MB\n", cache->UsedObjs == 0 ? 0. : (double)((cache->UsedObjs * cache->ObjectSize)/1024)/1024);
+		ShowMessage("\tmemory allocated   : %.2f MB\n", (cache->Free+cache->UsedObjs) == 0 ? 0. : (double)(((cache->UsedObjs+cache->Free) * cache->ObjectSize)/1024)/1024);
+		blocks_u += cache->UsedObjs;
+		blocks_a += cache->UsedObjs + cache->Free;
+		memory_b += cache->UsedObjs * cache->ObjectSize;
+		memory_t += (cache->UsedObjs+cache->Free) * cache->ObjectSize;
 	}
 	}
+	ShowInfo("ers_report: '"CL_WHITE"%u"CL_NORMAL"' caches in use\n",cache_c);
+	ShowInfo("ers_report: '"CL_WHITE"%u"CL_NORMAL"' blocks in use, consuming '"CL_WHITE"%.2f MB"CL_NORMAL"'\n",blocks_u,(double)((memory_b)/1024)/1024);
+	ShowInfo("ers_report: '"CL_WHITE"%u"CL_NORMAL"' blocks total, consuming '"CL_WHITE"%.2f MB"CL_NORMAL"' \n",blocks_a,(double)((memory_t)/1024)/1024);
 }
 }
 
 
-void ers_force_destroy_all(void)
-{
-	ers_cache_t *cache;
-	
-	for (cache = CacheList; cache; cache = cache->Next)
-			ers_free_cache(cache, false);
+/**
+ * Call on shutdown to clear remaining entries
+ **/
+void ers_final(void) {
+	struct ers_instance_t *instance = InstanceList, *next;
+
+	while( instance ) {
+		next = instance->Next;
+		ers_obj_destroy((ERS*)instance);
+		instance = next;
+	}
 }
 }
 
 
 #endif
 #endif

+ 36 - 30
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.       *
@@ -49,13 +49,13 @@
  *  ERS                   - Entry manager.                                   *
  *  ERS                   - Entry manager.                                   *
  *  ers_new               - Allocate an instance of an entry manager.        *
  *  ers_new               - Allocate an instance of an entry manager.        *
  *  ers_report            - Print a report about the current state.          *
  *  ers_report            - Print a report about the current state.          *
- *  ers_force_destroy_all - Force the destruction of all the managers.       *
+ *  ers_final             - Clears the remainder of the managers.           *
 \*****************************************************************************/
 \*****************************************************************************/
 
 
 /**
 /**
  * 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,16 +63,23 @@
  * 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
 #endif /* not ERS_ALIGN_ENTRY */
 #endif /* not ERS_ALIGN_ENTRY */
 
 
 enum ERSOptions {
 enum ERSOptions {
-	ERS_OPT_NONE           = 0,
-	ERS_OPT_CLEAR          = 1,/* silently clears any entries left in the manager upon destruction */
+	ERS_OPT_NONE        = 0x00,
+	ERS_OPT_CLEAR       = 0x01,/* silently clears any entries left in the manager upon destruction */
+	ERS_OPT_WAIT        = 0x02,/* wait for entries to come in order to list! */
+	ERS_OPT_FREE_NAME   = 0x04,/* name is dynamic memory, and should be freed */
+	ERS_OPT_CLEAN       = 0x08,/* clears used memory upon ers_free so that its all new to be reused on the next alloc */
+	ERS_OPT_FLEX_CHUNK  = 0x10,/* signs that it should look for its own cache given it'll have a dynamic chunk size, so that it doesn't affect the other ERS it'd otherwise be sharing */
+
+	/* Compound, is used to determine whether it should be looking for a cache of matching options */
+	ERS_CACHE_OPTIONS   = ERS_OPT_CLEAN|ERS_OPT_FLEX_CHUNK,
 };
 };
 
 
 /**
 /**
@@ -95,7 +102,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,13 +118,15 @@ 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
 	 */
 	 */
 	void (*destroy)(struct eri *self);
 	void (*destroy)(struct eri *self);
 
 
-} *ERS;
+	/* */
+	void (*chunk_size) (struct eri *self, unsigned int new_size);
+} ERS;
 
 
 #ifdef DISABLE_ERS
 #ifdef DISABLE_ERS
 // Use memory manager to allocate/free and disable other interface functions
 // Use memory manager to allocate/free and disable other interface functions
@@ -125,48 +134,45 @@ typedef struct eri {
 #	define ers_free(obj,entry) aFree(entry)
 #	define ers_free(obj,entry) aFree(entry)
 #	define ers_entry_size(obj) (size_t)0
 #	define ers_entry_size(obj) (size_t)0
 #	define ers_destroy(obj)
 #	define ers_destroy(obj)
+#	define ers_chunk_size(obj,size)
 // Disable the public functions
 // Disable the public functions
 #	define ers_new(size,name,options) NULL
 #	define ers_new(size,name,options) NULL
 #	define ers_report()
 #	define ers_report()
-#	define ers_force_destroy_all()
+#	define ers_final()
 #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_free(obj,entry) (obj)->free((obj),(entry))
-#	define ers_entry_size(obj) (obj)->entry_size(obj)
-#	define ers_destroy(obj)    (obj)->destroy(obj)
+#	define ers_alloc(obj,type) ((type *)(obj)->alloc(obj))
+#	define ers_free(obj,entry) ((obj)->free((obj),(entry)))
+#	define ers_entry_size(obj) ((obj)->entry_size(obj))
+#	define ers_destroy(obj)    ((obj)->destroy(obj))
+#	define ers_chunk_size(obj,size) ((obj)->chunk_size((obj),(size)))
 
 
 /**
 /**
  * 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
  */
  */
-ERS ers_new(uint32 size, char *name, enum ERSOptions options);
+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.
  */
  */
 void ers_report(void);
 void ers_report(void);
 
 
 /**
 /**
- * Forcibly destroy all the entry managers, checking for nothing.
- * The system is left as if no instances or entries had ever been allocated.
- * All previous entries and instances of the managers become invalid.
- * The use of this is NOT recommended.
- * It should only be used in extreme situations to make shure all the memory 
- * allocated by this system is released.
- */
-void ers_force_destroy_all(void);
+ * Clears the remainder of the managers
+ **/
+void ers_final(void);
 #endif /* DISABLE_ERS / not DISABLE_ERS */
 #endif /* DISABLE_ERS / not DISABLE_ERS */
 
 
 #endif /* _ERS_H_ */
 #endif /* _ERS_H_ */

+ 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]
@@ -482,15 +481,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;
@@ -814,6 +804,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;
 }
 }

+ 9 - 9
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"
@@ -8956,7 +8957,7 @@ ACMD_FUNC(set) {
 	int toset = 0, len;
 	int toset = 0, len;
 	bool is_str = false;
 	bool is_str = false;
 
 
-	if( !message || !*message || (toset = sscanf(message, "%31s %128[^\n]s", reg, val)) < 1  ) {
+	if( !message || !*message || (toset = sscanf(message, "%31s %127[^\n]s", reg, val)) < 1  ) {
 		clif_displaymessage(fd, msg_txt(sd,1367)); // Usage: @set <variable name> <value>
 		clif_displaymessage(fd, msg_txt(sd,1367)); // Usage: @set <variable name> <value>
 		clif_displaymessage(fd, msg_txt(sd,1368)); // Usage: ex. "@set PoringCharVar 50"
 		clif_displaymessage(fd, msg_txt(sd,1368)); // Usage: ex. "@set PoringCharVar 50"
 		clif_displaymessage(fd, msg_txt(sd,1369)); // Usage: ex. "@set PoringCharVarSTR$ Super Duper String"
 		clif_displaymessage(fd, msg_txt(sd,1369)); // Usage: ex. "@set PoringCharVarSTR$ Super Duper String"
@@ -9004,12 +9005,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;
 		}
 		}
 
 
@@ -9033,18 +9034,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
@@ -9379,7 +9379,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);
@@ -1932,8 +1935,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

@@ -14276,7 +14276,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);
 }
 }

+ 16 - 18
src/map/guild.c

@@ -96,15 +96,14 @@ static TBL_PC* guild_sd_check(int guild_id, uint32 account_id, uint32 char_id) {
 	return sd;
 	return sd;
 }
 }
 
 
- // Modified [Komurka]
+// Modified [Komurka]
 int guild_skill_get_max (int id) {
 int guild_skill_get_max (int id) {
 	if ((id = guild_skill_get_index(id)) < 0)
 	if ((id = guild_skill_get_index(id)) < 0)
 		return 0;
 		return 0;
 	return guild_skill_tree[id].max;
 	return guild_skill_tree[id].max;
 }
 }
 
 
-// Retrive skill_lv learned by guild
-
+// Retrieve skill_lv learned by guild
 int guild_checkskill(struct guild *g, int id) {
 int guild_checkskill(struct guild *g, int id) {
 	if ((id = guild_skill_get_index(id)) < 0)
 	if ((id = guild_skill_get_index(id)) < 0)
 		return 0;
 		return 0;
@@ -187,7 +186,7 @@ struct guild* guild_searchname(char* str) {
 	struct guild* g;
 	struct guild* g;
 	DBIterator *iter = db_iterator(guild_db);
 	DBIterator *iter = db_iterator(guild_db);
 
 
-	for( g = dbi_first(iter); dbi_exists(iter); g = dbi_next(iter) ) 	{
+	for( g = dbi_first(iter); dbi_exists(iter); g = dbi_next(iter) ) {
 		if( strcmpi(g->name, str) == 0 )
 		if( strcmpi(g->name, str) == 0 )
 			break;
 			break;
 	}
 	}
@@ -206,7 +205,7 @@ struct guild_castle* guild_mapindex2gc(short mapindex) {
 	struct guild_castle* gc;
 	struct guild_castle* gc;
 	DBIterator *iter = db_iterator(castle_db);
 	DBIterator *iter = db_iterator(castle_db);
 
 
-	for( gc = dbi_first(iter); dbi_exists(iter); gc = dbi_next(iter) ) 	{
+	for( gc = dbi_first(iter); dbi_exists(iter); gc = dbi_next(iter) ) {
 		if( gc->mapindex == mapindex )
 		if( gc->mapindex == mapindex )
 			break;
 			break;
 	}
 	}
@@ -256,17 +255,17 @@ void guild_makemember(struct guild_member *m,struct map_session_data *sd) {
 	nullpo_retv(sd);
 	nullpo_retv(sd);
 
 
 	memset(m,0,sizeof(struct guild_member));
 	memset(m,0,sizeof(struct guild_member));
-	m->account_id	=sd->status.account_id;
-	m->char_id		=sd->status.char_id;
-	m->hair			=sd->status.hair;
-	m->hair_color	=sd->status.hair_color;
-	m->gender		=sd->status.sex;
-	m->class_		=sd->status.class_;
-	m->lv			=sd->status.base_level;
-//	m->exp			=0;
-//	m->exp_payper	=0;
-	m->online		=1;
-	m->position		=MAX_GUILDPOSITION-1;
+	m->account_id	= sd->status.account_id;
+	m->char_id		= sd->status.char_id;
+	m->hair			= sd->status.hair;
+	m->hair_color	= sd->status.hair_color;
+	m->gender		= sd->status.sex;
+	m->class_		= sd->status.class_;
+	m->lv			= sd->status.base_level;
+//	m->exp			= 0;
+//	m->exp_payper	= 0;
+	m->online		= 1;
+	m->position		= MAX_GUILDPOSITION-1;
 	memcpy(m->name,sd->status.name,NAME_LENGTH);
 	memcpy(m->name,sd->status.name,NAME_LENGTH);
 	return;
 	return;
 }
 }
@@ -1960,12 +1959,11 @@ int guild_castledatasave(int castle_id, int index, int value) {
 	return 0;
 	return 0;
 }
 }
 
 
-int guild_castle_reconnect_sub(void *key, void *data, va_list ap) {
+void guild_castle_reconnect_sub(void *key, void *data, va_list ap) {
 	int castle_id = GetWord((int)__64BPRTSIZE(key), 0);
 	int castle_id = GetWord((int)__64BPRTSIZE(key), 0);
 	int index = GetWord((int)__64BPRTSIZE(key), 1);
 	int index = GetWord((int)__64BPRTSIZE(key), 1);
 	intif_guild_castle_datasave(castle_id, index, *(int *)data);
 	intif_guild_castle_datasave(castle_id, index, *(int *)data);
 	aFree(data);
 	aFree(data);
-	return 1;
 }
 }
 
 
 /**
 /**

+ 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

@@ -26,7 +26,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);

+ 225 - 110
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,140 @@ 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( i64db_exists(regs.vars, reference_uid(s, i)) ) {
+			ShowWarning("load_mapreg: duplicate! '%s' => '%s' skipping...\n",varname,value);
+			continue;
+		}
+		if( varname[length-1] == '$' ) {
+			mapreg_setregstr(reference_uid(s, i), value);
+		} else {
+			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);
+	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);
+}
 
 
-	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];
+/**
+ * Timer event to auto-save permanent variables.
+ */
+static int script_autosave_mapreg(int tid, unsigned int tick, int id, intptr_t data)
+{
+	script_save_mapreg();
+	return 0;
+}
 
 
-		if( name[1] == '@' )
-			continue;
+/**
+ * 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;
 
 
-		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);
-	}
-	dbi_destroy(iter);
+	if (data->type != DB_DATA_PTR) // Sanity check
+		return 0;
 
 
-	mapreg_dirty = false;
-}
+	m = db_data2ptr(data);
 
 
-static int script_autosave_mapreg(int tid, unsigned int tick, int id, intptr_t data)
-{
-	if( mapreg_dirty )
-		script_save_mapreg();
+	if (m->is_string) {
+		if (m->u.str)
+			aFree(m->u.str);
+	}
+	ers_free(mapreg_ers, m);
 
 
 	return 0;
 	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);
+		regs.arrays = NULL;
+	}
 
 
 	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->destroy(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_CLEAN);
+
+	skip_insert = false;
+	regs.arrays = NULL;
 
 
 	script_load_mapreg();
 	script_load_mapreg();
 
 
@@ -223,9 +335,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_ */

+ 3 - 3
src/map/mob.c

@@ -2711,11 +2711,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 )
@@ -4981,7 +4981,7 @@ void do_init_mob(void){
 	memset(mob_db_data,0,sizeof(mob_db_data)); //Clear the array
 	memset(mob_db_data,0,sizeof(mob_db_data)); //Clear the array
 	mob_db_data[0] = (struct mob_db*)aCalloc(1, sizeof (struct mob_db));	//This mob is used for random spawns
 	mob_db_data[0] = (struct mob_db*)aCalloc(1, sizeof (struct mob_db));	//This mob is used for random spawns
 	mob_makedummymobdb(0); //The first time this is invoked, it creates the dummy mob
 	mob_makedummymobdb(0); //The first time this is invoked, it creates the dummy mob
-	item_drop_ers = ers_new(sizeof(struct item_drop),"mob.c::item_drop_ers",ERS_OPT_NONE);
+	item_drop_ers = ers_new(sizeof(struct item_drop),"mob.c::item_drop_ers",ERS_OPT_CLEAN);
 	item_drop_list_ers = ers_new(sizeof(struct item_drop_list),"mob.c::item_drop_list_ers",ERS_OPT_NONE);
 	item_drop_list_ers = ers_new(sizeof(struct item_drop_list),"mob.c::item_drop_list_ers",ERS_OPT_NONE);
 	mob_item_drop_ratio = idb_alloc(DB_OPT_BASE);
 	mob_item_drop_ratio = idb_alloc(DB_OPT_BASE);
 	mob_skill_db = idb_alloc(DB_OPT_BASE);
 	mob_skill_db = idb_alloc(DB_OPT_BASE);

+ 13 - 10
src/map/npc.c

@@ -1280,11 +1280,14 @@ int npc_click(struct map_session_data* sd, struct npc_data* nd)
  *------------------------------------------*/
  *------------------------------------------*/
 int npc_scriptcont(struct map_session_data* sd, int id, bool closing)
 int npc_scriptcont(struct map_session_data* sd, int id, bool closing)
 {
 {
+	struct block_list *target = map_id2bl(id);
+
 	nullpo_retr(1, sd);
 	nullpo_retr(1, sd);
 
 
 	if( id != sd->npc_id ){
 	if( id != sd->npc_id ){
-		TBL_NPC* nd_sd=(TBL_NPC*)map_id2bl(sd->npc_id);
-		TBL_NPC* nd=(TBL_NPC*)map_id2bl(id);
+		TBL_NPC* nd_sd = (TBL_NPC*)map_id2bl(sd->npc_id);
+		TBL_NPC* nd = BL_CAST(BL_NPC, target);
+
 		ShowDebug("npc_scriptcont: %s (sd->npc_id=%d) is not %s (id=%d).\n",
 		ShowDebug("npc_scriptcont: %s (sd->npc_id=%d) is not %s (id=%d).\n",
 			nd_sd?(char*)nd_sd->name:"'Unknown NPC'", (int)sd->npc_id,
 			nd_sd?(char*)nd_sd->name:"'Unknown NPC'", (int)sd->npc_id,
 			nd?(char*)nd->name:"'Unknown NPC'", (int)id);
 			nd?(char*)nd->name:"'Unknown NPC'", (int)id);
@@ -1292,7 +1295,7 @@ int npc_scriptcont(struct map_session_data* sd, int id, bool closing)
 	}
 	}
 
 
 	if(id != fake_nd->bl.id) { // Not item script
 	if(id != fake_nd->bl.id) { // Not item script
-		if ((npc_checknear(sd,map_id2bl(id))) == NULL){
+		if ((npc_checknear(sd, target)) == NULL) {
 			ShowWarning("npc_scriptcont: failed npc_checknear test.\n");
 			ShowWarning("npc_scriptcont: failed npc_checknear test.\n");
 			return 1;
 			return 1;
 		}
 		}
@@ -1307,6 +1310,9 @@ int npc_scriptcont(struct map_session_data* sd, int id, bool closing)
 	if( sd->progressbar.npc_id && DIFF_TICK(sd->progressbar.timeout,gettick()) > 0 )
 	if( sd->progressbar.npc_id && DIFF_TICK(sd->progressbar.timeout,gettick()) > 0 )
 		return 1;
 		return 1;
 
 
+	if (!sd->st)
+		return 1;
+
 	if( closing && sd->st && sd->st->state == CLOSE )
 	if( closing && sd->st && sd->st->state == CLOSE )
 		sd->st->state = END;
 		sd->st->state = END;
 
 
@@ -2065,8 +2071,6 @@ int npc_unload(struct npc_data* nd, bool single) {
 			guild_flag_remove(nd);
 			guild_flag_remove(nd);
 	}
 	}
 
 
-	script_stop_sleeptimers(nd->bl.id);
-
 	aFree(nd);
 	aFree(nd);
 
 
 	return 0;
 	return 0;
@@ -3540,8 +3544,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
 	}
 	}
@@ -3556,11 +3559,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);
 	}
 	}

+ 253 - 302
src/map/pc.c

@@ -1194,6 +1194,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);
@@ -1310,7 +1316,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;
 }
 }
@@ -1322,24 +1328,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)
@@ -1356,31 +1365,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;
@@ -1849,7 +1858,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))
@@ -1866,7 +1875,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))
@@ -4152,11 +4161,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 );
 	}
 	}
@@ -4184,7 +4193,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 );
 		}
 		}
@@ -4210,7 +4219,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 );
 		}
 		}
@@ -6888,7 +6897,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);
@@ -7024,7 +7033,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;
@@ -7038,7 +7047,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;
 }
 }
@@ -7279,7 +7288,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
@@ -8053,12 +8062,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) {
@@ -8066,11 +8075,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) {
@@ -8078,11 +8087,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
@@ -8517,332 +8526,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;
 }
 }
 
 
 /**
 /**
@@ -8873,9 +8816,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;
@@ -8906,9 +8849,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;
@@ -11698,6 +11641,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) {
@@ -11738,6 +11683,12 @@ void do_init_pc(void) {
 
 
 	do_init_pc_groups();
 	do_init_pc_groups();
 
 
-	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_FLEX_CHUNK);
 	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_CLEAN|ERS_OPT_FLEX_CHUNK);
+	str_reg_ers = ers_new(sizeof(struct script_reg_str), "pc.c:str_reg_ers", ERS_OPT_CLEAN|ERS_OPT_FLEX_CHUNK);
+
+	ers_chunk_size(pc_sc_display_ers, 150);
+	ers_chunk_size(num_reg_ers, 300);
+	ers_chunk_size(str_reg_ers, 50);
 }
 }

+ 39 - 30
src/map/pc.h

@@ -200,7 +200,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;
@@ -261,7 +260,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];
@@ -465,11 +463,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 {
@@ -607,6 +600,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;
@@ -659,6 +660,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;
 
 
@@ -1024,29 +1033,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);

File diff suppressed because it is too large
+ 497 - 356
src/map/script.c


+ 191 - 19
src/map/script.h

@@ -4,13 +4,138 @@
 #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;
+struct eri;
 
 
 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 +202,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 +233,16 @@ 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;
+	unsigned short instances;
 };
 };
 
 
 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
 };
 };
 
 
 
 
@@ -123,7 +257,7 @@ struct script_state {
 	int pos;
 	int pos;
 	enum e_script_state state;
 	enum e_script_state state;
 	int rid,oid;
 	int rid,oid;
-	struct script_code *script, *scriptroot;
+	struct script_code *script;
 	struct sleep_data {
 	struct sleep_data {
 		int tick,timer,charid;
 		int tick,timer,charid;
 	} sleep;
 	} sleep;
@@ -135,24 +269,41 @@ struct script_state {
 	unsigned npc_item_flag : 1;
 	unsigned npc_item_flag : 1;
 	unsigned mes_active : 1;  // Store if invoking character has a NPC dialog box open.
 	unsigned mes_active : 1;  // Store if invoking character has a NPC dialog box open.
 	char* funcname; // Stores the current running function name
 	char* funcname; // Stores the current running function name
+	unsigned int id;
 };
 };
 
 
 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;
+DBMap *st_db;
+unsigned int active_scripts;
+unsigned int next_id;
+struct eri *st_ers;
+struct eri *stack_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,17 +312,19 @@ 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);
 
 
-void script_stop_sleeptimers(int id);
+void script_stop_instances(struct script_code *code);
 struct linkdb_node* script_erase_sleepdb(struct linkdb_node *n);
 struct linkdb_node* script_erase_sleepdb(struct linkdb_node *n);
 void script_free_code(struct script_code* code);
 void script_free_code(struct script_code* code);
 void script_free_vars(struct DBMap *storage);
 void script_free_vars(struct DBMap *storage);
-struct script_state* script_alloc_state(struct script_code* script, int pos, int rid, int oid);
+struct script_state* script_alloc_state(struct script_code* rootscript, int pos, int rid, int oid);
 void script_free_state(struct script_state* st);
 void script_free_state(struct script_state* st);
 
 
 struct DBMap* script_get_label_db(void);
 struct DBMap* script_get_label_db(void);
@@ -183,7 +336,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 +346,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);

+ 13 - 10
src/map/skill.c

@@ -2775,8 +2775,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
@@ -2799,8 +2799,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;
@@ -6397,7 +6397,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);
 		}
 		}
@@ -8232,7 +8232,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.
 		}
 		}
@@ -18709,7 +18709,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;
 			}
 			}
@@ -18821,7 +18821,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;
 		}
 		}
@@ -20843,8 +20843,11 @@ void do_init_skill(void)
 	skillunit_db = idb_alloc(DB_OPT_BASE);
 	skillunit_db = idb_alloc(DB_OPT_BASE);
 	skillusave_db = idb_alloc(DB_OPT_RELEASE_DATA);
 	skillusave_db = idb_alloc(DB_OPT_RELEASE_DATA);
 	bowling_db = idb_alloc(DB_OPT_BASE);
 	bowling_db = idb_alloc(DB_OPT_BASE);
-	skill_unit_ers = ers_new(sizeof(struct skill_unit_group),"skill.c::skill_unit_ers",ERS_OPT_NONE);
-	skill_timer_ers  = ers_new(sizeof(struct skill_timerskill),"skill.c::skill_timer_ers",ERS_OPT_NONE);
+	skill_unit_ers = ers_new(sizeof(struct skill_unit_group),"skill.c::skill_unit_ers",ERS_OPT_CLEAN|ERS_OPT_FLEX_CHUNK);
+	skill_timer_ers  = ers_new(sizeof(struct skill_timerskill),"skill.c::skill_timer_ers",ERS_OPT_CLEAN|ERS_OPT_FLEX_CHUNK);
+
+	ers_chunk_size(skill_unit_ers, 150);
+	ers_chunk_size(skill_timer_ers, 150);
 
 
 	add_timer_func_list(skill_unit_timer,"skill_unit_timer");
 	add_timer_func_list(skill_unit_timer,"skill_unit_timer");
 	add_timer_func_list(skill_castend_id,"skill_castend_id");
 	add_timer_func_list(skill_castend_id,"skill_castend_id");

+ 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

@@ -3161,24 +3161,6 @@ int unit_free(struct block_list *bl, clr_type clrtype)
 			pc_delspiritball(sd, sd->spiritball, 1);
 			pc_delspiritball(sd, sd->spiritball, 1);
 			pc_delspiritcharm(sd, sd->spiritcharm, sd->spiritcharm_type);
 			pc_delspiritcharm(sd, sd->spiritcharm, sd->spiritcharm_type);
 
 
-			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;

Some files were not shown because too many files changed in this diff