Forráskód Böngészése

Skill_colldown

Move skillcooldown from ram saving to sql saving.
This will fix multimap skillcooldown and avoind increasing ram by
offline users.
lighta 11 éve
szülő
commit
69b28292cf

+ 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);