summaryrefslogtreecommitdiff
path: root/src/map/channel.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/map/channel.c')
-rw-r--r--src/map/channel.c833
1 files changed, 833 insertions, 0 deletions
diff --git a/src/map/channel.c b/src/map/channel.c
new file mode 100644
index 000000000..34ce60211
--- /dev/null
+++ b/src/map/channel.c
@@ -0,0 +1,833 @@
+// Copyright (c) Hercules Dev Team, licensed under GNU GPL.
+// See the LICENSE file
+
+#define HERCULES_CORE
+
+#include "channel.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "atcommand.h"
+#include "guild.h"
+#include "instance.h"
+#include "irc-bot.h"
+#include "map.h"
+#include "pc.h"
+#include "../common/cbasetypes.h"
+#include "../common/conf.h"
+#include "../common/db.h"
+#include "../common/malloc.h"
+#include "../common/nullpo.h"
+#include "../common/random.h"
+#include "../common/showmsg.h"
+#include "../common/socket.h"
+#include "../common/strlib.h"
+#include "../common/timer.h"
+#include "../common/utils.h"
+
+struct channel_interface channel_s;
+
+static struct Channel_Config channel_config;
+
+/**
+ * Returns the named channel.
+ *
+ * @param name The channel name
+ * @param sd The issuer character, for character-specific channels (i.e. map, ally)
+ * @return a pointer to the channel, or NULL.
+ */
+struct channel_data *channel_search(const char *name, struct map_session_data *sd)
+{
+ const char *realname = name;
+ if (!realname || !*realname)
+ return NULL;
+ if (*realname == '#') {
+ realname++;
+ if (!*realname)
+ return NULL;
+ }
+
+ if (channel->config->local && strcmpi(realname, channel->config->local_name) == 0) {
+ if (!sd)
+ return NULL;
+ if (!map->list[sd->bl.m].channel) {
+ channel->map_join(sd);
+ }
+ return map->list[sd->bl.m].channel;
+ }
+
+ if (channel->config->ally && strcmpi(realname, channel->config->ally_name) == 0) {
+ if (!sd || !sd->status.guild_id || !sd->guild)
+ return NULL;
+ return sd->guild->channel;
+ }
+
+ return strdb_get(channel->db, realname);
+}
+
+/**
+ * Creates a chat channel.
+ *
+ * If the channel type isn't HCS_TYPE_MAP or HCS_TYPE_ALLY, the channel is added to the channel->db.
+ *
+ * @param type The channel type.
+ * @param name The channel name.
+ * @param color The channel chat color.
+ * @return A pointer to the created channel.
+ */
+struct channel_data *channel_create(enum channel_types type, const char *name, unsigned char color)
+{
+ struct channel_data *chan;
+
+ if (!name)
+ return NULL;
+
+ CREATE(chan, struct channel_data, 1);
+ chan->users = idb_alloc(DB_OPT_BASE);
+ safestrncpy(chan->name, name, HCS_NAME_LENGTH);
+ chan->color = color;
+
+ chan->options = HCS_OPT_BASE;
+ chan->banned = NULL;
+
+ chan->msg_delay = 0;
+
+ chan->type = type;
+ if (chan->type != HCS_TYPE_MAP && chan->type != HCS_TYPE_ALLY)
+ strdb_put(channel->db, chan->name, chan);
+ return chan;
+}
+
+/**
+ * Deletes a chat channel.
+ *
+ * @param chan The channel to delete
+ */
+void channel_delete(struct channel_data *chan)
+{
+ nullpo_retv(chan);
+
+ if (db_size(chan->users) && !channel->config->closing) {
+ DBIterator *iter;
+ struct map_session_data *sd;
+ iter = db_iterator(chan->users);
+ for (sd = dbi_first(iter); dbi_exists(iter); sd = dbi_next(iter)) {
+ channel->leave_sub(chan, sd);
+ }
+ dbi_destroy(iter);
+ }
+ if (chan->banned) {
+ db_destroy(chan->banned);
+ chan->banned = NULL;
+ }
+ db_destroy(chan->users);
+ if (chan->m) {
+ map->list[chan->m].channel = NULL;
+ aFree(chan);
+ } else if (chan->type == HCS_TYPE_ALLY) {
+ aFree(chan);
+ } else if (!channel->config->closing) {
+ strdb_remove(channel->db, chan->name);
+ }
+}
+
+/**
+ * Sets a chat channel password.
+ *
+ * @param chan The channel to edit.
+ * @param pass The password to set. Pass NULL to remove existing passwords.
+ */
+void channel_set_password(struct channel_data *chan, const char *password)
+{
+ nullpo_retv(chan);
+ if (password)
+ safestrncpy(chan->password, password, HCS_NAME_LENGTH);
+ else
+ chan->password[0] = '\0';
+}
+
+/**
+ * Bans a character from the given channel.
+ *
+ * @param chan The channel.
+ * @param ssd The source character, if any.
+ * @param tsd The target character.
+ * @retval HCS_STATUS_OK if the operation succeeded.
+ * @retval HCS_STATUS_ALREADY if the target character is already banned.
+ * @retval HCS_STATUS_NOPERM if the source character doesn't have enough permissions.
+ * @retval HCS_STATUS_FAIL in case of generic failure.
+ */
+enum channel_operation_status channel_ban(struct channel_data *chan, const struct map_session_data *ssd, struct map_session_data *tsd)
+{
+ struct channel_ban_entry *entry = NULL;
+
+ nullpo_retr(HCS_STATUS_FAIL, chan);
+ nullpo_retr(HCS_STATUS_FAIL, tsd);
+
+ if (ssd && chan->owner != ssd->status.char_id && !pc_has_permission(ssd, PC_PERM_HCHSYS_ADMIN))
+ return HCS_STATUS_NOPERM;
+
+ if (pc_has_permission(tsd, PC_PERM_HCHSYS_ADMIN))
+ return HCS_STATUS_FAIL;
+
+ if (chan->banned && idb_exists(chan->banned, tsd->status.account_id))
+ return HCS_STATUS_ALREADY;
+
+ if (!chan->banned)
+ chan->banned = idb_alloc(DB_OPT_BASE|DB_OPT_ALLOW_NULL_DATA|DB_OPT_RELEASE_DATA);
+
+ CREATE(entry, struct channel_ban_entry, 1);
+ safestrncpy(entry->name, tsd->status.name, NAME_LENGTH);
+ idb_put(chan->banned, tsd->status.account_id, entry);
+
+ channel->leave(chan, tsd);
+
+ return HCS_STATUS_OK;
+}
+
+/**
+ * Unbans a character from the given channel.
+ *
+ * @param chan The channel.
+ * @param ssd The source character, if any.
+ * @param tsd The target character. If no target character is specified, all characters are unbanned.
+ * @retval HCS_STATUS_OK if the operation succeeded.
+ * @retval HCS_STATUS_ALREADY if the target character is not banned.
+ * @retval HCS_STATUS_NOPERM if the source character doesn't have enough permissions.
+ * @retval HCS_STATUS_FAIL in case of generic failure.
+ */
+enum channel_operation_status channel_unban(struct channel_data *chan, const struct map_session_data *ssd, struct map_session_data *tsd)
+{
+ nullpo_retr(HCS_STATUS_FAIL, chan);
+
+ if (ssd && chan->owner != ssd->status.char_id && !pc_has_permission(ssd, PC_PERM_HCHSYS_ADMIN))
+ return HCS_STATUS_NOPERM;
+
+ if (!chan->banned)
+ return HCS_STATUS_ALREADY;
+
+ if (!tsd) {
+ // Unban all
+ db_destroy(chan->banned);
+ chan->banned = NULL;
+ return HCS_STATUS_OK;
+ }
+
+ // Unban one
+ if (!idb_exists(chan->banned, tsd->status.account_id))
+ return HCS_STATUS_ALREADY;
+
+ idb_remove(chan->banned, tsd->status.account_id);
+ if (!db_size(chan->banned)) {
+ db_destroy(chan->banned);
+ chan->banned = NULL;
+ }
+
+ return HCS_STATUS_OK;
+}
+
+/**
+ * Sets or edits a channel's options.
+ *
+ * @param chan The channel.
+ * @param options The new options set to apply.
+ */
+void channel_set_options(struct channel_data *chan, unsigned int options)
+{
+ nullpo_retv(chan);
+
+ chan->options = options;
+}
+
+/**
+ * Sends a message to a channel.
+ *
+ * @param chan The destination channel.
+ * @param sd The source character.
+ * @param msg The message to send.
+ *
+ * If no source character is specified, it'll send an anonymous message.
+ */
+void channel_send(struct channel_data *chan, struct map_session_data *sd, const char *msg)
+{
+ char message[150];
+ nullpo_retv(chan);
+
+ if (sd && chan->msg_delay != 0
+ && DIFF_TICK(sd->hchsysch_tick + chan->msg_delay*1000, timer->gettick()) > 0
+ && !pc_has_permission(sd, PC_PERM_HCHSYS_ADMIN)) {
+ clif->colormes(sd->fd,COLOR_RED,msg_txt(1455));
+ return;
+ } else if (sd) {
+ snprintf(message, 150, "[ #%s ] %s : %s",chan->name,sd->status.name, msg);
+ clif->channel_msg(chan,sd,message);
+ if (chan->type == HCS_TYPE_IRC)
+ ircbot->relay(sd->status.name,msg);
+ if (chan->msg_delay != 0)
+ sd->hchsysch_tick = timer->gettick();
+ } else {
+ snprintf(message, 150, "[ #%s ] %s",chan->name, msg);
+ clif->channel_msg2(chan, message);
+ if (chan->type == HCS_TYPE_IRC)
+ ircbot->relay(NULL, msg);
+ }
+}
+
+/**
+ * Joins a channel, without any permission checks.
+ *
+ * @param chan The channel to join
+ * @param sd The character
+ * @param stealth If true, hide join announcements.
+ */
+void channel_join_sub(struct channel_data *chan, struct map_session_data *sd, bool stealth)
+{
+ nullpo_retv(chan);
+ nullpo_retv(sd);
+
+ if (idb_put(chan->users, sd->status.char_id, sd))
+ return;
+
+ RECREATE(sd->channels, struct channel_data *, ++sd->channel_count);
+ sd->channels[sd->channel_count - 1] = chan;
+
+ if (!stealth && (chan->options&HCS_OPT_ANNOUNCE_JOIN)) {
+ char message[60];
+ sprintf(message, "#%s '%s' joined",chan->name,sd->status.name);
+ clif->channel_msg(chan,sd,message);
+ }
+
+ /* someone is cheating, we kindly disconnect the bastard */
+ if (sd->channel_count > 200) {
+ set_eof(sd->fd);
+ }
+
+}
+
+/**
+ * Joins a channel.
+ *
+ * Ban and password checks are performed before joining the channel.
+ * If the channel is an HCS_TYPE_ALLY channel, alliance channels are automatically joined.
+ *
+ * @param chan The channel to join
+ * @param sd The character
+ * @param password The specified join password, if any
+ * @param silent If true, suppress the "You're now in the <x> channel" greeting message
+ * @retval HCS_STATUS_OK if the operation succeeded
+ * @retval HCS_STATUS_ALREADY if the character is already in the channel
+ * @retval HCS_STATUS_NOPERM if the specified password doesn't match
+ * @retval HCS_STATUS_BANNED if the character is in the channel's ban list
+ * @retval HCS_STATUS_FAIL in case of generic error
+ */
+enum channel_operation_status channel_join(struct channel_data *chan, struct map_session_data *sd, const char *password, bool silent)
+{
+ bool stealth = false;
+
+ nullpo_retr(HCS_STATUS_FAIL, chan);
+ nullpo_retr(HCS_STATUS_FAIL, sd);
+
+ if (idb_exists(chan->users, sd->status.char_id)) {
+ return HCS_STATUS_ALREADY;
+ }
+
+ if (chan->password[0] != '\0' && strcmp(chan->password, password) != 0) {
+ if (pc_has_permission(sd, PC_PERM_HCHSYS_ADMIN)) {
+ stealth = true;
+ } else {
+ return HCS_STATUS_NOPERM;
+ }
+ }
+
+ if (chan->banned && idb_exists(chan->banned, sd->status.account_id)) {
+ return HCS_STATUS_BANNED;
+ }
+
+ if (!silent && !(chan->options&HCS_OPT_ANNOUNCE_JOIN)) {
+ char output[CHAT_SIZE_MAX];
+ if (chan->type == HCS_TYPE_MAP) {
+ sprintf(output, msg_txt(1435), chan->name, map->list[chan->m].name); // You're now in the '#%s' channel for '%s'
+ } else {
+ sprintf(output, msg_txt(1403), chan->name); // You're now in the '%s' channel
+ }
+ clif->colormes(sd->fd, COLOR_DEFAULT, output);
+ }
+
+ if (chan->type == HCS_TYPE_ALLY) {
+ struct guild *g = sd->guild;
+ int i;
+ for (i = 0; i < MAX_GUILDALLIANCE; i++) {
+ struct guild *sg = NULL;
+ if (g->alliance[i].opposition == 0 && g->alliance[i].guild_id && (sg = guild->search(g->alliance[i].guild_id))) {
+ if (!(sg->channel->banned && idb_exists(sg->channel->banned, sd->status.account_id))) {
+ channel->join_sub(sg->channel, sd, stealth);
+ }
+ }
+ }
+ }
+
+ channel->join_sub(chan, sd, stealth);
+
+ return HCS_STATUS_OK;
+}
+
+/**
+ * Removes a channel from a character's join lists.
+ *
+ * @param chan The channel to leave
+ * @param sd The character
+ */
+void channel_leave_sub(struct channel_data *chan, struct map_session_data *sd)
+{
+ unsigned char i;
+
+ nullpo_retv(chan);
+ nullpo_retv(sd);
+ for (i = 0; i < sd->channel_count; i++) {
+ if (sd->channels[i] == chan) {
+ sd->channels[i] = NULL;
+ break;
+ }
+ }
+ if (i < sd->channel_count) {
+ unsigned char cursor = 0;
+ for (i = 0; i < sd->channel_count; i++) {
+ if (sd->channels[i] == NULL)
+ continue;
+ if (cursor != i) {
+ sd->channels[cursor] = sd->channels[i];
+ }
+ cursor++;
+ }
+ if (!(sd->channel_count = cursor)) {
+ aFree(sd->channels);
+ sd->channels = NULL;
+ }
+ }
+}
+/**
+ * Leaves a channel.
+ *
+ * @param chan The channel to leave
+ * @param sd The character
+ */
+void channel_leave(struct channel_data *chan, struct map_session_data *sd)
+{
+ nullpo_retv(chan);
+ nullpo_retv(sd);
+
+ if (!idb_remove(chan->users,sd->status.char_id))
+ return;
+
+ if (chan == sd->gcbind)
+ sd->gcbind = NULL;
+
+ if (!db_size(chan->users) && chan->type == HCS_TYPE_PRIVATE) {
+ channel->delete(chan);
+ } else if (!channel->config->closing && (chan->options & HCS_OPT_ANNOUNCE_JOIN)) {
+ char message[60];
+ sprintf(message, "#%s '%s' left",chan->name,sd->status.name);
+ clif->channel_msg(chan,sd,message);
+ }
+
+ channel->leave_sub(chan, sd);
+}
+
+/**
+ * Quits the channel system.
+ *
+ * Leaves all joined channels.
+ *
+ * @param sd The target character
+ */
+void channel_quit(struct map_session_data *sd)
+{
+ while (sd->channel_count > 0) {
+ // Loop downward to avoid unnecessary array compactions by channel_leave
+ struct channel_data *chan = sd->channels[sd->channel_count-1];
+
+ if (chan == NULL) {
+ sd->channel_count--;
+ continue;
+ }
+
+ channel->leave(chan, sd);
+ }
+}
+
+/**
+ * Joins the local map channel.
+ *
+ * @param sd The target character
+ */
+void channel_map_join(struct map_session_data *sd)
+{
+ if (sd->state.autotrade || sd->state.standalone)
+ return;
+ if (!map->list[sd->bl.m].channel) {
+ if (map->list[sd->bl.m].flag.chsysnolocalaj || (map->list[sd->bl.m].instance_id >= 0 && instance->list[map->list[sd->bl.m].instance_id].owner_type != IOT_NONE))
+ return;
+
+ map->list[sd->bl.m].channel = channel->create(HCS_TYPE_MAP, channel->config->local_name, channel->config->local_color);
+ map->list[sd->bl.m].channel->m = sd->bl.m;
+ }
+
+ channel->join(map->list[sd->bl.m].channel, sd, NULL, false);
+}
+
+/**
+ * Lets a guild's members join a newly allied guild's channel.
+ *
+ * Note: g_source members will join g_ally's channel.
+ * To have g_ally members join g_source's channel, call this function twice, with inverted arguments.
+ *
+ * @param g_source Source guild
+ * @param g_ally Allied guild
+ */
+void channel_guild_join_alliance(const struct guild *g_source, const struct guild *g_ally)
+{
+ struct channel_data *chan;
+
+ nullpo_retv(g_source);
+ nullpo_retv(g_ally);
+
+ if ((chan = g_ally->channel)) {
+ int i;
+ for (i = 0; i < g_source->max_member; i++) {
+ struct map_session_data *sd = g_source->member[i].sd;
+ if (sd == NULL)
+ continue;
+ if (!(g_ally->channel->banned && idb_exists(g_ally->channel->banned, sd->status.account_id)))
+ channel->join_sub(chan,sd, false);
+ }
+ }
+}
+
+/**
+ * Makes a guild's members leave a no former allied guild's channel.
+ *
+ * Note: g_source members will leave g_ally's channel.
+ * To have g_ally members leave g_source's channel, call this function twice, with inverted arguments.
+ *
+ * @param g_source Source guild
+ * @param g_ally Former allied guild
+ */
+void channel_guild_leave_alliance(const struct guild *g_source, const struct guild *g_ally)
+{
+ struct channel_data *chan;
+
+ nullpo_retv(g_source);
+ nullpo_retv(g_ally);
+
+ if ((chan = g_ally->channel)) {
+ int i;
+ for (i = 0; i < g_source->max_member; i++) {
+ struct map_session_data *sd = g_source->member[i].sd;
+ if (sd == NULL)
+ continue;
+
+ channel->leave(chan,sd);
+ }
+ }
+}
+
+/**
+ * Makes a character quit all guild-related channels.
+ *
+ * @param sd The character
+ */
+void channel_quit_guild(struct map_session_data *sd)
+{
+ unsigned char i;
+
+ for (i = 0; i < sd->channel_count; i++) {
+ struct channel_data *chan = sd->channels[i];
+
+ if (chan == NULL || chan->type != HCS_TYPE_ALLY)
+ continue;
+
+ channel->leave(chan, sd);
+ }
+}
+
+void read_channels_config(void)
+{
+ config_t channels_conf;
+ config_setting_t *chsys = NULL;
+ const char *config_filename = "conf/channels.conf"; // FIXME hardcoded name
+
+ if (libconfig->read_file(&channels_conf, config_filename))
+ return;
+
+ chsys = libconfig->lookup(&channels_conf, "chsys");
+
+ if (chsys != NULL) {
+ config_setting_t *settings = libconfig->setting_get_elem(chsys, 0);
+ config_setting_t *channels;
+ config_setting_t *colors;
+ int i,k;
+ const char *local_name, *ally_name,
+ *local_color, *ally_color,
+ *irc_name, *irc_color;
+ int ally_enabled = 0, local_enabled = 0,
+ local_autojoin = 0, ally_autojoin = 0,
+ allow_user_channel_creation = 0,
+ irc_enabled = 0;
+
+ if( !libconfig->setting_lookup_string(settings, "map_local_channel_name", &local_name) )
+ local_name = "map";
+ safestrncpy(channel->config->local_name, local_name, HCS_NAME_LENGTH);
+
+ if( !libconfig->setting_lookup_string(settings, "ally_channel_name", &ally_name) )
+ ally_name = "ally";
+ safestrncpy(channel->config->ally_name, ally_name, HCS_NAME_LENGTH);
+
+ if( !libconfig->setting_lookup_string(settings, "irc_channel_name", &irc_name) )
+ irc_name = "irc";
+ safestrncpy(channel->config->irc_name, irc_name, HCS_NAME_LENGTH);
+
+ libconfig->setting_lookup_bool(settings, "map_local_channel", &local_enabled);
+ libconfig->setting_lookup_bool(settings, "ally_channel_enabled", &ally_enabled);
+ libconfig->setting_lookup_bool(settings, "irc_channel_enabled", &irc_enabled);
+
+ if (local_enabled)
+ channel->config->local = true;
+ if (ally_enabled)
+ channel->config->ally = true;
+ if (irc_enabled)
+ channel->config->irc = true;
+
+ channel->config->irc_server[0] = channel->config->irc_channel[0] = channel->config->irc_nick[0] = channel->config->irc_nick_pw[0] = '\0';
+
+ if (channel->config->irc) {
+ const char *irc_server, *irc_channel,
+ *irc_nick, *irc_nick_pw;
+ int irc_use_ghost = 0;
+ if( libconfig->setting_lookup_string(settings, "irc_channel_network", &irc_server) ) {
+ if( !strstr(irc_server,":") ) {
+ channel->config->irc = false;
+ ShowWarning("channels.conf : network port wasn't found in 'irc_channel_network', disabling irc channel...\n");
+ } else {
+ unsigned char d = 0, dlen = strlen(irc_server);
+ char server[40];
+ if (dlen > 39)
+ dlen = 39;
+ memset(server, '\0', sizeof(server));
+
+ for(d = 0; d < dlen; d++) {
+ if(irc_server[d] == ':') {
+ memcpy(server, irc_server, d);
+ safestrncpy(channel->config->irc_server, server, 40);
+ memcpy(server, &irc_server[d+1], dlen - d - 1);
+ channel->config->irc_server_port = atoi(server);
+ break;
+ }
+ }
+ }
+ } else {
+ channel->config->irc = false;
+ ShowWarning("channels.conf : irc channel enabled but irc_channel_network wasn't found, disabling irc channel...\n");
+ }
+ if( libconfig->setting_lookup_string(settings, "irc_channel_channel", &irc_channel) )
+ safestrncpy(channel->config->irc_channel, irc_channel, 50);
+ else {
+ channel->config->irc = false;
+ ShowWarning("channels.conf : irc channel enabled but irc_channel_channel wasn't found, disabling irc channel...\n");
+ }
+ if( libconfig->setting_lookup_string(settings, "irc_channel_nick", &irc_nick) ) {
+ if( strcmpi(irc_nick,"Hercules_chSysBot") == 0 ) {
+ sprintf(channel->config->irc_nick, "Hercules_chSysBot%d",rnd()%777);
+ } else
+ safestrncpy(channel->config->irc_nick, irc_nick, 40);
+ } else {
+ channel->config->irc = false;
+ ShowWarning("channels.conf : irc channel enabled but irc_channel_nick wasn't found, disabling irc channel...\n");
+ }
+ if( libconfig->setting_lookup_string(settings, "irc_channel_nick_pw", &irc_nick_pw) ) {
+ safestrncpy(channel->config->irc_nick_pw, irc_nick_pw, 30);
+ config_setting_lookup_bool(settings, "irc_channel_use_ghost", &irc_use_ghost);
+ channel->config->irc_use_ghost = irc_use_ghost;
+ }
+
+ }
+
+ libconfig->setting_lookup_bool(settings, "map_local_channel_autojoin", &local_autojoin);
+ libconfig->setting_lookup_bool(settings, "ally_channel_autojoin", &ally_autojoin);
+
+ if (local_autojoin)
+ channel->config->local_autojoin = true;
+ if (ally_autojoin)
+ channel->config->ally_autojoin = true;
+
+ libconfig->setting_lookup_bool(settings, "allow_user_channel_creation", &allow_user_channel_creation);
+
+ if( allow_user_channel_creation )
+ channel->config->allow_user_channel_creation = true;
+
+ if( (colors = libconfig->setting_get_member(settings, "colors")) != NULL ) {
+ int color_count = libconfig->setting_length(colors);
+ CREATE(channel->config->colors, unsigned int, color_count);
+ CREATE(channel->config->colors_name, char *, color_count);
+ for(i = 0; i < color_count; i++) {
+ config_setting_t *color = libconfig->setting_get_elem(colors, i);
+
+ CREATE(channel->config->colors_name[i], char, HCS_NAME_LENGTH);
+
+ safestrncpy(channel->config->colors_name[i], config_setting_name(color), HCS_NAME_LENGTH);
+
+ channel->config->colors[i] = (unsigned int)strtoul(libconfig->setting_get_string_elem(colors,i),NULL,0);
+ channel->config->colors[i] = (channel->config->colors[i] & 0x0000FF) << 16 | (channel->config->colors[i] & 0x00FF00) | (channel->config->colors[i] & 0xFF0000) >> 16;//RGB to BGR
+ }
+ channel->config->colors_count = color_count;
+ }
+
+ libconfig->setting_lookup_string(settings, "map_local_channel_color", &local_color);
+
+ for (k = 0; k < channel->config->colors_count; k++) {
+ if (strcmpi(channel->config->colors_name[k], local_color) == 0)
+ break;
+ }
+
+ if (k < channel->config->colors_count) {
+ channel->config->local_color = k;
+ } else {
+ ShowError("channels.conf: unknown color '%s' for 'map_local_channel_color', disabling '#%s'...\n",local_color,local_name);
+ channel->config->local = false;
+ }
+
+ libconfig->setting_lookup_string(settings, "ally_channel_color", &ally_color);
+
+ for (k = 0; k < channel->config->colors_count; k++) {
+ if (strcmpi(channel->config->colors_name[k], ally_color) == 0)
+ break;
+ }
+
+ if( k < channel->config->colors_count ) {
+ channel->config->ally_color = k;
+ } else {
+ ShowError("channels.conf: unknown color '%s' for 'ally_channel_color', disabling '#%s'...\n",ally_color,ally_name);
+ channel->config->ally = false;
+ }
+
+ libconfig->setting_lookup_string(settings, "irc_channel_color", &irc_color);
+
+ for (k = 0; k < channel->config->colors_count; k++) {
+ if (strcmpi(channel->config->colors_name[k], irc_color) == 0)
+ break;
+ }
+
+ if (k < channel->config->colors_count) {
+ channel->config->irc_color = k;
+ } else {
+ ShowError("channels.conf: unknown color '%s' for 'irc_channel_color', disabling '#%s'...\n",irc_color,irc_name);
+ channel->config->irc = false;
+ }
+
+ if (channel->config->irc) {
+ ircbot->channel = channel->create(HCS_TYPE_IRC, channel->config->irc_name, channel->config->irc_color);
+ }
+
+ if( (channels = libconfig->setting_get_member(settings, "default_channels")) != NULL ) {
+ int channel_count = libconfig->setting_length(channels);
+
+ for(i = 0; i < channel_count; i++) {
+ config_setting_t *chan = libconfig->setting_get_elem(channels, i);
+ const char *name = config_setting_name(chan);
+ const char *color = libconfig->setting_get_string_elem(channels,i);
+
+ ARR_FIND(0, channel->config->colors_count, k, strcmpi(channel->config->colors_name[k],color) == 0);
+ if (k == channel->config->colors_count) {
+ ShowError("channels.conf: unknown color '%s' for channel '%s', skipping channel...\n",color,name);
+ continue;
+ }
+ if (strcmpi(name, channel->config->local_name) == 0
+ || strcmpi(name, channel->config->ally_name) == 0
+ || strcmpi(name, channel->config->irc_name) == 0
+ || strdb_exists(channel->db, name)) {
+ ShowError("channels.conf: duplicate channel '%s', skipping channel...\n",name);
+ continue;
+
+ }
+ channel->create(HCS_TYPE_PUBLIC, name, k);
+ }
+ }
+
+ ShowStatus("Done reading '"CL_WHITE"%d"CL_RESET"' channels in '"CL_WHITE"%s"CL_RESET"'.\n", db_size(channel->db), config_filename);
+ libconfig->destroy(&channels_conf);
+ }
+}
+
+/*==========================================
+ *
+ *------------------------------------------*/
+int do_init_channel(bool minimal)
+{
+ if (minimal)
+ return 0;
+
+ channel->db = stridb_alloc(DB_OPT_DUP_KEY|DB_OPT_RELEASE_DATA, HCS_NAME_LENGTH);
+ channel->config->ally = channel->config->local = channel->config->irc = channel->config->ally_autojoin = channel->config->local_autojoin = false;
+ channel->config_read();
+
+ return 0;
+}
+
+void do_final_channel(void)
+{
+ DBIterator *iter = db_iterator(channel->db);
+ struct channel_data *chan;
+ unsigned char i;
+
+ for( chan = dbi_first(iter); dbi_exists(iter); chan = dbi_next(iter) ) {
+ channel->delete(chan);
+ }
+
+ dbi_destroy(iter);
+
+ for(i = 0; i < channel->config->colors_count; i++) {
+ aFree(channel->config->colors_name[i]);
+ }
+
+ if (channel->config->colors_count) {
+ aFree(channel->config->colors_name);
+ aFree(channel->config->colors);
+ }
+
+ db_destroy(channel->db);
+}
+
+void channel_defaults(void)
+{
+ channel = &channel_s;
+
+ channel->db = NULL;
+ channel->config = &channel_config;
+
+ channel->init = do_init_channel;
+ channel->final = do_final_channel;
+
+ channel->search = channel_search;
+ channel->create = channel_create;
+ channel->delete = channel_delete;
+
+ channel->set_password = channel_set_password;
+ channel->ban = channel_ban;
+ channel->unban = channel_unban;
+ channel->set_options = channel_set_options;
+
+ channel->send = channel_send;
+ channel->join_sub = channel_join_sub;
+ channel->join = channel_join;
+ channel->leave = channel_leave;
+ channel->leave_sub = channel_leave_sub;
+ channel->quit = channel_quit;
+
+ channel->map_join = channel_map_join;
+ channel->guild_join_alliance = channel_guild_join_alliance;
+ channel->guild_leave_alliance = channel_guild_leave_alliance;
+ channel->quit_guild = channel_quit_guild;
+
+ channel->config_read = read_channels_config;
+}