Browse Source

Skill_colldown

Move skillcooldown from ram saving to sql saving.
This will fix multimap skillcooldown and avoind increasing ram by
offline users.
lighta 11 years ago
parent
commit
69b28292cf
15 changed files with 313 additions and 149 deletions
  1. 14 0
      sql-files/main.sql
  2. 8 0
      sql-files/upgrades/upgrade_svn17541.sql
  3. 77 0
      src/char/char.c
  4. 6 0
      src/common/mmo.h
  5. 2 1
      src/map/battle.c
  6. 86 6
      src/map/chrif.c
  7. 3 0
      src/map/chrif.h
  8. 12 11
      src/map/guild.c
  9. 3 6
      src/map/intif.c
  10. 2 2
      src/map/npc.c
  11. 1 6
      src/map/pc.c
  12. 10 1
      src/map/pc.h
  13. 84 114
      src/map/skill.c
  14. 4 2
      src/map/skill.h
  15. 1 0
      src/map/unit.c

+ 14 - 0
sql-files/main.sql

@@ -1,3 +1,17 @@
+
+--
+-- Table structure for table `skillcooldown`
+--
+
+CREATE TABLE IF NOT EXISTS `skillcooldown` (
+  `account_id` int(11) unsigned NOT NULL,
+  `char_id` int(11) unsigned NOT NULL,
+  `skill` smallint(11) unsigned NOT NULL DEFAULT '0',
+  `tick` int(11) NOT NULL,
+  KEY `account_id` (`account_id`),
+  KEY `char_id` (`char_id`)
+) ENGINE=MyISAM DEFAULT CHARSET=latin1;
+
 --
 -- Table structure for table `auction`
 --

+ 8 - 0
sql-files/upgrades/upgrade_svn17541.sql

@@ -0,0 +1,8 @@
+CREATE TABLE IF NOT EXISTS `skillcooldown` (
+  `account_id` int(11) unsigned NOT NULL,
+  `char_id` int(11) unsigned NOT NULL,
+  `skill` smallint(11) unsigned NOT NULL DEFAULT '0',
+  `tick` int(11) NOT NULL,
+  KEY `account_id` (`account_id`),
+  KEY `char_id` (`char_id`)
+) ENGINE=MyISAM DEFAULT CHARSET=latin1;

+ 77 - 0
src/char/char.c

@@ -39,6 +39,7 @@ static char* msg_table[CHAR_MAX_MSG]; // Login Server messages_conf
 
 char char_db[256] = "char";
 char scdata_db[256] = "sc_data";
+char skillcooldown_db[256] = "skillcooldown";
 char cart_db[256] = "cart_inventory";
 char inventory_db[256] = "inventory";
 char charlog_db[256] = "charlog";
@@ -2923,6 +2924,51 @@ int parse_frommap(int fd)
 		}
 		break;
 
+		case 0x2b0a: //Request skillcooldown data
+			if (RFIFOREST(fd) < 10)
+				return 0;
+		{
+			int aid, cid;
+			aid = RFIFOL(fd,2);
+			cid = RFIFOL(fd,6);
+			if( SQL_ERROR == Sql_Query(sql_handle, "SELECT skill, tick FROM `%s` WHERE `account_id` = '%d' AND `char_id`='%d'",
+				skillcooldown_db, aid, cid) )
+			{
+				Sql_ShowDebug(sql_handle);
+				break;
+			}
+			if( Sql_NumRows(sql_handle) > 0 )
+			{
+				int count;
+				char* data;
+				struct skill_cooldown_data scd;
+
+				WFIFOHEAD(fd,14 + MAX_SKILLCOOLDOWN * sizeof(struct skill_cooldown_data));
+				WFIFOW(fd,0) = 0x2b0b;
+				WFIFOL(fd,4) = aid;
+				WFIFOL(fd,8) = cid;
+				for( count = 0; count < MAX_SKILLCOOLDOWN && SQL_SUCCESS == Sql_NextRow(sql_handle); ++count )
+				{
+					Sql_GetData(sql_handle, 0, &data, NULL); scd.skill_id = atoi(data);
+					Sql_GetData(sql_handle, 1, &data, NULL); scd.tick = atoi(data);
+					memcpy(WFIFOP(fd,14+count*sizeof(struct skill_cooldown_data)), &scd, sizeof(struct skill_cooldown_data));
+				}
+				if( count >= MAX_SKILLCOOLDOWN )
+					ShowWarning("Too many skillcooldowns for %d:%d, some of them were not loaded.\n", aid, cid);
+				if( count > 0 )
+				{
+					WFIFOW(fd,2) = 14 + count * sizeof(struct skill_cooldown_data);
+					WFIFOW(fd,12) = count;
+					WFIFOSET(fd,WFIFOW(fd,2));
+					//Clear the data once loaded.
+					if( SQL_ERROR == Sql_Query(sql_handle, "DELETE FROM `%s` WHERE `account_id` = '%d' AND `char_id`='%d'", skillcooldown_db, aid, cid) )
+						Sql_ShowDebug(sql_handle);
+				}
+			}
+			Sql_FreeResult(sql_handle);
+			RFIFOSKIP(fd, 10);
+		}
+		break;
 		case 0x2afe: //set MAP user count
 			if (RFIFOREST(fd) < 4)
 				return 0;
