summaryrefslogtreecommitdiff
path: root/src/char
diff options
context:
space:
mode:
authorHaru <haru@dotalux.com>2018-07-28 03:21:42 +0200
committerGitHub <noreply@github.com>2018-07-28 03:21:42 +0200
commit621611e6e90af0c5551b4daca75ba1267d8e8478 (patch)
tree92ca087267778474df4c3589e22ec801f4c34622 /src/char
parent164e79976b926da35612ce28cf84ebd9c2ac3d3e (diff)
parent7f5b041efd88bae052c98e1bd1c5e28676c73272 (diff)
downloadhercules-621611e6e90af0c5551b4daca75ba1267d8e8478.tar.gz
hercules-621611e6e90af0c5551b4daca75ba1267d8e8478.tar.bz2
hercules-621611e6e90af0c5551b4daca75ba1267d8e8478.tar.xz
hercules-621611e6e90af0c5551b4daca75ba1267d8e8478.zip
Merge pull request #2067 from dastgirp/1-achievement
Implemented Achievement System and Title System
Diffstat (limited to 'src/char')
-rw-r--r--src/char/HPMchar.c2
-rw-r--r--src/char/Makefile.in8
-rw-r--r--src/char/char.c27
-rw-r--r--src/char/char.h1
-rw-r--r--src/char/int_achievement.c252
-rw-r--r--src/char/int_achievement.h53
-rw-r--r--src/char/inter.c6
-rw-r--r--src/char/mapif.c120
-rw-r--r--src/char/mapif.h5
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);