/** * This file is part of Hercules. * http://herc.ws - http://github.com/HerculesWS/Hercules * * Copyright (C) 2017 Hercules Dev Team * * 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 . */ #define HERCULES_CORE #include "config/core.h" #include "clan.h" #include "map/clif.h" #include "map/chrif.h" #include "map/homunculus.h" #include "map/intif.h" #include "map/log.h" #include "map/mercenary.h" #include "map/mob.h" #include "map/npc.h" #include "map/pc.h" #include "map/pet.h" #include "map/script.h" #include "map/skill.h" #include "map/status.h" #include "common/HPM.h" #include "common/conf.h" #include "common/cbasetypes.h" #include "common/db.h" #include "common/memmgr.h" #include "common/mapindex.h" #include "common/nullpo.h" #include "common/showmsg.h" #include "common/socket.h" #include "common/strlib.h" #include "common/timer.h" #include "common/utils.h" #include #include #include static struct clan_interface clan_s; struct clan_interface *clan; /** * Searches a Clan by clan_id * * @param clan_id Clan ID * @return struct clan* */ static struct clan *clan_search(int clan_id) { if (clan_id <= 0) { return NULL; } return (struct clan *)idb_get(clan->db, clan_id); } /** * Searches a Clan by clan_name or constant * * @param name Clan Name * @return struct clan* */ static struct clan *clan_searchname(const char *name) { struct clan *c; struct DBIterator *iter; nullpo_retr(NULL, name); iter = db_iterator(clan->db); for (c = dbi_first(iter); dbi_exists(iter); c = dbi_next(iter)) { if (strncmpi(c->name, name, NAME_LENGTH) == 0 || strncmpi(c->constant, name, NAME_LENGTH) == 0) { break; } } dbi_destroy(iter); return c; } /** * Returns the first online character of clan * * @param (struct clan *) c clan structure * @return (struct map_session_data *) */ static struct map_session_data *clan_getonlinesd(struct clan *c) { int i; nullpo_retr(NULL, c); ARR_FIND(0, VECTOR_LENGTH(c->members), i, (VECTOR_INDEX(c->members, i).sd != NULL && VECTOR_INDEX(c->members, i).online == 1)); return (i < VECTOR_LENGTH(c->members)) ? VECTOR_INDEX(c->members, i).sd : NULL; } /** * Returns the member index of given Player * * @param c Clan Data * @param char_id Player's Char ID * @return int */ static int clan_getindex(const struct clan *c, int char_id) { int i; nullpo_retr(INDEX_NOT_FOUND, c); ARR_FIND(0, VECTOR_LENGTH(c->members), i, VECTOR_INDEX(c->members, i).char_id == char_id); if (i == VECTOR_LENGTH(c->members)) { return INDEX_NOT_FOUND; } return i; } /** * Starts clan buff */ static void clan_buff_start(struct map_session_data *sd, struct clan *c) { nullpo_retv(sd); nullpo_retv(c); if (c->buff.icon <= 0) { return; } clif->sc_load(&sd->bl, sd->bl.id, SELF, c->buff.icon, 0, c->clan_id, 0); script->run(c->buff.script, 0, sd->bl.id, npc->fake_nd->bl.id); } /** * Ends clan buff */ static void clan_buff_end(struct map_session_data *sd, struct clan *c) { nullpo_retv(sd); nullpo_retv(c); if (c->buff.icon <= 0) { return; } clif->sc_end(&sd->bl, sd->bl.id, SELF, c->buff.icon); } /** * Joins a Player into a Clan * * @param sd Player's Map Session Data * @param clan_id Clan which will add this player * @return bool */ static bool clan_join(struct map_session_data *sd, int clan_id) { struct clan *c; struct clan_member m; nullpo_ret(sd); // Already joined a guild or clan if (sd->status.guild_id > 0 || sd->guild != NULL) { ShowError("clan_join: Player already joined in a guild. char_id: %d\n", sd->status.char_id); return false; } else if ( sd->status.clan_id > 0 || sd->clan != NULL) { ShowError("clan_join: Player already joined in a clan. char_id: %d\n", sd->status.char_id); return false; } c = clan->search(clan_id); if (c == NULL) { ShowError("clan_join: Invalid Clan ID: %d\n", clan_id); return false; } if (!c->received) { return false; } if (clan->getindex(c, sd->status.char_id) != INDEX_NOT_FOUND) { ShowError("clan_join: Player already joined this clan. char_id: %d clan_id: %d\n", sd->status.char_id, clan_id); return false; } if (VECTOR_LENGTH(c->members) >= c->max_member || c->member_count >= c->max_member) { ShowError("clan_join: Clan '%s' already reached its max capacity!\n", c->name); return false; } VECTOR_ENSURE(c->members, 1, 1); m.sd = sd; m.char_id = sd->status.char_id; m.online = 1; m.last_login = sd->status.last_login; VECTOR_PUSH(c->members, m); c->connect_member++; c->member_count++; sd->status.clan_id = c->clan_id; sd->clan = c; sc_start2(NULL, &sd->bl, SC_CLAN_INFO, 10000, 0, c->clan_id, INFINITE_DURATION); status_calc_pc(sd, SCO_FORCE); chrif->save(sd, 0); clif->clan_basicinfo(sd); clif->clan_onlinecount(c); return true; } /** * Invoked when a player joins * It assumes that clan_id is not 0 * * @param sd Player Data */ static void clan_member_online(struct map_session_data *sd, bool first) { struct clan *c; int i, inactivity; nullpo_retv(sd); // For invalid values we must reset it to 0 (no clan) if (sd->status.clan_id < 0) { ShowError("clan_member_online: Invalid clan id, changing to '0'. clan_id='%d' char_id='%d'\n", sd->status.clan_id, sd->status.char_id); sd->status.clan_id = 0; return; } c = clan->search(sd->status.clan_id); if (c == NULL) { // This is a silent return because it will reset clan_id in case // a custom clan that was removed and this is a remaining member sd->status.clan_id = 0; sd->clan = NULL; if (!first) { status_change_end(&sd->bl, SC_CLAN_INFO, INVALID_TIMER); // Remove the status status_calc_pc(sd, SCO_FORCE); } clif->clan_leave(sd); return; } if (!c->received) { return; } if (c->max_member <= c->member_count || VECTOR_LENGTH(c->members) >= c->max_member) { ShowError("Clan System: More members than the maximum allowed in clan #%d\n", c->clan_id); return; } i = clan->getindex(c, sd->status.char_id); inactivity = (int)(time(NULL) - sd->status.last_login); if (i == INDEX_NOT_FOUND) { struct clan_member m; if (c->kick_time > 0 && inactivity > c->kick_time) { sd->status.clan_id = 0; sd->clan = NULL; clan->buff_end(sd, c); status_change_end(&sd->bl, SC_CLAN_INFO, INVALID_TIMER); clif->clan_leave(sd); return; } VECTOR_ENSURE(c->members, 1, 1); m.sd = sd; m.char_id = sd->status.char_id; m.online = 1; m.last_login = sd->status.last_login; VECTOR_PUSH(c->members, m); } else { struct clan_member *m = &VECTOR_INDEX(c->members, i); if (c->kick_time > 0 && inactivity > c->kick_time) { if (m->online == 1) { m->online = 0; m->sd = NULL; c->connect_member--; c->member_count--; } clan->buff_end(sd, c); sd->status.clan_id = 0; sd->clan = NULL; status_change_end(&sd->bl, SC_CLAN_INFO, INVALID_TIMER); VECTOR_ERASE(c->members, i); clif->clan_leave(sd); return; } m->sd = sd; m->online = 1; m->last_login = sd->status.last_login; } sd->clan = c; c->connect_member++; sc_start2(NULL, &sd->bl, SC_CLAN_INFO, 10000, 0, c->clan_id, INFINITE_DURATION); if (!first) { // When first called from pc.c we don't need to do status_calc status_calc_pc(sd, SCO_FORCE); } clif->clan_basicinfo(sd); clif->clan_onlinecount(c); } /** * Re-join a player on its clan */ static int clan_rejoin(struct map_session_data *sd, va_list ap) { nullpo_ret(sd); if (sd->status.clan_id != 0) { // Note: Even if the value is invalid (lower than zero) // the function will fix the invalid value clan->member_online(sd, false); } return 0; } /** * Removes Player from clan */ static bool clan_leave(struct map_session_data *sd, bool first) { int i; struct clan *c; nullpo_ret(sd); c = sd->clan; if (c == NULL) { return false; } if (!c->received) { return false; } i = clan->getindex(c, sd->status.char_id); if (i != INDEX_NOT_FOUND) { VECTOR_ERASE(c->members, i); c->connect_member--; c->member_count--; } sd->status.clan_id = 0; sd->clan = NULL; clan->buff_end(sd, c); status_change_end(&sd->bl, SC_CLAN_INFO, INVALID_TIMER); if (!first) { status_calc_pc(sd, SCO_FORCE); } chrif->save(sd, 0); clif->clan_onlinecount(c); clif->clan_leave(sd); return true; } /** * Sets a player offline * * @param (struct map_session_data *) sd Player Data */ static void clan_member_offline(struct map_session_data *sd) { struct clan *c; int i; nullpo_retv(sd); c = sd->clan; if (c == NULL) { return; } i = clan->getindex(c, sd->status.char_id); if (i != INDEX_NOT_FOUND && VECTOR_INDEX(c->members, i).online == 1) { // Only if it is online, because unit->free is called twice VECTOR_INDEX(c->members, i).online = 0; VECTOR_INDEX(c->members, i).sd = NULL; c->connect_member--; } clif->clan_onlinecount(c); } /** * Sends a message to the whole clan */ static bool clan_send_message(struct map_session_data *sd, const char *mes) { int len; nullpo_retr(false, sd); nullpo_retr(false, mes); len = (int)strlen(mes); if (sd->status.clan_id == 0) { return false; } clan->recv_message(sd->clan, mes, len); // Chat logging type 'C' / Clan Chat logs->chat(LOG_CHAT_CLAN, sd->status.clan_id, sd->status.char_id, sd->status.account_id, mapindex_id2name(sd->mapindex), sd->bl.x, sd->bl.y, NULL, mes); return true; } /** * Clan receive a message, will be displayed to whole clan */ static void clan_recv_message(struct clan *c, const char *mes, int len) { clif->clan_message(c, mes, len); } /** * Set constants for each clan */ static void clan_set_constants(void) { struct DBIterator *iter = db_iterator(clan->db); struct clan *c; for (c = dbi_first(iter); dbi_exists(iter); c = dbi_next(iter)) { script->set_constant2(c->constant, c->clan_id, false, false); } dbi_destroy(iter); } /** * Returns the clan_id of bl */ static int clan_get_id(const struct block_list *bl) { nullpo_ret(bl); switch (bl->type) { case BL_PC: { const struct map_session_data *sd = BL_UCCAST(BL_PC, bl); return sd->status.clan_id; } case BL_NPC: { const struct npc_data *nd = BL_UCCAST(BL_NPC, bl); return nd->clan_id; } case BL_PET: { const struct pet_data *pd = BL_UCCAST(BL_PET, bl); if (pd->msd != NULL) return pd->msd->status.clan_id; } break; case BL_MOB: { const struct mob_data *md = BL_UCCAST(BL_MOB, bl); const struct map_session_data *msd; if (md->special_state.ai != AI_NONE && (msd = map->id2sd(md->master_id)) != NULL) { return msd->status.clan_id; } return md->clan_id; } case BL_HOM: { const struct homun_data *hd = BL_UCCAST(BL_HOM, bl); if (hd->master != NULL) { return hd->master->status.clan_id; } } break; case BL_MER: { const struct mercenary_data *md = BL_UCCAST(BL_MER, bl); if (md->master != NULL) { return md->master->status.clan_id; } } break; case BL_SKILL: { const struct skill_unit *su = BL_UCCAST(BL_SKILL, bl); if (su->group != NULL) return su->group->clan_id; } break; } return 0; } /** * Checks every clan player and remove those who are inactive */ static int clan_inactivity_kick(int tid, int64 tick, int id, intptr_t data) { struct clan *c = NULL; int i; if ((c = clan->search(id)) != NULL) { if (!c->kick_time || c->tid != tid || tid == INVALID_TIMER || c->tid == INVALID_TIMER) { ShowError("Timer Mismatch (Time: %d seconds) %d != %d", c->kick_time, c->tid, tid); Assert_report(0); return 0; } for (i = 0; i < VECTOR_LENGTH(c->members); i++) { struct clan_member *m = &VECTOR_INDEX(c->members, i); if (m->char_id <= 0 || m->online <= 0) continue; if (m->online) { struct map_session_data *sd = m->sd; if (DIFF_TICK32(sockt->last_tick, sd->idletime) > c->kick_time) { clan->leave(sd, false); } } else if ((time(NULL) - m->last_login) > c->kick_time) { VECTOR_ERASE(c->members, i); c->member_count--; clif->clan_onlinecount(c); } } //Perform the kick for offline members that didn't connect after a server restart c->received = false; intif->clan_kickoffline(c->clan_id, c->kick_time); c->tid = timer->add(timer->gettick() + c->check_time, clan->inactivity_kick, c->clan_id, 0); } return 1; } /** * Timeout for the request of offline kick */ static int clan_request_kickoffline(int tid, int64 tick, int id, intptr_t data) { struct clan *c = NULL; if ((c = clan->search(id)) != NULL) { if (c->req_kick_tid != tid || c->req_kick_tid == INVALID_TIMER) { ShowError("Timer Mismatch %d != %d", c->tid, tid); return 0; } if (c->received) { c->req_kick_tid = INVALID_TIMER; return 1; } intif->clan_kickoffline(c->clan_id, c->kick_time); c->req_kick_tid = timer->add(timer->gettick() + clan->req_timeout, clan->request_kickoffline, c->clan_id, 0); } return 1; } /** * Timeout of the request for counting members */ static int clan_request_membercount(int tid, int64 tick, int id, intptr_t data) { struct clan *c = NULL; if ((c = clan->search(id)) != NULL) { if (c->req_count_tid != tid || c->req_count_tid == INVALID_TIMER) { ShowError("Timer Mismatch %d != %d", c->tid, tid); return 0; } if (c->received) { c->req_count_tid = INVALID_TIMER; return 1; } intif->clan_membercount(c->clan_id, c->kick_time); c->req_count_tid = timer->add(timer->gettick() + clan->req_timeout, clan->request_membercount, c->clan_id, 0); } return 1; } /** * Processes any (plugin-defined) additional fields for a clan entry. * * @param[in, out] entry The destination clan entry, already initialized (clan_id is expected to be already set). * @param[in] t The libconfig entry. * @param[in] n Ordinal number of the entry, to be displayed in case of validation errors. * @param[in] source Source of the entry (file name), to be displayed in case of validation errors. */ static void clan_read_db_additional_fields(struct clan *entry, struct config_setting_t *t, int n, const char *source) { // do nothing. plugins can do own work } static void clan_read_buffs(struct clan *c, struct config_setting_t *buff, const char *source) { struct clan_buff *b; const char *str = NULL; nullpo_retv(c); nullpo_retv(buff); b = &c->buff; if (!libconfig->setting_lookup_int(buff, "Icon", &b->icon)) { const char *temp_str = NULL; if (libconfig->setting_lookup_string(buff, "Icon", &temp_str)) { if (*temp_str && !script->get_constant(temp_str, &b->icon)) { ShowWarning("Clan %d: Invalid Buff icon value. Defaulting to SI_BLANK.\n", c->clan_id); b->icon = -1; // SI_BLANK } } } if (libconfig->setting_lookup_string(buff, "Script", &str)) { b->script = *str ? script->parse(str, source, -b->icon, SCRIPT_IGNORE_EXTERNAL_BRACKETS, NULL) : NULL; } } /** * Processes one clandb entry from the libconfig file, loading and inserting it * into the clan database. * * @param settings Libconfig setting entry. It is expected to be valid and it * won't be freed (it is care of the caller to do so if * necessary). * @param source Source of the entry (file name), to be displayed in case of * validation errors. * @return int. */ static int clan_read_db_sub(struct config_setting_t *settings, const char *source, bool reload) { int total, s, valid = 0; nullpo_retr(false, settings); total = libconfig->setting_length(settings); for (s = 0; s < total; s++) { struct clan *c; struct config_setting_t *cl, *buff, *allies, *antagonists; const char *aName, *aLeader, *aMap, *aConst; int max_members, kicktime = 0, kickchecktime = 0, id, i, j; cl = libconfig->setting_get_elem(settings, s); if (libconfig->setting_lookup_int(cl, "Id", &id)) { if (id <= 0) { ShowError("clan_read_db: Invalid Clan Id %d, skipping...\n", id); return false; } if (clan->search(id) != NULL) { ShowError("clan_read_db: Duplicate entry for Clan Id %d, skipping...\n", id); return false; } } else { ShowError("clan_read_db: failed to find 'Id' for clan #%d\n", s); return false; } if (libconfig->setting_lookup_string(cl, "Const", &aConst)) { if (!*aConst) { ShowError("clan_read_db: Invalid Clan Const '%s', skipping...\n", aConst); return false; } if (strlen(aConst) > NAME_LENGTH) { ShowError("clan_read_db: Clan Name '%s' is longer than %d characters, skipping...\n", aConst, NAME_LENGTH); return false; } if (clan->searchname(aConst) != NULL) { ShowError("clan_read_db: Duplicate entry for Clan Const '%s', skipping...\n", aConst); return false; } } else { ShowError("clan_read_db: failed to find 'Const' for clan #%d\n", s); return false; } if (libconfig->setting_lookup_string(cl, "Name", &aName)) { if (!*aName) { ShowError("clan_read_db: Invalid Clan Name '%s', skipping...\n", aName); return false; } if (strlen(aName) > NAME_LENGTH) { ShowError("clan_read_db: Clan Name '%s' is longer than %d characters, skipping...\n", aName, NAME_LENGTH); return false; } if (clan->searchname(aName) != NULL) { ShowError("clan_read_db: Duplicate entry for Clan Name '%s', skipping...\n", aName); return false; } } else { ShowError("clan_read_db: failed to find 'Name' for clan #%d\n", s); return false; } if (!libconfig->setting_lookup_string(cl, "Leader", &aLeader)) { ShowError("clan_read_db: failed to find 'Leader' for clan #%d\n", s); return false; } if (!libconfig->setting_lookup_string(cl, "Map", &aMap)) { ShowError("clan_read_db: failed to find 'Map' for clan #%d\n", s); return false; } CREATE(c, struct clan, 1); c->clan_id = id; safestrncpy(c->constant, aConst, NAME_LENGTH); safestrncpy(c->name, aName, NAME_LENGTH); safestrncpy(c->master, aLeader, NAME_LENGTH); safestrncpy(c->map, aMap, MAP_NAME_LENGTH_EXT); c->connect_member = 0; c->member_count = 0; // Char server will count members for us c->received = false; c->req_count_tid = INVALID_TIMER; c->req_kick_tid = INVALID_TIMER; c->tid = INVALID_TIMER; if (libconfig->setting_lookup_int(cl, "MaxMembers", &max_members)) { if (max_members > 0) { c->max_member = max_members; } else { ShowError("clan_read_db: Clan #%d has invalid value for 'MaxMembers' setting, defaulting to 'clan->max'...\n", id); c->max_member = clan->max; } } else { c->max_member = clan->max; } if (libconfig->setting_lookup_int(cl, "KickTime", &kicktime)) { c->kick_time = 60 * 60 * kicktime; } else { c->kick_time = clan->kicktime; } if (libconfig->setting_lookup_int(cl, "CheckTime", &kickchecktime)) { c->check_time = 60 * 60 * max(1, kickchecktime) * 1000; } else { c->check_time = clan->checktime; } if ((buff = libconfig->setting_get_member(cl, "Buff")) != NULL) { clan->read_buffs(c, buff, source); } allies = libconfig->setting_get_member(cl, "Allies"); if (allies != NULL) { int a = libconfig->setting_length(allies); VECTOR_INIT(c->allies); if (a > 0 && clan->max_relations > 0) { const char *allyConst; if (a > clan->max_relations) { ShowWarning("clan_read_db: Clan %d has more allies(%d) than allowed(%d), reading only the first %d...\n", c->clan_id, a, clan->max_relations, clan->max_relations); a = clan->max_relations; } VECTOR_ENSURE(c->allies, a, 1); for (i = 0; i < a; i++) { struct clan_relationship r; if ((allyConst = libconfig->setting_get_string_elem(allies, i)) != NULL) { ARR_FIND(0, VECTOR_LENGTH(c->allies), j, strcmp(VECTOR_INDEX(c->allies, j).constant, allyConst) == 0); if (j != VECTOR_LENGTH(c->allies)) { ShowError("clan_read_db: Duplicate entry '%s' in allies for Clan %d in '%s', skipping...\n", allyConst, c->clan_id, source); continue; } else if (strcmp(allyConst, c->constant) == 0) { ShowError("clan_read_db: Clans can't be allies of themselves! Clan Id: %d, in '%s'\n", c->clan_id, source); continue; } safestrncpy(r.constant, allyConst, NAME_LENGTH); VECTOR_PUSH(c->allies, r); } } } } antagonists = libconfig->setting_get_member(cl, "Antagonists"); if (antagonists != NULL) { int a = libconfig->setting_length(antagonists); VECTOR_INIT(c->antagonists); if (a > 0 && clan->max_relations > 0) { const char *antagonistConst; if (a > clan->max_relations) { ShowWarning("clan_read_db: Clan %d has more antagonists(%d) than allowed(%d), reading only the first %d...\n", c->clan_id, a, clan->max_relations, clan->max_relations); a = clan->max_relations; } VECTOR_ENSURE(c->antagonists, a, 1); for (i = 0; i < a; i++) { struct clan_relationship r; if ((antagonistConst = libconfig->setting_get_string_elem(antagonists, i)) != NULL) { ARR_FIND(0, VECTOR_LENGTH(c->antagonists), j, strcmp(VECTOR_INDEX(c->antagonists, j).constant, antagonistConst) == 0); if (j != VECTOR_LENGTH(c->antagonists)) { ShowError("clan_read_db: Duplicate entry '%s' in antagonists for Clan %d in '%s', skipping...\n", antagonistConst, c->clan_id, source); continue; } else if (strcmp(antagonistConst, c->constant) == 0) { ShowError("clan_read_db: Clans can't be antagonists of themselves! Clan Id: %d, in '%s'\n", c->clan_id, source); continue; } safestrncpy(r.constant, antagonistConst, NAME_LENGTH); VECTOR_PUSH(c->antagonists, r); } } } } clan->read_db_additional_fields(c, cl, s, source); if (c->kick_time > 0) { c->tid = timer->add(timer->gettick() + c->check_time, clan->inactivity_kick, c->clan_id, 0); } c->received = false; c->req_state = reload ? CLAN_REQ_RELOAD : CLAN_REQ_FIRST; c->req_count_tid = timer->add(timer->gettick() + clan->req_timeout, clan->request_membercount, c->clan_id, 0); idb_put(clan->db, c->clan_id, c); VECTOR_INIT(c->members); valid++; } // Validating Relations if (valid > 0) { struct DBIterator *iter = db_iterator(clan->db); struct clan *c_ok, *c; int i; for (c_ok = dbi_first(iter); dbi_exists(iter); c_ok = dbi_next(iter)) { i = VECTOR_LENGTH(c_ok->allies); while ( i > 0) { struct clan_relationship *r; i--; r = &VECTOR_INDEX(c_ok->allies, i); if ((c = clan->searchname(r->constant)) == NULL) { ShowError("clan_read_db: Invalid (nonexistent) Ally '%s' for clan %d in '%s', skipping ally...\n", r->constant, c_ok->clan_id, source); VECTOR_ERASE(c_ok->allies, i); } else { r->clan_id = c->clan_id; } } i = VECTOR_LENGTH(c_ok->antagonists); while ( i > 0) { struct clan_relationship *r; i--; r = &VECTOR_INDEX(c_ok->antagonists, i); if ((c = clan->searchname(r->constant)) == NULL) { ShowError("clan_read_db: Invalid (nonexistent) Antagonist '%s' for clan %d in '%s', skipping antagonist...", r->constant, c_ok->clan_id, source); VECTOR_ERASE(c_ok->antagonists, i); } else { r->clan_id = c->clan_id; } } } dbi_destroy(iter); } ShowStatus("Done reading '" CL_WHITE "%d" CL_RESET "' valid clans of '" CL_WHITE "%d" CL_RESET "' entries in '" CL_WHITE "%s" CL_RESET "'.\n", valid, total, source); return valid; } /** * Reads Clan DB included by clan configuration file. * * @param settings The Settings Group from config file. * @param source File name. */ static void clan_read_db(struct config_setting_t *settings, const char *source, bool reload) { struct config_setting_t *clans; nullpo_retv(settings); if ((clans = libconfig->setting_get_member(settings, "clans")) != NULL) { int read; read = clan->read_db_sub(clans, source, reload); if (read > 0) { clan->set_constants(); } } else { ShowError("clan_read_db: Can't find setting 'clans' in '%s'. No Clans found.\n", source); } } /** * Reads clan config file * * @param bool clear Whether to clear clan->db before reading clans */ static bool clan_config_read(bool reload) { struct config_t clan_conf; struct config_setting_t *settings = NULL; const char *config_filename = "conf/clans.conf"; // FIXME: hardcoded name int kicktime = 0, kickchecktime = 0; if (reload) { struct DBIterator *iter = db_iterator(clan->db); struct clan *c_clear; for (c_clear = dbi_first(iter); dbi_exists(iter); c_clear = dbi_next(iter)) { if (c_clear->buff.script != NULL) { script->free_code(c_clear->buff.script); } if (c_clear->tid != INVALID_TIMER) { timer->delete(c_clear->tid, clan->inactivity_kick); } VECTOR_CLEAR(c_clear->allies); VECTOR_CLEAR(c_clear->antagonists); VECTOR_CLEAR(c_clear->members); HPM->data_store_destroy(&c_clear->hdata); } dbi_destroy(iter); clan->db->clear(clan->db, NULL); } if (!libconfig->load_file(&clan_conf, config_filename)) { return false; } if ((settings = libconfig->lookup(&clan_conf, "clan_configuration")) == NULL) { ShowError("clan_config_read: failed to find 'clan_configuration'.\n"); return false; } libconfig->setting_lookup_int(settings, "MaxMembers", &clan->max); libconfig->setting_lookup_int(settings, "MaxRelations", &clan->max_relations); libconfig->setting_lookup_int(settings, "InactivityKickTime", &kicktime); if (!libconfig->setting_lookup_int(settings, "InactivityCheckTime", &kickchecktime)) { ShowError("clan_config_read: failed to find InactivityCheckTime using official value.\n"); kickchecktime = 24; } // On config file we set the time in hours but here we use in seconds clan->kicktime = 60 * 60 * kicktime; clan->checktime = 60 * 60 * max(kickchecktime, 1) * 1000; clan->config_read_additional_settings(settings, config_filename); clan->read_db(settings, config_filename, reload); libconfig->destroy(&clan_conf); return true; } /** * Processes any (plugin-defined) additional settings for clan config. * * @param settings The Settings Group from config file. * @param source Source of the entry (file name), to be displayed in * case of validation errors. */ static void clan_config_read_additional_settings(struct config_setting_t *settings, const char *source) { // do nothing. plugins can do own work } /** * Reloads Clan DB */ static void clan_reload(void) { clan->config_read(true); } /** * */ static void do_init_clan(bool minimal) { clan->db = idb_alloc(DB_OPT_RELEASE_DATA); if (minimal) { return; } clan->config_read(false); timer->add_func_list(clan->inactivity_kick, "clan_inactivity_kick"); } /** * */ static void do_final_clan(void) { struct DBIterator *iter = db_iterator(clan->db); struct clan *c; for (c = dbi_first(iter); dbi_exists(iter); c = dbi_next(iter)) { if (c->buff.script) { script->free_code(c->buff.script); } if (c->tid != INVALID_TIMER) { timer->delete(c->tid, clan->inactivity_kick); } VECTOR_CLEAR(c->allies); VECTOR_CLEAR(c->antagonists); VECTOR_CLEAR(c->members); HPM->data_store_destroy(&c->hdata); } dbi_destroy(iter); db_destroy(clan->db); } /** * Inits Clan Defaults */ void clan_defaults(void) { clan = &clan_s; clan->init = do_init_clan; clan->final = do_final_clan; /* */ clan->db = NULL; clan->max = 0; clan->max_relations = 0; clan->kicktime = 0; clan->checktime = 0; clan->req_timeout = 60; /* */ clan->config_read = clan_config_read; clan->config_read_additional_settings = clan_config_read_additional_settings; clan->read_db = clan_read_db; clan->read_db_sub = clan_read_db_sub; clan->read_db_additional_fields = clan_read_db_additional_fields; clan->read_buffs = clan_read_buffs; clan->search = clan_search; clan->searchname = clan_searchname; clan->getonlinesd = clan_getonlinesd; clan->getindex = clan_getindex; clan->join = clan_join; clan->member_online = clan_member_online; clan->leave = clan_leave; clan->send_message = clan_send_message; clan->recv_message = clan_recv_message; clan->member_offline = clan_member_offline; clan->set_constants = clan_set_constants; clan->get_id = clan_get_id; clan->buff_start = clan_buff_start; clan->buff_end = clan_buff_end; clan->reload = clan_reload; clan->rejoin = clan_rejoin; clan->inactivity_kick = clan_inactivity_kick; clan->request_kickoffline = clan_request_kickoffline; clan->request_membercount = clan_request_membercount; }