@@ -3398,6 +3444,37 @@ int parse_frommap(int fd)
 		}
 		break;
 
+		case 0x2b15: //Request to save skill cooldown data
+			if( RFIFOREST(fd) < 4 || RFIFOREST(fd) < RFIFOW(fd,2) )
+				return 0;
+		{
+			int count, aid, cid;
+			aid = RFIFOL(fd,4);
+			cid = RFIFOL(fd,8);
+			count = RFIFOW(fd,12);
+			if( count > 0 )
+			{
+				struct skill_cooldown_data data;
+				StringBuf buf;
+				int i;
+
+				StringBuf_Init(&buf);
+				StringBuf_Printf(&buf, "INSERT INTO `%s` (`account_id`, `char_id`, `skill`, `tick`) VALUES ", skillcooldown_db);
+				for( i = 0; i < count; ++i )
+				{
+					memcpy(&data,RFIFOP(fd,14+i*sizeof(struct skill_cooldown_data)),sizeof(struct skill_cooldown_data));
+					if( i > 0 )
+						StringBuf_AppendStr(&buf, ", ");
+					StringBuf_Printf(&buf, "('%d','%d','%d','%d')", aid, cid, data.skill_id, data.tick);
+				}
+				if( SQL_ERROR == Sql_QueryStr(sql_handle, StringBuf_Value(&buf)) )
+					Sql_ShowDebug(sql_handle);
+				StringBuf_Destroy(&buf);
+			}
+			RFIFOSKIP(fd, RFIFOW(fd, 2));
+		}
+		break;
+
 		case 0x2b23: // map-server alive packet
 			WFIFOHEAD(fd,2);
 			WFIFOW(fd,0) = 0x2b24;

+ 6 - 0
src/common/mmo.h

@@ -128,6 +128,7 @@
 
 #define MAX_FRIENDS 40
 #define MAX_MEMOPOINTS 3
+#define MAX_SKILLCOOLDOWN 20
 
 //Size of the fame list arrays.
 #define MAX_FAME_LIST 10
@@ -248,6 +249,11 @@ struct status_change_data {
 	long val1, val2, val3, val4, tick; //Remaining duration.
 };
 
+struct skill_cooldown_data {
+	unsigned short skill_id;
+	long tick;
+};
+
 struct storage_data {
 	int storage_amount;
 	struct item items[MAX_STORAGE];

+ 2 - 1
src/map/battle.c

@@ -2021,9 +2021,10 @@ static int is_attack_piercing(struct Damage wd, struct block_list *src, struct b
 				sd->left_weapon.def_ratio_atk_race & (1<<tstatus->race) ||
 				sd->left_weapon.def_ratio_atk_race & (1<<(is_boss(target)?RC_BOSS:RC_NONBOSS))) )
 			{ //Pass effect onto right hand if configured so. [Skotlex]
-				if (battle_config.left_cardfix_to_right && is_attack_right_handed(src, skill_id))
+				if (battle_config.left_cardfix_to_right && is_attack_right_handed(src, skill_id)){
 					if (weapon_position == EQI_HAND_R)
 						return 1;
+				}
 				else if (weapon_position == EQI_HAND_L)
 					return 1;
 			}

+ 86 - 6
src/map/chrif.c

@@ -41,8 +41,8 @@ static DBMap* auth_db; // int id -> struct auth_node*
 static const int packet_len_table[0x3d] = { // U - used, F - free
 	60, 3,-1,27,10,-1, 6,-1,	// 2af8-2aff: U->2af8, U->2af9, U->2afa, U->2afb, U->2afc, U->2afd, U->2afe, U->2aff
 	 6,-1,18, 7,-1,39,30, 10,	// 2b00-2b07: U->2b00, U->2b01, U->2b02, U->2b03, U->2b04, U->2b05, U->2b06, U->2b07
-	 6,30, 0, 0,86, 7,44,34,	// 2b08-2b0f: U->2b08, U->2b09, F->2b0a, F->2b0b, U->2b0c, U->2b0d, U->2b0e, U->2b0f
-	11,10,10, 0,11, 0,266,10,	// 2b10-2b17: U->2b10, U->2b11, U->2b12, F->2b13, U->2b14, F->2b15, U->2b16, U->2b17
+	 6,30, 10, -1,86, 7,44,34,	// 2b08-2b0f: U->2b08, U->2b09, U->2b0a, U->2b0b, U->2b0c, U->2b0d, U->2b0e, U->2b0f
+	11,10,10, 0,11, -1,266,10,	// 2b10-2b17: U->2b10, U->2b11, U->2b12, F->2b13, U->2b14, U->2b15, U->2b16, U->2b17
 	 2,10, 2,-1,-1,-1, 2, 7,	// 2b18-2b1f: U->2b18, U->2b19, U->2b1a, U->2b1b, U->2b1c, U->2b1d, U->2b1e, U->2b1f
 	-1,10, 8, 2, 2,14,19,19,	// 2b20-2b27: U->2b20, U->2b21, U->2b22, U->2b23, U->2b24, U->2b25, U->2b26, U->2b27
 };
