diff options
Diffstat (limited to 'src/char')
-rw-r--r-- | src/char/HPMchar.c | 2 | ||||
-rw-r--r-- | src/char/Makefile.in | 8 | ||||
-rw-r--r-- | src/char/char.c | 27 | ||||
-rw-r--r-- | src/char/char.h | 1 | ||||
-rw-r--r-- | src/char/int_achievement.c | 252 | ||||
-rw-r--r-- | src/char/int_achievement.h | 53 | ||||
-rw-r--r-- | src/char/inter.c | 6 | ||||
-rw-r--r-- | src/char/mapif.c | 120 | ||||
-rw-r--r-- | src/char/mapif.h | 5 |
9 files changed, 463 insertions, 11 deletions
diff --git a/src/char/HPMchar.c b/src/char/HPMchar.c index 9f075d909..db2c3702e 100644 --- a/src/char/HPMchar.c +++ b/src/char/HPMchar.c @@ -27,6 +27,7 @@ #include "char/char.h" #include "char/geoip.h" #include "char/inter.h" +#include "char/int_achievement.h" #include "char/int_auction.h" #include "char/int_clan.h" #include "char/int_elemental.h" @@ -42,6 +43,7 @@ #include "char/loginif.h" #include "char/mapif.h" #include "char/pincode.h" + #include "common/HPMi.h" #include "common/conf.h" #include "common/console.h" diff --git a/src/char/Makefile.in b/src/char/Makefile.in index 49fc8ec92..95c8df813 100644 --- a/src/char/Makefile.in +++ b/src/char/Makefile.in @@ -40,12 +40,12 @@ MT19937AR_D = $(THIRDPARTY_D)/mt19937ar MT19937AR_OBJ = $(MT19937AR_D)/mt19937ar.o MT19937AR_H = $(MT19937AR_D)/mt19937ar.h -CHAR_C = char.c HPMchar.c loginif.c mapif.c geoip.c inter.c int_auction.c int_clan.c int_elemental.c int_guild.c \ - int_homun.c int_mail.c int_mercenary.c int_party.c int_pet.c \ +CHAR_C = char.c HPMchar.c loginif.c mapif.c geoip.c inter.c int_achievement.c int_auction.c int_clan.c int_elemental.c \ + int_guild.c int_homun.c int_mail.c int_mercenary.c int_party.c int_pet.c \ int_quest.c int_rodex.c int_storage.c pincode.c CHAR_OBJ = $(addprefix obj_sql/, $(patsubst %.c,%.o,$(CHAR_C))) -CHAR_H = char.h HPMchar.h loginif.h mapif.h geoip.h inter.h int_auction.h int_clan.h int_elemental.h int_guild.h \ - int_homun.h int_mail.h int_mercenary.h int_party.h int_pet.h \ +CHAR_H = char.h HPMchar.h loginif.h mapif.h geoip.h inter.h int_achievement.h int_auction.h int_clan.h int_elemental.h \ + int_guild.h int_homun.h int_mail.h int_mercenary.h int_party.h int_pet.h \ int_quest.h int_rodex.h int_storage.h pincode.h CHAR_PH = diff --git a/src/char/char.c b/src/char/char.c index 99198fa50..54f6ca7d1 100644 --- a/src/char/char.c +++ b/src/char/char.c @@ -37,6 +37,7 @@ #include "char/int_quest.h" #include "char/int_rodex.h" #include "char/int_storage.h" +#include "char/int_achievement.h" #include "char/inter.h" #include "char/loginif.h" #include "char/mapif.h" @@ -112,6 +113,7 @@ char acc_reg_num_db[32] = "acc_reg_num_db"; char acc_reg_str_db[32] = "acc_reg_str_db"; char char_reg_str_db[32] = "char_reg_str_db"; char char_reg_num_db[32] = "char_reg_num_db"; +char char_achievement_db[256] = "char_achievements"; static struct char_interface char_s; struct char_interface *chr; @@ -291,12 +293,18 @@ static void char_set_char_offline(int char_id, int account_id) } else { - struct mmo_charstatus* cp = (struct mmo_charstatus*) idb_get(chr->char_db_,char_id); + struct mmo_charstatus *cp = (struct mmo_charstatus*) idb_get(chr->char_db_, char_id); + /* Character Achievements */ + struct char_achievements *c_ach = (struct char_achievements *) idb_get(inter_achievement->char_achievements, char_id); inter_guild->CharOffline(char_id, cp?cp->guild_id:-1); - if (cp) + if (cp != NULL) idb_remove(chr->char_db_,char_id); + if (c_ach != NULL) { + VECTOR_CLEAR(*c_ach); + idb_remove(inter_achievement->char_achievements, char_id); + } if( SQL_ERROR == SQL->Query(inter->sql_handle, "UPDATE `%s` SET `online`='0' WHERE `char_id`='%d' LIMIT 1", char_db, char_id) ) Sql_ShowDebug(inter->sql_handle); @@ -468,7 +476,7 @@ static int char_mmo_char_tosql(int char_id, struct mmo_charstatus *p) (p->show_equip != cp->show_equip) || (p->allow_party != cp->allow_party) || (p->font != cp->font) || (p->uniqueitem_counter != cp->uniqueitem_counter) || (p->hotkey_rowshift != cp->hotkey_rowshift) || (p->clan_id != cp->clan_id) || (p->last_login != cp->last_login) || (p->attendance_count != cp->attendance_count) || - (p->attendance_timer != cp->attendance_timer) + (p->attendance_timer != cp->attendance_timer) || (p->title_id != cp->title_id) ) { //Save status unsigned int opt = 0; @@ -486,7 +494,8 @@ static int char_mmo_char_tosql(int char_id, struct mmo_charstatus *p) "`weapon`='%d',`shield`='%d',`head_top`='%d',`head_mid`='%d',`head_bottom`='%d'," "`last_map`='%s',`last_x`='%d',`last_y`='%d',`save_map`='%s',`save_x`='%d',`save_y`='%d', `rename`='%d'," "`delete_date`='%lu',`robe`='%d',`slotchange`='%d', `char_opt`='%u', `font`='%u', `uniqueitem_counter` ='%u'," - "`hotkey_rowshift`='%d',`clan_id`='%d',`last_login`='%"PRId64"',`attendance_count`='%d',`attendance_timer`='%"PRId64"'" + "`hotkey_rowshift`='%d',`clan_id`='%d',`last_login`='%"PRId64"',`attendance_count`='%d',`attendance_timer`='%"PRId64"'," + "`title_id`='%d'" " WHERE `account_id`='%d' AND `char_id` = '%d'", char_db, p->base_level, p->job_level, p->base_exp, p->job_exp, p->zeny, @@ -499,6 +508,7 @@ static int char_mmo_char_tosql(int char_id, struct mmo_charstatus *p) (unsigned long)p->delete_date, // FIXME: platform-dependent size p->look.robe,p->slotchange,opt,p->font,p->uniqueitem_counter, p->hotkey_rowshift,p->clan_id,p->last_login, p->attendance_count, p->attendance_timer, + p->title_id, p->account_id, p->char_id) ) { Sql_ShowDebug(inter->sql_handle); @@ -1069,7 +1079,7 @@ static int char_mmo_chars_fromsql(struct char_session_data *sd, uint8 *buf) "`str`,`agi`,`vit`,`int`,`dex`,`luk`,`max_hp`,`hp`,`max_sp`,`sp`," "`status_point`,`skill_point`,`option`,`karma`,`manner`,`hair`,`hair_color`," "`clothes_color`,`body`,`weapon`,`shield`,`head_top`,`head_mid`,`head_bottom`,`last_map`,`rename`,`delete_date`," - "`robe`,`slotchange`,`unban_time`,`sex`" + "`robe`,`slotchange`,`unban_time`,`sex`,`title_id`" " FROM `%s` WHERE `account_id`='%d' AND `char_num` < '%d'", char_db, sd->account_id, MAX_CHARS) || SQL_ERROR == SQL->StmtExecute(stmt) || SQL_ERROR == SQL->StmtBindColumn(stmt, 0, SQLDT_INT, &p.char_id, sizeof p.char_id, NULL, NULL) @@ -1112,6 +1122,7 @@ static int char_mmo_chars_fromsql(struct char_session_data *sd, uint8 *buf) || SQL_ERROR == SQL->StmtBindColumn(stmt, 37, SQLDT_USHORT, &p.slotchange, sizeof p.slotchange, NULL, NULL) || SQL_ERROR == SQL->StmtBindColumn(stmt, 38, SQLDT_TIME, &unban_time, sizeof unban_time, NULL, NULL) || SQL_ERROR == SQL->StmtBindColumn(stmt, 39, SQLDT_ENUM, &sex, sizeof sex, NULL, NULL) + || SQL_ERROR == SQL->StmtBindColumn(stmt, 40, SQLDT_INT, &p.title_id, sizeof p.title_id, NULL, NULL) ) { SqlStmt_ShowDebug(stmt); SQL->StmtFree(stmt); @@ -1176,7 +1187,8 @@ static int char_mmo_char_fromsql(int char_id, struct mmo_charstatus *p, bool loa "`status_point`,`skill_point`,`option`,`karma`,`manner`,`party_id`,`guild_id`,`pet_id`,`homun_id`,`elemental_id`,`hair`," "`hair_color`,`clothes_color`,`body`,`weapon`,`shield`,`head_top`,`head_mid`,`head_bottom`,`last_map`,`last_x`,`last_y`," "`save_map`,`save_x`,`save_y`,`partner_id`,`father`,`mother`,`child`,`fame`,`rename`,`delete_date`,`robe`,`slotchange`," - "`char_opt`,`font`,`uniqueitem_counter`,`sex`,`hotkey_rowshift`,`clan_id`,`last_login`, `attendance_count`, `attendance_timer`" + "`char_opt`,`font`,`uniqueitem_counter`,`sex`,`hotkey_rowshift`,`clan_id`,`last_login`, `attendance_count`, `attendance_timer`," + "`title_id`" " FROM `%s` WHERE `char_id`=? LIMIT 1", char_db) || SQL_ERROR == SQL->StmtBindParam(stmt, 0, SQLDT_INT, &char_id, sizeof char_id) || SQL_ERROR == SQL->StmtExecute(stmt) @@ -1243,6 +1255,7 @@ static int char_mmo_char_fromsql(int char_id, struct mmo_charstatus *p, bool loa || SQL_ERROR == SQL->StmtBindColumn(stmt, 60, SQLDT_INT64, &p->last_login, sizeof p->last_login, NULL, NULL) || SQL_ERROR == SQL->StmtBindColumn(stmt, 61, SQLDT_SHORT, &p->attendance_count, sizeof p->attendance_count, NULL, NULL) || SQL_ERROR == SQL->StmtBindColumn(stmt, 62, SQLDT_INT64, &p->attendance_timer, sizeof p->attendance_timer, NULL, NULL) + || SQL_ERROR == SQL->StmtBindColumn(stmt, 63, SQLDT_INT, &p->title_id, sizeof p->title_id, NULL, NULL) ) { SqlStmt_ShowDebug(stmt); SQL->StmtFree(stmt); @@ -5443,6 +5456,7 @@ static bool char_sql_config_read_pc(const char *filename, const struct config_t libconfig->setting_lookup_mutable_string(setting, "hotkey_db", hotkey_db, sizeof(hotkey_db)); libconfig->setting_lookup_mutable_string(setting, "scdata_db", scdata_db, sizeof(scdata_db)); libconfig->setting_lookup_mutable_string(setting, "inventory_db", inventory_db, sizeof(inventory_db)); + libconfig->setting_lookup_mutable_string(setting, "achievement_db", char_achievement_db, sizeof(char_achievement_db)); libconfig->setting_lookup_mutable_string(setting, "cart_db", cart_db, sizeof(cart_db)); libconfig->setting_lookup_mutable_string(setting, "charlog_db", charlog_db, sizeof(charlog_db)); libconfig->setting_lookup_mutable_string(setting, "storage_db", storage_db, sizeof(storage_db)); @@ -6319,6 +6333,7 @@ void char_load_defaults(void) inter_quest_defaults(); inter_storage_defaults(); inter_rodex_defaults(); + inter_achievement_defaults(); inter_defaults(); geoip_defaults(); } diff --git a/src/char/char.h b/src/char/char.h index 4d816583a..81cab1eaf 100644 --- a/src/char/char.h +++ b/src/char/char.h @@ -340,6 +340,7 @@ extern char acc_reg_num_db[32]; extern char acc_reg_str_db[32]; extern char char_reg_str_db[32]; extern char char_reg_num_db[32]; +extern char char_achievement_db[256]; extern int guild_exp_rate; diff --git a/src/char/int_achievement.c b/src/char/int_achievement.c new file mode 100644 index 000000000..14311ecf0 --- /dev/null +++ b/src/char/int_achievement.c @@ -0,0 +1,252 @@ +/** +* This file is part of Hercules. +* http://herc.ws - http://github.com/HerculesWS/Hercules +* +* Copyright (C) 2017 Hercules Dev Team +* Copyright (C) Smokexyz +* +* Hercules is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ +#define HERCULES_CORE + +#include "int_achievement.h" + +#include "char/char.h" +#include "char/inter.h" +#include "char/mapif.h" + +#include "common/db.h" +#include "common/memmgr.h" +#include "common/mmo.h" +#include "common/nullpo.h" +#include "common/showmsg.h" +#include "common/socket.h" +#include "common/sql.h" +#include "common/strlib.h" + +#include <stdio.h> +#include <stdlib.h> + +static struct inter_achievement_interface inter_achievement_s; +struct inter_achievement_interface *inter_achievement; + +/** + * Saves changed achievements for a character. + * @param[in] char_id character identifier. + * @param[out] cp pointer to loaded achievements. + * @param[in] p pointer to map-sent character achievements. + * @return number of achievements saved. + */ +static int inter_achievement_tosql(int char_id, struct char_achievements *cp, const struct char_achievements *p) +{ + StringBuf buf; + int i = 0, rows = 0; + + nullpo_ret(cp); + nullpo_ret(p); + Assert_ret(char_id > 0); + + StrBuf->Init(&buf); + StrBuf->Printf(&buf, "REPLACE INTO `%s` (`char_id`, `ach_id`, `completed_at`, `rewarded_at`", char_achievement_db); + for (i = 0; i < MAX_ACHIEVEMENT_OBJECTIVES; i++) + StrBuf->Printf(&buf, ", `obj_%d`", i); + StrBuf->AppendStr(&buf, ") VALUES "); + + for (i = 0; i < VECTOR_LENGTH(*p); i++) { + int j = 0; + bool save = false; + struct achievement *pa = &VECTOR_INDEX(*p, i), *cpa = NULL; + + ARR_FIND(0, VECTOR_LENGTH(*cp), j, ((cpa = &VECTOR_INDEX(*cp, j)) && cpa->id == pa->id)); + + if (j == VECTOR_LENGTH(*cp)) + save = true; + else if (memcmp(cpa, pa, sizeof(struct achievement)) != 0) + save = true; + + if (save) { + StrBuf->Printf(&buf, "%s('%d', '%d', '%"PRId64"', '%"PRId64"'", rows ?", ":"", char_id, pa->id, (int64)pa->completed_at, (int64)pa->rewarded_at); + for (j = 0; j < MAX_ACHIEVEMENT_OBJECTIVES; j++) + StrBuf->Printf(&buf, ", '%d'", pa->objective[j]); + StrBuf->AppendStr(&buf, ")"); + rows++; + } + } + + if (rows > 0 && SQL_ERROR == SQL->QueryStr(inter->sql_handle, StrBuf->Value(&buf))) { + Sql_ShowDebug(inter->sql_handle); + StrBuf->Destroy(&buf); // Destroy the buffer. + return 0; + } + // Destroy the buffer. + StrBuf->Destroy(&buf); + + if (rows) { + ShowInfo("achievements saved for char %d (total: %d, saved: %d)\n", char_id, VECTOR_LENGTH(*p), rows); + + /* Sync with inter-db acheivements. */ + VECTOR_CLEAR(*cp); + VECTOR_ENSURE(*cp, VECTOR_LENGTH(*p), 1); + VECTOR_PUSHARRAY(*cp, VECTOR_DATA(*p), VECTOR_LENGTH(*p)); + } + + return rows; +} + +/** + * Retrieves all achievements of a character. + * @param[in] char_id character identifier. + * @param[out] cp pointer to character achievements structure. + * @return true on success, false on failure. + */ +static bool inter_achievement_fromsql(int char_id, struct char_achievements *cp) +{ + StringBuf buf; + char *data; + int i = 0, num_rows = 0; + + nullpo_ret(cp); + + Assert_ret(char_id > 0); + + // char_achievements (`char_id`, `ach_id`, `completed_at`, `rewarded_at`, `obj_0`, `obj_2`, ...`obj_9`) + StrBuf->Init(&buf); + StrBuf->AppendStr(&buf, "SELECT `ach_id`, `completed_at`, `rewarded_at`"); + for (i = 0; i < MAX_ACHIEVEMENT_OBJECTIVES; i++) + StrBuf->Printf(&buf, ", `obj_%d`", i); + StrBuf->Printf(&buf, " FROM `%s` WHERE `char_id` = '%d' ORDER BY `ach_id`", char_achievement_db, char_id); + + if (SQL_ERROR == SQL->QueryStr(inter->sql_handle, StrBuf->Value(&buf))) { + Sql_ShowDebug(inter->sql_handle); + StrBuf->Destroy(&buf); + return false; + } + + VECTOR_CLEAR(*cp); + + if ((num_rows = (int) SQL->NumRows(inter->sql_handle)) != 0) { + int j = 0; + + VECTOR_ENSURE(*cp, num_rows, 1); + + for (i = 0; i < num_rows && SQL_SUCCESS == SQL->NextRow(inter->sql_handle); i++) { + struct achievement t_ach = { 0 }; + SQL->GetData(inter->sql_handle, 0, &data, NULL); t_ach.id = atoi(data); + SQL->GetData(inter->sql_handle, 1, &data, NULL); t_ach.completed_at = atoi(data); + SQL->GetData(inter->sql_handle, 2, &data, NULL); t_ach.rewarded_at = atoi(data); + /* Objectives */ + for (j = 0; j < MAX_ACHIEVEMENT_OBJECTIVES; j++) { + SQL->GetData(inter->sql_handle, j + 3, &data, NULL); + t_ach.objective[j] = atoi(data); + } + /* Add Entry */ + VECTOR_PUSH(*cp, t_ach); + } + } + + SQL->FreeResult(inter->sql_handle); + + StrBuf->Destroy(&buf); + + if (num_rows > 0) + ShowInfo("achievements loaded for char %d (total: %d)\n", char_id, num_rows); + + return true; +} + +/** + * Handles checking of map server packets and calls appropriate functions. + * @param fd socket descriptor. + * @return 0 on failure, 1 on succes. + */ +static int inter_achievement_parse_frommap(int fd) +{ + RFIFOHEAD(fd); + + switch (RFIFOW(fd,0)) { + case 0x3012: + mapif->pLoadAchievements(fd); + break; + case 0x3013: + mapif->pSaveAchievements(fd); + break; + default: + return 0; + } + + return 1; +} + +/** + * Initialization function + */ +static int inter_achievement_sql_init(void) +{ + // Initialize the loaded db storage. + // used as a comparand against map-server achievement data before saving. + inter_achievement->char_achievements = idb_alloc(DB_OPT_RELEASE_DATA); + return 1; +} + +/** + * This function ensures idb's entry. + */ +static struct DBData inter_achievement_ensure_char_achievements(union DBKey key, va_list args) +{ + struct char_achievements *ca = NULL; + + CREATE(ca, struct char_achievements, 1); + VECTOR_INIT(*ca); + + return DB->ptr2data(ca); +} + +/** + * Cleaning function called through db_destroy() + */ +static int inter_achievement_char_achievements_clear(union DBKey key, struct DBData *data, va_list args) +{ + struct char_achievements *ca = DB->data2ptr(data); + + VECTOR_CLEAR(*ca); + + return 0; +} + +/** + * Finalization function. + */ +static void inter_achievement_sql_final(void) +{ + inter_achievement->char_achievements->destroy(inter_achievement->char_achievements, inter_achievement->char_achievements_clear); +} + +/** + * Inter-achievement interface. + */ +void inter_achievement_defaults(void) +{ + inter_achievement = &inter_achievement_s; + /* */ + inter_achievement->ensure_char_achievements = inter_achievement_ensure_char_achievements; + /* */ + inter_achievement->sql_init = inter_achievement_sql_init; + inter_achievement->sql_final = inter_achievement_sql_final; + /* */ + inter_achievement->tosql = inter_achievement_tosql; + inter_achievement->fromsql = inter_achievement_fromsql; + /* */ + inter_achievement->parse_frommap = inter_achievement_parse_frommap; + inter_achievement->char_achievements_clear = inter_achievement_char_achievements_clear; +} diff --git a/src/char/int_achievement.h b/src/char/int_achievement.h new file mode 100644 index 000000000..4a44a798d --- /dev/null +++ b/src/char/int_achievement.h @@ -0,0 +1,53 @@ +/** +* This file is part of Hercules. +* http://herc.ws - http://github.com/HerculesWS/Hercules +* +* Copyright (C) 2017 Hercules Dev Team +* Copyright (C) Smokexyz +* +* Hercules is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ +#ifndef CHAR_INT_ACHIEVEMENT_H +#define CHAR_INT_ACHIEVEMENT_H + +#include "common/hercules.h" +#include "common/db.h" + +struct achievement; +struct char_achievements; + +/** + * inter_achievement Interface + */ +struct inter_achievement_interface { + struct DBMap *char_achievements; + /* */ + int (*sql_init) (void); + void (*sql_final) (void); + /* */ + int (*tosql) (int char_id, struct char_achievements *cp, const struct char_achievements *p); + bool (*fromsql) (int char_id, struct char_achievements *a); + /* */ + struct DBData(*ensure_char_achievements) (union DBKey key, va_list args); + int (*char_achievements_clear) (union DBKey key, struct DBData *data, va_list args); + /* */ + int (*parse_frommap) (int fd); +}; + +#ifdef HERCULES_CORE +void inter_achievement_defaults(void); +#endif // HERCULES_CORE + +HPShared struct inter_achievement_interface *inter_achievement; +#endif /* CHAR_INT_ACHIEVEMENT_H */ diff --git a/src/char/inter.c b/src/char/inter.c index 7269009a7..418c9b0a1 100644 --- a/src/char/inter.c +++ b/src/char/inter.c @@ -36,6 +36,7 @@ #include "char/int_quest.h" #include "char/int_rodex.h" #include "char/int_storage.h" +#include "char/int_achievement.h" #include "char/mapif.h" #include "common/cbasetypes.h" #include "common/conf.h" @@ -70,7 +71,7 @@ int party_share_level = 10; // recv. packet list static int inter_recv_packet_length[] = { -1,-1, 7,-1, -1,13,36, (2 + 4 + 4 + 4 + NAME_LENGTH), 0, 0, 0, 0, 0, 0, 0, 0, // 3000- - 6,-1, 0, 0, 0, 0, 0, 0, 10,-1, 0, 0, 0, 0, 0, 0, // 3010- Account Storage [Smokexyz] + 6,-1, 6,-1, 0, 0, 0, 0, 10,-1, 0, 0, 0, 0, 0, 0, // 3010- Account Storage, Achievements [Smokexyz] -1,10,-1,14, 14,19, 6,-1, 14,14, 0, 0, 0, 0, 0, 0, // 3020- Party -1, 6,-1,-1, 55,19, 6,-1, 14,-1,-1,-1, 18,19,186,-1, // 3030- -1, 9, 0, 0, 10,10, 0, 0, 7, 6,10,10, 10,-1, 0, 0, // 3040- Clan System(3044-3045) @@ -974,6 +975,7 @@ static int inter_init_sql(const char *file) inter_mail->sql_init(); inter_auction->sql_init(); inter_rodex->sql_init(); + inter_achievement->sql_init(); geoip->init(); inter->msg_config_read("conf/messages.conf", false); @@ -995,6 +997,7 @@ static void inter_final(void) inter_mail->sql_final(); inter_auction->sql_final(); inter_rodex->sql_final(); + inter_achievement->sql_final(); geoip->final(true); inter->do_final_msg(); @@ -1133,6 +1136,7 @@ static int inter_parse_frommap(int fd) || inter_quest->parse_frommap(fd) || inter_rodex->parse_frommap(fd) || inter_clan->parse_frommap(fd) + || inter_achievement->parse_frommap(fd) ) break; else diff --git a/src/char/mapif.c b/src/char/mapif.c index 30f8c1178..dc5735550 100644 --- a/src/char/mapif.c +++ b/src/char/mapif.c @@ -24,6 +24,7 @@ #include "mapif.h" #include "char/char.h" +#include "char/int_achievement.h" #include "char/int_auction.h" #include "char/int_clan.h" #include "char/int_guild.h" @@ -2346,6 +2347,120 @@ static int mapif_parse_ClanMemberCount(int fd, int clan_id, int kick_interval) return 0; } +// Achievement System +/** + * Parse achievement load request from the map server + * @param[in] fd socket descriptor. + */ +static void mapif_parse_load_achievements(int fd) +{ + int char_id = 0; + + /* Read received information from map-server. */ + RFIFOHEAD(fd); + char_id = RFIFOL(fd, 2); + + /* Load and send achievements to map */ + mapif->achievement_load(fd, char_id); +} + +/** + * Loads achievements and sends to the map server. + * @param[in] fd socket descriptor + * @param[in] char_id character Id. + */ +static void mapif_achievement_load(int fd, int char_id) +{ + struct char_achievements *cp = NULL; + + /* Ensure data exists */ + cp = idb_ensure(inter_achievement->char_achievements, char_id, inter_achievement->ensure_char_achievements); + + /* Load storage for char-server. */ + inter_achievement->fromsql(char_id, cp); + + /* Send Achievements to map server. */ + mapif->sAchievementsToMap(fd, char_id, cp); +} + +/** + * Sends achievement data of a character to the map server. + * @packet[out] 0x3810 <packet_id>.W <payload_size>.W <char_id>.L <char_achievements[]>.P + * @param[in] fd socket descriptor. + * @param[in] char_id Character ID. + * @param[in] cp Pointer to character's achievement data vector. + */ +static void mapif_send_achievements_to_map(int fd, int char_id, const struct char_achievements *cp) +{ + int i = 0; + int data_size = 0; + + nullpo_retv(cp); + + data_size = sizeof(struct achievement) * VECTOR_LENGTH(*cp); + +STATIC_ASSERT((sizeof(struct achievement) * MAX_ACHIEVEMENT_DB + 8 <= UINT16_MAX), + "The achievements data can potentially be larger than the maximum packet size. This may cause errors at run-time."); + + /* Send to the map server. */ + WFIFOHEAD(fd, (8 + data_size)); + WFIFOW(fd, 0) = 0x3810; + WFIFOW(fd, 2) = (8 + data_size); + WFIFOL(fd, 4) = char_id; + for (i = 0; i < VECTOR_LENGTH(*cp); i++) + memcpy(WFIFOP(fd, 8 + i * sizeof(struct achievement)), &VECTOR_INDEX(*cp, i), sizeof(struct achievement)); + WFIFOSET(fd, 8 + data_size); +} + +/** + * Handles achievement request and saves data from map server. + * @packet[in] 0x3013 <packet_size>.W <char_id>.L <char_achievement>.P + * @param[in] fd session socket descriptor. + */ +static void mapif_parse_save_achievements(int fd) +{ + int size = 0, char_id = 0, payload_count = 0, i = 0; + struct char_achievements p = { 0 }; + + RFIFOHEAD(fd); + size = RFIFOW(fd, 2); + char_id = RFIFOL(fd, 4); + + payload_count = (size - 8) / sizeof(struct achievement); + + VECTOR_INIT(p); + VECTOR_ENSURE(p, payload_count, 1); + + for (i = 0; i < payload_count; i++) { + struct achievement ach = { 0 }; + memcpy(&ach, RFIFOP(fd, 8 + i * sizeof(struct achievement)), sizeof(struct achievement)); + VECTOR_PUSH(p, ach); + } + + mapif->achievement_save(char_id, &p); + + VECTOR_CLEAR(p); +} + +/** + * Handles inter-server achievement db ensuring + * and saves current achievements to sql. + * @param[in] char_id character identifier. + * @param[out] p pointer to character achievements vector. + */ +static void mapif_achievement_save(int char_id, struct char_achievements *p) +{ + struct char_achievements *cp = NULL; + + nullpo_retv(p); + + /* Get loaded achievements. */ + cp = idb_ensure(inter_achievement->char_achievements, char_id, inter_achievement->ensure_char_achievements); + + if (VECTOR_LENGTH(*p)) /* Save current achievements. */ + inter_achievement->tosql(char_id, cp, p); +} + void mapif_defaults(void) { mapif = &mapif_s; @@ -2361,6 +2476,11 @@ void mapif_defaults(void) mapif->sendallwos = mapif_sendallwos; mapif->send = mapif_send; mapif->send_users_count = mapif_send_users_count; + mapif->pLoadAchievements = mapif_parse_load_achievements; + mapif->sAchievementsToMap = mapif_send_achievements_to_map; + mapif->pSaveAchievements = mapif_parse_save_achievements; + mapif->achievement_load = mapif_achievement_load; + mapif->achievement_save = mapif_achievement_save; mapif->auction_message = mapif_auction_message; mapif->auction_sendlist = mapif_auction_sendlist; mapif->parse_auction_requestlist = mapif_parse_auction_requestlist; diff --git a/src/char/mapif.h b/src/char/mapif.h index d67ce1c79..bfdefe4ea 100644 --- a/src/char/mapif.h +++ b/src/char/mapif.h @@ -40,6 +40,11 @@ struct mapif_interface { int (*sendallwos) (int sfd, unsigned char *buf, unsigned int len); int (*send) (int fd, unsigned char *buf, unsigned int len); void (*send_users_count) (int users); + void (*pLoadAchievements) (int fd); + void (*sAchievementsToMap) (int fd, int char_id, const struct char_achievements *p); + void (*pSaveAchievements) (int fd); + void (*achievement_load) (int fd, int char_id); + void (*achievement_save) (int char_id, struct char_achievements *p); void (*auction_message) (int char_id, unsigned char result); void (*auction_sendlist) (int fd, int char_id, short count, short pages, unsigned char *buf); void (*parse_auction_requestlist) (int fd); |