summaryrefslogtreecommitdiff
path: root/src/map/clan.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/map/clan.c')
-rw-r--r--src/map/clan.c1071
1 files changed, 1071 insertions, 0 deletions
diff --git a/src/map/clan.c b/src/map/clan.c
new file mode 100644
index 000000000..b67726b37
--- /dev/null
+++ b/src/map/clan.c
@@ -0,0 +1,1071 @@
+/**
+ * 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 <http://www.gnu.org/licenses/>.
+ */
+#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 <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+struct clan_interface clan_s;
+struct clan_interface *clan;
+
+/**
+ * Searches a Clan by clan_id
+ *
+ * @param clan_id Clan ID
+ * @return struct clan*
+ */
+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*
+ */
+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 *)
+ */
+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);
+ 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
+ */
+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
+ */
+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
+ */
+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
+ */
+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
+ */
+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;
+ 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->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
+ */
+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
+ */
+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
+ */
+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;
+ c->connect_member--;
+ }
+ clif->clan_onlinecount(c);
+}
+
+
+/**
+ * Sends a message to the whole clan
+ */
+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
+ */
+void clan_recv_message(struct clan *c, const char *mes, int len)
+{
+ clif->clan_message(c, mes, len);
+}
+
+/**
+ * Set constants for each clan
+ */
+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
+ */
+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
+ */
+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_retr(0, 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
+ */
+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
+ */
+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.
+ */
+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
+}
+
+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.
+ */
+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.
+ */
+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
+ */
+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.
+ */
+void clan_config_read_additional_settings(struct config_setting_t *settings, const char *source)
+{
+ // do nothing. plugins can do own work
+}
+
+/**
+ * Reloads Clan DB
+ */
+void clan_reload(void)
+{
+ clan->config_read(true);
+}
+
+/**
+ *
+ */
+void do_init_clan(bool minimal)
+{
+ if (minimal) {
+ return;
+ }
+ clan->db = idb_alloc(DB_OPT_RELEASE_DATA);
+ clan->config_read(false);
+ timer->add_func_list(clan->inactivity_kick, "clan_inactivity_kick");
+}
+
+/**
+ *
+ */
+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;
+}