@@ -275,8 +275,10 @@ int chrif_save(struct map_session_data *sd, int flag) {
 
 	if (flag && sd->state.active) { //Store player data which is quitting
 		//FIXME: SC are lost if there's no connection at save-time because of the way its related data is cleared immediately after this function. [Skotlex]
-		if ( chrif_isconnected() )
-			chrif_save_scdata(sd);
+        if (chrif_isconnected()) {
+            chrif_save_scdata(sd);
+            chrif_skillcooldown_save(sd);
+        }
 		if ( !chrif_auth_logout(sd,flag == 1 ? ST_LOGOUT : ST_MAPCHANGE) )
 			ShowError("chrif_save: Failed to set up player %d:%d for proper quitting!\n", sd->status.account_id, sd->status.char_id);
 	}
@@ -575,8 +577,20 @@ int chrif_scdata_request(int account_id, int char_id) {
 	WFIFOL(char_fd,6) = char_id;
 	WFIFOSET(char_fd,10);
 #endif
+    return 0;
+}
 
-	return 0;
+/*==========================================
+ * Request skillcooldown from charserver
+ *------------------------------------------*/
+int chrif_skillcooldown_request(int account_id, int char_id) {
+    chrif_check(-1);
+    WFIFOHEAD(char_fd, 10);
+    WFIFOW(char_fd, 0) = 0x2b0a;
+    WFIFOL(char_fd, 2) = account_id;
+    WFIFOL(char_fd, 6) = char_id;
+    WFIFOSET(char_fd, 10);
+    return 0;
 }
 
 /*==========================================
@@ -1214,6 +1228,44 @@ int chrif_save_scdata(struct map_session_data *sd) { //parses the sc_data of the
 	WFIFOW(char_fd,2) = 14 +count*sizeof(struct status_change_data); //Total packet size
 	WFIFOSET(char_fd,WFIFOW(char_fd,2));
 #endif
+    return 0;
+}
+
+int chrif_skillcooldown_save(struct map_session_data *sd) {
+	int i, count = 0;
+	struct skill_cooldown_data data;
+	unsigned int tick;
+	const struct TimerData *timer;
+
+	chrif_check(-1);
+	tick = gettick();
+
+	WFIFOHEAD(char_fd, 14 + MAX_SKILLCOOLDOWN * sizeof (struct skill_cooldown_data));
+	WFIFOW(char_fd, 0) = 0x2b15;
+	WFIFOL(char_fd, 4) = sd->status.account_id;
+	WFIFOL(char_fd, 8) = sd->status.char_id;
+	for (i = 0; i < MAX_SKILLCOOLDOWN; i++) {
+		if (!sd->scd[i])
+			continue;
+
+		if (!battle_config.guild_skill_relog_delay && (sd->scd[i]->skill_id >= GD_BATTLEORDER && sd->scd[i]->skill_id <= GD_EMERGENCYCALL))
+			continue;
+
+		timer = get_timer(sd->scd[i]->timer);
+		if (timer == NULL || timer->func != skill_blockpc_end || DIFF_TICK(timer->tick, tick) < 0)
+			continue;
+
+		data.tick = DIFF_TICK(timer->tick, tick);
+		data.skill_id = sd->scd[i]->skill_id;
+		memcpy(WFIFOP(char_fd, 14 + count * sizeof (struct skill_cooldown_data)), &data, sizeof (struct skill_cooldown_data));
+		count++;
+	}
+	if (count == 0)
+		return 0;
+
+	WFIFOW(char_fd, 12) = count;
+	WFIFOW(char_fd, 2) = 14 + count * sizeof (struct skill_cooldown_data);
+	WFIFOSET(char_fd, WFIFOW(char_fd, 2));
 
 	return 0;
 }
@@ -1248,8 +1300,35 @@ int chrif_load_scdata(int fd) {
 		status_change_start(NULL,&sd->bl, (sc_type)data->type, 10000, data->val1, data->val2, data->val3, data->val4, data->tick, 1|2|4|8);
 	}
 #endif
+    return 0;
+}
 
-	return 0;
+//Retrieve and load skillcooldown for a player
+
+int chrif_skillcooldown_load(int fd) {
+    struct map_session_data *sd;
+    struct skill_cooldown_data *data;
+    int aid, cid, i, count;
+
+    aid = RFIFOL(fd, 4);
+    cid = RFIFOL(fd, 8);
+
+
+    sd = map_id2sd(aid);
+    if (!sd) {
+        ShowError("chrif_skillcooldown_load: Player of AID %d not found!\n", aid);
+        return -1;
+    }
+    if (sd->status.char_id != cid) {
+        ShowError("chrif_skillcooldown_load: Receiving data for account %d, char id does not matches (%d != %d)!\n", aid, sd->status.char_id, cid);
+        return -1;
+    }
+    count = RFIFOW(fd, 12); //sc_count
+    for (i = 0; i < count; i++) {
+        data = (struct skill_cooldown_data*) RFIFOP(fd, 14 + i * sizeof (struct skill_cooldown_data));
+        skill_blockpc_start(sd, data->skill_id, data->tick);
+    }
+    return 0;
 }
 
 /*==========================================
@@ -1443,6 +1522,7 @@ int chrif_parse(int fd) {
 			case 0x2b04: chrif_recvmap(fd); break;
 			case 0x2b06: chrif_changemapserverack(RFIFOL(fd,2), RFIFOL(fd,6), RFIFOL(fd,10), RFIFOL(fd,14), RFIFOW(fd,18), RFIFOW(fd,20), RFIFOW(fd,22), RFIFOL(fd,24), RFIFOW(fd,28)); break;
 			case 0x2b09: map_addnickdb(RFIFOL(fd,2), (char*)RFIFOP(fd,6)); break;
+            case 0x2b0b: chrif_skillcooldown_load(fd); break;
 			case 0x2b0d: chrif_changedsex(fd); break;
 			case 0x2b0f: chrif_char_ask_name_answer(RFIFOL(fd,2), (char*)RFIFOP(fd,6), RFIFOW(fd,30), RFIFOW(fd,32)); break;
 			case 0x2b12: chrif_divorceack(RFIFOL(fd,2), RFIFOL(fd,6)); break;

+ 3 - 0
src/map/chrif.h

@@ -38,6 +38,9 @@ bool chrif_auth_finished(struct map_session_data* sd);
 void chrif_authreq(struct map_session_data* sd);
 void chrif_authok(int fd);
 int chrif_scdata_request(int account_id, int char_id);
+int chrif_skillcooldown_request(int account_id, int char_id);
+int chrif_skillcooldown_save(struct map_session_data *sd);
+int chrif_skillcooldown_load(int fd);
 int chrif_save(struct map_session_data* sd, int flag);
 int chrif_charselectreq(struct map_session_data* sd, uint32 s_ip);
 int chrif_changemapserver(struct map_session_data* sd, uint32 ip, uint16 port);

+ 12 - 11
src/map/guild.c

@@ -856,15 +856,16 @@ int guild_member_withdraw(int guild_id, int account_id, int char_id, int flag, c
 	if( i == -1 )
 		return 0; // not a member (inconsistency!)
 
-	online_member_sd = guild_getavailablesd(g);
-	if(online_member_sd == NULL)
-		return 0; // noone online to inform
-
 #ifdef BOUND_ITEMS
 	//Guild bound item check
 	guild_retrieveitembound(char_id,account_id,guild_id);
 #endif
 
+	online_member_sd = guild_getavailablesd(g);
+	if(online_member_sd == NULL)
+		return 0; // noone online to inform
+
+
 	if(!flag)
 		clif_guild_leave(online_member_sd, name, mes);
 	else
@@ -912,15 +913,15 @@ void guild_retrieveitembound(int char_id,int aid,int guild_id)
 	}
 	else { //Character is offline, ask char server to do the job
 		struct guild_storage* stor = guild2storage2(guild_id);
+		struct guild *g = guild_search(guild_id);
+		int i;
+		nullpo_retv(g);
 		if(stor && stor->storage_status == 1) { //Someone is in guild storage, close them
-			struct s_mapiterator* iter = mapit_getallusers();
-			for( sd = (TBL_PC*)mapit_first(iter); mapit_exists(iter); sd = (TBL_PC*)mapit_next(iter) ) {
-				if(sd->status.guild_id == guild_id && sd->state.storage_flag == 2) {
+			for(i=0; i<g->max_member; i++){
+				TBL_PC *pl_sd = g->member[i].sd;
+				if(pl_sd && pl_sd->state.storage_flag == 2)
 					storage_guild_storageclose(sd);
-					break;
-				}
 			}
-			mapit_free(iter);
 		}
 		intif_itembound_req(char_id,aid,guild_id);
 	}
@@ -1398,7 +1399,7 @@ void guild_block_skill(struct map_session_data *sd, int time)
 	uint16 skill_id[] = { GD_BATTLEORDER, GD_REGENERATION, GD_RESTORE, GD_EMERGENCYCALL };
 	int i;
 	for (i = 0; i < 4; i++)
-		skill_blockpc_start_(sd, skill_id[i], time , true);
+		skill_blockpc_start(sd, skill_id[i], time);
 }
 
 /*====================================================

+ 3 - 6
src/map/intif.c

@@ -2182,16 +2182,13 @@ void intif_itembound_req(int char_id,int aid,int guild_id) {
 	WFIFOL(inter_fd,6) = aid;
 	WFIFOW(inter_fd,10) = guild_id;
 	WFIFOSET(inter_fd,12);
-	if(gstor)
-		gstor->lock = 1; //Lock for retrieval process
+	if(gstor) gstor->lock = 1; //Lock for retrieval process
 }
 
 //3856
 void intif_parse_itembound_ack(int fd) {
-	struct guild_storage *gstor;
-	int guild_id = RFIFOW(char_fd,6);
-
-	gstor = guild2storage2(guild_id);
+	int guild_id = RFIFOW(fd,6);
+	struct guild_storage *gstor = guild2storage2(guild_id);
 	if(gstor) gstor->lock = 0; //Unlock now that operation is completed
 }
 #endif

+ 2 - 2
src/map/npc.c

@@ -963,7 +963,7 @@ int npc_touch_areanpc(struct map_session_data* sd, int16 m, int16 x, int16 y)
 	}
 	switch(map[m].npc[i]->subtype) {
 		case WARP:
-			if (pc_ishiding(sd) || sd->sc.count && sd->sc.data[SC_CAMOUFLAGE] || pc_isdead(sd))
+			if (pc_ishiding(sd) || (sd->sc.count && sd->sc.data[SC_CAMOUFLAGE]) || pc_isdead(sd))
 				break; // hidden or dead chars cannot use warps
 			pc_setpos(sd,map[m].npc[i]->u.warp.mapindex,map[m].npc[i]->u.warp.x,map[m].npc[i]->u.warp.y,CLR_OUTSIGHT);
 			break;
@@ -2725,7 +2725,7 @@ int npc_duplicate4instance(struct npc_data *snd, int16 m) {
 
 		if(!imap)
 			imap = map_mapname2mapid(map[dm].name);
-	
+
 		if( imap == -1 ) {
 			ShowError("npc_duplicate4instance: warp (%s) leading to instanced map (%s), but instance map is not attached to current instance.\n", map[dm].name, snd->exname);
 			return 1;

+ 1 - 6
src/map/pc.c

@@ -1147,11 +1147,6 @@ bool pc_authok(struct map_session_data *sd, int login_id2, time_t expiration_tim
 		clif_changemap(sd,sd->bl.m,sd->bl.x,sd->bl.y);
 	}
 
-	/**
-	 * Check if player have any cool downs on
-	 **/
-	skill_cooldown_load(sd);
-
 	/**
 	 * Check if player have any item cooldowns on
 	 **/
@@ -1296,7 +1291,7 @@ int pc_reg_received(struct map_session_data *sd)
 
 	status_calc_pc(sd,1);
 	chrif_scdata_request(sd->status.account_id, sd->status.char_id);
-
+	chrif_skillcooldown_request(sd->status.account_id, sd->status.char_id);
 	intif_Mail_requestinbox(sd->status.char_id, 0); // MAIL SYSTEM - Request Mail Inbox
 	intif_request_questlog(sd);
 

+ 10 - 1
src/map/pc.h

@@ -115,6 +115,11 @@ struct s_autobonus {
 	unsigned short pos;
 };
 
+struct skill_cooldown_entry {
+    unsigned short skill_id;
+    int timer;
+};
+
 enum npc_timeout_type {
 	NPCT_INPUT = 0,
 	NPCT_MENU  = 1,
@@ -244,7 +249,7 @@ struct map_session_data {
 	uint16 skill_id_old,skill_lv_old;
 	uint16 skill_id_dance,skill_lv_dance;
 	short cook_mastery; // range: [0,1999] [Inkfish]
-	unsigned char blockskill[MAX_SKILL];
+    struct skill_cooldown_entry * scd[MAX_SKILLCOOLDOWN]; // Skill Cooldown
 	int cloneskill_id, reproduceskill_id;
 	int menuskill_id, menuskill_val, menuskill_val2;
 
@@ -321,6 +326,10 @@ struct map_session_data {
 		short flag, rate;
 		unsigned char ele;
 	} subele2[MAX_PC_BONUS];
+	struct {
+		int id;
+		int val;
+	} cooldown[MAX_PC_BONUS];
 	struct {
 		short value;
 		int rate, tick;

+ 84 - 114
src/map/skill.c

@@ -63,20 +63,6 @@ static DBMap* bowling_db = NULL; // int mob_id -> struct mob_data*
 
 DBMap* skillunit_db = NULL; // int id -> struct skill_unit*
 
-/**
- * Skill Cool Down Delay Saving
- * Struct skill_cd is not a member of struct map_session_data
- * to keep cooldowns in memory between player log-ins.
- * All cooldowns are reset when server is restarted.
- **/
-DBMap* skillcd_db = NULL; // char_id -> struct skill_cd
-struct skill_cd {
-	int duration[MAX_SKILL_TREE];//milliseconds
-	short skidx[MAX_SKILL_TREE];//the skill index entries belong to
-	short nameid[MAX_SKILL_TREE];//skill id
-	unsigned char cursor;
-};
-
 /**
  * Skill Unit Persistency during endack routes (mostly for songs see bugreport:4574)
  **/
@@ -257,6 +243,27 @@ int skill_tree_get_max(uint16 skill_id, int b_class)
 		return skill_get_max(skill_id);
 }
 
+int skill_get_cooldown_(struct map_session_data *sd, int id, int lv) {
+	int i, cooldown;
+	int idx = skill_get_index (id);
+	if (!idx) return 0;
+
+	cooldown = 0;
+	if (skill_db[idx].cooldown[lv - 1])
+		cooldown = skill_db[idx].cooldown[lv - 1];
+
+	for (i = 0; i < ARRAYLENGTH(sd->cooldown) && sd->cooldown[i].id; i++) {
+		if (sd->cooldown[i].id == id) {
+			cooldown += sd->cooldown[i].val;
+			if (cooldown < 0)
+				cooldown = 0;
+			break;
+		}
+	}
+
+	return cooldown;
+}
+
 int skill_frostjoke_scream(struct block_list *bl,va_list ap);
 int skill_attack_area(struct block_list *bl,va_list ap);
 struct skill_unit_group *skill_locate_element_field(struct block_list *bl); // [Skotlex]
@@ -446,7 +453,7 @@ static short skill_isCopyable (struct map_session_data *sd, uint16 skill_id, str
 	// Only copy skill that player doesn't have or the skill is old clone
 	if (sd->status.skill[skill_id].id != 0 && sd->status.skill[skill_id].flag != SKILL_FLAG_PLAGIARIZED)
 		return 0;
-	
+
 	// Never copy NPC/Wedding Skills
 	if (skill_get_inf2(skill_id)&(INF2_NPC_SKILL|INF2_WEDDING_SKILL))
 		return 0;
@@ -510,7 +517,7 @@ bool skill_isNotOk(uint16 skill_id, struct map_session_data *sd)
 		return true;
 	}
 
-	if (sd->blockskill[idx] > 0) {
+	if (skill_blockpc_get(sd, skill_id) != -1){
 		clif_skill_fail(sd,skill_id,USESKILL_FAIL_SKILLINTERVAL,0);
 		return true;
 	}
@@ -711,7 +718,7 @@ bool skill_isNotOk_npcRange(struct block_list *src, struct block_list *target, u
 
 	if (src->type == BL_PC && pc_has_permission(BL_CAST(BL_PC,src),PC_PERM_SKILL_UNCONDITIONAL))
 		return false;
-	
+
 	inf = skill_get_inf(skill_id);
 	//if self skill
 	if (inf&INF_SELF_SKILL) {
@@ -9650,15 +9657,8 @@ int skill_castend_id(int tid, unsigned int tick, int id, intptr_t data)
 		if( !sd || sd->skillitem != ud->skill_id || skill_get_delay(ud->skill_id,ud->skill_lv) )
 			ud->canact_tick = tick + skill_delayfix(src, ud->skill_id, ud->skill_lv); //Tests show wings don't overwrite the delay but skill scrolls do. [Inkfish]
 		if (sd) { //Cooldown application
-			int i, cooldown = skill_get_cooldown(ud->skill_id, ud->skill_lv);
-			for (i = 0; i < ARRAYLENGTH(sd->skillcooldown) && sd->skillcooldown[i].id; i++) { // Increases/Decreases cooldown of a skill by item/card bonuses.
-				if (sd->skillcooldown[i].id == ud->skill_id){
-					cooldown += sd->skillcooldown[i].val;
-					break;
-				}
-			}
-			if(cooldown)
-			skill_blockpc_start(sd, ud->skill_id, cooldown);
+			int cooldown = skill_get_cooldown_(sd,ud->skill_id, ud->skill_lv); // Increases/Decreases cooldown of a skill by item/card bonuses.
+			if(cooldown) skill_blockpc_start(sd, ud->skill_id, cooldown);
 		}
 		if( battle_config.display_status_timers && sd )
 			clif_status_change(src, SI_ACTIONDELAY, 1, skill_delayfix(src, ud->skill_id, ud->skill_lv), 0, 0, 0);
@@ -17337,86 +17337,81 @@ static int skill_destroy_trap( struct block_list *bl, va_list ap ) {
 	return 0;
 }
 /*==========================================
- *
- *------------------------------------------*/
+*
+*------------------------------------------*/
+int skill_blockpc_get(struct map_session_data *sd, int skillid) {
+	int i;
+	nullpo_retr(-1, sd);
+
+	ARR_FIND(0, MAX_SKILLCOOLDOWN, i, sd->scd[i] && sd->scd[i]->skill_id == skillid);
+	return (i >= MAX_SKILLCOOLDOWN) ? -1 : i;
+}
+
 int skill_blockpc_end(int tid, unsigned int tick, int id, intptr_t data) {
 	struct map_session_data *sd = map_id2sd(id);
-	struct skill_cd * cd = NULL;
+	int i = (int) data;
 
-	if (data <= 0 || data >= MAX_SKILL)
+	if (!sd || data < 0 || data >= MAX_SKILLCOOLDOWN)
+		return 0;
+
+	if (!sd->scd[i] || sd->scd[i]->timer != tid) {
+		ShowWarning("skill_blockpc_end: Invalid Timer or not Skill Cooldown.\n");
 		return 0;
-	if (!sd) return 0;
-	if (sd->blockskill[data] != (0x1|(tid&0xFE))) return 0;
-
-	if( ( cd = idb_get(skillcd_db,sd->status.char_id) ) ) {
-		int i,cursor;
-		ARR_FIND( 0, cd->cursor+1, cursor, cd->skidx[cursor] == data );
-		cd->duration[cursor] = 0;
-		cd->skidx[cursor] = 0;
-		cd->nameid[cursor] = 0;
-		// compact the cool down list
-		for( i = 0, cursor = 0; i < cd->cursor; i++ ) {
-			if( cd->duration[i] == 0 )
-				continue;
-			if( cursor != i ) {
-				cd->duration[cursor] = cd->duration[i];
-				cd->skidx[cursor] = cd->skidx[i];
-				cd->nameid[cursor] = cd->nameid[i];
-			}
-			cursor++;
-		}
-		if( cursor == 0 )
-			idb_remove(skillcd_db,sd->status.char_id);
-		else
-			cd->cursor = cursor;
 	}
 
-	sd->blockskill[data] = 0;
-	return 1;
+	aFree(sd->scd[i]);
+	sd->scd[i] = NULL;
+		return 1;
 }
 
 /**
- * flags a singular skill as being blocked from persistent usage.
- * @param   sd        the player the skill delay affects
- * @param   skill_id   the skill which should be delayed
- * @param   tick      the length of time the delay should last
- * @param   load      whether this assignment is being loaded upon player login
- * @return  0 if successful, -1 otherwise
- */
-int skill_blockpc_start_(struct map_session_data *sd, uint16 skill_id, int tick, bool load)
-{
-	int oskill_id = skill_id;
-	struct skill_cd* cd = NULL;
-	uint16 idx = skill_get_index(skill_id);
-
-	nullpo_retr (-1, sd);
-
-	if (idx == 0)
+* flags a singular skill as being blocked from persistent usage.
+* @param   sd        the player the skill delay affects
+* @param   skill_id   the skill which should be delayed
+* @param   tick      the length of time the delay should last
+* @param   load      whether this assignment is being loaded upon player login
+* @return  0 if successful, -1 otherwise
+*/
+int skill_blockpc_start(struct map_session_data *sd, int skillid, int tick) {
+	int i;
+	nullpo_retr(-1, sd);
+	if (skillid == 0 || tick < 1)
 		return -1;
 
-	if (tick < 1) {
-		sd->blockskill[idx] = 0;
-		return -1;
+	ARR_FIND(0, MAX_SKILLCOOLDOWN, i, sd->scd[i] && sd->scd[i]->skill_id == skillid);
+	if (i < MAX_SKILLCOOLDOWN) { // Skill already with cooldown
+		delete_timer(sd->scd[i]->timer, skill_blockpc_end);
+		aFree(sd->scd[i]);
+		sd->scd[i] = NULL;
 	}
 
-	if( battle_config.display_status_timers )
-		clif_skill_cooldown(sd, idx, tick);
+	ARR_FIND(0, MAX_SKILLCOOLDOWN, i, !sd->scd[i]);
+	if (i < MAX_SKILLCOOLDOWN) { // Free Slot found
+		CREATE(sd->scd[i], struct skill_cooldown_entry, 1);
+		sd->scd[i]->skill_id = skillid;
+		sd->scd[i]->timer = add_timer(gettick() + tick, skill_blockpc_end, sd->bl.id, i);
 
-	if( !load ) {// not being loaded initially so ensure the skill delay is recorded
-		if( !(cd = idb_get(skillcd_db,sd->status.char_id)) ) {// create a new skill cooldown object for map storage
-			CREATE( cd, struct skill_cd, 1 );
-			idb_put( skillcd_db, sd->status.char_id, cd );
-		}
+		if (battle_config.display_status_timers && tick > 0)
+			clif_skill_cooldown(sd, skillid, tick);
 
-		// record the skill duration in the database map
-		cd->duration[cd->cursor] = tick;
-		cd->skidx[cd->cursor] = idx;
-		cd->nameid[cd->cursor] = oskill_id;
-		cd->cursor++;
+		return 1;
+	} else {
+		ShowWarning("skill_blockpc_start: Too many skillcooldowns, increase MAX_SKILLCOOLDOWN.\n");
+		return 0;
 	}
+}
 
-	sd->blockskill[idx] = 0x1|(0xFE&add_timer(gettick()+tick,skill_blockpc_end,sd->bl.id,idx));
-	return 0;
+int skill_blockpc_clear(struct map_session_data *sd) {
+	int i;
+	nullpo_ret(sd);
+	for (i = 0; i < MAX_SKILLCOOLDOWN; i++) {
+		if (!sd->scd[i])
+			continue;
+		delete_timer(sd->scd[i]->timer, skill_blockpc_end);
+		aFree(sd->scd[i]);
+		sd->scd[i] = NULL;
+	}
+	return 1;
 }
 
 int skill_blockhomun_end(int tid, unsigned int tick, int id, intptr_t data)	//[orn]
@@ -17938,29 +17933,6 @@ int skill_get_elemental_type( uint16 skill_id , uint16 skill_lv ) {
 	return type;
 }
 
-/**
- * reload stored skill cooldowns when a player logs in.
- * @param   sd     the affected player structure
- */
-void skill_cooldown_load(struct map_session_data * sd)
-{
-	int i;
-	struct skill_cd* cd = NULL;
-
-	// always check to make sure the session properly exists
-	nullpo_retv(sd);
-
-	if( !(cd = idb_get(skillcd_db, sd->status.char_id)) ) {// no skill cooldown is associated with this character
-		return;
-	}
-
-	// process each individual cooldown associated with the character
-	for( i = 0; i < cd->cursor; i++ ) {
-		// block the skill from usage but ensure it is not recorded (load = true)
-		skill_blockpc_start_( sd, cd->nameid[i], cd->duration[i], true );
-	}
-}
-
 /*==========================================
  * sub-function of DB reading.
  * skill_db.txt
@@ -18492,7 +18464,6 @@ int do_init_skill (void)
 
 	group_db = idb_alloc(DB_OPT_BASE);
 	skillunit_db = idb_alloc(DB_OPT_BASE);
-	skillcd_db = idb_alloc(DB_OPT_RELEASE_DATA);
 	skillusave_db = idb_alloc(DB_OPT_RELEASE_DATA);
 	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);
@@ -18514,7 +18485,6 @@ int do_final_skill(void)
 	db_destroy(skilldb_name2id);
 	db_destroy(group_db);
 	db_destroy(skillunit_db);
-	db_destroy(skillcd_db);
 	db_destroy(skillusave_db);
 	db_destroy(bowling_db);
 	ers_destroy(skill_unit_ers);

+ 4 - 2
src/map/skill.h

@@ -429,11 +429,13 @@ int skill_castend_nodamage_id( struct block_list *src, struct block_list *bl,uin
 int skill_castend_damage_id( struct block_list* src, struct block_list *bl,uint16 skill_id,uint16 skill_lv,unsigned int tick,int flag );
 int skill_castend_pos2( struct block_list *src, int x,int y,uint16 skill_id,uint16 skill_lv,unsigned int tick,int flag);
 
-int skill_blockpc_start_(struct map_session_data*, uint16 skill_id, int, bool);
+int skill_blockpc_start(struct map_session_data*, int, int);
+int skill_blockpc_get(struct map_session_data *sd, int skillid);
+int skill_blockpc_clear(struct map_session_data *sd);
+int skill_blockpc_end(int tid, unsigned int tick, int id, intptr_t data);
 int skill_blockhomun_start (struct homun_data*,uint16 skill_id,int);
 int skill_blockmerc_start (struct mercenary_data*,uint16 skill_id,int);
 
-#define skill_blockpc_start(sd, skill_id, tick) skill_blockpc_start_( sd, skill_id, tick, false )
 
 // (Epoque:) To-do: replace this macro with some sort of skill tree check (rather than hard-coded skill names)
 #define skill_ischangesex(id) ( \

+ 1 - 0
src/map/unit.c

@@ -2370,6 +2370,7 @@ int unit_free(struct block_list *bl, clr_type clrtype)
 				duel_reject(sd->duel_invite, sd);
 
 			channel_pcquit(sd,0xF); //leave all chan
+			skill_blockpc_clear(sd); //clear all skill cooldown related
 
 			// Notify friends that this char logged out. [Skotlex]
 			map_foreachpc(clif_friendslist_toggle_sub, sd->status.account_id, sd->status.char_id, 0);