From 95705d41bee8d15d50d34c484617790e4aae9b21 Mon Sep 17 00:00:00 2001 From: Cydh Ramdh Date: Sat, 31 Jan 2015 15:07:13 +0700 Subject: [PATCH 1/4] Bonus Script clean up * Removed fixed array size (MAX_PC_BONUS_SCRIPT) of bonus_script on players. * Changed fixed script string on player's bonus_script to StringBuf, reduce memory usage for each connected player. * Added new flags to expand duplicated script and flag to allow duplicate script. * Reduced buf lenght from 1024 to 512 on bonus_script_data (used for saving/loading bonus_script from char-server). Still, MAX_PC_BONUS_SCRIPT is used to gives limit data can be loaded. NOTE: Please import 'upgrade_20150131.sql' for `bonus_script` table changes Signed-off-by: Cydh Ramdh --- db/const.txt | 2 + doc/packet_interserv.txt | 8 +- doc/script_commands.txt | 14 +- sql-files/main.sql | 12 +- sql-files/upgrades/upgrade_20150131.sql | 6 + src/char/char_mapif.c | 124 ++++++----- src/common/mmo.h | 14 +- src/map/chrif.c | 248 +++++++++++----------- src/map/chrif.h | 3 +- src/map/pc.c | 261 ++++++++++++++++++------ src/map/pc.h | 17 +- src/map/script.c | 65 ++---- src/map/status.c | 8 +- src/map/status.h | 32 +-- 14 files changed, 489 insertions(+), 325 deletions(-) create mode 100644 sql-files/upgrades/upgrade_20150131.sql diff --git a/db/const.txt b/db/const.txt index fe3f82d58d..57250e4837 100644 --- a/db/const.txt +++ b/db/const.txt @@ -4643,6 +4643,8 @@ BSF_REM_ON_LUXANIMA 0x040 BSF_REM_ON_MADOGEAR 0x080 BSF_REM_ON_DAMAGED 0x100 BSF_PERMANENT 0x200 +BSF_FORCE_REPLACE 0x400 +BSF_FORCE_DUPLICATE 0x800 false 0 true 1 diff --git a/doc/packet_interserv.txt b/doc/packet_interserv.txt index 06f6f9ed2b..fb8cf17b4c 100644 --- a/doc/packet_interserv.txt +++ b/doc/packet_interserv.txt @@ -2491,9 +2491,9 @@ Currently the max packet size is 0xFFFF (see 'WFIFOSET()' in 'src/common/socket. 0x2b2f Type: AZ - Structure: .W .W .L .W + Structure: .W .W .L .B { .?B } index: 0,2,4,8 - len: variable: 10+50*bonus_script_data + len: variable: 9+count*bonus_script_data parameter: - cmd : packet identification (0x2b2f) desc: @@ -2840,9 +2840,9 @@ Currently the max packet size is 0xFFFF (see 'WFIFOSET()' in 'src/common/socket. 0x2b2e Type: ZA - Structure: .W .W .L .W + Structure: .W .W .L .B { .?B } index: 0,2,4,8 - len: variable: 10+MAX_PC_BONUS_SCRIPT*bonus_script_data + len: variable: 9+count*bonus_script_data parameter: - cmd : packet identification (0x2b2e) - len diff --git a/doc/script_commands.txt b/doc/script_commands.txt index 552755f325..cb02e79488 100644 --- a/doc/script_commands.txt +++ b/doc/script_commands.txt @@ -5350,9 +5350,6 @@ After that time, the script will automatically expire. The same bonus cannot be stacked. By default, this bonus will be stored on `bonus_script` table when player logs out. -Note that the maximum number of 'bonus_script' commands that can run simultaneously -for a player is 10 (MAX_PC_BONUS_SCRIPT in 'src/map/pc.h'). - Flags (bitmask): 1 : Remove when dead. 2 : Removable by Dispell. @@ -5360,10 +5357,13 @@ Flags (bitmask): 8 : Remove when player logs out. 16 : Removeable by Banishing Buster. 32 : Removable by Refresh. - 128 : Removable by Luxanima. - 256 : Remove when Madogear is activated or deactivated. - 512 : Remove when receive damage. - 1024: Script is permanent, cannot be cleared by bonus_script_clear + 64 : Removable by Luxanima. + 128 : Remove when Madogear is activated or deactivated. + 256 : Remove when receive damage. + 512 : Script is permanent, cannot be cleared by bonus_script_clear. + 1024: Force to replace duplicated script by expanding the duration. + 2048: Force to add duplicated script. This flag cannot be stacked with 1024, + if both are defined, 1024 will be checked first and ignore this flag. Types: This will be used to decide negative or positive buff for 'debuff_on_logout'. diff --git a/sql-files/main.sql b/sql-files/main.sql index a3ced98d13..5c29335504 100644 --- a/sql-files/main.sql +++ b/sql-files/main.sql @@ -706,12 +706,12 @@ CREATE TABLE IF NOT EXISTS `interreg` ( -- CREATE TABLE IF NOT EXISTS `bonus_script` ( - `char_id` varchar(11) NOT NULL, - `script` varchar(1024) NOT NULL, - `tick` varchar(11) NOT NULL DEFAULT '0', - `flag` varchar(3) NOT NULL DEFAULT '0', - `type` char(1) NOT NULL DEFAULT '0', - `icon` varchar(3) NOT NULL DEFAULT '-1' + `char_id` INT(11) UNSIGNED NOT NULL, + `script` TEXT NOT NULL, + `tick` INT(11) UNSIGNED NOT NULL DEFAULT '0', + `flag` SMALLINT(5) UNSIGNED NOT NULL DEFAULT '0', + `type` TINYINT(1) UNSIGNED NOT NULL DEFAULT '0', + `icon` SMALLINT(3) NOT NULL DEFAULT '-1' ) ENGINE=InnoDB; CREATE TABLE IF NOT EXISTS `vending_items` ( diff --git a/sql-files/upgrades/upgrade_20150131.sql b/sql-files/upgrades/upgrade_20150131.sql new file mode 100644 index 0000000000..6ab8d692da --- /dev/null +++ b/sql-files/upgrades/upgrade_20150131.sql @@ -0,0 +1,6 @@ +ALTER TABLE `bonus_script` MODIFY COLUMN `char_id` INT(11) UNSIGNED NOT NULL; +ALTER TABLE `bonus_script` MODIFY COLUMN `script` TEXT NOT NULL; +ALTER TABLE `bonus_script` MODIFY COLUMN `tick` INT(11) UNSIGNED NOT NULL DEFAULT '0'; +ALTER TABLE `bonus_script` MODIFY COLUMN `flag` SMALLINT(5) UNSIGNED NOT NULL DEFAULT '0'; +ALTER TABLE `bonus_script` MODIFY COLUMN `type` TINYINT(1) UNSIGNED NOT NULL DEFAULT '0'; +ALTER TABLE `bonus_script` MODIFY COLUMN `icon` SMALLINT(3) NOT NULL DEFAULT '-1'; diff --git a/src/char/char_mapif.c b/src/char/char_mapif.c index 5eba5d896a..88a72c1c9a 100644 --- a/src/char/char_mapif.c +++ b/src/char/char_mapif.c @@ -1242,90 +1242,112 @@ int chmapif_parse_reqcharunban(int fd){ return 1; } -/** [Cydh] -* Get bonus_script data(s) from table to load -* @param fd -*/ +/** + * ZA 0x2b2d + * .W .L + * AZ 0x2b2f + * .W .W .L .B { .?B } + * Get bonus_script data(s) from table to load then send to player + * @param fd + * @author [Cydh] + **/ int chmapif_bonus_script_get(int fd) { if (RFIFOREST(fd) < 6) return 0; else { - int cid; - cid = RFIFOL(fd,2); + uint8 num_rows = 0; + uint32 cid = RFIFOL(fd,2); + struct bonus_script_data tmp_bsdata; + SqlStmt* stmt = SqlStmt_Malloc(sql_handle); + RFIFOSKIP(fd,6); - if (SQL_ERROR == Sql_Query(sql_handle,"SELECT `script`, `tick`, `flag`, `type`, `icon` FROM `%s` WHERE `char_id`='%d'", - schema_config.bonus_script_db,cid)) + if (SQL_ERROR == SqlStmt_Prepare(stmt, + "SELECT `script`, `tick`, `flag`, `type`, `icon` FROM `%s` WHERE `char_id` = '%d' LIMIT %d", + schema_config.bonus_script_db, cid, MAX_PC_BONUS_SCRIPT) || + SQL_ERROR == SqlStmt_Execute(stmt) || + SQL_ERROR == SqlStmt_BindColumn(stmt, 0, SQLDT_STRING, &tmp_bsdata.script_str, sizeof(tmp_bsdata.script_str), NULL, NULL) || + SQL_ERROR == SqlStmt_BindColumn(stmt, 1, SQLDT_UINT32, &tmp_bsdata.tick, 0, NULL, NULL) || + SQL_ERROR == SqlStmt_BindColumn(stmt, 2, SQLDT_UINT16, &tmp_bsdata.flag, 0, NULL, NULL) || + SQL_ERROR == SqlStmt_BindColumn(stmt, 3, SQLDT_UINT8, &tmp_bsdata.type, 0, NULL, NULL) || + SQL_ERROR == SqlStmt_BindColumn(stmt, 4, SQLDT_INT16, &tmp_bsdata.icon, 0, NULL, NULL) + ) { - Sql_ShowDebug(sql_handle); + SqlStmt_ShowDebug(stmt); + SqlStmt_Free(stmt); return 1; } - if (Sql_NumRows(sql_handle) > 0) { - struct bonus_script_data bsdata; - int count; - char *data; - WFIFOHEAD(fd,10+50*sizeof(struct bonus_script_data)); - WFIFOW(fd,0) = 0x2b2f; - WFIFOL(fd,4) = cid; - for (count = 0; count < MAX_PC_BONUS_SCRIPT && SQL_SUCCESS == Sql_NextRow(sql_handle); ++count) { - Sql_GetData(sql_handle,0,&data,NULL); memcpy(bsdata.script,data,strlen(data)+1); - Sql_GetData(sql_handle,1,&data,NULL); bsdata.tick = atoi(data); - Sql_GetData(sql_handle,2,&data,NULL); bsdata.flag = atoi(data); - Sql_GetData(sql_handle,3,&data,NULL); bsdata.type = atoi(data); - Sql_GetData(sql_handle,4,&data,NULL); bsdata.icon = atoi(data); - memcpy(WFIFOP(fd,10+count*sizeof(struct bonus_script_data)),&bsdata,sizeof(struct bonus_script_data)); - } - if (count >= MAX_PC_BONUS_SCRIPT) - ShowWarning("Too many bonus_script for %d, some of them were not loaded.\n",cid); - if (count > 0) { - WFIFOW(fd,2) = 10 + count*sizeof(struct bonus_script_data); - WFIFOW(fd,8) = count; - WFIFOSET(fd,WFIFOW(fd,2)); + if ((num_rows = (uint8)SqlStmt_NumRows(stmt)) > 0) { + uint8 i; + uint32 size = 9 + num_rows * sizeof(struct bonus_script_data); - //Clear the data once loaded. - if (SQL_ERROR == Sql_Query(sql_handle,"DELETE FROM `%s` WHERE `char_id`='%d'",schema_config.bonus_script_db,cid)) - Sql_ShowDebug(sql_handle); - ShowInfo("Loaded %d bonus_script for char_id: %d\n",count,cid); + WFIFOHEAD(fd, size); + WFIFOW(fd, 0) = 0x2b2f; + WFIFOW(fd, 2) = size; + WFIFOL(fd, 4) = cid; + WFIFOB(fd, 8) = num_rows; + + for (i = 0; i < num_rows && SQL_SUCCESS == SqlStmt_NextRow(stmt); i++) { + struct bonus_script_data bsdata; + memset(&bsdata, 0, sizeof(bsdata)); + memset(bsdata.script_str, '\0', sizeof(bsdata.script_str)); + + safestrncpy(bsdata.script_str, tmp_bsdata.script_str, strlen(tmp_bsdata.script_str)+1); + bsdata.tick = tmp_bsdata.tick; + bsdata.flag = tmp_bsdata.flag; + bsdata.type = tmp_bsdata.type; + bsdata.icon = tmp_bsdata.icon; + memcpy(WFIFOP(fd, 9 + i * sizeof(struct bonus_script_data)), &bsdata, sizeof(struct bonus_script_data)); } + + WFIFOSET(fd, size); + + ShowInfo("Bonus Script loaded for CID=%d. Total: %d.\n", cid, i); + + if (SQL_ERROR == SqlStmt_Prepare(stmt,"DELETE FROM `%s` WHERE `char_id`='%d'",schema_config.bonus_script_db,cid) || + SQL_ERROR == SqlStmt_Execute(stmt)) + SqlStmt_ShowDebug(stmt); } - Sql_FreeResult(sql_handle); + SqlStmt_Free(stmt); } return 1; } -/** [Cydh] -* Save bonus_script data(s) to the table -* @param fd -*/ +/** + * ZA 0x2b2e + * .W .W .L .B { .?B } + * Save bonus_script data(s) to the table + * @param fd + * @author [Cydh] + **/ int chmapif_bonus_script_save(int fd) { if (RFIFOREST(fd) < 4 || RFIFOREST(fd) < RFIFOW(fd,2)) return 0; else { - int count, cid; - - cid = RFIFOL(fd,4); - count = RFIFOW(fd,8); + uint32 cid = RFIFOL(fd,4); + uint8 count = RFIFOB(fd,8); if (count > 0) { - struct bonus_script_data bs; + char esc_script[MAX_BONUS_SCRIPT_LENGTH*2]; + struct bonus_script_data bsdata; StringBuf buf; - int i; - char esc_script[MAX_BONUS_SCRIPT_LENGTH] = ""; + uint8 i; StringBuf_Init(&buf); - StringBuf_Printf(&buf,"INSERT INTO `%s` (`char_id`, `script`, `tick`, `flag`, `type`, `icon`) VALUES ",schema_config.bonus_script_db); + StringBuf_Printf(&buf, "INSERT INTO `%s` (`char_id`, `script`, `tick`, `flag`, `type`, `icon`) VALUES ", schema_config.bonus_script_db); for (i = 0; i < count; ++i) { - memcpy(&bs,RFIFOP(fd,10+i*sizeof(struct bonus_script_data)),sizeof(struct bonus_script_data)); - Sql_EscapeString(sql_handle,esc_script,bs.script); + memcpy(&bsdata, RFIFOP(fd, 9 + i*sizeof(struct bonus_script_data)), sizeof(struct bonus_script_data)); + Sql_EscapeString(sql_handle, esc_script, bsdata.script_str); if (i > 0) StringBuf_AppendStr(&buf,", "); - StringBuf_Printf(&buf,"('%d','%s','%d','%d','%d','%d')",cid,esc_script,bs.tick,bs.flag,bs.type,bs.icon); + StringBuf_Printf(&buf, "('%d','%s','%d','%d','%d','%d')", cid, esc_script, bsdata.tick, bsdata.flag, bsdata.type, bsdata.icon); } if (SQL_ERROR == Sql_QueryStr(sql_handle,StringBuf_Value(&buf))) Sql_ShowDebug(sql_handle); + + ShowInfo("Bonus Script saved for CID=%d. Total: %d.\n", cid, count); StringBuf_Destroy(&buf); - ShowInfo("Saved %d bonus_script for char_id: %d\n",count,cid); } RFIFOSKIP(fd,RFIFOW(fd,2)); } diff --git a/src/common/mmo.h b/src/common/mmo.h index 375e86dd5a..22e01a2215 100644 --- a/src/common/mmo.h +++ b/src/common/mmo.h @@ -7,6 +7,7 @@ #include "cbasetypes.h" #include "../common/db.h" #include +#include "../common/strlib.h"// StringBuf // server->client protocol version // 0 - pre-? @@ -73,7 +74,7 @@ #define MAX_GUILDLEVEL 50 ///Max Guild level #define MAX_GUARDIANS 8 ///Local max per castle. If this value is increased, need to add more fields on MySQL `guild_castle` table [Skotlex] #define MAX_QUEST_OBJECTIVES 3 ///Max quest objectives for a quest -#define MAX_PC_BONUS_SCRIPT 20 ///Max bonus script +#define MAX_PC_BONUS_SCRIPT 50 ///Max bonus script can be fetched from `bonus_script` table on player load [Cydh] // for produce #define MIN_ATTRIBUTE 0 @@ -259,12 +260,13 @@ struct status_change_data { long val1, val2, val3, val4, tick; //Remaining duration. }; -#define MAX_BONUS_SCRIPT_LENGTH 1024 +#define MAX_BONUS_SCRIPT_LENGTH 512 struct bonus_script_data { - char script[MAX_BONUS_SCRIPT_LENGTH]; - long tick; - char type; - short flag, icon; + char script_str[MAX_BONUS_SCRIPT_LENGTH]; //< Script string + uint32 tick; ///< Tick + uint16 flag; ///< Flags @see enum e_bonus_script_flags + int16 icon; ///< Icon SI + uint8 type; ///< 0 - None, 1 - Buff, 2 - Debuff }; struct skill_cooldown_data { diff --git a/src/map/chrif.c b/src/map/chrif.c index 4810b7f524..937a0b7662 100644 --- a/src/map/chrif.c +++ b/src/map/chrif.c @@ -97,8 +97,8 @@ static const int packet_len_table[0x3d] = { // U - used, F - free //2b2b: Incoming, chrif_parse_ack_vipActive -> vip info result //2b2c: FREE //2b2d: Outgoing, chrif_bsdata_request -> request bonus_script for pc_authok'ed char. -//2b2e: Outgoing, chrif_save_bsdata -> Send bonus_script of player for saving. -//2b2f: Incoming, chrif_load_bsdata -> received bonus_script of player for loading. +//2b2e: Outgoing, chrif_bsdata_save -> Send bonus_script of player for saving. +//2b2f: Incoming, chrif_bsdata_received -> received bonus_script of player for loading. int chrif_connected = 0; int char_fd = -1; @@ -282,7 +282,8 @@ int chrif_save(struct map_session_data *sd, int flag) { if (chrif_isconnected()) { chrif_save_scdata(sd); chrif_skillcooldown_save(sd); - chrif_save_bsdata(sd); + if (flag != 3) + chrif_bsdata_save(sd); chrif_req_login_operation(sd->status.account_id, sd->status.name, CHRIF_OP_LOGIN_BANK, 0, 2, sd->status.bank_vault); //save Bank data } if ( flag != 3 && !chrif_auth_logout(sd,flag == 1 ? ST_LOGOUT : ST_MAPCHANGE) ) @@ -1598,6 +1599,123 @@ void chrif_parse_ack_vipActive(int fd) { #endif } + +/** + * ZA 0x2b2d + * .W .L + * Requets bonus_script datas + * @param char_id + * @author [Cydh] + **/ +int chrif_bsdata_request(uint32 char_id) { + chrif_check(-1); + WFIFOHEAD(char_fd,6); + WFIFOW(char_fd,0) = 0x2b2d; + WFIFOL(char_fd,2) = char_id; + WFIFOSET(char_fd,6); + return 0; +} + +/** + * ZA 0x2b2e + * .W .W .L .B { .?B } + * Stores bonus_script data(s) to the table when player log out + * @param sd + * @author [Cydh] + **/ +int chrif_bsdata_save(struct map_session_data *sd) { + uint16 i; + uint8 count = 0; + unsigned int tick; + + chrif_check(-1); + + if (!sd || !sd->bonus_script_num) + return 0; + + tick = gettick(); + + WFIFOHEAD(char_fd, 9 + sd->bonus_script_num * sizeof(struct bonus_script_data)); + WFIFOW(char_fd, 0) = 0x2b2e; + WFIFOL(char_fd, 4) = sd->status.char_id; + + i = BSF_REM_ON_LOGOUT; //Remove bonus with this flag + if (battle_config.debuff_on_logout&1) //Remove negative buffs + i |= BSF_REM_DEBUFF; + if (battle_config.debuff_on_logout&2) //Remove positive buffs + i |= BSF_REM_BUFF; + + //Clear data that won't be stored + pc_bonus_script_clear(sd, i); + + if (!sd->bonus_script_num) + return 0; + + for (i = 0; i < sd->bonus_script_num; i++) { + const struct TimerData *timer = get_timer(sd->bonus_script[i]->tid); + struct bonus_script_data bs; + + if (timer == NULL || DIFF_TICK(timer->tick,tick) < 0) + continue; + + memset(&bs, 0, sizeof(bs)); + safestrncpy(bs.script_str, StringBuf_Value(sd->bonus_script[i]->script_buf), StringBuf_Length(sd->bonus_script[i]->script_buf)+1); + bs.tick = DIFF_TICK(timer->tick, tick); + bs.flag = sd->bonus_script[i]->flag; + bs.type = sd->bonus_script[i]->type; + bs.icon = sd->bonus_script[i]->icon; + memcpy(WFIFOP(char_fd, 9 + count * sizeof(struct bonus_script_data)), &bs, sizeof(struct bonus_script_data)); + count++; + } + + if (count > 0) { + WFIFOB(char_fd, 8) = count; + WFIFOW(char_fd, 2) = 9 + sd->bonus_script_num * sizeof(struct bonus_script_data); + WFIFOSET(char_fd, WFIFOW(char_fd, 2)); + } + + // Clear All + pc_bonus_script_clear_all(sd,3); + + return 0; +} + +/** + * AZ 0x2b2f + * .W .W .L .B { .?B } + * Bonus script received, set to player + * @param fd + * @author [Cydh] + **/ +int chrif_bsdata_received(int fd) { + struct map_session_data *sd; + uint32 cid = RFIFOL(fd,4); + uint8 i, count = 0; + bool calc = false; + + sd = map_charid2sd(cid); + + if (!sd) { + ShowError("chrif_bsdata_received: Player with CID %d not found!\n",cid); + return -1; + } + + count = RFIFOB(fd,8); + + for (i = 0; i < count; i++) { + struct bonus_script_data *bs = (struct bonus_script_data*)RFIFOP(fd,9 + i*sizeof(struct bonus_script_data)); + + if (bs->script_str[0] == '\0' || !bs->tick) + continue; + + if (pc_bonus_script_add(sd, bs->script_str, bs->tick, (enum si_type)bs->icon, bs->flag, bs->type)) + calc = true; + } + if (calc) + status_calc_pc(sd,SCO_NONE); + return 0; +} + /*========================================== * *------------------------------------------*/ @@ -1676,7 +1794,7 @@ int chrif_parse(int fd) { case 0x2b27: chrif_authfail(fd); break; case 0x2b29: chrif_load_bankdata(fd); break; case 0x2b2b: chrif_parse_ack_vipActive(fd); break; - case 0x2b2f: chrif_load_bsdata(fd); break; + case 0x2b2f: chrif_bsdata_received(fd); break; default: ShowError("chrif_parse : unknown packet (session #%d): 0x%x. Disconnecting.\n", fd, cmd); set_eof(fd); @@ -1798,121 +1916,6 @@ int chrif_send_report(char* buf, int len) { return 0; } -/** [Cydh] -* Requets bonus_script datas -* @param char_id -*/ -int chrif_bsdata_request(uint32 char_id) { - chrif_check(-1); - WFIFOHEAD(char_fd,6); - WFIFOW(char_fd,0) = 0x2b2d; - WFIFOL(char_fd,2) = char_id; - WFIFOSET(char_fd,6); - return 0; -} - -/** [Cydh] -* Stores bonus_script data(s) to the table -* @param sd -*/ -int chrif_save_bsdata(struct map_session_data *sd) { - int i; - uint8 count = 0; - unsigned int tick; - struct bonus_script_data bs; - const struct TimerData *timer; - - chrif_check(-1); - tick = gettick(); - - WFIFOHEAD(char_fd,10+MAX_PC_BONUS_SCRIPT*sizeof(struct bonus_script_data)); - WFIFOW(char_fd,0) = 0x2b2e; - WFIFOL(char_fd,4) = sd->status.char_id; - - i = BSF_REM_ON_LOGOUT; //Remove bonus with this flag - if (battle_config.debuff_on_logout&1) //Remove negative buffs - i |= BSF_REM_DEBUFF; - if (battle_config.debuff_on_logout&2) //Remove positive buffs - i |= BSF_REM_BUFF; - - //Clear data that won't be stored - pc_bonus_script_clear(sd,i); - - for (i = 0; i < MAX_PC_BONUS_SCRIPT; i++) { - if (!(&sd->bonus_script[i]) || !sd->bonus_script[i].script || sd->bonus_script[i].script_str[0] == '\0') - continue; - - timer = get_timer(sd->bonus_script[i].tid); - if (timer == NULL || DIFF_TICK(timer->tick,tick) < 0) - continue; - - memcpy(bs.script,sd->bonus_script[i].script_str,strlen(sd->bonus_script[i].script_str)+1); - bs.tick = DIFF_TICK(timer->tick,tick); - bs.flag = sd->bonus_script[i].flag; - bs.type = sd->bonus_script[i].type; - bs.icon = sd->bonus_script[i].icon; - - memcpy(WFIFOP(char_fd,10+count*sizeof(struct bonus_script_data)),&bs,sizeof(struct bonus_script_data)); - pc_bonus_script_remove(&sd->bonus_script[i]); - count++; - } - - if (count == 0) - return 0; - - WFIFOW(char_fd,8) = count; - WFIFOW(char_fd,2) = 10+count*sizeof(struct bonus_script_data); - WFIFOSET(char_fd,WFIFOW(char_fd,2)); - return 0; -} - -/** [Cydh] -* Loads bonus_script datas -* @param fd -*/ -int chrif_load_bsdata(int fd) { - struct map_session_data *sd; - int cid, count; - uint8 i; - bool calc = false; - - cid = RFIFOL(fd,4); - sd = map_charid2sd(cid); - - if (!sd) { - ShowError("chrif_load_bsdata: Player with CID %d not found!\n",cid); - return -1; - } - - if (sd->status.char_id != cid) { - ShowError("chrif_load_bsdata: Receiving data for char id does not matches (%d != %d)!\n",sd->status.char_id,cid); - return -1; - } - - count = RFIFOW(fd,8); - - for (i = 0; i < count; i++) { - struct script_code *script; - struct bonus_script_data *bs = (struct bonus_script_data*)RFIFOP(fd,10 + i*sizeof(struct bonus_script_data)); - - if (bs->script[0] == '\0' || !(script = parse_script(bs->script,"chrif_load_bsdata",1,1))) - continue; - - memcpy(sd->bonus_script[i].script_str,bs->script,strlen(bs->script)); - sd->bonus_script[i].script = script; - sd->bonus_script[i].tick = gettick() + bs->tick; - sd->bonus_script[i].flag = (uint8)bs->flag; - sd->bonus_script[i].type = bs->type; - sd->bonus_script[i].icon = bs->icon; - if (bs->icon != SI_BLANK) //Gives status icon if exist - clif_status_change(&sd->bl,sd->bonus_script[i].icon,1,bs->tick,1,0,0); - calc = true; - } - if (calc) - status_calc_pc(sd,SCO_NONE); - return 0; -} - /** * @see DBApply */ @@ -1954,6 +1957,13 @@ void do_init_chrif(void) { sizeof(struct mmo_charstatus)); exit(EXIT_FAILURE); } + + if((sizeof(struct bonus_script_data) * MAX_PC_BONUS_SCRIPT) > 0xFFFF){ + ShowError("bonus_script_data size = %d is too, please reduce MAX_PC_BONUS_SCRIPT (%d) size. (must be below 0xFFFF).\n", + (sizeof(struct bonus_script_data) * MAX_PC_BONUS_SCRIPT), MAX_PC_BONUS_SCRIPT); + exit(EXIT_FAILURE); + } + auth_db = idb_alloc(DB_OPT_BASE); auth_db_ers = ers_new(sizeof(struct auth_node),"chrif.c::auth_db_ers",ERS_OPT_NONE); diff --git a/src/map/chrif.h b/src/map/chrif.h index 950a61c904..942443de89 100644 --- a/src/map/chrif.h +++ b/src/map/chrif.h @@ -89,8 +89,7 @@ int chrif_req_charunban(int aid, const char* character_name); int chrif_load_bankdata(int fd); int chrif_bsdata_request(uint32 char_id); -int chrif_save_bsdata(struct map_session_data *sd); -int chrif_load_bsdata(int fd); +int chrif_bsdata_save(struct map_session_data *sd); void do_final_chrif(void); void do_init_chrif(void); diff --git a/src/map/pc.c b/src/map/pc.c index f7fea6464b..7bfed3901e 100755 --- a/src/map/pc.c +++ b/src/map/pc.c @@ -1143,8 +1143,6 @@ bool pc_authok(struct map_session_data *sd, uint32 login_id2, time_t expiration_ sd->autobonus2[i].active = INVALID_TIMER; for(i = 0; i < ARRAYLENGTH(sd->autobonus3); i++) sd->autobonus3[i].active = INVALID_TIMER; - for(i = 0; i < ARRAYLENGTH(sd->bonus_script); i++) - sd->bonus_script[i].tid = INVALID_TIMER; if (battle_config.item_auto_get) sd->state.autoloot = 10000; @@ -1266,6 +1264,9 @@ bool pc_authok(struct map_session_data *sd, uint32 login_id2, time_t expiration_ sd->last_addeditem_index = -1; + sd->bonus_script = NULL; + sd->bonus_script_num = 0; + // Request all registries (auth is considered completed whence they arrive) intif_request_registry(sd,7); return true; @@ -11038,106 +11039,252 @@ void pc_show_version(struct map_session_data *sd) { clif_displaymessage(sd->fd,buf); } -/** [Cydh] +/** + * Run bonus_script on player + * @param sd + * @author [Cydh] + **/ +void pc_bonus_script(struct map_session_data *sd) { + uint8 i = 0; + int now = gettick(); + + if (!sd || !sd->bonus_script_num) + return; + + for (i = 0; i < sd->bonus_script_num; i++) { + if (sd->bonus_script[i]->tid == INVALID_TIMER) { // Start new timer for new bonus_script + sd->bonus_script[i]->tick += now; + sd->bonus_script[i]->tid = add_timer(sd->bonus_script[i]->tick, pc_bonus_script_timer, sd->bl.id, 0); + if (sd->bonus_script[i]->icon != SI_BLANK) //Gives status icon if exist + clif_status_change(&sd->bl, sd->bonus_script[i]->icon, 1, sd->bonus_script[i]->tick, 1, 0, 0); + } + if (!sd->bonus_script[i]->script) { + ShowError("pc_bonus_script: The script has been removed somewhere. \"%s\"\n", StringBuf_Value(sd->bonus_script[i]->script_buf)); + continue; + } + run_script(sd->bonus_script[i]->script, 0, sd->bl.id, 0); + } +} + +/** + * Add bonus_script to player + * @param sd Player + * @param script_str Script string + * @param dur Duration in ms + * @param icon SI + * @param flag Flags @see enum e_bonus_script_flags + * @param type 0 - None, 1 - Buff, 2 - Debuff + * @return True if added, False if cannot + * @author [Cydh] + **/ +bool pc_bonus_script_add(struct map_session_data *sd, const char *script_str, uint32 dur, enum si_type icon, uint16 flag, uint8 type) { + struct script_code *script = NULL; + + if (!sd) + return false; + + if (!(script = parse_script(script_str, "bonus_script", 0, SCRIPT_IGNORE_EXTERNAL_BRACKETS))) { + ShowError("pc_bonus_script_add: Failed to parse script '%s' (CID:%d).\n", script_str, sd->status.char_id); + return false; + } + + if (!sd->bonus_script_num) + CREATE(sd->bonus_script, struct s_bonus_script *, 1); + else { + uint8 i = 0; + for (i = 0; i < sd->bonus_script_num; i++) { + if (strcmpi(script_str, StringBuf_Value(sd->bonus_script[i]->script_buf)) == 0) + break; + } + + // Duplication checks + if (i < sd->bonus_script_num) { + int newdur = gettick() + dur; + if (flag&BSF_FORCE_REPLACE && sd->bonus_script[i]->tick < newdur) { + settick_timer(sd->bonus_script[i]->tid, newdur); + script_free_code(script); + return true; + } + else if (flag&BSF_FORCE_DUPLICATE) { + ; + } + else { + // No duplicate bonus + script_free_code(script); + return false; + } + } + + if (i >= UINT8_MAX) { + ShowError("pc_bonus_script_add: Reached max (%d) possible bonuses for this player %d\n", UINT8_MAX); + script_free_code(script); + return false; + } + + RECREATE(sd->bonus_script, struct s_bonus_script *, sd->bonus_script_num+1); + } + + CREATE(sd->bonus_script[sd->bonus_script_num], struct s_bonus_script, 1); + + sd->bonus_script[sd->bonus_script_num]->script_buf = StringBuf_Malloc(); + StringBuf_AppendStr(sd->bonus_script[sd->bonus_script_num]->script_buf, script_str); + sd->bonus_script[sd->bonus_script_num]->tid = INVALID_TIMER; + sd->bonus_script[sd->bonus_script_num]->flag = flag; + sd->bonus_script[sd->bonus_script_num]->icon = icon; + sd->bonus_script[sd->bonus_script_num]->tick = dur; // Use duration first, on run change to expire time + sd->bonus_script[sd->bonus_script_num]->type = type; + sd->bonus_script[sd->bonus_script_num]->script = script; + + sd->bonus_script_num++; + return true; +} + +/** + * Move bonus_script allocation to empty space + * @param sd + * @author [Cydh] + **/ +static void pc_bonus_script_move(struct map_session_data *sd) { + if (sd && sd->bonus_script_num) { + uint8 i, cur; + + for (i = 0, cur = 0; i < sd->bonus_script_num; i++) { + if (sd->bonus_script[i] == NULL) + continue; + + if (i != cur) + sd->bonus_script[cur] = sd->bonus_script[i]; + + cur++; + } + + if (!(sd->bonus_script_num = cur)) { + aFree(sd->bonus_script); + sd->bonus_script = NULL; + sd->bonus_script_num = 0; + } + } +} + +/** +* Remove bonus_script data from player +* @param sd: Target player +* @param idx: Bonus script idx in player array +* @author [Cydh] +**/ +static void pc_bonus_script_remove(struct map_session_data *sd, uint8 idx) { + uint8 i = 0, cursor = 0; + + if (!sd || !sd->bonus_script_num) + return; + + if (idx >= sd->bonus_script_num) { + ShowError("pc_bonus_script_remove: Invalid index: %d\n", idx); + return; + } + + if (sd->bonus_script[idx]->tid != INVALID_TIMER) + delete_timer(sd->bonus_script[idx]->tid, pc_bonus_script_timer); + + if (sd->bonus_script[idx]->icon != SI_BLANK) + clif_status_load(&sd->bl, sd->bonus_script[idx]->icon, 0); + + script_free_code(sd->bonus_script[idx]->script); + StringBuf_Free(sd->bonus_script[idx]->script_buf); + + aFree(sd->bonus_script[idx]); + sd->bonus_script[idx] = NULL; +} + +/** * Timer for bonus_script * @param tid * @param tick * @param id * @param data +* @author [Cydh] **/ int pc_bonus_script_timer(int tid, unsigned int tick, int id, intptr_t data) { - uint8 i = (uint8)data; + uint8 i; struct map_session_data *sd; sd = map_id2sd(id); if (!sd) { - ShowDebug("pc_bonus_script_timer: Null pointer id: %d data: %d\n",id,data); + ShowError("pc_bonus_script_timer: Null pointer id: %d tid: %d\n", id, tid); return 0; } - if (i >= MAX_PC_BONUS_SCRIPT || !(&sd->bonus_script[i]) || !sd->bonus_script[i].script) { - ShowDebug("pc_bonus_script_timer: Invalid index %d\n",i); + if (tid == INVALID_TIMER || !sd->bonus_script_num) + return 0; + + for (i = 0; i < sd->bonus_script_num; i++) { + if (sd->bonus_script[i]->tid == tid) + break; + } + + if (i == sd->bonus_script_num) { + ShowError("pc_bonus_script_timer: Timer %d is not found.\n", tid); return 0; } - if (sd->bonus_script[i].icon != SI_BLANK) - clif_status_load(&sd->bl, sd->bonus_script[i].icon, 0); - pc_bonus_script_remove(&sd->bonus_script[i]); + pc_bonus_script_remove(sd, i); + pc_bonus_script_move(sd); status_calc_pc(sd,SCO_NONE); return 0; } -/** [Cydh] -* Remove bonus_script data from player -* @param sd: Target player -* @param i: Bonus script index -**/ -void pc_bonus_script_remove(struct s_bonus_script *bscript) { - if (!bscript) - return; - - if (bscript->script) - script_free_code(bscript->script); - bscript->script = NULL; - memset(bscript->script_str, '\0', sizeof(bscript->script_str)); - bscript->tick = 0; - bscript->flag = 0; - bscript->icon = SI_BLANK; - if (bscript->tid != INVALID_TIMER) - delete_timer(bscript->tid,pc_bonus_script_timer); - bscript->tid = INVALID_TIMER; -} - -/** [Cydh] +/** * Check then clear all active timer(s) of bonus_script data from player based on reason * @param sd: Target player * @param flag: Reason to remove the bonus_script. e_bonus_script_flags or e_bonus_script_types +* @author [Cydh] **/ void pc_bonus_script_clear(struct map_session_data *sd, uint16 flag) { uint8 i, count = 0; - if (!sd) + if (!sd || !sd->bonus_script_num) return; - for (i = 0; i < MAX_PC_BONUS_SCRIPT; i++) { - if (&sd->bonus_script[i] && sd->bonus_script[i].script && - (sd->bonus_script[i].flag&flag || //Remove bonus script based on e_bonus_script_flags - (sd->bonus_script[i].type && ( - (flag&BSF_REM_BUFF && sd->bonus_script[i].type == 1) || //Remove bonus script based on buff type - (flag&BSF_REM_DEBUFF && sd->bonus_script[i].type == 2)) //Remove bonus script based on debuff type - ))) + for (i = 0; i < sd->bonus_script_num; i++) { + if ((flag&sd->bonus_script[i]->flag) || // Matched flag + (sd->bonus_script[i]->type && ( + (flag&BSF_REM_BUFF && sd->bonus_script[i]->type == 1) || // Buff type + (flag&BSF_REM_DEBUFF && sd->bonus_script[i]->type == 2)) // Debuff type + )) { - if (sd->bonus_script[i].icon != SI_BLANK) - clif_status_load(&sd->bl, sd->bonus_script[i].icon, 0); - pc_bonus_script_remove(&sd->bonus_script[i]); + pc_bonus_script_remove(sd, i); count++; } } - if (count && !(flag&BSF_REM_ON_LOGOUT)) //Don't need to do this if log out - status_calc_pc(sd,SCO_NONE); + if (count) { + pc_bonus_script_move(sd); + if (!(flag&BSF_REM_ON_LOGOUT)) { //Don't need to do this if log out + status_calc_pc(sd,SCO_NONE); + } + } } /** * Clear all bonus script from player * @param sd -* @param permanent If true, will removes permanent bonus script. +* @param flag &1 - Remove permanent bonus_script, &2 - Logout * @author [Cydh] -*/ -void pc_bonus_script_clear_all(struct map_session_data *sd, bool permanent) { +**/ +void pc_bonus_script_clear_all(struct map_session_data *sd, uint8 flag) { uint8 i, count = 0; - if (!sd) + if (!sd || !sd->bonus_script_num) return; - for (i = 0; i < MAX_PC_BONUS_SCRIPT; i++) { - if (!&sd->bonus_script[i] && !sd->bonus_script[i].script) + for (i = 0; i < sd->bonus_script_num; i++) { + if (!(flag&1) && (sd->bonus_script[i]->flag&BSF_PERMANENT)) continue; - if (!permanent && sd->bonus_script[i].flag&BSF_PERMANENT) - continue; - if (sd->bonus_script[i].icon != SI_BLANK) - clif_status_load(&sd->bl, sd->bonus_script[i].icon, 0); - pc_bonus_script_remove(&sd->bonus_script[i]); + pc_bonus_script_remove(sd, i); count++; } - status_calc_pc(sd,SCO_NONE); + if (count) { + pc_bonus_script_move(sd); + if (!(flag&2)) + status_calc_pc(sd,SCO_NONE); + } } /** [Cydh] diff --git a/src/map/pc.h b/src/map/pc.h index 3032717c78..dfd4d052a1 100644 --- a/src/map/pc.h +++ b/src/map/pc.h @@ -7,6 +7,7 @@ #include "../common/mmo.h" // JOB_*, MAX_FAME_LIST, struct fame_list, struct mmo_charstatus #include "../common/ers.h" #include "../common/timer.h" // INVALID_TIMER +#include "../common/strlib.h"// StringBuf #include "map.h" // RC_ALL #include "atcommand.h" // AtCommandType #include "battle.h" // battle_config @@ -159,11 +160,11 @@ struct s_pc_itemgrouphealrate { ///Timed bonus 'bonus_script' struct [Cydh] struct s_bonus_script { struct script_code *script; - char script_str[MAX_BONUS_SCRIPT_LENGTH]; //Used for comparing and storing on table + StringBuf *script_buf; //Used for comparing and storing on table uint32 tick; - uint8 flag; - char type; //0 - Ignore; 1 - Buff; 2 - Debuff - int16 icon; + uint16 flag; + enum si_type icon; + uint8 type; //0 - Ignore; 1 - Buff; 2 - Debuff int tid; }; @@ -603,7 +604,8 @@ struct map_session_data { struct vip_info vip; bool disableshowrate; //State to disable clif_display_pinfo(). [Cydh] #endif - struct s_bonus_script bonus_script[MAX_PC_BONUS_SCRIPT]; ///Bonus Script [Cydh] + struct s_bonus_script **bonus_script; ///Bonus Script [Cydh] + uint8 bonus_script_num; struct s_pc_itemgrouphealrate **itemgrouphealrate; /// List of Item Group Heal rate bonus uint8 itemgrouphealrate_count; /// Number of rate bonuses @@ -1118,9 +1120,10 @@ void pc_crimson_marker_clear(struct map_session_data *sd); void pc_show_version(struct map_session_data *sd); int pc_bonus_script_timer(int tid, unsigned int tick, int id, intptr_t data); -void pc_bonus_script_remove(struct s_bonus_script *bscript); +void pc_bonus_script(struct map_session_data *sd); +bool pc_bonus_script_add(struct map_session_data *sd, const char *script_str, uint32 dur, enum si_type icon, uint16 flag, uint8 type); void pc_bonus_script_clear(struct map_session_data *sd, uint16 flag); -void pc_bonus_script_clear_all(struct map_session_data *sd, bool permanent); +void pc_bonus_script_clear_all(struct map_session_data *sd, uint8 flag); void pc_cell_basilica(struct map_session_data *sd); diff --git a/src/map/script.c b/src/map/script.c index c8099c3cef..9a8f3539b7 100644 --- a/src/map/script.c +++ b/src/map/script.c @@ -2352,12 +2352,6 @@ void script_hardcoded_constants(void) { script_set_constant("Option_Dragon",OPTION_DRAGON,false); script_set_constant("Option_Costume",OPTION_COSTUME,false); - /* bonus_script commands */ - script_set_constant("BSF_REM_BUFF",BSF_REM_BUFF,false); - script_set_constant("BSF_REM_DEBUFF",BSF_REM_DEBUFF,false); - script_set_constant("BSF_ALL",BSF_ALL,false); - script_set_constant("BSF_CLEARALL",BSF_CLEARALL,false); - /* sc_start flags */ script_set_constant("SCSTART_NONE",SCSTART_NONE,false); script_set_constant("SCSTART_NOAVOID",SCSTART_NOAVOID,false); @@ -18909,16 +18903,19 @@ BUILDIN_FUNC(montransform) { * @author [Cydh] **/ BUILDIN_FUNC(bonus_script) { - uint8 i, flag = 0; + uint16 flag = 0; int16 icon = SI_BLANK; uint32 dur; - char type = 0; + uint8 type = 0; TBL_PC* sd; const char *script_str = NULL; - struct script_code *script = NULL; - if (script_hasdata(st,7)) - sd = map_charid2sd(script_getnum(st,7)); + if (script_hasdata(st,7)) { + if (!(sd = map_charid2sd(script_getnum(st,7)))) { + ShowError("buildin_bonus_script: Player CID=%d is not found.\n", script_getnum(st,7)); + return SCRIPT_CMD_FAILURE; + } + } else sd = script_rid2sd(st); @@ -18927,46 +18924,26 @@ BUILDIN_FUNC(bonus_script) { script_str = script_getstr(st,2); dur = 1000 * abs(script_getnum(st,3)); - FETCH(4,flag); - FETCH(5,type); - FETCH(6,icon); + FETCH(4, flag); + FETCH(5, type); + FETCH(6, icon); + // No Script string, No Duration! if (script_str[0] == '\0' || !dur) { - //ShowWarning("buildin_bonus_script: Invalid script. Skipping...\n"); + ShowError("buildin_bonus_script: Invalid! Script: \"%s\". Duration: %d\n", script_str, dur); return SCRIPT_CMD_FAILURE; } - //Skip duplicate entry - ARR_FIND(0,MAX_PC_BONUS_SCRIPT,i,&sd->bonus_script[i] && sd->bonus_script[i].script_str && strcmp(sd->bonus_script[i].script_str,script_str) == 0); - if (i < MAX_PC_BONUS_SCRIPT) { - //ShowWarning("buildin_bonus_script: Duplicate entry with bonus '%d'. Skipping...\n",i); - return SCRIPT_CMD_SUCCESS; - } - - if (!(script = parse_script(script_str,"bonus_script",0,1))) { - ShowWarning("buildin_bonus_script: Failed to parse script '%s' (cid:%d). Skipping...\n",script_str,sd->status.char_id); + if (strlen(script_str) >= MAX_BONUS_SCRIPT_LENGTH) { + ShowError("buildin_bonus_script: Script string to long: \"%s\".\n", script_str); return SCRIPT_CMD_FAILURE; } - //Find the empty slot - ARR_FIND(0,MAX_PC_BONUS_SCRIPT,i,!sd->bonus_script[i].script); - if (i >= MAX_PC_BONUS_SCRIPT) { - ShowWarning("buildin_itemscript: Maximum script_bonus is reached (cid:%d max: %d). Skipping...\n",sd->status.char_id,MAX_PC_BONUS_SCRIPT); - return SCRIPT_CMD_SUCCESS; - } + if (icon <= SI_BLANK || icon >= SI_MAX) + icon = SI_BLANK; - //Add the script data - memcpy(sd->bonus_script[i].script_str,script_str,strlen(script_str)+1); - sd->bonus_script[i].script = script; - sd->bonus_script[i].tick = gettick() + dur; - sd->bonus_script[i].flag = flag; - sd->bonus_script[i].type = type; - sd->bonus_script[i].icon = icon; - - if (sd->bonus_script[i].icon != SI_BLANK) //Gives status icon if exist - clif_status_change(&sd->bl,sd->bonus_script[i].icon,1,dur,1,0,0); - - status_calc_pc(sd,SCO_NONE); + if (pc_bonus_script_add(sd, script_str, dur, (enum si_type)icon, flag, type)) + status_calc_pc(sd,SCO_NONE); return SCRIPT_CMD_SUCCESS; } @@ -18979,7 +18956,7 @@ BUILDIN_FUNC(bonus_script) { */ BUILDIN_FUNC(bonus_script_clear) { TBL_PC* sd; - bool flag = 0; + bool flag = false; if (script_hasdata(st,2)) flag = script_getnum(st,2); @@ -18992,7 +18969,7 @@ BUILDIN_FUNC(bonus_script_clear) { if (sd == NULL) return SCRIPT_CMD_FAILURE; - pc_bonus_script_clear_all(sd,flag); /// Don't remove permanent script + pc_bonus_script_clear_all(sd,(flag ? 1 : 0)); return SCRIPT_CMD_SUCCESS; } diff --git a/src/map/status.c b/src/map/status.c index 1c3c64ea46..b7eba77f6b 100644 --- a/src/map/status.c +++ b/src/map/status.c @@ -3146,13 +3146,7 @@ int status_calc_pc_(struct map_session_data* sd, enum e_status_calc_opt opt) run_script(data->script,0,sd->bl.id,0); } - for (i = 0; i < MAX_PC_BONUS_SCRIPT; i++) { //Process script Bonus [Cydh] - if (!(&sd->bonus_script[i]) || !sd->bonus_script[i].script) - continue; - if (sd->bonus_script[i].tid == INVALID_TIMER) //Just add timer only for new attached script - sd->bonus_script[i].tid = add_timer(sd->bonus_script[i].tick,pc_bonus_script_timer,sd->bl.id,i); - run_script(sd->bonus_script[i].script,0,sd->bl.id,0); - } + pc_bonus_script(sd); if( sd->pd ) { // Pet Bonus struct pet_data *pd = sd->pd; diff --git a/src/map/status.h b/src/map/status.h index 0a986796f2..48f9a0f64c 100644 --- a/src/map/status.h +++ b/src/map/status.h @@ -1746,23 +1746,25 @@ enum e_status_change_start_flags { ///Enum for bonus_script's flag [Cydh] enum e_bonus_script_flags { - BSF_REM_ON_DEAD = 0x001, ///Removed when dead - BSF_REM_ON_DISPELL = 0x002, ///Removed by Dispell - BSF_REM_ON_CLEARANCE = 0x004, ///Removed by Clearance - BSF_REM_ON_LOGOUT = 0x008, ///Removed when player logged out - BSF_REM_ON_BANISHING_BUSTER = 0x010, ///Removed by Banishing Buster - BSF_REM_ON_REFRESH = 0x020, ///Removed by Refresh - BSF_REM_ON_LUXANIMA = 0x040, ///Removed by Luxanima - BSF_REM_ON_MADOGEAR = 0x080, ///Removed when Madogear is activated or deactivated - BSF_REM_ON_DAMAGED = 0x100, ///Removed when receive damage - BSF_PERMANENT = 0x200, ///Cannot be removed by sc_end SC_ALL + BSF_REM_ON_DEAD = 0x001, ///< Removed when dead + BSF_REM_ON_DISPELL = 0x002, ///< Removed by Dispell + BSF_REM_ON_CLEARANCE = 0x004, ///< Removed by Clearance + BSF_REM_ON_LOGOUT = 0x008, ///< Removed when player logged out + BSF_REM_ON_BANISHING_BUSTER = 0x010, ///< Removed by Banishing Buster + BSF_REM_ON_REFRESH = 0x020, ///< Removed by Refresh + BSF_REM_ON_LUXANIMA = 0x040, ///< Removed by Luxanima + BSF_REM_ON_MADOGEAR = 0x080, ///< Removed when Madogear is activated or deactivated + BSF_REM_ON_DAMAGED = 0x100, ///< Removed when receive damage + BSF_PERMANENT = 0x200, ///< Cannot be removed by sc_end SC_ALL - // These flags better in the last of everything - BSF_REM_BUFF = 0x1000, ///Remove positive buff - BSF_REM_DEBUFF = 0x2000, ///Remove negative buff + // These flags cannot be stacked, BSF_FORCE_REPLACE has highest priority to check if YOU force to add both + BSF_FORCE_REPLACE = 0x400, ///< Force to replace duplicated script by expanding the duration + BSF_FORCE_DUPLICATE = 0x800, ///< Force to add duplicated script - BSF_ALL = 0x0FFF|BSF_REM_BUFF|BSF_REM_DEBUFF, - BSF_CLEARALL = BSF_ALL&~BSF_PERMANENT, + // These flags aren't part of 'bonus_script' scripting flags + + BSF_REM_BUFF = 0x4000, ///< Remove positive buff if battle_config.debuff_on_logout&1 + BSF_REM_DEBUFF = 0x8000, ///< Remove negative buff if battle_config.debuff_on_logout&2 }; ///Enum for status_get_hpbonus and status_get_spbonus From 148353eed9813f4df6b12fa2164dd6fc0f218972 Mon Sep 17 00:00:00 2001 From: Cydh Ramdh Date: Tue, 3 Feb 2015 11:56:09 +0700 Subject: [PATCH 2/4] Follow up 95705d41bee8d15d50d34c484617790e4aae9b21 * Changed bonus script list to link list * Merged 'pc_bonus_script_clear_all' to 'pc_bonus_script_clear' * Now bonus script will be saved on auto-save timer or other save request, prevent losing the bonus when "something" happen. But, saving when player is quiting, will removes the bonus that won't be saved on logout. Signed-off-by: Cydh Ramdh --- src/char/char_mapif.c | 5 +- src/common/db.c | 25 +++- src/common/db.h | 5 +- src/map/chrif.c | 112 ++++++++-------- src/map/chrif.h | 2 +- src/map/guild.c | 3 +- src/map/pc.c | 300 +++++++++++++++++++++--------------------- src/map/pc.h | 16 ++- src/map/script.c | 7 +- src/map/status.h | 2 +- src/map/unit.c | 4 + 11 files changed, 261 insertions(+), 220 deletions(-) diff --git a/src/char/char_mapif.c b/src/char/char_mapif.c index 88a72c1c9a..6bf2368c75 100644 --- a/src/char/char_mapif.c +++ b/src/char/char_mapif.c @@ -1328,6 +1328,9 @@ int chmapif_bonus_script_save(int fd) { uint32 cid = RFIFOL(fd,4); uint8 count = RFIFOB(fd,8); + if (SQL_ERROR == Sql_Query(sql_handle,"DELETE FROM `%s` WHERE `char_id` = '%d'", schema_config.bonus_script_db, cid)) + Sql_ShowDebug(sql_handle); + if (count > 0) { char esc_script[MAX_BONUS_SCRIPT_LENGTH*2]; struct bonus_script_data bsdata; @@ -1346,9 +1349,9 @@ int chmapif_bonus_script_save(int fd) { if (SQL_ERROR == Sql_QueryStr(sql_handle,StringBuf_Value(&buf))) Sql_ShowDebug(sql_handle); - ShowInfo("Bonus Script saved for CID=%d. Total: %d.\n", cid, count); StringBuf_Destroy(&buf); } + ShowInfo("Bonus Script saved for CID=%d. Total: %d.\n", cid, count); RFIFOSKIP(fd,RFIFOW(fd,2)); } return 1; diff --git a/src/common/db.c b/src/common/db.c index 04aff81245..915aa580f0 100644 --- a/src/common/db.c +++ b/src/common/db.c @@ -2717,18 +2717,29 @@ void linkdb_insert( struct linkdb_node** head, void *key, void* data) node->data = data; } -void linkdb_foreach( struct linkdb_node** head, LinkDBFunc func, ... ) -{ +int linkdb_vforeach( struct linkdb_node** head, LinkDBFunc func, va_list ap) { struct linkdb_node *node; - if( head == NULL ) return; + int retCount = 0; + if( head == NULL ) + return 0; node = *head; while ( node ) { - va_list args; - va_start(args, func); - func( node->key, node->data, args ); - va_end(args); + va_list argscopy; + va_copy(argscopy, ap); + retCount += func(node->key, node->data, argscopy); + va_end(argscopy); node = node->next; } + return retCount; +} + +int linkdb_foreach( struct linkdb_node** head, LinkDBFunc func, ... ) { + va_list ap; + int retCount = 0; + va_start(ap, func); + retCount = linkdb_vforeach(head, func, ap); + va_end(ap); + return retCount; } void* linkdb_search( struct linkdb_node** head, void *key) diff --git a/src/common/db.h b/src/common/db.h index 041bf5b737..55bcee9aa2 100644 --- a/src/common/db.h +++ b/src/common/db.h @@ -875,14 +875,15 @@ struct linkdb_node { void *data; }; -typedef void (*LinkDBFunc)(void* key, void* data, va_list args); +typedef int (*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 ); -void linkdb_foreach( struct linkdb_node** head, LinkDBFunc func, ... ); +int linkdb_vforeach(struct linkdb_node** head, LinkDBFunc func, va_list ap); +int linkdb_foreach( struct linkdb_node** head, LinkDBFunc func, ... ); diff --git a/src/map/chrif.c b/src/map/chrif.c index 937a0b7662..eabe785676 100644 --- a/src/map/chrif.c +++ b/src/map/chrif.c @@ -282,8 +282,6 @@ int chrif_save(struct map_session_data *sd, int flag) { if (chrif_isconnected()) { chrif_save_scdata(sd); chrif_skillcooldown_save(sd); - if (flag != 3) - chrif_bsdata_save(sd); chrif_req_login_operation(sd->status.account_id, sd->status.name, CHRIF_OP_LOGIN_BANK, 0, 2, sd->status.bank_vault); //save Bank data } if ( flag != 3 && !chrif_auth_logout(sd,flag == 1 ? ST_LOGOUT : ST_MAPCHANGE) ) @@ -292,6 +290,8 @@ int chrif_save(struct map_session_data *sd, int flag) { chrif_check(-1); //Character is saved on reconnect. + chrif_bsdata_save(sd, (flag && (flag != 3))); + //For data sync if (sd->state.storage_flag == 2) gstorage_storagesave(sd->status.account_id, sd->status.guild_id, flag); @@ -1619,63 +1619,63 @@ int chrif_bsdata_request(uint32 char_id) { /** * ZA 0x2b2e * .W .W .L .B { .?B } - * Stores bonus_script data(s) to the table when player log out + * Stores bonus_script data(s) to the table * @param sd * @author [Cydh] **/ -int chrif_bsdata_save(struct map_session_data *sd) { - uint16 i; - uint8 count = 0; - unsigned int tick; +int chrif_bsdata_save(struct map_session_data *sd, bool quit) { + uint8 i = 0; chrif_check(-1); - if (!sd || !sd->bonus_script_num) + if (!sd) return 0; - tick = gettick(); + // Removing... + if (quit && sd->bonus_script.head) { + uint16 flag = BSF_REM_ON_LOGOUT; //Remove bonus when logout + if (battle_config.debuff_on_logout&1) //Remove negative buffs + flag |= BSF_REM_DEBUFF; + if (battle_config.debuff_on_logout&2) //Remove positive buffs + flag |= BSF_REM_BUFF; + pc_bonus_script_clear(sd, flag); + } - WFIFOHEAD(char_fd, 9 + sd->bonus_script_num * sizeof(struct bonus_script_data)); + //ShowInfo("Saving %d bonus script for CID=%d\n", sd->bonus_script.count, sd->status.char_id); + + WFIFOHEAD(char_fd, 9 + sd->bonus_script.count * sizeof(struct bonus_script_data)); WFIFOW(char_fd, 0) = 0x2b2e; WFIFOL(char_fd, 4) = sd->status.char_id; - i = BSF_REM_ON_LOGOUT; //Remove bonus with this flag - if (battle_config.debuff_on_logout&1) //Remove negative buffs - i |= BSF_REM_DEBUFF; - if (battle_config.debuff_on_logout&2) //Remove positive buffs - i |= BSF_REM_BUFF; + if (sd->bonus_script.count) { + unsigned int tick = gettick(); + struct linkdb_node *node = NULL; - //Clear data that won't be stored - pc_bonus_script_clear(sd, i); + for (node = sd->bonus_script.head; node && i < MAX_PC_BONUS_SCRIPT; node = node->next) { + const struct TimerData *timer = NULL; + struct bonus_script_data bs; + struct s_bonus_script_entry *entry = (struct s_bonus_script_entry *)node->data; - if (!sd->bonus_script_num) - return 0; + if (!entry || !(timer = get_timer(entry->tid)) || DIFF_TICK(timer->tick,tick) < 0) + continue; - for (i = 0; i < sd->bonus_script_num; i++) { - const struct TimerData *timer = get_timer(sd->bonus_script[i]->tid); - struct bonus_script_data bs; + memset(&bs, 0, sizeof(bs)); + safestrncpy(bs.script_str, StringBuf_Value(entry->script_buf), StringBuf_Length(entry->script_buf)+1); + bs.tick = DIFF_TICK(timer->tick, tick); + bs.flag = entry->flag; + bs.type = entry->type; + bs.icon = entry->icon; + memcpy(WFIFOP(char_fd, 9 + i * sizeof(struct bonus_script_data)), &bs, sizeof(struct bonus_script_data)); + i++; + } - if (timer == NULL || DIFF_TICK(timer->tick,tick) < 0) - continue; - - memset(&bs, 0, sizeof(bs)); - safestrncpy(bs.script_str, StringBuf_Value(sd->bonus_script[i]->script_buf), StringBuf_Length(sd->bonus_script[i]->script_buf)+1); - bs.tick = DIFF_TICK(timer->tick, tick); - bs.flag = sd->bonus_script[i]->flag; - bs.type = sd->bonus_script[i]->type; - bs.icon = sd->bonus_script[i]->icon; - memcpy(WFIFOP(char_fd, 9 + count * sizeof(struct bonus_script_data)), &bs, sizeof(struct bonus_script_data)); - count++; + if (i != sd->bonus_script.count && sd->bonus_script.count > MAX_PC_BONUS_SCRIPT) + ShowWarning("Only allowed to save %d (mmo.h::MAX_PC_BONUS_SCRIPT) bonus script each player.\n", MAX_PC_BONUS_SCRIPT); } - if (count > 0) { - WFIFOB(char_fd, 8) = count; - WFIFOW(char_fd, 2) = 9 + sd->bonus_script_num * sizeof(struct bonus_script_data); - WFIFOSET(char_fd, WFIFOW(char_fd, 2)); - } - - // Clear All - pc_bonus_script_clear_all(sd,3); + WFIFOB(char_fd, 8) = i; + WFIFOW(char_fd, 2) = 9 + sd->bonus_script.count * sizeof(struct bonus_script_data); + WFIFOSET(char_fd, WFIFOW(char_fd, 2)); return 0; } @@ -1690,8 +1690,7 @@ int chrif_bsdata_save(struct map_session_data *sd) { int chrif_bsdata_received(int fd) { struct map_session_data *sd; uint32 cid = RFIFOL(fd,4); - uint8 i, count = 0; - bool calc = false; + uint8 count = 0; sd = map_charid2sd(cid); @@ -1700,19 +1699,28 @@ int chrif_bsdata_received(int fd) { return -1; } - count = RFIFOB(fd,8); + if ((count = RFIFOB(fd,8))) { + struct s_bonus_script *list = NULL; + uint8 i = 0; - for (i = 0; i < count; i++) { - struct bonus_script_data *bs = (struct bonus_script_data*)RFIFOP(fd,9 + i*sizeof(struct bonus_script_data)); + //ShowInfo("Loaded %d bonus script for CID=%d\n", count, sd->status.char_id); - if (bs->script_str[0] == '\0' || !bs->tick) - continue; + for (i = 0; i < count; i++) { + struct bonus_script_data *bs = (struct bonus_script_data*)RFIFOP(fd,9 + i*sizeof(struct bonus_script_data)); + struct s_bonus_script_entry *entry = NULL; - if (pc_bonus_script_add(sd, bs->script_str, bs->tick, (enum si_type)bs->icon, bs->flag, bs->type)) - calc = true; + if (bs->script_str[0] == '\0' || !bs->tick) + continue; + + if (!(entry = pc_bonus_script_add(sd, bs->script_str, bs->tick, (enum si_type)bs->icon, bs->flag, bs->type))) + continue; + + linkdb_insert(&sd->bonus_script.head, (void *)((intptr_t)entry), entry); + } + + if (sd->bonus_script.head) + status_calc_pc(sd,SCO_NONE); } - if (calc) - status_calc_pc(sd,SCO_NONE); return 0; } diff --git a/src/map/chrif.h b/src/map/chrif.h index 942443de89..e2d296e3e0 100644 --- a/src/map/chrif.h +++ b/src/map/chrif.h @@ -89,7 +89,7 @@ int chrif_req_charunban(int aid, const char* character_name); int chrif_load_bankdata(int fd); int chrif_bsdata_request(uint32 char_id); -int chrif_bsdata_save(struct map_session_data *sd); +int chrif_bsdata_save(struct map_session_data *sd, bool quit); void do_final_chrif(void); void do_init_chrif(void); diff --git a/src/map/guild.c b/src/map/guild.c index 5e0b8c8cab..38fa9cf336 100644 --- a/src/map/guild.c +++ b/src/map/guild.c @@ -1945,11 +1945,12 @@ int guild_castledatasave(int castle_id, int index, int value) { return 0; } -void guild_castle_reconnect_sub(void *key, void *data, va_list ap) { +int guild_castle_reconnect_sub(void *key, void *data, va_list ap) { int castle_id = GetWord((int)__64BPRTSIZE(key), 0); int index = GetWord((int)__64BPRTSIZE(key), 1); intif_guild_castle_datasave(castle_id, index, *(int *)data); aFree(data); + return 1; } /** diff --git a/src/map/pc.c b/src/map/pc.c index 7bfed3901e..5fdf01a976 100755 --- a/src/map/pc.c +++ b/src/map/pc.c @@ -1263,9 +1263,9 @@ bool pc_authok(struct map_session_data *sd, uint32 login_id2, time_t expiration_ sd->status.cashshop_sent = false; sd->last_addeditem_index = -1; - - sd->bonus_script = NULL; - sd->bonus_script_num = 0; + + sd->bonus_script.head = NULL; + sd->bonus_script.count = 0; // Request all registries (auth is considered completed whence they arrive) intif_request_registry(sd,7); @@ -11039,6 +11039,16 @@ void pc_show_version(struct map_session_data *sd) { clif_displaymessage(sd->fd,buf); } +int pc_bonus_script_list(void *key, void *data, va_list ap) { + struct s_bonus_script_entry *entry = (struct s_bonus_script_entry *)data; + struct map_session_data *sd = va_arg(ap, struct map_session_data *); + struct linkdb_node *node = (struct linkdb_node *)key; + if (sd) + ShowDebug(" cid=%d aid=%d\n",sd->status.char_id, sd->status.account_id); + ShowDebug(" key:%d e:0x%08X n:0x%08X nn:0x%08X np:0x%08X\n",(intptr_t)key, entry, key, node->next, node->prev); + return 1; +} + /** * Run bonus_script on player * @param sd @@ -11047,22 +11057,32 @@ void pc_show_version(struct map_session_data *sd) { void pc_bonus_script(struct map_session_data *sd) { uint8 i = 0; int now = gettick(); + struct linkdb_node *node = NULL, *next = NULL; - if (!sd || !sd->bonus_script_num) + if (!sd || !(node = sd->bonus_script.head)) return; - for (i = 0; i < sd->bonus_script_num; i++) { - if (sd->bonus_script[i]->tid == INVALID_TIMER) { // Start new timer for new bonus_script - sd->bonus_script[i]->tick += now; - sd->bonus_script[i]->tid = add_timer(sd->bonus_script[i]->tick, pc_bonus_script_timer, sd->bl.id, 0); - if (sd->bonus_script[i]->icon != SI_BLANK) //Gives status icon if exist - clif_status_change(&sd->bl, sd->bonus_script[i]->icon, 1, sd->bonus_script[i]->tick, 1, 0, 0); + while (node) { + struct s_bonus_script_entry *entry = NULL; + next = node->next; + + if ((entry = (struct s_bonus_script_entry *)node->data)) { + // Only start timer for new bonus_script + if (entry->tid == INVALID_TIMER) { + if (entry->icon != SI_BLANK) // Gives status icon if exist + clif_status_change(&sd->bl, entry->icon, 1, entry->tick, 1, 0, 0); + + entry->tick += now; + entry->tid = add_timer(entry->tick, pc_bonus_script_timer, sd->bl.id, (intptr_t)entry); + } + + if (entry->script) + run_script(entry->script, 0, sd->bl.id, 0); + else + ShowError("pc_bonus_script: The script has been removed somewhere. \"%s\"\n", StringBuf_Value(entry->script_buf)); } - if (!sd->bonus_script[i]->script) { - ShowError("pc_bonus_script: The script has been removed somewhere. \"%s\"\n", StringBuf_Value(sd->bonus_script[i]->script_buf)); - continue; - } - run_script(sd->bonus_script[i]->script, 0, sd->bl.id, 0); + + node = next; } } @@ -11074,126 +11094,93 @@ void pc_bonus_script(struct map_session_data *sd) { * @param icon SI * @param flag Flags @see enum e_bonus_script_flags * @param type 0 - None, 1 - Buff, 2 - Debuff - * @return True if added, False if cannot + * @return New created entry pointer or NULL if failed or NULL if duplicate fail * @author [Cydh] **/ -bool pc_bonus_script_add(struct map_session_data *sd, const char *script_str, uint32 dur, enum si_type icon, uint16 flag, uint8 type) { +struct s_bonus_script_entry *pc_bonus_script_add(struct map_session_data *sd, const char *script_str, uint32 dur, enum si_type icon, uint16 flag, uint8 type) { struct script_code *script = NULL; + struct linkdb_node *node = NULL; + struct s_bonus_script_entry *entry = NULL; if (!sd) - return false; + return NULL; if (!(script = parse_script(script_str, "bonus_script", 0, SCRIPT_IGNORE_EXTERNAL_BRACKETS))) { ShowError("pc_bonus_script_add: Failed to parse script '%s' (CID:%d).\n", script_str, sd->status.char_id); - return false; + return NULL; } - if (!sd->bonus_script_num) - CREATE(sd->bonus_script, struct s_bonus_script *, 1); - else { - uint8 i = 0; - for (i = 0; i < sd->bonus_script_num; i++) { - if (strcmpi(script_str, StringBuf_Value(sd->bonus_script[i]->script_buf)) == 0) - break; - } - - // Duplication checks - if (i < sd->bonus_script_num) { - int newdur = gettick() + dur; - if (flag&BSF_FORCE_REPLACE && sd->bonus_script[i]->tick < newdur) { - settick_timer(sd->bonus_script[i]->tid, newdur); - script_free_code(script); - return true; - } - else if (flag&BSF_FORCE_DUPLICATE) { - ; - } - else { - // No duplicate bonus - script_free_code(script); - return false; + // Duplication checks + if ((node = sd->bonus_script.head)) { + while (node) { + entry = (struct s_bonus_script_entry *)node->data; + if (strcmpi(script_str, StringBuf_Value(entry->script_buf)) == 0) { + int newdur = gettick() + dur; + if (flag&BSF_FORCE_REPLACE && entry->tick < newdur) { // Change duration + settick_timer(entry->tid, newdur); + script_free_code(script); + return NULL; + } + else if (flag&BSF_FORCE_DUPLICATE) // Allow duplicate + break; + else { // No duplicate bonus + script_free_code(script); + return NULL; + } } + node = node->next; } - - if (i >= UINT8_MAX) { - ShowError("pc_bonus_script_add: Reached max (%d) possible bonuses for this player %d\n", UINT8_MAX); - script_free_code(script); - return false; - } - - RECREATE(sd->bonus_script, struct s_bonus_script *, sd->bonus_script_num+1); } - CREATE(sd->bonus_script[sd->bonus_script_num], struct s_bonus_script, 1); + CREATE(entry, struct s_bonus_script_entry, 1); - sd->bonus_script[sd->bonus_script_num]->script_buf = StringBuf_Malloc(); - StringBuf_AppendStr(sd->bonus_script[sd->bonus_script_num]->script_buf, script_str); - sd->bonus_script[sd->bonus_script_num]->tid = INVALID_TIMER; - sd->bonus_script[sd->bonus_script_num]->flag = flag; - sd->bonus_script[sd->bonus_script_num]->icon = icon; - sd->bonus_script[sd->bonus_script_num]->tick = dur; // Use duration first, on run change to expire time - sd->bonus_script[sd->bonus_script_num]->type = type; - sd->bonus_script[sd->bonus_script_num]->script = script; - - sd->bonus_script_num++; - return true; -} - -/** - * Move bonus_script allocation to empty space - * @param sd - * @author [Cydh] - **/ -static void pc_bonus_script_move(struct map_session_data *sd) { - if (sd && sd->bonus_script_num) { - uint8 i, cur; - - for (i = 0, cur = 0; i < sd->bonus_script_num; i++) { - if (sd->bonus_script[i] == NULL) - continue; - - if (i != cur) - sd->bonus_script[cur] = sd->bonus_script[i]; - - cur++; - } - - if (!(sd->bonus_script_num = cur)) { - aFree(sd->bonus_script); - sd->bonus_script = NULL; - sd->bonus_script_num = 0; - } - } + entry->script_buf = StringBuf_Malloc(); + StringBuf_AppendStr(entry->script_buf, script_str); + entry->tid = INVALID_TIMER; + entry->flag = flag; + entry->icon = icon; + entry->tick = dur; // Use duration first, on run change to expire time + entry->type = type; + entry->script = script; + sd->bonus_script.count++; + return entry; } /** * Remove bonus_script data from player * @param sd: Target player -* @param idx: Bonus script idx in player array +* @param list: Bonus script entry from player * @author [Cydh] **/ -static void pc_bonus_script_remove(struct map_session_data *sd, uint8 idx) { - uint8 i = 0, cursor = 0; +void pc_bonus_script_free_entry(struct map_session_data *sd, struct s_bonus_script_entry *entry) { + if (entry->tid != INVALID_TIMER) + delete_timer(entry->tid, pc_bonus_script_timer); - if (!sd || !sd->bonus_script_num) - return; + if (entry->script) + script_free_code(entry->script); - if (idx >= sd->bonus_script_num) { - ShowError("pc_bonus_script_remove: Invalid index: %d\n", idx); - return; + if (entry->script_buf) + StringBuf_Free(entry->script_buf); + + if (sd) { + if (entry->icon != SI_BLANK) + clif_status_load(&sd->bl, entry->icon, 0); + if (sd->bonus_script.count > 0) + sd->bonus_script.count--; } + aFree(entry); +} - if (sd->bonus_script[idx]->tid != INVALID_TIMER) - delete_timer(sd->bonus_script[idx]->tid, pc_bonus_script_timer); - - if (sd->bonus_script[idx]->icon != SI_BLANK) - clif_status_load(&sd->bl, sd->bonus_script[idx]->icon, 0); - - script_free_code(sd->bonus_script[idx]->script); - StringBuf_Free(sd->bonus_script[idx]->script_buf); - - aFree(sd->bonus_script[idx]); - sd->bonus_script[idx] = NULL; +/** + * Do final process if no entry left + * @param sd + **/ +static void inline pc_bonus_script_check_final(struct map_session_data *sd) { + if (sd->bonus_script.count == 0) { + if (sd->bonus_script.head && sd->bonus_script.head->data) + pc_bonus_script_free_entry(sd, (struct s_bonus_script_entry *)sd->bonus_script.head->data); + linkdb_final(&sd->bonus_script.head); + } } /** @@ -11205,8 +11192,8 @@ static void pc_bonus_script_remove(struct map_session_data *sd, uint8 idx) { * @author [Cydh] **/ int pc_bonus_script_timer(int tid, unsigned int tick, int id, intptr_t data) { - uint8 i; struct map_session_data *sd; + struct s_bonus_script_entry *entry = (struct s_bonus_script_entry *)data; sd = map_id2sd(id); if (!sd) { @@ -11214,21 +11201,17 @@ int pc_bonus_script_timer(int tid, unsigned int tick, int id, intptr_t data) { return 0; } - if (tid == INVALID_TIMER || !sd->bonus_script_num) + if (tid == INVALID_TIMER) return 0; - for (i = 0; i < sd->bonus_script_num; i++) { - if (sd->bonus_script[i]->tid == tid) - break; - } - - if (i == sd->bonus_script_num) { - ShowError("pc_bonus_script_timer: Timer %d is not found.\n", tid); + if (!sd->bonus_script.head || entry == NULL) { + ShowError("pc_bonus_script_timer: Invalid entry pointer 0x%08X!\n", entry); return 0; } - pc_bonus_script_remove(sd, i); - pc_bonus_script_move(sd); + linkdb_erase(&sd->bonus_script.head, (void *)((intptr_t)entry)); + pc_bonus_script_free_entry(sd, entry); + pc_bonus_script_check_final(sd); status_calc_pc(sd,SCO_NONE); return 0; } @@ -11240,27 +11223,37 @@ int pc_bonus_script_timer(int tid, unsigned int tick, int id, intptr_t data) { * @author [Cydh] **/ void pc_bonus_script_clear(struct map_session_data *sd, uint16 flag) { - uint8 i, count = 0; - if (!sd || !sd->bonus_script_num) + struct linkdb_node *node = NULL; + uint16 count = 0; + + if (!sd || !(node = sd->bonus_script.head)) return; - for (i = 0; i < sd->bonus_script_num; i++) { - if ((flag&sd->bonus_script[i]->flag) || // Matched flag - (sd->bonus_script[i]->type && ( - (flag&BSF_REM_BUFF && sd->bonus_script[i]->type == 1) || // Buff type - (flag&BSF_REM_DEBUFF && sd->bonus_script[i]->type == 2)) // Debuff type - )) + while (node) { + struct linkdb_node *next = node->next; + struct s_bonus_script_entry *entry = (struct s_bonus_script_entry *)node->data; + + if (entry && ( + (flag == BSF_PERMANENT) || // Remove all with permanent bonus + (!flag && !(entry->flag&BSF_PERMANENT)) || // Remove all WITHOUT permanent bonus + (flag&entry->flag) || // Matched flag + (flag&BSF_REM_BUFF && entry->type == 1) || // Remove buff + (flag&BSF_REM_DEBUFF && entry->type == 2) // Remove debuff + ) + ) { - pc_bonus_script_remove(sd, i); + linkdb_erase(&sd->bonus_script.head, (void *)((intptr_t)entry)); + pc_bonus_script_free_entry(sd, entry); count++; } + + node = next; } - if (count) { - pc_bonus_script_move(sd); - if (!(flag&BSF_REM_ON_LOGOUT)) { //Don't need to do this if log out - status_calc_pc(sd,SCO_NONE); - } - } + + pc_bonus_script_check_final(sd); + + if (count && !(flag&BSF_REM_ON_LOGOUT)) //Don't need to do this if log out + status_calc_pc(sd,SCO_NONE); } /** @@ -11270,21 +11263,34 @@ void pc_bonus_script_clear(struct map_session_data *sd, uint16 flag) { * @author [Cydh] **/ void pc_bonus_script_clear_all(struct map_session_data *sd, uint8 flag) { - uint8 i, count = 0; - if (!sd || !sd->bonus_script_num) + struct linkdb_node *node = NULL; + uint16 count = 0; + + if (!sd || !(node = sd->bonus_script.head)) return; - for (i = 0; i < sd->bonus_script_num; i++) { - if (!(flag&1) && (sd->bonus_script[i]->flag&BSF_PERMANENT)) - continue; - pc_bonus_script_remove(sd, i); - count++; - } - if (count) { - pc_bonus_script_move(sd); - if (!(flag&2)) - status_calc_pc(sd,SCO_NONE); + while (node) { + struct linkdb_node *next = node->next; + struct s_bonus_script_entry *entry = (struct s_bonus_script_entry *)node->data; + + if (entry && ( + !(entry->flag&BSF_PERMANENT) || + ((flag&1) && entry->flag&BSF_PERMANENT) + ) + ) + { + linkdb_erase(&sd->bonus_script.head, (void *)((intptr_t)entry)); + pc_bonus_script_free_entry(sd, entry); + count++; + } + + node = next; } + + pc_bonus_script_check_final(sd); + + if (count && !(flag&2)) + status_calc_pc(sd,SCO_NONE); } /** [Cydh] diff --git a/src/map/pc.h b/src/map/pc.h index dfd4d052a1..de18dfd0a9 100644 --- a/src/map/pc.h +++ b/src/map/pc.h @@ -158,7 +158,7 @@ struct s_pc_itemgrouphealrate { }; ///Timed bonus 'bonus_script' struct [Cydh] -struct s_bonus_script { +struct s_bonus_script_entry { struct script_code *script; StringBuf *script_buf; //Used for comparing and storing on table uint32 tick; @@ -604,9 +604,13 @@ struct map_session_data { struct vip_info vip; bool disableshowrate; //State to disable clif_display_pinfo(). [Cydh] #endif - struct s_bonus_script **bonus_script; ///Bonus Script [Cydh] - uint8 bonus_script_num; - + + /// Bonus Script [Cydh] + struct s_bonus_script_list { + struct linkdb_node *head; ///< Bonus script head node. data: struct s_bonus_script_entry *entry, key: (intptr_t)entry + uint16 count; + } bonus_script; + struct s_pc_itemgrouphealrate **itemgrouphealrate; /// List of Item Group Heal rate bonus uint8 itemgrouphealrate_count; /// Number of rate bonuses @@ -1121,9 +1125,9 @@ void pc_show_version(struct map_session_data *sd); int pc_bonus_script_timer(int tid, unsigned int tick, int id, intptr_t data); void pc_bonus_script(struct map_session_data *sd); -bool pc_bonus_script_add(struct map_session_data *sd, const char *script_str, uint32 dur, enum si_type icon, uint16 flag, uint8 type); +struct s_bonus_script_entry *pc_bonus_script_add(struct map_session_data *sd, const char *script_str, uint32 dur, enum si_type icon, uint16 flag, uint8 type); void pc_bonus_script_clear(struct map_session_data *sd, uint16 flag); -void pc_bonus_script_clear_all(struct map_session_data *sd, uint8 flag); +int pc_bonus_script_list(void *key, void *data, va_list ap); void pc_cell_basilica(struct map_session_data *sd); diff --git a/src/map/script.c b/src/map/script.c index 9a8f3539b7..1a993050d2 100644 --- a/src/map/script.c +++ b/src/map/script.c @@ -18909,6 +18909,7 @@ BUILDIN_FUNC(bonus_script) { uint8 type = 0; TBL_PC* sd; const char *script_str = NULL; + struct s_bonus_script_entry *entry = NULL; if (script_hasdata(st,7)) { if (!(sd = map_charid2sd(script_getnum(st,7)))) { @@ -18942,8 +18943,10 @@ BUILDIN_FUNC(bonus_script) { if (icon <= SI_BLANK || icon >= SI_MAX) icon = SI_BLANK; - if (pc_bonus_script_add(sd, script_str, dur, (enum si_type)icon, flag, type)) + if ((entry = pc_bonus_script_add(sd, script_str, dur, (enum si_type)icon, flag, type))) { + linkdb_insert(&sd->bonus_script.head, (void *)((intptr_t)entry), entry); status_calc_pc(sd,SCO_NONE); + } return SCRIPT_CMD_SUCCESS; } @@ -18969,7 +18972,7 @@ BUILDIN_FUNC(bonus_script_clear) { if (sd == NULL) return SCRIPT_CMD_FAILURE; - pc_bonus_script_clear_all(sd,(flag ? 1 : 0)); + pc_bonus_script_clear(sd,(flag ? BSF_PERMANENT : BSF_REM_ALL)); return SCRIPT_CMD_SUCCESS; } diff --git a/src/map/status.h b/src/map/status.h index 48f9a0f64c..01ae9bb0a6 100644 --- a/src/map/status.h +++ b/src/map/status.h @@ -1762,7 +1762,7 @@ enum e_bonus_script_flags { BSF_FORCE_DUPLICATE = 0x800, ///< Force to add duplicated script // These flags aren't part of 'bonus_script' scripting flags - + BSF_REM_ALL = 0x0, ///< Remove all bonus script BSF_REM_BUFF = 0x4000, ///< Remove positive buff if battle_config.debuff_on_logout&1 BSF_REM_DEBUFF = 0x8000, ///< Remove negative buff if battle_config.debuff_on_logout&2 }; diff --git a/src/map/unit.c b/src/map/unit.c index ad1fcbed32..b3029e9aca 100644 --- a/src/map/unit.c +++ b/src/map/unit.c @@ -3169,6 +3169,10 @@ int unit_free(struct block_list *bl, clr_type clrtype) sd->num_quests = sd->avail_quests = 0; } + // Clearing... + if (sd->bonus_script.head) + pc_bonus_script_clear(sd, BSF_REM_ALL); + pc_itemgrouphealrate_clear(sd); break; } From 892e961823a5fd3e285f03cab37be9d1f3274768 Mon Sep 17 00:00:00 2001 From: Cydh Ramdh Date: Thu, 12 Feb 2015 14:57:59 +0700 Subject: [PATCH 3/4] * Fixed Superstar_Snack (22843) and Sealed_Dracula_Card (22846) fields * Removed debug leftover Signed-off-by: Cydh Ramdh --- db/re/item_db.txt | 4 ++-- sql-files/item_db_re.sql | 4 ++-- src/map/chrif.c | 2 +- src/map/pc.c | 10 ---------- src/map/pc.h | 1 - 5 files changed, 5 insertions(+), 16 deletions(-) diff --git a/db/re/item_db.txt b/db/re/item_db.txt index e7cf861adb..9661543bb4 100644 --- a/db/re/item_db.txt +++ b/db/re/item_db.txt @@ -9878,10 +9878,10 @@ 22837,Integer_Time,Integer Time,2,10,,100,,,,,,,,,,,,,{ getitem 673,5; },{},{} 22838,Something_Present_Candy_Holders,Something Present Candy Holders,2,10,,100,,,,,,,,,,,,,{},{},{} 22842,Sealed_Dracula_Scroll_II,Sealed Dracula Scroll II,2,10,,10,,,,,,,,,,,,,{ getitem callfunc("F_Rand",22846,6228,6232,24223,24227,17474/*, Enchant Letter*/),1; },{},{} -22843,Superstar_Snack,Superstar Snack,2,10,,10,,,,,,,,,,,,,{ bonus_script "{ bonus bAtk2,50; bonus bMatk,50; }",300; },{},{} +22843,Superstar_Snack,Superstar Snack,2,10,,10,,,,,0xFFFFFFFF,63,2,,,,,,{ bonus_script "{ bonus bAtk2,50; bonus bMatk,50; }",300; },{},{} 22844,Sealed_Dracula_Card_Album,Sealed Dracula Card Album,2,10,,10,,,,,,,,,,,,,{/*No Info*/},{},{} 22845,Sealed_Fortune_Egg,Sealed Fortune Egg,2,10,,10,,,,,,,,,,,,,{ getitem callfunc("F_Rand",4488,4497,4486,4480,4485,4539,4487,4494,4538,4489,4490,4482,4503,22846),1; },{},{} -22846,Sealed_Dracula_Card,Sealed Dracula Card,2,10,,10,,,,,,,,,,,,,{ bonus_script "{ bonus3 bSPDrainRate,50,5,0; }",300; },{},{} +22846,Sealed_Dracula_Card,Sealed Dracula Card,2,10,,10,,,,,0xFFFFFFFF,63,2,,,,,,{ bonus_script "{ bonus3 bSPDrainRate,50,5,0; }",300; },{},{} // Shadow Equip 24000,T_STR1_Armor_Shadow,T STR1 Armor Shadow,12,10,,0,,,,0,0xFFFFFFFF,63,2,65536,,1,,,{ bonus bStr,1; },{},{} 24001,T_DEX1_Weapon_Shadow,T DEX1 Weapon Shadow,12,10,,0,,,,0,0xFFFFFFFF,63,2,131072,,1,,,{ bonus bDex,1; },{},{} diff --git a/sql-files/item_db_re.sql b/sql-files/item_db_re.sql index 56a52a807e..bc0f53a519 100644 --- a/sql-files/item_db_re.sql +++ b/sql-files/item_db_re.sql @@ -9909,10 +9909,10 @@ REPLACE INTO `item_db_re` VALUES (22829,'Sealed_Card_Album','Sealed Card Album', REPLACE INTO `item_db_re` VALUES (22837,'Integer_Time','Integer Time',2,10,NULL,100,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,'getitem 673,5;',NULL,NULL); REPLACE INTO `item_db_re` VALUES (22838,'Something_Present_Candy_Holders','Something Present Candy Holders',2,10,NULL,100,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL); REPLACE INTO `item_db_re` VALUES (22842,'Sealed_Dracula_Scroll_II','Sealed Dracula Scroll II',2,10,NULL,10,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,'getitem callfunc("F_Rand",22846,6228,6232,24223,24227,17474/*, Enchant Letter*/),1;',NULL,NULL); -REPLACE INTO `item_db_re` VALUES (22843,'Superstar_Snack','Superstar Snack',2,10,NULL,10,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,'bonus_script "{ bonus bAtk2,50; bonus bMatk,50; }",300;',NULL,NULL); +REPLACE INTO `item_db_re` VALUES (22843,'Superstar_Snack','Superstar Snack',2,10,NULL,10,NULL,NULL,NULL,NULL,0xFFFFFFFF,63,2,NULL,NULL,NULL,NULL,NULL,'bonus_script "{ bonus bAtk2,50; bonus bMatk,50; }",300;',NULL,NULL); REPLACE INTO `item_db_re` VALUES (22844,'Sealed_Dracula_Card_Album','Sealed Dracula Card Album',2,10,NULL,10,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,'/*No Info*/',NULL,NULL); REPLACE INTO `item_db_re` VALUES (22845,'Sealed_Fortune_Egg','Sealed Fortune Egg',2,10,NULL,10,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,'getitem callfunc("F_Rand",4488,4497,4486,4480,4485,4539,4487,4494,4538,4489,4490,4482,4503,22846),1;',NULL,NULL); -REPLACE INTO `item_db_re` VALUES (22846,'Sealed_Dracula_Card','Sealed Dracula Card',2,10,NULL,10,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,'bonus_script "{ bonus3 bSPDrainRate,50,5,0; }",300;',NULL,NULL); +REPLACE INTO `item_db_re` VALUES (22846,'Sealed_Dracula_Card','Sealed Dracula Card',2,10,NULL,10,NULL,NULL,NULL,NULL,0xFFFFFFFF,63,2,NULL,NULL,NULL,NULL,NULL,'bonus_script "{ bonus3 bSPDrainRate,50,5,0; }",300;',NULL,NULL); # Shadow Equip REPLACE INTO `item_db_re` VALUES (24000,'T_STR1_Armor_Shadow','T STR1 Armor Shadow',12,10,NULL,0,NULL,NULL,NULL,0,0xFFFFFFFF,63,2,65536,NULL,'1',NULL,NULL,'bonus bStr,1;',NULL,NULL); REPLACE INTO `item_db_re` VALUES (24001,'T_DEX1_Weapon_Shadow','T DEX1 Weapon Shadow',12,10,NULL,0,NULL,NULL,NULL,0,0xFFFFFFFF,63,2,131072,NULL,'1',NULL,NULL,'bonus bDex,1;',NULL,NULL); diff --git a/src/map/chrif.c b/src/map/chrif.c index eabe785676..e30d9e02e9 100644 --- a/src/map/chrif.c +++ b/src/map/chrif.c @@ -1967,7 +1967,7 @@ void do_init_chrif(void) { } if((sizeof(struct bonus_script_data) * MAX_PC_BONUS_SCRIPT) > 0xFFFF){ - ShowError("bonus_script_data size = %d is too, please reduce MAX_PC_BONUS_SCRIPT (%d) size. (must be below 0xFFFF).\n", + ShowError("bonus_script_data size = %d is too big, please reduce MAX_PC_BONUS_SCRIPT (%d) size. (must be below 0xFFFF).\n", (sizeof(struct bonus_script_data) * MAX_PC_BONUS_SCRIPT), MAX_PC_BONUS_SCRIPT); exit(EXIT_FAILURE); } diff --git a/src/map/pc.c b/src/map/pc.c index 5fdf01a976..d032b31021 100755 --- a/src/map/pc.c +++ b/src/map/pc.c @@ -11039,16 +11039,6 @@ void pc_show_version(struct map_session_data *sd) { clif_displaymessage(sd->fd,buf); } -int pc_bonus_script_list(void *key, void *data, va_list ap) { - struct s_bonus_script_entry *entry = (struct s_bonus_script_entry *)data; - struct map_session_data *sd = va_arg(ap, struct map_session_data *); - struct linkdb_node *node = (struct linkdb_node *)key; - if (sd) - ShowDebug(" cid=%d aid=%d\n",sd->status.char_id, sd->status.account_id); - ShowDebug(" key:%d e:0x%08X n:0x%08X nn:0x%08X np:0x%08X\n",(intptr_t)key, entry, key, node->next, node->prev); - return 1; -} - /** * Run bonus_script on player * @param sd diff --git a/src/map/pc.h b/src/map/pc.h index de18dfd0a9..9b47de3a85 100644 --- a/src/map/pc.h +++ b/src/map/pc.h @@ -1127,7 +1127,6 @@ int pc_bonus_script_timer(int tid, unsigned int tick, int id, intptr_t data); void pc_bonus_script(struct map_session_data *sd); struct s_bonus_script_entry *pc_bonus_script_add(struct map_session_data *sd, const char *script_str, uint32 dur, enum si_type icon, uint16 flag, uint8 type); void pc_bonus_script_clear(struct map_session_data *sd, uint16 flag); -int pc_bonus_script_list(void *key, void *data, va_list ap); void pc_cell_basilica(struct map_session_data *sd); From 4726f4c9689c6b86abbacef6a9e5e607dbb6d9f9 Mon Sep 17 00:00:00 2001 From: Cydh Ramdh Date: Fri, 13 Feb 2015 16:28:55 +0700 Subject: [PATCH 4/4] Fixed warning from unused variables Signed-off-by: Cydh Ramdh --- src/map/chrif.c | 1 - src/map/pc.c | 1 - 2 files changed, 2 deletions(-) diff --git a/src/map/chrif.c b/src/map/chrif.c index e30d9e02e9..21412dfcff 100644 --- a/src/map/chrif.c +++ b/src/map/chrif.c @@ -1700,7 +1700,6 @@ int chrif_bsdata_received(int fd) { } if ((count = RFIFOB(fd,8))) { - struct s_bonus_script *list = NULL; uint8 i = 0; //ShowInfo("Loaded %d bonus script for CID=%d\n", count, sd->status.char_id); diff --git a/src/map/pc.c b/src/map/pc.c index d032b31021..923f138655 100755 --- a/src/map/pc.c +++ b/src/map/pc.c @@ -11045,7 +11045,6 @@ void pc_show_version(struct map_session_data *sd) { * @author [Cydh] **/ void pc_bonus_script(struct map_session_data *sd) { - uint8 i = 0; int now = gettick(); struct linkdb_node *node = NULL, *next = NULL;