|
@@ -0,0 +1,1168 @@
|
|
|
|
+// Copyright (c) rAthena Dev Teams - Licensed under GNU GPL
|
|
|
|
+// For more information, see LICENCE in the main folder
|
|
|
|
+
|
|
|
|
+#include "../common/cbasetypes.h"
|
|
|
|
+#include "../common/malloc.h"
|
|
|
|
+#include "../common/conf.h" //libconfig
|
|
|
|
+#include "../common/showmsg.h"
|
|
|
|
+#include "../common/strlib.h" //safestrncpy
|
|
|
|
+#include "../common/socket.h" //set_eof
|
|
|
|
+#include "../common/nullpo.h" //nullpo chk
|
|
|
|
+
|
|
|
|
+#include "map.h" //msg_conf
|
|
|
|
+#include "clif.h" //clif_chsys_msg
|
|
|
|
+#include "channel.h"
|
|
|
|
+#include "pc.h"
|
|
|
|
+
|
|
|
|
+#include <stdio.h>
|
|
|
|
+#include <stdlib.h>
|
|
|
|
+
|
|
|
|
+static DBMap* channel_db; // channels
|
|
|
|
+DBMap* channel_get_db(void){ return channel_db; }
|
|
|
|
+
|
|
|
|
+/*
|
|
|
|
+ * Create *channel
|
|
|
|
+ * - will then add it in channel_db if type not map or ally
|
|
|
|
+ * @name : the name channel will be given, can't be null
|
|
|
|
+ * @pass : can be null. if we want to restrain access
|
|
|
|
+ * @color : display color type
|
|
|
|
+ * @chantype : type of channel
|
|
|
|
+ * return
|
|
|
|
+ * NULL : creation failed
|
|
|
|
+ */
|
|
|
|
+struct Channel* channel_create(char *name, char *pass, unsigned char color, enum Channel_Type chantype, int val) {
|
|
|
|
+ struct Channel* channel;
|
|
|
|
+
|
|
|
|
+ if(!name) return NULL;
|
|
|
|
+
|
|
|
|
+ CREATE( channel, struct Channel, 1 ); //will exit on fail allocation
|
|
|
|
+ channel->users = idb_alloc(DB_OPT_BASE);
|
|
|
|
+ channel->banned = idb_alloc(DB_OPT_BASE);
|
|
|
|
+ channel->opt = CHAN_OPT_BASE;
|
|
|
|
+ channel->type = chantype;
|
|
|
|
+ channel->color = color;
|
|
|
|
+ safestrncpy(channel->name, name, CHAN_NAME_LENGTH);
|
|
|
|
+ if( !pass )
|
|
|
|
+ channel->pass[0] = '\0';
|
|
|
|
+ else
|
|
|
|
+ safestrncpy(channel->pass, pass, CHAN_NAME_LENGTH);
|
|
|
|
+
|
|
|
|
+ //ShowInfo("Create channel %s\n",channel->name);
|
|
|
|
+ switch(channel->type){
|
|
|
|
+ case CHAN_TYPE_MAP: channel->m = val; break;
|
|
|
|
+ case CHAN_TYPE_ALLY: channel->gid = val; break;
|
|
|
|
+ case CHAN_TYPE_PRIVATE: channel->owner = val; //dont break here private need to put in db
|
|
|
|
+ default: strdb_put(channel_db, channel->name, channel);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ return channel;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+/*
|
|
|
|
+ * Delete channel *channel
|
|
|
|
+ * - check if there is any user in channel and make them all quit
|
|
|
|
+ * return
|
|
|
|
+ * 0 : succes
|
|
|
|
+ * -1 : invalid channel
|
|
|
|
+ */
|
|
|
|
+int channel_delete(struct Channel *channel) {
|
|
|
|
+ if(!channel)
|
|
|
|
+ return -1;
|
|
|
|
+ else if( db_size(channel->users)) {
|
|
|
|
+ struct map_session_data *sd;
|
|
|
|
+ DBIterator *iter = db_iterator(channel->users);
|
|
|
|
+ for( sd = dbi_first(iter); dbi_exists(iter); sd = dbi_next(iter) ) { //for all users
|
|
|
|
+ channel_clean(channel,sd,1); //make all quit
|
|
|
|
+ }
|
|
|
|
+ dbi_destroy(iter);
|
|
|
|
+ }
|
|
|
|
+ //ShowInfo("Deleting channel %s\n",channel->name);
|
|
|
|
+ db_destroy(channel->users);
|
|
|
|
+ db_destroy(channel->banned);
|
|
|
|
+ switch(channel->type){
|
|
|
|
+ case CHAN_TYPE_MAP:
|
|
|
|
+ map[channel->m].channel = NULL;
|
|
|
|
+ aFree(channel);
|
|
|
|
+ break;
|
|
|
|
+ case CHAN_TYPE_ALLY: {
|
|
|
|
+ struct guild *g = guild_search(channel->gid);
|
|
|
|
+ if(g) g->channel = NULL;
|
|
|
|
+ aFree(channel);
|
|
|
|
+ break;
|
|
|
|
+ }
|
|
|
|
+ default:
|
|
|
|
+ strdb_remove(channel_db, channel->name);
|
|
|
|
+ break;
|
|
|
|
+ }
|
|
|
|
+ return 0;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+/*
|
|
|
|
+ * Make player *sd join an channel *channel
|
|
|
|
+ * - add charid to channel user list
|
|
|
|
+ * - add *channel to user channel list
|
|
|
|
+ * return
|
|
|
|
+ * 0 : success
|
|
|
|
+ * -1 : invalid channel or sd
|
|
|
|
+ * -2 : already in here
|
|
|
|
+ * -3 : sd banned
|
|
|
|
+ */
|
|
|
|
+int channel_join(struct Channel *channel, struct map_session_data *sd) {
|
|
|
|
+ char output[128];
|
|
|
|
+
|
|
|
|
+ if(!channel || !sd)
|
|
|
|
+ return -1;
|
|
|
|
+ if(channel_haspc(channel,sd)==1) //already in here
|
|
|
|
+ return -2;
|
|
|
|
+
|
|
|
|
+ if(channel_haspcbanned(channel,sd)==1){ //banned ?
|
|
|
|
+ sprintf(output, msg_txt(sd,1438),channel->name); //You cannot join the '%s' channel because you've been banned from it
|
|
|
|
+ clif_displaymessage(sd->fd, output);
|
|
|
|
+ return -3;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ RECREATE(sd->channels, struct Channel *, ++sd->channel_count);
|
|
|
|
+ sd->channels[ sd->channel_count - 1 ] = channel;
|
|
|
|
+ idb_put(channel->users, sd->status.char_id, sd);
|
|
|
|
+
|
|
|
|
+ if( sd->stealth ) {
|
|
|
|
+ sd->stealth = false;
|
|
|
|
+ } else if( channel->opt & CHAN_OPT_ANNOUNCE_JOIN ) {
|
|
|
|
+ char message[60];
|
|
|
|
+ sprintf(message, "#%s '%s' joined",channel->name,sd->status.name);
|
|
|
|
+ clif_channel_msg(channel,sd,message);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /* someone is cheating, we kindly disconnect the bastard */
|
|
|
|
+ if( sd->channel_count > 200 ) {
|
|
|
|
+ set_eof(sd->fd);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ return 0;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+/*
|
|
|
|
+ * Make *sd join the map channel
|
|
|
|
+ * create the map_channel if not exist
|
|
|
|
+ * return
|
|
|
|
+ * -1 : invalid sd
|
|
|
|
+ * -2 : already in here (channel_join)
|
|
|
|
+ * -3 : sd banned (channel_join)
|
|
|
|
+ */
|
|
|
|
+int channel_mjoin(struct map_session_data *sd) {
|
|
|
|
+ if(!sd) return -1;
|
|
|
|
+
|
|
|
|
+ if( !map[sd->bl.m].channel ) {
|
|
|
|
+ map[sd->bl.m].channel = channel_create(Channel_Config.map_chname,NULL,Channel_Config.map_chcolor,CHAN_TYPE_MAP,sd->bl.m);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ if( !( map[sd->bl.m].channel->opt & CHAN_OPT_ANNOUNCE_JOIN ) ) {
|
|
|
|
+ char mout[60];
|
|
|
|
+ sprintf(mout, msg_txt(sd,1435),Channel_Config.map_chname,map[sd->bl.m].name); // You're now in the '#%s' channel for '%s'.
|
|
|
|
+ clif_disp_onlyself(sd, mout, strlen(mout));
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ return channel_join(map[sd->bl.m].channel,sd);
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+/*
|
|
|
|
+ * Make all ally member of guild *g join our guild chan
|
|
|
|
+ * nb : they only join if they are into their own guild channel (if they not they probably left it)
|
|
|
|
+ * return
|
|
|
|
+ * 0 : succes
|
|
|
|
+ * -1 : invalide guild or no channel for guild
|
|
|
|
+ */
|
|
|
|
+int channel_ajoin(struct guild *g){
|
|
|
|
+ int i;
|
|
|
|
+ struct map_session_data *pl_sd;
|
|
|
|
+
|
|
|
|
+ if(!g || !g->channel) return -1;
|
|
|
|
+ for (i = 0; i < MAX_GUILDALLIANCE; i++){
|
|
|
|
+ struct guild *ag; //allied guld
|
|
|
|
+ struct guild_alliance *ga = &g->alliance[i]; //guild alliance
|
|
|
|
+ if(ga->guild_id && (ga->opposition==0) && (ag=guild_search(ga->guild_id))){
|
|
|
|
+ for (i = 0; i < ag->max_member; i++){ //load all guildmember
|
|
|
|
+ pl_sd = ag->member[i].sd;
|
|
|
|
+ if(channel_haspc(ag->channel,pl_sd)==1) //only if they are in there own guildchan
|
|
|
|
+ channel_join(g->channel,pl_sd);
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ return 0;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+/*
|
|
|
|
+ * Make *sd join the guild channel
|
|
|
|
+ * create a chan guild if not exist
|
|
|
|
+ * return
|
|
|
|
+ * 0 : succes
|
|
|
|
+ * -1 : invalid sd
|
|
|
|
+ * -2 : sd have no guild attach
|
|
|
|
+ */
|
|
|
|
+int channel_gjoin(struct map_session_data *sd, int flag){
|
|
|
|
+ struct Channel *channel;
|
|
|
|
+ struct guild *g;
|
|
|
|
+ int i;
|
|
|
|
+
|
|
|
|
+ if(!sd) return -1;
|
|
|
|
+
|
|
|
|
+ g = sd->guild;
|
|
|
|
+ if(!g) return -2;
|
|
|
|
+
|
|
|
|
+ channel = g->channel;
|
|
|
|
+ if(!channel){
|
|
|
|
+ channel = channel_create(Channel_Config.ally_chname,NULL,Channel_Config.ally_chcolor,CHAN_TYPE_ALLY,g->guild_id);
|
|
|
|
+ g->channel = channel;
|
|
|
|
+ channel_ajoin(g);
|
|
|
|
+ }
|
|
|
|
+ if(flag&1) {
|
|
|
|
+ channel_join(channel,sd); //join our guild chat
|
|
|
|
+ }
|
|
|
|
+ if(flag&2){
|
|
|
|
+ for (i = 0; i < MAX_GUILDALLIANCE; i++){
|
|
|
|
+ struct guild *ag; //allied guld
|
|
|
|
+ struct guild_alliance *ga = &g->alliance[i]; //guild alliance
|
|
|
|
+ if(ga->guild_id && (ga->opposition==0) && (ag=guild_search(ga->guild_id)) ) //only join allies
|
|
|
|
+ channel_join(ag->channel,sd);
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ return 0;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+/*
|
|
|
|
+ * Make *sd leave *channel and cleanup association.
|
|
|
|
+ * - if no one remain in chat delete it
|
|
|
|
+ * @flag&1 called from delete do not recall delete
|
|
|
|
+ *return
|
|
|
|
+ * 0 : success
|
|
|
|
+ * -1 : invalid sd or channel
|
|
|
|
+ */
|
|
|
|
+int channel_clean(struct Channel *channel, struct map_session_data *sd, int flag) {
|
|
|
|
+ unsigned char i;
|
|
|
|
+
|
|
|
|
+ if(!channel || !sd)
|
|
|
|
+ return -1;
|
|
|
|
+
|
|
|
|
+ if( channel == sd->gcbind )
|
|
|
|
+ sd->gcbind = NULL;
|
|
|
|
+
|
|
|
|
+ ARR_FIND(0, sd->channel_count, i, sd->channels[i] == channel);
|
|
|
|
+ if( i < sd->channel_count ) {
|
|
|
|
+ unsigned char cursor = i;
|
|
|
|
+ sd->channels[i] = NULL;
|
|
|
|
+ for(; i < sd->channel_count; i++ ) { //slice move list down
|
|
|
|
+ if( sd->channels[i] == NULL )
|
|
|
|
+ continue;
|
|
|
|
+ if(i != cursor)
|
|
|
|
+ sd->channels[cursor] = sd->channels[i];
|
|
|
|
+ cursor++;
|
|
|
|
+ }
|
|
|
|
+ if ( !(sd->channel_count = cursor) ) { //if in no more chan delete db
|
|
|
|
+ aFree(sd->channels);
|
|
|
|
+ sd->channels = NULL;
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ idb_remove(channel->users,sd->status.char_id); //remove user for channel user list
|
|
|
|
+ if( !db_size(channel->users) && !(flag&1) )
|
|
|
|
+ channel_delete(channel);
|
|
|
|
+
|
|
|
|
+ return 0;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+/*
|
|
|
|
+ * Make a *sd leave a type of chan.
|
|
|
|
+ * @type&1 : quit guild chan
|
|
|
|
+ * @type&2 : quit ally chans
|
|
|
|
+ * @type&4 : quit map chan
|
|
|
|
+ * @type&8 : quit all users joined chan
|
|
|
|
+ * return
|
|
|
|
+ * 0 : success
|
|
|
|
+ * -1 : invalid sd
|
|
|
|
+ *
|
|
|
|
+ */
|
|
|
|
+int channel_pcquit(struct map_session_data *sd, int type){
|
|
|
|
+ int i;
|
|
|
|
+
|
|
|
|
+ //On closing state we could have clean all chan by sd but pcquit is more used to free unit when
|
|
|
|
+ //he quit a map_server, not call in map_quit cause we need to cleanup when we switch map-server as well
|
|
|
|
+ if(!sd) return -1;
|
|
|
|
+
|
|
|
|
+ // Leave all chat channels.
|
|
|
|
+ if(type&(1|2) && Channel_Config.ally_enable && sd->guild){ //quit guild and ally chan
|
|
|
|
+ struct guild *g = sd->guild;
|
|
|
|
+ if(type&1 && channel_haspc(g->channel,sd)==1){
|
|
|
|
+ channel_clean(g->channel,sd,0); //leave guild chan
|
|
|
|
+ }
|
|
|
|
+ if(type&2){
|
|
|
|
+ struct guild *ag; //allied guild
|
|
|
|
+ for (i = 0; i < MAX_GUILDALLIANCE; i++) { //leave all alliance chan
|
|
|
|
+ if( g->alliance[i].guild_id && (ag = guild_search(g->alliance[i].guild_id) ) ) {
|
|
|
|
+ if(channel_haspc(ag->channel,sd) == 1)
|
|
|
|
+ channel_clean(ag->channel,sd,0);
|
|
|
|
+ break;
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ if(type&4 && Channel_Config.map_enable && channel_haspc(map[sd->bl.m].channel,sd)==1){ //quit map chan
|
|
|
|
+ channel_clean(map[sd->bl.m].channel,sd,0);
|
|
|
|
+ }
|
|
|
|
+ if(type&8 && sd->channel_count ) { //quit all chan
|
|
|
|
+ uint8 count = sd->channel_count;
|
|
|
|
+ for( i = count-1; i >= 0; i--) { //going backward to avoid shifting
|
|
|
|
+ channel_clean(sd->channels[i],sd,0);
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ return 0;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+/*
|
|
|
|
+ * Format *msg from *sd to send it in *channel
|
|
|
|
+ * Also truncate extra char if msg too long (max=RACHSYS_MSG_LENGTH)
|
|
|
|
+ * return
|
|
|
|
+ * 0 : succes
|
|
|
|
+ * -1 : invalid sd, channel, or msg
|
|
|
|
+ * -2 : delay msg too low since last talk
|
|
|
|
+ */
|
|
|
|
+int channel_send(struct Channel *channel, struct map_session_data *sd, const char *msg) {
|
|
|
|
+ if(!channel || !sd || !msg)
|
|
|
|
+ return -1;
|
|
|
|
+
|
|
|
|
+ if(!pc_has_permission(sd, PC_PERM_CHANNEL_ADMIN) && channel->msg_delay != 0 && DIFF_TICK(sd->channel_tick + ( channel->msg_delay * 1000 ), gettick()) > 0) {
|
|
|
|
+ clif_colormes(sd,COLOR_RED,msg_txt(sd,1455)); //You're talking too fast!
|
|
|
|
+ return -2;
|
|
|
|
+ }
|
|
|
|
+ else {
|
|
|
|
+ char message[CHAN_MSG_LENGTH];
|
|
|
|
+ snprintf(message, CHAN_MSG_LENGTH, "[ #%s ] %s : %s",channel->name,sd->status.name, msg);
|
|
|
|
+ clif_channel_msg(channel,sd,message);
|
|
|
|
+ sd->channel_tick = gettick();
|
|
|
|
+ }
|
|
|
|
+ return 0;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+/*
|
|
|
|
+ * Chk parameter for channel creation
|
|
|
|
+ * @type (bitflag)
|
|
|
|
+ * 1 : check name # + lenght
|
|
|
|
+ * 2 : check if already exist, need 1
|
|
|
|
+ * 4 : check pass lenght
|
|
|
|
+ * return
|
|
|
|
+ * 0 : success
|
|
|
|
+ * -1 : bad chan name
|
|
|
|
+ * -2 : bad chan name lenght
|
|
|
|
+ * -3 : pass given too long
|
|
|
|
+ * -4 : chan already exist
|
|
|
|
+ */
|
|
|
|
+int channel_chk(char *chname, char *chpass, int type){
|
|
|
|
+ if(type&1){ //check name
|
|
|
|
+ if( chname[0] != '#' ) return -1; // Channel name must start with '#'
|
|
|
|
+ if ( strlen(chname) < 3 || strlen(chname) > CHAN_NAME_LENGTH )
|
|
|
|
+ return -2; // Channel length must be between 3 and %d.
|
|
|
|
+ if( (type&2) && (
|
|
|
|
+ strcmpi(chname + 1,Channel_Config.map_chname) == 0
|
|
|
|
+ || strcmpi(chname + 1,Channel_Config.ally_chname) == 0
|
|
|
|
+ || strdb_exists(channel_db, chname + 1) )
|
|
|
|
+ ) {
|
|
|
|
+ return -4; // Channel '%s' already exist
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ if (type&4 && (chpass != '\0' && strlen(chpass) > CHAN_NAME_LENGTH ) ) {
|
|
|
|
+ return -3; // Channel pass can't be higher then %d.
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ return 0;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+/*
|
|
|
|
+ * Lookup to found a channel from his name.
|
|
|
|
+ * @chname : channel name
|
|
|
|
+ * @sd : can be NULL, use to solve #map and #ally case
|
|
|
|
+ * @flag&1 : create channel if not exist (map or ally only)
|
|
|
|
+ * @flag&3 : join channel if not exist (map or ally only)
|
|
|
|
+ * return
|
|
|
|
+ * NULL : channel not found
|
|
|
|
+ */
|
|
|
|
+struct Channel* channel_name2channel(char *chname, struct map_session_data *sd, int flag){
|
|
|
|
+ struct Channel *channel;
|
|
|
|
+ if(channel_chk(chname, NULL, 1)) return NULL;
|
|
|
|
+ if(sd && strcmpi(chname + 1,Channel_Config.map_chname) == 0){
|
|
|
|
+ channel = map[sd->bl.m].channel;
|
|
|
|
+ if(flag&1 && !channel) {
|
|
|
|
+ channel = channel_create(Channel_Config.map_chname,NULL,Channel_Config.map_chcolor,CHAN_TYPE_MAP,sd->bl.m);
|
|
|
|
+ if(flag&2) channel_mjoin(sd);
|
|
|
|
+ map[sd->bl.m].channel = channel;
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ else if(sd && (strcmpi(chname + 1,Channel_Config.ally_chname) == 0) && sd->guild){
|
|
|
|
+ channel = sd->guild->channel;
|
|
|
|
+ if(flag&1 && !channel) {
|
|
|
|
+ channel = channel_create(Channel_Config.map_chname,NULL,Channel_Config.map_chcolor,CHAN_TYPE_ALLY,sd->guild->guild_id);
|
|
|
|
+ if(flag&2) channel_gjoin(sd,3);
|
|
|
|
+ sd->guild->channel = channel;
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ else if( !(channel = strdb_get(channel_db, chname + 1)) ) {
|
|
|
|
+ return NULL;
|
|
|
|
+ }
|
|
|
|
+ return channel;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+/*
|
|
|
|
+ * Channel check if he has *sd in his user list
|
|
|
|
+ * return
|
|
|
|
+ * -1 : invalide sd or channel
|
|
|
|
+ * 0 : not found
|
|
|
|
+ * 1 : has pc
|
|
|
|
+ */
|
|
|
|
+int channel_haspc(struct Channel *channel,struct map_session_data *sd){
|
|
|
|
+ if(!channel || !sd) return -1;
|
|
|
|
+ return (idb_exists(channel->users, sd->status.char_id))?1:0;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+/*
|
|
|
|
+ * Channel check if *sd is banned from channel (banned ?)
|
|
|
|
+ * return
|
|
|
|
+ * -1 : invalide sd or channel
|
|
|
|
+ * 0 : not found
|
|
|
|
+ * 1 : has pc
|
|
|
|
+ */
|
|
|
|
+int channel_haspcbanned(struct Channel *channel,struct map_session_data *sd){
|
|
|
|
+ if(!channel || !sd) return -1;
|
|
|
|
+ return (idb_exists(channel->banned, sd->status.char_id))?1:0;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+/*
|
|
|
|
+ * Player *sd check if he has Channel *channel in his channel list
|
|
|
|
+ * return
|
|
|
|
+ * -1 : invalid channel or sd
|
|
|
|
+ * -2 : not found
|
|
|
|
+ * x>0 : has_chan at index x
|
|
|
|
+ */
|
|
|
|
+int channel_pc_haschan(struct map_session_data *sd, struct Channel *channel){
|
|
|
|
+ int k;
|
|
|
|
+ if(!channel || !sd) return -1; //channel or player doesn't exist
|
|
|
|
+ ARR_FIND(0, sd->channel_count, k, strcmpi(channel->name,sd->channels[k]->name) == 0);
|
|
|
|
+ if( k >= sd->channel_count ) return -2;
|
|
|
|
+ return k;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+/*
|
|
|
|
+ * Display some info to user *sd on channels
|
|
|
|
+ * @options :
|
|
|
|
+ * colors : display availables colors for chan system
|
|
|
|
+ * mine : list of chan *sd is in + nb of user
|
|
|
|
+ * void : list of public chan + map + guild + nb of user
|
|
|
|
+ * return
|
|
|
|
+ * 0 : succes
|
|
|
|
+ * -1 : fail
|
|
|
|
+ */
|
|
|
|
+int channel_display_list(struct map_session_data *sd, char *options){
|
|
|
|
+ struct Channel *channel;
|
|
|
|
+ char output[128];
|
|
|
|
+ int k;
|
|
|
|
+
|
|
|
|
+ if(!sd || !options)
|
|
|
|
+ return -1;
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+ //display availaible colors
|
|
|
|
+ if( options[0] != '\0' && strcmpi(options,"colors") == 0 ) {
|
|
|
|
+ char msg[40];
|
|
|
|
+ for( k = 0; k < Channel_Config.colors_count; k++ ) {
|
|
|
|
+ sprintf(msg, "[ Channel list colors ] : %s",Channel_Config.colors_name[k]);
|
|
|
|
+ clif_colormes(sd, k, msg);
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ else if( options[0] != '\0' && strcmpi(options,"mine") == 0 ) { //display chan I'm into
|
|
|
|
+ clif_displaymessage(sd->fd, " ---- Joined Channels ----"); // ---- Joined Channels ----
|
|
|
|
+ if(!sd->channel_count)
|
|
|
|
+ clif_displaymessage(sd->fd, "You have not joined any channel yet");
|
|
|
|
+ else {
|
|
|
|
+ for(k=0; k<sd->channel_count; k++){
|
|
|
|
+ channel = sd->channels[k];
|
|
|
|
+ sprintf(output, msg_txt(sd,1409), channel->name, db_size(channel->users));// - #%s (%d users)
|
|
|
|
+ clif_displaymessage(sd->fd, output);
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ else { //display public chanels
|
|
|
|
+ DBIterator *iter;
|
|
|
|
+ bool has_perm = pc_has_permission(sd, PC_PERM_CHANNEL_ADMIN) ? true : false;
|
|
|
|
+
|
|
|
|
+ clif_displaymessage(sd->fd, msg_txt(sd,1410)); // ---- Public Channels ----
|
|
|
|
+ if( Channel_Config.map_enable ) {
|
|
|
|
+ sprintf(output, msg_txt(sd,1409), Channel_Config.map_chname, map[sd->bl.m].channel ? db_size(map[sd->bl.m].channel->users) : 0);// - #%s ( %d users )
|
|
|
|
+ clif_displaymessage(sd->fd, output);
|
|
|
|
+ }
|
|
|
|
+ if( Channel_Config.ally_enable && sd->status.guild_id ) {
|
|
|
|
+ struct guild *g = sd->guild;
|
|
|
|
+ if( !g ) return -1; //how can this happen if status.guild_id true ?
|
|
|
|
+ sprintf(output, msg_txt(sd,1409), Channel_Config.ally_chname, db_size(((struct Channel *)g->channel)->users));// - #%s ( %d users )
|
|
|
|
+ clif_displaymessage(sd->fd, output);
|
|
|
|
+ }
|
|
|
|
+ iter = db_iterator(channel_db);
|
|
|
|
+ for(channel = dbi_first(iter); dbi_exists(iter); channel = dbi_next(iter)) {
|
|
|
|
+ if( has_perm || channel->type == CHAN_TYPE_PUBLIC ) {
|
|
|
|
+ sprintf(output, msg_txt(sd,1409), channel->name, db_size(channel->users));// - #%s (%d users)
|
|
|
|
+ clif_displaymessage(sd->fd, output);
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ dbi_destroy(iter);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ return 0;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+/*
|
|
|
|
+ * An user *sd is attempting to create a channel named *chname with pass *chpass
|
|
|
|
+ * return
|
|
|
|
+ * 0 : succes
|
|
|
|
+ * -1 : fail
|
|
|
|
+ */
|
|
|
|
+int channel_pccreate(struct map_session_data *sd, char *chname, char *chpass){
|
|
|
|
+ struct Channel *channel;
|
|
|
|
+ char output[128];
|
|
|
|
+ int8 res;
|
|
|
|
+
|
|
|
|
+ if(!sd || !chname)
|
|
|
|
+ return 0;
|
|
|
|
+
|
|
|
|
+ res = channel_chk(chname,chpass,7);
|
|
|
|
+ if(res==0){ //succes
|
|
|
|
+ channel = channel_create(chname + 1,chpass,0,CHAN_TYPE_PRIVATE,sd->status.char_id);
|
|
|
|
+ channel_join(channel,sd);
|
|
|
|
+ if( !( channel->opt & CHAN_OPT_ANNOUNCE_JOIN ) ) {
|
|
|
|
+ sprintf(output, msg_txt(sd,1403),chname); // You're now in the '%s' channel.
|
|
|
|
+ clif_displaymessage(sd->fd, output);
|
|
|
|
+ }
|
|
|
|
+ } else { //failure display cause
|
|
|
|
+ switch(res){
|
|
|
|
+ case -1: sprintf(output, msg_txt(sd,1405), CHAN_NAME_LENGTH); break;// Channel name must start with '#'.
|
|
|
|
+ case -2: sprintf(output, msg_txt(sd,1406), CHAN_NAME_LENGTH); break;// Channel length must be between 3 and %d.
|
|
|
|
+ case -3: sprintf(output, msg_txt(sd,1436), CHAN_NAME_LENGTH); break;// Channel pass can't be higher then %d.
|
|
|
|
+ case -4: sprintf(output, msg_txt(sd,1407), chname);// Channel '%s' is not available.
|
|
|
|
+ }
|
|
|
|
+ clif_displaymessage(sd->fd, output);
|
|
|
|
+ return -1;
|
|
|
|
+ }
|
|
|
|
+ return 0;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+/*
|
|
|
|
+ * An user *sd is attempting to delete a channel named *chname
|
|
|
|
+ * return
|
|
|
|
+ * 0 : succes
|
|
|
|
+ * -1 : fail
|
|
|
|
+ */
|
|
|
|
+int channel_pcdelete(struct map_session_data *sd, char *chname){
|
|
|
|
+ struct Channel *channel;
|
|
|
|
+ char output[128];
|
|
|
|
+
|
|
|
|
+ if(!sd || !chname) return 0;
|
|
|
|
+
|
|
|
|
+ if( channel_chk(chname,NULL,1) ) {
|
|
|
|
+ clif_displaymessage(sd->fd, msg_txt(sd,1405));// Channel name must start with '#'.
|
|
|
|
+ return -1;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ channel = channel_name2channel(chname,sd,0);
|
|
|
|
+ if(channel_pc_haschan(sd,channel)<0){
|
|
|
|
+ sprintf(output, "Channel %s doesn't exist",chname);// You're not part of the '%s' channel.
|
|
|
|
+ clif_displaymessage(sd->fd, output);
|
|
|
|
+ return -2; //channel doesn't exist or player don't have it
|
|
|
|
+ }
|
|
|
|
+ channel_delete(channel);
|
|
|
|
+
|
|
|
|
+ sprintf(output, "Channel %s deleted",chname); // You've left the '%s' channel.
|
|
|
|
+ clif_displaymessage(sd->fd, output);
|
|
|
|
+
|
|
|
|
+ return 0;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+/*
|
|
|
|
+ * An user *sd is attempting to leave a channel named *chname
|
|
|
|
+ * return
|
|
|
|
+ * 0 : succes
|
|
|
|
+ * -1 : fail
|
|
|
|
+ */
|
|
|
|
+int channel_pcleave(struct map_session_data *sd, char *chname){
|
|
|
|
+ struct Channel *channel;
|
|
|
|
+ char output[128];
|
|
|
|
+
|
|
|
|
+ if(!sd || !chname)
|
|
|
|
+ return 0;
|
|
|
|
+
|
|
|
|
+ if( channel_chk(chname,NULL,1) ) {
|
|
|
|
+ clif_displaymessage(sd->fd, msg_txt(sd,1405));// Channel name must start with '#'.
|
|
|
|
+ return -1;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ channel = channel_name2channel(chname,sd,0);
|
|
|
|
+ if(channel_pc_haschan(sd,channel)<0){
|
|
|
|
+ sprintf(output, msg_txt(sd,1425),chname);// You're not part of the '%s' channel.
|
|
|
|
+ clif_displaymessage(sd->fd, output);
|
|
|
|
+ return -2; //channel doesn't exist or player don't have it
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ if( !Channel_Config.closing && (channel->opt & CHAN_OPT_ANNOUNCE_JOIN) ) {
|
|
|
|
+ char message[60];
|
|
|
|
+ sprintf(message, "#%s '%s' left",channel->name,sd->status.name);
|
|
|
|
+ clif_channel_msg(channel,sd,message);
|
|
|
|
+ }
|
|
|
|
+ switch(channel->type){
|
|
|
|
+ case CHAN_TYPE_ALLY: channel_pcquit(sd,3); break;
|
|
|
|
+ case CHAN_TYPE_MAP: channel_pcquit(sd,4); break;
|
|
|
|
+ default: //private and public atm
|
|
|
|
+ channel_clean(channel,sd,0);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ sprintf(output, msg_txt(sd,1426),chname); // You've left the '%s' channel.
|
|
|
|
+ clif_displaymessage(sd->fd, output);
|
|
|
|
+ return 0;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+/*
|
|
|
|
+ * An user *sd is attempting to join a channel named *chname
|
|
|
|
+ * return
|
|
|
|
+ * 0 : succes
|
|
|
|
+ * -1 : fail
|
|
|
|
+ */
|
|
|
|
+int channel_pcjoin(struct map_session_data *sd, char *chname, char *pass){
|
|
|
|
+ struct Channel *channel;
|
|
|
|
+ char output[128];
|
|
|
|
+
|
|
|
|
+ if(!sd || !chname)
|
|
|
|
+ return 0;
|
|
|
|
+
|
|
|
|
+ if( channel_chk(chname,NULL,1) ) {
|
|
|
|
+ clif_displaymessage(sd->fd, msg_txt(sd,1405));// Channel name must start with '#'.
|
|
|
|
+ return -1;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ channel = channel_name2channel(chname,sd,1);
|
|
|
|
+ if(channel){
|
|
|
|
+ if(channel_haspc(channel,sd)==1) {
|
|
|
|
+ sprintf(output, msg_txt(sd,1434),chname); // You're already in the '%s' channel.
|
|
|
|
+ clif_displaymessage(sd->fd, output);
|
|
|
|
+ return -1;
|
|
|
|
+ }
|
|
|
|
+ else if( channel->pass[0] != '\0') { //chan has a pass
|
|
|
|
+ if(strcmp(channel->pass,pass) != 0){ //wrong pass entry
|
|
|
|
+ if( pc_has_permission(sd, PC_PERM_CHANNEL_ADMIN) ) {
|
|
|
|
+ sd->stealth = true;
|
|
|
|
+ } else {
|
|
|
|
+ sprintf(output, msg_txt(sd,1401),chname,"@Join"); // '%s' Channel is password protected (usage: %s <#channel_name> <password>)
|
|
|
|
+ clif_displaymessage(sd->fd, output);
|
|
|
|
+ return -1;
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ else {
|
|
|
|
+ sprintf(output, msg_txt(sd,1400),chname,"@Join"); // Unknown Channel '%s' (usage: %s <#channel_name>)
|
|
|
|
+ clif_displaymessage(sd->fd, output);
|
|
|
|
+ return -1;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ if( !( channel->opt & CHAN_OPT_ANNOUNCE_JOIN ) ) {
|
|
|
|
+ sprintf(output, msg_txt(sd,1403),chname); // You're now in the '%s' channel.
|
|
|
|
+ clif_displaymessage(sd->fd, output);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ switch(channel->type){
|
|
|
|
+ case CHAN_TYPE_ALLY: channel_gjoin(sd,3); break;
|
|
|
|
+ case CHAN_TYPE_MAP: channel_mjoin(sd); break;
|
|
|
|
+ default: //private and public atm
|
|
|
|
+ channel_join(channel,sd);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ return 0;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+/*
|
|
|
|
+ * An user *sd is attempting to change color with *color of a channel named *chname
|
|
|
|
+ * return
|
|
|
|
+ * 0 : succes
|
|
|
|
+ * -1 : fail
|
|
|
|
+ */
|
|
|
|
+int channel_pccolor(struct map_session_data *sd, char *chname, char *color){
|
|
|
|
+ struct Channel *channel;
|
|
|
|
+ char output[128];
|
|
|
|
+ int k;
|
|
|
|
+
|
|
|
|
+ if(!sd)
|
|
|
|
+ return 0;
|
|
|
|
+
|
|
|
|
+ if( channel_chk(chname,NULL,1) ) {
|
|
|
|
+ clif_displaymessage(sd->fd, msg_txt(sd,1405));// Channel name must start with '#'.
|
|
|
|
+ return -1;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+ channel = channel_name2channel(chname,sd,0);
|
|
|
|
+ if( !channel ) {
|
|
|
|
+ sprintf(output, msg_txt(sd,1407), chname);// Channel '%s' is not available.
|
|
|
|
+ clif_displaymessage(sd->fd, output);
|
|
|
|
+ return -1;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ if( channel->owner != sd->status.char_id && !pc_has_permission(sd, PC_PERM_CHANNEL_ADMIN) ) {
|
|
|
|
+ sprintf(output, msg_txt(sd,1412), chname);// You're not the owner of channel '%s'.
|
|
|
|
+ clif_displaymessage(sd->fd, output);
|
|
|
|
+ return -1;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ ARR_FIND(0,Channel_Config.colors_count,k,( strcmpi(color,Channel_Config.colors_name[k]) == 0 ) );
|
|
|
|
+ if( k >= Channel_Config.colors_count ) {
|
|
|
|
+ sprintf(output, msg_txt(sd,1411), color);// Unknown color '%s'.
|
|
|
|
+ clif_displaymessage(sd->fd, output);
|
|
|
|
+ return -1;
|
|
|
|
+ }
|
|
|
|
+ channel->color = k;
|
|
|
|
+ sprintf(output, msg_txt(sd,1413),chname,Channel_Config.colors_name[k]);// '%s' channel color updated to '%s'.
|
|
|
|
+ clif_displaymessage(sd->fd, output);
|
|
|
|
+ return 0;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+/*
|
|
|
|
+ * An user *sd is attempting to bind (make default message output display chan talk)
|
|
|
|
+ * from a channel named *chname
|
|
|
|
+ * return
|
|
|
|
+ * 0 : succes
|
|
|
|
+ * -1 : fail
|
|
|
|
+ */
|
|
|
|
+int channel_pcbind(struct map_session_data *sd, char *chname){
|
|
|
|
+ struct Channel *channel;
|
|
|
|
+ char output[128];
|
|
|
|
+
|
|
|
|
+ if(!sd)
|
|
|
|
+ return 0;
|
|
|
|
+
|
|
|
|
+ if( channel_chk(chname,NULL,1) ) {
|
|
|
|
+ clif_displaymessage(sd->fd, msg_txt(sd,1405));// Channel name must start with '#'.
|
|
|
|
+ return -1;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ channel = channel_name2channel(chname,sd,0);
|
|
|
|
+ if(channel_pc_haschan(sd,channel)<0){
|
|
|
|
+ sprintf(output, msg_txt(sd,1425),chname);// You're not part of the '%s' channel.
|
|
|
|
+ clif_displaymessage(sd->fd, output);
|
|
|
|
+ return -2; //channel doesn't exist or player don't have it
|
|
|
|
+ }
|
|
|
|
+ sd->gcbind = channel;
|
|
|
|
+ sprintf(output, msg_txt(sd,1431),chname); // Your global chat is now binded to the '%s' channel.
|
|
|
|
+ clif_displaymessage(sd->fd, output);
|
|
|
|
+ return 0;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+/*
|
|
|
|
+ * An user *sd is attempting to unbind
|
|
|
|
+ * return
|
|
|
|
+ * 0 : succes
|
|
|
|
+ * -1 : fail
|
|
|
|
+ */
|
|
|
|
+int channel_pcunbind(struct map_session_data *sd){
|
|
|
|
+ char output[128];
|
|
|
|
+
|
|
|
|
+ if(!sd)
|
|
|
|
+ return 0;
|
|
|
|
+
|
|
|
|
+ if( sd->gcbind == NULL ) {
|
|
|
|
+ clif_displaymessage(sd->fd, msg_txt(sd,1432));// Your global chat is not binded to any channel.
|
|
|
|
+ return -1;
|
|
|
|
+ }
|
|
|
|
+ sprintf(output, msg_txt(sd,1433),sd->gcbind->name); // Your global chat is now unbinded from the '#%s' channel.
|
|
|
|
+ clif_displaymessage(sd->fd, output);
|
|
|
|
+ sd->gcbind = NULL;
|
|
|
|
+ return 0;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+/*
|
|
|
|
+ * An user *sd is attempting to do something with the banlist
|
|
|
|
+ * @flag == 0 : ban
|
|
|
|
+ * @flag == 1 : unban
|
|
|
|
+ * @flag == 2 : unbanall
|
|
|
|
+ * @flag == 3 : banlist
|
|
|
|
+ * return
|
|
|
|
+ * 0 : succes
|
|
|
|
+ * -1 : fail
|
|
|
|
+ */
|
|
|
|
+int channel_pcban(struct map_session_data *sd, char *chname, struct map_session_data *tsd, int flag){
|
|
|
|
+ struct Channel *channel;
|
|
|
|
+ char output[128];
|
|
|
|
+
|
|
|
|
+ if( channel_chk(chname,NULL,1) ) {
|
|
|
|
+ clif_displaymessage(sd->fd, msg_txt(sd,1405));// Channel name must start with '#'.
|
|
|
|
+ return -1;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ channel = channel_name2channel(chname,sd,0);
|
|
|
|
+ if( !channel ) {
|
|
|
|
+ sprintf(output, msg_txt(sd,1407), chname);// Channel '%s' is not available.
|
|
|
|
+ clif_displaymessage(sd->fd, output);
|
|
|
|
+ return -1;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ if( channel->owner != sd->status.char_id && !pc_has_permission(sd, PC_PERM_CHANNEL_ADMIN) ) {
|
|
|
|
+ sprintf(output, msg_txt(sd,1412), chname);// You're not the owner of channel '%s'.
|
|
|
|
+ clif_displaymessage(sd->fd, output);
|
|
|
|
+ return -1;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ if(flag != 2 && flag != 3){
|
|
|
|
+ char banned;
|
|
|
|
+ if(!tsd || pc_has_permission(tsd, PC_PERM_CHANNEL_ADMIN) ) {
|
|
|
|
+ clif_displaymessage(sd->fd, msg_txt(sd,1464)); // Ban failed, not possible to ban/unban this user.
|
|
|
|
+ return -1;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ banned = channel_haspcbanned(channel,tsd);
|
|
|
|
+ if(!flag && banned==1) {
|
|
|
|
+ sprintf(output, msg_txt(sd,1465), tsd->status.name);// Player '%s' is already banned from this channel
|
|
|
|
+ clif_displaymessage(sd->fd, output);
|
|
|
|
+ return -1;
|
|
|
|
+ }
|
|
|
|
+ else if(flag==1 && banned==0) {
|
|
|
|
+ sprintf(output, msg_txt(sd,1440), tsd->status.name);// 1440: Player '%s' is not banned from this channel
|
|
|
|
+ clif_displaymessage(sd->fd, output);
|
|
|
|
+ return -1;
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ else {
|
|
|
|
+ if( !db_size(channel->banned) ) {
|
|
|
|
+ sprintf(output, msg_txt(sd,1439), chname);// Channel '%s' has no banned players
|
|
|
|
+ clif_displaymessage(sd->fd, output);
|
|
|
|
+ return -1;
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ //let properly alter the list now
|
|
|
|
+ switch(flag){
|
|
|
|
+ case 0:
|
|
|
|
+ idb_put(channel->banned, tsd->status.char_id, tsd);
|
|
|
|
+ channel_clean(channel,tsd,0);
|
|
|
|
+ sprintf(output, msg_txt(sd,1437),tsd->status.name,chname); // Player '%s' has now been banned from '%s' channel
|
|
|
|
+ break;
|
|
|
|
+ case 1:
|
|
|
|
+ idb_remove(channel->banned, tsd->status.char_id);
|
|
|
|
+ sprintf(output, msg_txt(sd,1441),tsd->status.name,chname); // Player '%s' has now been unbanned from the '%s' channel
|
|
|
|
+ break;
|
|
|
|
+ case 2:
|
|
|
|
+ db_clear(channel->banned);
|
|
|
|
+ sprintf(output, msg_txt(sd,1442),chname); // Removed all bans from '%s' channel
|
|
|
|
+ break;
|
|
|
|
+ case 3: {
|
|
|
|
+ DBIterator *iter = db_iterator(channel->banned);
|
|
|
|
+ struct map_session_data *pl_sd;
|
|
|
|
+ sprintf(output, msg_txt(sd,1443), channel->name);// -- '%s' ban list
|
|
|
|
+ clif_displaymessage(sd->fd, output);
|
|
|
|
+ for( pl_sd = dbi_first(iter); dbi_exists(iter); pl_sd = dbi_next(iter) ) { //for all users
|
|
|
|
+ sprintf(output, "%d: %s",pl_sd->status.char_id,pl_sd->status.name); // %d: %s
|
|
|
|
+ clif_displaymessage(sd->fd, output);
|
|
|
|
+ }
|
|
|
|
+ dbi_destroy(iter);
|
|
|
|
+ }
|
|
|
|
+ break;
|
|
|
|
+ }
|
|
|
|
+ clif_displaymessage(sd->fd, output);
|
|
|
|
+
|
|
|
|
+ return 0;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+/*
|
|
|
|
+ * An user *sd is attempting to set an option on channel named *chname
|
|
|
|
+ * @chname = channel name
|
|
|
|
+ * @option = available = opt_str
|
|
|
|
+ * @val = value to assign to option
|
|
|
|
+ * return
|
|
|
|
+ * 0 : succes
|
|
|
|
+ * -1 : fail
|
|
|
|
+ */
|
|
|
|
+int channel_pcsetopt(struct map_session_data *sd, char *chname, const char *option, const char *val){
|
|
|
|
+ struct Channel *channel;
|
|
|
|
+ char output[128];
|
|
|
|
+ int k, s=0;
|
|
|
|
+ const char* opt_str[3] = {
|
|
|
|
+ "None",
|
|
|
|
+ "JoinAnnounce",
|
|
|
|
+ "MessageDelay",
|
|
|
|
+ };
|
|
|
|
+
|
|
|
|
+ if( channel_chk(chname,NULL,1) ) {
|
|
|
|
+ clif_displaymessage(sd->fd, msg_txt(sd,1405));// Channel name must start with '#'.
|
|
|
|
+ return -1;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ channel = channel_name2channel(chname,sd,0);
|
|
|
|
+ if( !channel ) {
|
|
|
|
+ sprintf(output, msg_txt(sd,1407), chname);// Channel '%s' is not available.
|
|
|
|
+ clif_displaymessage(sd->fd, output);
|
|
|
|
+ return -1;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ if( channel->owner != sd->status.char_id && !pc_has_permission(sd, PC_PERM_CHANNEL_ADMIN) ) {
|
|
|
|
+ sprintf(output, msg_txt(sd,1412), chname);// You're not the owner of channel '%s'.
|
|
|
|
+ clif_displaymessage(sd->fd, output);
|
|
|
|
+ return -1;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+ if( option == '\0' ) {
|
|
|
|
+ clif_displaymessage(sd->fd, msg_txt(sd,1446));// You need to input a option
|
|
|
|
+ return -1;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ s = ARRAYLENGTH(opt_str);
|
|
|
|
+ ARR_FIND(1,s,k,( strncmpi(option,opt_str[k],3) == 0 )); //we only cmp 3 letter atm
|
|
|
|
+ if( k == 3 ) {
|
|
|
|
+ sprintf(output, msg_txt(sd,1447), option);// '%s' is not a known channel option
|
|
|
|
+ clif_displaymessage(sd->fd, output);
|
|
|
|
+ clif_displaymessage(sd->fd, msg_txt(sd,1448)); // -- Available options
|
|
|
|
+ for( k = 1; k < s; k++ ) {
|
|
|
|
+ sprintf(output, msg_txt(sd,1445), opt_str[k]);// - '%s'
|
|
|
|
+ clif_displaymessage(sd->fd, output);
|
|
|
|
+ }
|
|
|
|
+ return -1;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ if( val[0] == '\0' ) {
|
|
|
|
+ if ( k == CHAN_OPT_MSG_DELAY ) {
|
|
|
|
+ sprintf(output, msg_txt(sd,1466), opt_str[k]);// For '%s' you need the amount of seconds (from 0 to 10)
|
|
|
|
+ clif_displaymessage(sd->fd, output);
|
|
|
|
+ return -1;
|
|
|
|
+ } else if( channel->opt & k ) {
|
|
|
|
+ sprintf(output, msg_txt(sd,1449), opt_str[k],opt_str[k]); // option '%s' is already enabled, if you'd like to disable it type '@channel setopt %s 0'
|
|
|
|
+ clif_displaymessage(sd->fd, output);
|
|
|
|
+ return -1;
|
|
|
|
+ } else {
|
|
|
|
+ channel->opt |= k;
|
|
|
|
+ sprintf(output, msg_txt(sd,1450), opt_str[k],channel->name);//option '%s' is now enabled for channel '%s'
|
|
|
|
+ clif_displaymessage(sd->fd, output);
|
|
|
|
+ return 0;
|
|
|
|
+ }
|
|
|
|
+ } else {
|
|
|
|
+ int v = atoi(val);
|
|
|
|
+ if( k == CHAN_OPT_MSG_DELAY ) {
|
|
|
|
+ if( v < 0 || v > 10 ) {
|
|
|
|
+ sprintf(output, msg_txt(sd,1451), v, opt_str[k]);// value '%d' for option '%s' is out of range (limit is 0-10)
|
|
|
|
+ clif_displaymessage(sd->fd, output);
|
|
|
|
+ return false;
|
|
|
|
+ }
|
|
|
|
+ if( v == 0 ) {
|
|
|
|
+ channel->opt &=~ k;
|
|
|
|
+ channel->msg_delay = 0;
|
|
|
|
+ sprintf(output, msg_txt(sd,1453), opt_str[k],channel->name,v);// option '%s' is now disabled for channel '%s'
|
|
|
|
+ clif_displaymessage(sd->fd, output);
|
|
|
|
+ return true;
|
|
|
|
+ } else {
|
|
|
|
+ channel->opt |= k;
|
|
|
|
+ channel->msg_delay = v;
|
|
|
|
+ sprintf(output, msg_txt(sd,1452), opt_str[k],channel->name,v);// option '%s' is now enabled for channel '%s' with %d seconds
|
|
|
|
+ clif_displaymessage(sd->fd, output);
|
|
|
|
+ return true;
|
|
|
|
+ }
|
|
|
|
+ } else {
|
|
|
|
+ if( v ) {
|
|
|
|
+ if( channel->opt & k ) {
|
|
|
|
+ sprintf(output, msg_txt(sd,1449), opt_str[k],opt_str[k]); // option '%s' is already enabled, if you'd like to disable it type '@channel opt %s 0'
|
|
|
|
+ clif_displaymessage(sd->fd, output);
|
|
|
|
+ return false;
|
|
|
|
+ } else {
|
|
|
|
+ channel->opt |= k;
|
|
|
|
+ sprintf(output, msg_txt(sd,1454), opt_str[k],channel->name);//option '%s' is now enabled for channel '%s'
|
|
|
|
+ clif_displaymessage(sd->fd, output);
|
|
|
|
+ }
|
|
|
|
+ } else {
|
|
|
|
+ if( !(channel->opt & k) ) {
|
|
|
|
+ sprintf(output, msg_txt(sd,1454), opt_str[k],channel->name); // option '%s' is not enabled on channel '%s'
|
|
|
|
+ clif_displaymessage(sd->fd, output);
|
|
|
|
+ return false;
|
|
|
|
+ } else {
|
|
|
|
+ channel->opt &=~ k;
|
|
|
|
+ sprintf(output, msg_txt(sd,1453), opt_str[k],channel->name);// option '%s' is now disabled for channel '%s'
|
|
|
|
+ clif_displaymessage(sd->fd, output);
|
|
|
|
+ return true;
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ return 0;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+/*
|
|
|
|
+ * Read and verify configuration in confif_filename
|
|
|
|
+ * Assign table value with value
|
|
|
|
+ */
|
|
|
|
+void channel_read_config(void) {
|
|
|
|
+ config_t channels_conf;
|
|
|
|
+ config_setting_t *chsys = NULL;
|
|
|
|
+ const char *config_filename = "conf/channels.conf"; // FIXME hardcoded name
|
|
|
|
+
|
|
|
|
+ if (conf_read_file(&channels_conf, config_filename))
|
|
|
|
+ return;
|
|
|
|
+
|
|
|
|
+ chsys = config_lookup(&channels_conf, "chsys");
|
|
|
|
+
|
|
|
|
+ if (chsys != NULL) {
|
|
|
|
+ config_setting_t *settings = config_setting_get_elem(chsys, 0);
|
|
|
|
+ config_setting_t *channels;
|
|
|
|
+ config_setting_t *colors;
|
|
|
|
+ int i,k;
|
|
|
|
+ const char *map_chname, *ally_chname,*map_color, *ally_color;
|
|
|
|
+ int ally_enabled = 0, local_enabled = 0;
|
|
|
|
+ int local_autojoin = 0, ally_autojoin = 0;
|
|
|
|
+ int allow_user_channel_creation = 0;
|
|
|
|
+
|
|
|
|
+ if( !config_setting_lookup_string(settings, "map_local_channel_name", &map_chname) )
|
|
|
|
+ map_chname = "map";
|
|
|
|
+ safestrncpy(Channel_Config.map_chname, map_chname, CHAN_NAME_LENGTH);
|
|
|
|
+
|
|
|
|
+ if( !config_setting_lookup_string(settings, "ally_channel_name", &ally_chname) )
|
|
|
|
+ ally_chname = "ally";
|
|
|
|
+ safestrncpy(Channel_Config.ally_chname, ally_chname, CHAN_NAME_LENGTH);
|
|
|
|
+
|
|
|
|
+ config_setting_lookup_bool(settings, "map_local_channel", &local_enabled);
|
|
|
|
+ config_setting_lookup_bool(settings, "ally_channel_enabled", &ally_enabled);
|
|
|
|
+
|
|
|
|
+ if( local_enabled )
|
|
|
|
+ Channel_Config.map_enable = true;
|
|
|
|
+ if( ally_enabled )
|
|
|
|
+ Channel_Config.ally_enable = true;
|
|
|
|
+
|
|
|
|
+ config_setting_lookup_bool(settings, "map_local_channel_autojoin", &local_autojoin);
|
|
|
|
+ config_setting_lookup_bool(settings, "ally_channel_autojoin", &ally_autojoin);
|
|
|
|
+
|
|
|
|
+ if( local_autojoin )
|
|
|
|
+ Channel_Config.map_autojoin = true;
|
|
|
|
+ if( ally_autojoin )
|
|
|
|
+ Channel_Config.ally_autojoin = true;
|
|
|
|
+
|
|
|
|
+ config_setting_lookup_bool(settings, "allow_user_channel_creation", &allow_user_channel_creation);
|
|
|
|
+
|
|
|
|
+ if( allow_user_channel_creation )
|
|
|
|
+ Channel_Config.user_chenable = true;
|
|
|
|
+
|
|
|
|
+ if( (colors = config_setting_get_member(settings, "colors")) != NULL ) {
|
|
|
|
+ int color_count = config_setting_length(colors);
|
|
|
|
+ CREATE( Channel_Config.colors, unsigned long, color_count );
|
|
|
|
+ CREATE( Channel_Config.colors_name, char *, color_count );
|
|
|
|
+ for(i = 0; i < color_count; i++) {
|
|
|
|
+ config_setting_t *color = config_setting_get_elem(colors, i);
|
|
|
|
+ CREATE( Channel_Config.colors_name[i], char, CHAN_NAME_LENGTH );
|
|
|
|
+
|
|
|
|
+ safestrncpy(Channel_Config.colors_name[i], config_setting_name(color), CHAN_NAME_LENGTH);
|
|
|
|
+ Channel_Config.colors[i] = strtoul(config_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;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ config_setting_lookup_string(settings, "map_local_channel_color", &map_color);
|
|
|
|
+
|
|
|
|
+ for (k = 0; k < Channel_Config.colors_count; k++) {
|
|
|
|
+ if( strcmpi(Channel_Config.colors_name[k],map_color) == 0 )
|
|
|
|
+ break;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ if( k < Channel_Config.colors_count ) {
|
|
|
|
+ Channel_Config.map_chcolor = k;
|
|
|
|
+ } else {
|
|
|
|
+ ShowError("channels.conf: unknown color '%s' for channel 'map_local_channel_color', disabling '#%s'...\n",map_color,map_chname);
|
|
|
|
+ Channel_Config.map_enable = false;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ config_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_chcolor = k;
|
|
|
|
+ } else {
|
|
|
|
+ ShowError("channels.conf: unknown color '%s' for channel 'ally_channel_color', disabling '#%s'...\n",map_color,ally_chname);
|
|
|
|
+ Channel_Config.ally_enable = false;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ if( (channels = config_setting_get_member(settings, "default_channels")) != NULL ) {
|
|
|
|
+ int channel_count = config_setting_length(channels);
|
|
|
|
+
|
|
|
|
+ for(i = 0; i < channel_count; i++) {
|
|
|
|
+ config_setting_t *channel = config_setting_get_elem(channels, i);
|
|
|
|
+ const char *color = config_setting_get_string_elem(channels,i);
|
|
|
|
+ char *name = config_setting_name(channel);
|
|
|
|
+ struct Channel *chd;
|
|
|
|
+
|
|
|
|
+ for (k = 0; k < Channel_Config.colors_count; k++) {
|
|
|
|
+ if( strcmpi(Channel_Config.colors_name[k],color) == 0 )
|
|
|
|
+ break;
|
|
|
|
+ }
|
|
|
|
+ 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.map_chname) == 0 || strcmpi(name,Channel_Config.ally_chname) == 0 || strdb_exists(channel_db, name) ) {
|
|
|
|
+ ShowError("channels.conf: duplicate channel '%s', skipping channel...\n",name);
|
|
|
|
+ continue;
|
|
|
|
+ }
|
|
|
|
+ chd = channel_create(name,NULL,k,CHAN_TYPE_PUBLIC,0);
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ ShowStatus("Done reading '"CL_WHITE"%d"CL_RESET"' channels in '"CL_WHITE"%s"CL_RESET"'.\n", db_size(channel_db), config_filename);
|
|
|
|
+ config_destroy(&channels_conf);
|
|
|
|
+ }
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+/*
|
|
|
|
+ * Initialise db and read config
|
|
|
|
+ * return
|
|
|
|
+ * 0 : success
|
|
|
|
+ */
|
|
|
|
+int do_init_channel(void) {
|
|
|
|
+ channel_db = stridb_alloc(DB_OPT_DUP_KEY|DB_OPT_RELEASE_DATA, CHAN_NAME_LENGTH);
|
|
|
|
+ Channel_Config.ally_enable = Channel_Config.map_enable = Channel_Config.ally_autojoin = Channel_Config.map_autojoin = false;
|
|
|
|
+ channel_read_config();
|
|
|
|
+ return 0;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+/*
|
|
|
|
+ * Close all and cleanup
|
|
|
|
+ * NB map and guild need to cleanup their chan as well
|
|
|
|
+ */
|
|
|
|
+void do_final_channel(void) {
|
|
|
|
+ DBIterator *iter;
|
|
|
|
+ struct Channel *channel;
|
|
|
|
+ struct guild *g;
|
|
|
|
+ int i=0;
|
|
|
|
+
|
|
|
|
+ //delete all in remaining chan db
|
|
|
|
+ iter = db_iterator(channel_db);
|
|
|
|
+ for( channel = dbi_first(iter); dbi_exists(iter); channel = dbi_next(iter) ) {
|
|
|
|
+ channel_delete(channel);
|
|
|
|
+ }
|
|
|
|
+ dbi_destroy(iter);
|
|
|
|
+ //at this point all user should have left their channel (private and public should be gone)
|
|
|
|
+ db_destroy(channel_db);
|
|
|
|
+
|
|
|
|
+ //delete all color thing
|
|
|
|
+ if( Channel_Config.colors_count ) {
|
|
|
|
+ for(i = 0; i < Channel_Config.colors_count; i++) {
|
|
|
|
+ aFree(Channel_Config.colors_name[i]);
|
|
|
|
+ }
|
|
|
|
+ aFree(Channel_Config.colors_name);
|
|
|
|
+ aFree(Channel_Config.colors);
|
|
|
|
+ }
|
|
|
|
+}
|