Ver código fonte

-Channel system refactoring.
-- Move channel into is own file
-- fix channel alliance not being refresh when ack alliance
-- improve ram consumtion of chan, (map and guild chan no longer zombie when no more player in there)
-- spead up cleanup
-- increase encapsulation
-- cleanup duplicate code
-Upd merge ban|unban|banlist|setopt new options


git-svn-id: https://svn.code.sf.net/p/rathena/svn/trunk@17284 54d463be-8e91-2dee-dedb-b68131a5f0ec

glighta 12 anos atrás
pai
commit
6967a84171
14 arquivos alterados com 1480 adições e 736 exclusões
  1. 34 3
      conf/msg_conf/map_msg.conf
  2. 4 4
      src/map/Makefile.in
  3. 98 232
      src/map/atcommand.c
  4. 1168 0
      src/map/channel.c
  5. 94 0
      src/map/channel.h
  6. 23 332
      src/map/clif.c
  7. 3 49
      src/map/clif.h
  8. 33 74
      src/map/guild.c
  9. 6 3
      src/map/map.c
  10. 3 3
      src/map/map.h
  11. 3 3
      src/map/npc.c
  12. 5 6
      src/map/pc.c
  13. 4 3
      src/map/pc.h
  14. 2 24
      src/map/unit.c

+ 34 - 3
conf/msg_conf/map_msg.conf

@@ -1420,14 +1420,12 @@
 // @skillid (extension)
 1398: -- Displaying first %d partial matches:
 
-// @join
+// @channel
 1399: Unknown Channel (usage: %s <#channel_name>)
 1400: Unknown Channel '%s' (usage: %s <#channel_name>)
 1401: '%s' Channel is password protected (usage: %s <#channel_name> <password>)
 1402: You're not in that channel, type '@join <#channel_name>'
 1403: You're now in the '%s' channel.
-
-// @channel
 1404: %s failed.
 1405: Channel name must start with '#'.
 1406: Channel length must be between 3 and %d.
@@ -1460,6 +1458,39 @@
 1433: Your global chat is now unbinded from the '#%s' channel.
 1434: You're already in the '%s' channel.
 1435: You're now in the '#%s' channel for '%s'.
+1436: Channel password can't be higher then %d.
+1437: Player '%s' has now been banned from '%s' channel
+1438: You cannot join the '%s' channel because you've been banned from it
+1439: Channel '%s' has no banned players
+1440: Player '%s' is not banned from this channel
+1441: Player '%s' has now been unbanned from the '%s' channel
+1442: Removed all bans from '%s' channel
+1443:  -- '%s' ban list
+1444: - %d: %s
+1445: - %s
+1446: You need to input a option
+1447: '%s' is not a known channel option
+1448: -- Available options
+1449: option '%s' is already enabled, if you'd like to disable it type '@channel setopt %s 0'
+1450: option '%s' is now enabled for channel '%s'
+1451: value '%d' for option '%s' is out of range (limit is 0-10)
+1452: option '%s' is now enabled for channel '%s' with %d seconds
+1453: option '%s' is now disabled for channel '%s'
+1454: option '%s' is now enabled for channel '%s'
+1455: You're talking too fast!
+1456: -- %s ban <channel name> <character name>
+1457: - bans <character name> from <channel name> channel
+1458: -- %s banlist <channel name>
+1459: - lists all banned characters from <channel name> channel
+1460: -- %s unban <channel name> <character name>
+1461: - unban <character name> from <channel name> channel
+1462: -- %s setopt <channel name> <option name> <option value>
+1463: - adds or removes <option name> with <option value> to <channel name> channel
+1464: Ban failed, not possible to ban/unban this user.
+1465: Player '%s' is already banned from this channel
+1466: For '%s' you need the amount of seconds (from 0 to 10)
+1467: -- %s unbanall <channel name>
+1468: - unbans everyone from <channel name>
 
 //Custom translations
 //import: conf/msg_conf/import/map_msg_eng_conf.txt

+ 4 - 4
src/map/Makefile.in

@@ -17,7 +17,7 @@ MAP_OBJ = map.o chrif.o clif.o pc.o status.o npc.o \
 	storage.o skill.o atcommand.o battle.o battleground.o \
 	intif.o trade.o party.o vending.o guild.o pet.o \
 	log.o mail.o date.o unit.o homunculus.o mercenary.o quest.o instance.o \
-	buyingstore.o searchstore.o duel.o pc_groups.o elemental.o cashshop.o
+	buyingstore.o searchstore.o duel.o pc_groups.o elemental.o cashshop.o channel.o
 MAP_SQL_OBJ = $(MAP_OBJ:%=obj_sql/%) \
 	obj_sql/mapreg_sql.o
 MAP_H = map.h chrif.h clif.h pc.h status.h npc.h \
@@ -25,7 +25,7 @@ MAP_H = map.h chrif.h clif.h pc.h status.h npc.h \
 	storage.h skill.h atcommand.h battle.h battleground.h \
 	intif.h trade.h party.h vending.h guild.h pet.h \
 	log.h mail.h date.h unit.h homunculus.h mercenary.h quest.h instance.h mapreg.h \
-	buyingstore.h searchstore.h duel.h pc_groups.h elemental.h cashshop.h \
+	buyingstore.h searchstore.h duel.h pc_groups.h elemental.h cashshop.h channel.h\
 	../config/core.h ../config/renewal.h ../config/secure.h ../config/const.h \
 	../config/classes/general.h
 
@@ -91,10 +91,10 @@ obj_sql/%.o: %.c $(MAP_H) $(COMMON_H)  $(MT19937AR_H) $(LIBCONFIG_H)
 # missing object files
 ../common/obj_all/common.a:
 	@$(MAKE) -C ../common sql
-    
+
 ../common/obj_sql/common_sql.a:
 	@$(MAKE) -C ../common sql
-        
+
 MT19937AR_OBJ:
 	@$(MAKE) -C ../../3rdparty/mt19937ar
 

+ 98 - 232
src/map/atcommand.c

@@ -17,6 +17,7 @@
 #include "atcommand.h"
 #include "battle.h"
 #include "chat.h"
+#include "channel.h"
 #include "clif.h"
 #include "chrif.h"
 #include "duel.h"
@@ -5621,28 +5622,7 @@ ACMD_FUNC(autotrade) {
 		status_change_start(NULL,&sd->bl, SC_AUTOTRADE, 10000, 0, 0, 0, 0, ((timeout > 0) ? min(timeout,battle_config.at_timeout) : battle_config.at_timeout) * 60000, 0);
 	}
 
-	// Leave all chat channels.
-	if( raChSys.ally && sd->status.guild_id ) {
-		struct guild *g = sd->guild, *sg;
-		if( g ) {
-			if( idb_exists(((struct raChSysCh *)g->channel)->users, sd->status.char_id) )
-				clif_chsys_left((struct raChSysCh *)g->channel,sd);
-			for (i = 0; i < MAX_GUILDALLIANCE; i++) {
-				if( g->alliance[i].guild_id && (sg = guild_search(g->alliance[i].guild_id) ) ) {
-					if( idb_exists(((struct raChSysCh *)sg->channel)->users, sd->status.char_id) )
-						clif_chsys_left((struct raChSysCh *)sg->channel,sd);
-					break;
-				}
-			}
-		}
-	}
-	if( sd->channel_count ) { //quit all chan
-		uint8 count = sd->channel_count;
-		for( i = 0; i < count; i++ ) {
-			if( sd->channels[i] != NULL )
-				clif_chsys_left(sd->channels[i],sd);
-		}
-	}
+	channel_pcquit(sd,0xF); //leave all chan
 	clif_authfail_fd(sd->fd, 15);
 
 	return 0;
@@ -8782,251 +8762,148 @@ ACMD_FUNC(cart) {
 }
 
 /* Channel System [Ind] */
-ACMD_FUNC(join)
-{
-	struct raChSysCh *channel;
-	char name[RACHSYS_NAME_LENGTH], pass[RACHSYS_NAME_LENGTH];
-	DBMap* channel_db = clif_get_channel_db();
+ACMD_FUNC(join){
+	struct Channel *channel;
+	char chname[CHAN_NAME_LENGTH], pass[CHAN_NAME_LENGTH];
 
-	if( !message || !*message || sscanf(message, "%s %s", name, pass) < 1 ) {
+	if( !message || !*message || sscanf(message, "%s %s", chname, pass) < 1 ) {
 		sprintf(atcmd_output, msg_txt(sd,1399),command); // Unknown Channel (usage: %s <#channel_name>)
 		clif_displaymessage(fd, atcmd_output);
 		return -1;
 	}
-	if( raChSys.local && strcmpi(name + 1, raChSys.local_name) == 0 ) {
-		if( !map[sd->bl.m].channel ) {
-			clif_chsys_mjoin(sd);
-			return 0;
-		} else
-			channel = map[sd->bl.m].channel;
-	} else if( raChSys.ally && sd->status.guild_id && strcmpi(name + 1, raChSys.ally_name) == 0 ) {
-		struct guild *g = sd->guild;
-		if( !g ) return -1;/* unlikely, but we wont let it crash anyway. */
-		channel = (struct raChSysCh *)g->channel;
-	} else if( !( channel = strdb_get(channel_db, name + 1) ) ) {
-		sprintf(atcmd_output, msg_txt(sd,1400),name,command); // Unknown Channel '%s' (usage: %s <#channel_name>)
-		clif_displaymessage(fd, atcmd_output);
-		return -1;
-	}
-
-	if( idb_exists(channel->users, sd->status.char_id) ) {
-		sprintf(atcmd_output, msg_txt(sd,1434),name); // You're already in the '%s' channel.
-		clif_displaymessage(fd, atcmd_output);
-		return -1;
-	}
-	if( channel->pass[0] != '\0'  && strcmp(channel->pass,pass) != 0 ) {
-		if( pc_has_permission(sd, PC_PERM_CHANNEL_ADMIN) ) {
-			sd->stealth = true;
-		} else {
-			sprintf(atcmd_output, msg_txt(sd,1401),name,command); // '%s' Channel is password protected (usage: %s <#channel_name> <password>)
-			clif_displaymessage(fd, atcmd_output);
-			return -1;
-		}
-	}
-
-	if( !( channel->opt & raChSys_OPT_ANNOUNCE_JOIN ) ) {
-		sprintf(atcmd_output, msg_txt(sd,1403),name); // You're now in the '%s' channel.
-		clif_displaymessage(fd, atcmd_output);
-	}
-
-	clif_chsys_join(channel,sd);
-
-	return 0;
+	return channel_pcjoin(sd, chname, pass);
 }
-
-static inline void atcmd_channel_help(struct map_session_data *sd, const char *command, bool can_create)
+/*
+ * Display available option for @channel command
+ * @command : the name of used command (for alias case)
+ */
+static inline void atcmd_channel_help(struct map_session_data *sd, const char *command)
 {
 	int fd = sd->fd;
+	bool can_delete = pc_has_permission(sd, PC_PERM_CHANNEL_ADMIN);
+	bool can_create = (can_delete || Channel_Config.user_chenable);
 	clif_displaymessage(fd, msg_txt(sd,1414));// ---- Available options:
+
+	//option create
 	if( can_create ) {
 		sprintf(atcmd_output, msg_txt(sd,1415),command);// * %s create <#channel_name> <channel_password>
 		clif_displaymessage(fd, atcmd_output);
 		clif_displaymessage(fd, msg_txt(sd,1416));// -- Creates a new channel.
 	}
+
+	//option delete
+	if(can_delete){
+		sprintf(atcmd_output, "* %s delete <channel_name>",command);// * %s delete <channel_name>
+		clif_displaymessage(fd, atcmd_output);
+		clif_displaymessage(fd, "Force people leave and destroy the specified channel");
+	}
+
+	//option list
 	sprintf(atcmd_output, msg_txt(sd,1417),command);// * %s list
 	clif_displaymessage(fd, atcmd_output);
 	clif_displaymessage(fd, msg_txt(sd,1418));// -- Lists all public channels.
+	sprintf(atcmd_output, "* %s list mine",command);// * %s list mine
+	clif_displaymessage(fd, atcmd_output);
+	clif_displaymessage(fd, "List all your joined channel");
 	if( can_create ) {
 		sprintf(atcmd_output, msg_txt(sd,1419),command);// * %s list colors
 		clif_displaymessage(fd, atcmd_output);
 		clif_displaymessage(fd, msg_txt(sd,1420));// -- Lists all available colors for custom channels.
+	}
+
+	//option setcolor
+	if(can_create){
 		sprintf(atcmd_output, msg_txt(sd,1421),command);// * %s setcolor <#channel_name> <color_name>
 		clif_displaymessage(fd, atcmd_output);
 		clif_displaymessage(fd, msg_txt(sd,1422));// -- Changes channel text to the specified color (channel owners only).
 	}
+
+	//option join
+	sprintf(atcmd_output, "* %s join <channel_name> <channel_password>",command);// * %s join <channel_name>
+	clif_displaymessage(fd, atcmd_output);
+	clif_displaymessage(fd, "join specified channel");
+
+	//option leave
 	sprintf(atcmd_output, msg_txt(sd,1423),command);// * %s leave <#channel_name>
 	clif_displaymessage(fd, atcmd_output);
 	clif_displaymessage(fd, msg_txt(sd,1424));// -- Leaves the specified channel.
+
+	//option bindto
 	sprintf(atcmd_output, msg_txt(sd,1427),command);// * %s bindto <#channel_name>
 	clif_displaymessage(fd, atcmd_output);
 	clif_displaymessage(fd, msg_txt(sd,1428));// -- Binds your global chat to the specified channel, sending all global messages to that channel.
+
+	//option unbind
 	sprintf(atcmd_output, msg_txt(sd,1429),command);// * %s unbind
 	clif_displaymessage(fd, atcmd_output);
 	clif_displaymessage(fd, msg_txt(sd,1430));// -- Unbinds your global chat from the attached channel, if any.
+
+	//option ban/unban/banlist
+	if( can_create ) {
+		sprintf(atcmd_output, msg_txt(sd,1456),command);// -- %s ban <channel name> <character name>
+		clif_displaymessage(fd, atcmd_output);
+		clif_displaymessage(fd, msg_txt(sd,1457));// - bans <character name> from <channel name> channel
+		sprintf(atcmd_output, msg_txt(sd,1458),command);// -- %s banlist <channel name>
+		clif_displaymessage(fd, atcmd_output);
+		clif_displaymessage(fd, msg_txt(sd,1459));// - lists all banned characters from <channel name> channel
+		sprintf(atcmd_output, msg_txt(sd,1460),command);// -- %s unban <channel name> <character name>
+		clif_displaymessage(fd, atcmd_output);
+		clif_displaymessage(fd, msg_txt(sd,1461));// - unban <character name> from <channel name> channel
+		sprintf(atcmd_output, msg_txt(sd,1467),command);// -- %s unbanall <channel name>
+		clif_displaymessage(fd, atcmd_output);
+		clif_displaymessage(fd, msg_txt(sd,1468));// - unbans everyone from <channel name>
+	}
+
+	//option setopt
+	if(can_create){
+		sprintf(atcmd_output, msg_txt(sd,1462),command);// -- %s setopt <channel name> <option name> <option value>
+		clif_displaymessage(fd, atcmd_output);
+		clif_displaymessage(fd, msg_txt(sd,1463));// - adds or removes <option name> with <option value> to <channel name> channel
+	}
+
 	sprintf(atcmd_output, msg_txt(sd,1404),command); // %s failed.
 	clif_displaymessage(fd, atcmd_output);
 }
 
-ACMD_FUNC(channel)
-{
-	struct raChSysCh *channel;
-	char key[RACHSYS_NAME_LENGTH], sub1[RACHSYS_NAME_LENGTH], sub2[RACHSYS_NAME_LENGTH], sub3[RACHSYS_NAME_LENGTH];
-	unsigned char k = 0;
-	DBMap* channel_db = clif_get_channel_db();
+ACMD_FUNC(channel) {
+	char key[CHAN_NAME_LENGTH], sub1[CHAN_NAME_LENGTH], sub2[CHAN_NAME_LENGTH], sub3[CHAN_NAME_LENGTH];
 	sub1[0] = sub2[0] = sub3[0] = '\0';
 
 	if( !message || !*message || sscanf(message, "%s %s %s %s", key, sub1, sub2, sub3) < 1 ) {
-		atcmd_channel_help(sd,command,( raChSys.allow_user_channel_creation || pc_has_permission(sd, PC_PERM_CHANNEL_ADMIN) ));
+		atcmd_channel_help(sd,command);
 		return 0;
 	}
 
-	if( strcmpi(key,"create") == 0 && ( raChSys.allow_user_channel_creation || pc_has_permission(sd, PC_PERM_CHANNEL_ADMIN) ) ) {
-		if( sub1[0] != '#' ) {
-			clif_displaymessage(fd, msg_txt(sd,1405));// Channel name must start with '#'.
-			return -1;
-		} else if ( strlen(sub1) < 3 || strlen(sub1) > RACHSYS_NAME_LENGTH ) {
-			sprintf(atcmd_output, msg_txt(sd,1406), RACHSYS_NAME_LENGTH);// Channel length must be between 3 and %d.
-			clif_displaymessage(fd, atcmd_output);
-			return -1;
-		} else if ( sub3[0] != '\0' ) {
-			clif_displaymessage(fd, msg_txt(sd,1408)); // Channel password may not contain spaces.
-			return -1;
-		}
-		if( strcmpi(sub1 + 1,raChSys.local_name) == 0 || strcmpi(sub1 + 1,raChSys.ally_name) == 0 || strdb_exists(channel_db, sub1 + 1) ) {
-			sprintf(atcmd_output, msg_txt(sd,1407), sub1);// Channel '%s' is not available.
-			clif_displaymessage(fd, atcmd_output);
+	if( strcmpi(key,"create") == 0 && ( Channel_Config.user_chenable || pc_has_permission(sd, PC_PERM_CHANNEL_ADMIN) ) ) {
+		if(sub3[0] != '\0'){
+			clif_displaymessage(fd, msg_txt(sd,1408)); // Channel password may not contain spaces
 			return -1;
 		}
-
-		CREATE( channel, struct raChSysCh, 1 );
-
-		clif_chsys_create(channel,sub1 + 1,sub2,0);
-
-		channel->owner = sd->status.char_id;
-		channel->type = raChSys_PRIVATE;
-
-		if( !( channel->opt & raChSys_OPT_ANNOUNCE_JOIN ) ) {
-			sprintf(atcmd_output, msg_txt(sd,1403),sub1); // You're now in the '%s' channel.
-			clif_displaymessage(fd, atcmd_output);
-		}
-
-		clif_chsys_join(channel,sd);
-
+		return channel_pccreate(sd,sub1,sub2);
+	} else if( strcmpi(key,"delete") == 0 && pc_has_permission(sd, PC_PERM_CHANNEL_ADMIN) ) {
+		return channel_pcdelete(sd,sub1);
 	} else if ( strcmpi(key,"list") == 0 ) {
-		if( sub1[0] != '\0' && strcmpi(sub1,"colors") == 0 ) {
-			char mout[40];
-			for( k = 0; k < raChSys.colors_count; k++ ) {
-				unsigned short msg_len = 1;
-				msg_len += sprintf(mout, "[ %s list colors ] : %s",command,raChSys.colors_name[k]);
-
-				WFIFOHEAD(fd,msg_len + 12);
-				WFIFOW(fd,0) = 0x2C1;
-				WFIFOW(fd,2) = msg_len + 12;
-				WFIFOL(fd,4) = 0;
-				WFIFOL(fd,8) = raChSys.colors[k];
-				safestrncpy((char*)WFIFOP(fd,12), mout, msg_len);
-				WFIFOSET(fd, msg_len + 12);
-			}
-		} else {
-			DBIterator *iter;
-			bool show_all = pc_has_permission(sd, PC_PERM_CHANNEL_ADMIN) ? true : false;
-			clif_displaymessage(fd, msg_txt(sd,1410)); // ---- Public Channels ----
-			if( raChSys.local ) {
-				sprintf(atcmd_output, msg_txt(sd,1409), raChSys.local_name, map[sd->bl.m].channel ? db_size(map[sd->bl.m].channel->users) : 0);// - #%s ( %d users )
-				clif_displaymessage(fd, atcmd_output);
-			}
-			if( raChSys.ally && sd->status.guild_id ) {
-				struct guild *g = sd->guild;
-				if( !g ) return -1;
-				sprintf(atcmd_output, msg_txt(sd,1409), raChSys.ally_name, db_size(((struct raChSysCh *)g->channel)->users));// - #%s ( %d users )
-				clif_displaymessage(fd, atcmd_output);
-			}
-			iter = db_iterator(channel_db);
-			for(channel = dbi_first(iter); dbi_exists(iter); channel = dbi_next(iter)) {
-				if( show_all || channel->type == raChSys_PUBLIC ) {
-					sprintf(atcmd_output, msg_txt(sd,1409), channel->name, db_size(channel->users));// - #%s (%d users)
-					clif_displaymessage(fd, atcmd_output);
-				}
-			}
-			dbi_destroy(iter);
-		}
+		return channel_display_list(sd,sub1);
 	} else if ( strcmpi(key,"setcolor") == 0 ) {
-
-		if( sub1[0] != '#' ) {
-			clif_displaymessage(fd, msg_txt(sd,1405));// Channel name must start with '#'.
-			return -1;
-		}
-
-		if( !(channel = strdb_get(channel_db, sub1 + 1)) ) {
-			sprintf(atcmd_output, msg_txt(sd,1407), sub1);// Channel '%s' is not available.
-			clif_displaymessage(fd, atcmd_output);
-			return -1;
-		}
-
-		if( channel->owner != sd->status.char_id && !pc_has_permission(sd, PC_PERM_CHANNEL_ADMIN) ) {
-			sprintf(atcmd_output, msg_txt(sd,1412), sub1);// You're not the owner of channel '%s'.
-			clif_displaymessage(fd, atcmd_output);
-			return -1;
-		}
-
-		for( k = 0; k < raChSys.colors_count; k++ ) {
-			if( strcmpi(sub2,raChSys.colors_name[k]) == 0 )
-				break;
-		}
-		if( k == raChSys.colors_count ) {
-			sprintf(atcmd_output, msg_txt(sd,1411), sub2);// Unknown color '%s'.
-			clif_displaymessage(fd, atcmd_output);
-			return -1;
-		}
-		channel->color = k;
-		sprintf(atcmd_output, msg_txt(sd,1413),sub1,raChSys.colors_name[k]);// '%s' channel color updated to '%s'.
-		clif_displaymessage(fd, atcmd_output);
-	} else if ( strcmpi(key,"leave") == 0 ) {
-
-		if( sub1[0] != '#' ) {
-			clif_displaymessage(fd, msg_txt(sd,1405));// Channel name must start with '#'.
-			return -1;
-		}
-
-		ARR_FIND(0, sd->channel_count, k, strcmpi(sub1+1,sd->channels[k]->name) == 0);
-		if( k == sd->channel_count ) {
-			sprintf(atcmd_output, msg_txt(sd,1425),sub1);// You're not part of the '%s' channel.
-			clif_displaymessage(fd, atcmd_output);
-			return -1;
-		}
-		clif_chsys_left(sd->channels[k],sd);
-		sprintf(atcmd_output, msg_txt(sd,1426),sub1); // You've left the '%s' channel.
-		clif_displaymessage(fd, atcmd_output);
+		return channel_pccolor(sd, sub1, sub2);
+	} else if ( strcmpi(key,"join") == 0 ) {
+		return channel_pcjoin(sd, sub1, sub2);
+	}else if ( strcmpi(key,"leave") == 0 ) {
+		return channel_pcleave(sd, sub1);
 	} else if ( strcmpi(key,"bindto") == 0 ) {
-
-		if( sub1[0] != '#' ) {
-			clif_displaymessage(fd, msg_txt(sd,1405));// Channel name must start with '#'.
-			return -1;
-		}
-
-		ARR_FIND(0, sd->channel_count, k, strcmpi(sub1+1,sd->channels[k]->name) == 0);
-		if( k == sd->channel_count ) {
-			sprintf(atcmd_output, msg_txt(sd,1425),sub1);// You're not part of the '%s' channel.
-			clif_displaymessage(fd, atcmd_output);
-			return -1;
-		}
-
-		sd->gcbind = sd->channels[k];
-		sprintf(atcmd_output, msg_txt(sd,1431),sub1); // Your global chat is now binded to the '%s' channel.
-		clif_displaymessage(fd, atcmd_output);
+		return channel_pcbind(sd, sub1);
 	} else if ( strcmpi(key,"unbind") == 0 ) {
-
-		if( sd->gcbind == NULL ) {
-			clif_displaymessage(fd, msg_txt(sd,1432));// Your global chat is not binded to any channel.
-			return -1;
-		}
-
-		sprintf(atcmd_output, msg_txt(sd,1433),sd->gcbind->name); // Your global chat is now unbinded from the '#%s' channel.
-		clif_displaymessage(fd, atcmd_output);
-
-		sd->gcbind = NULL;
+		return channel_pcunbind(sd);
+	} else if ( strcmpi(key,"ban") == 0 ) {
+		return channel_pcban(sd,sub1,map_nick2sd(sub2),0);
+	} else if ( strcmpi(key,"unban") == 0 ) {
+		return channel_pcban(sd,sub1,map_nick2sd(sub2),1);
+	} else if ( strcmpi(key,"unbanall") == 0 ) {
+		return channel_pcban(sd,sub1,NULL,2);
+	} else if ( strcmpi(key,"setopt") == 0 ) {
+		return channel_pcsetopt(sd,sub1,sub2,sub3);
 	} else {
-		atcmd_channel_help(sd,command,( raChSys.allow_user_channel_creation || pc_has_permission(sd, PC_PERM_CHANNEL_ADMIN) ));
+		atcmd_channel_help(sd,command);
 	}
 
 	return 0;
@@ -9038,17 +8915,9 @@ ACMD_FUNC(fontcolor)
 
 	if( !message || !*message ) {
 		char mout[40];
-		for( k = 0; k < raChSys.colors_count; k++ ) {
-			unsigned short msg_len = 1;
-			msg_len += sprintf(mout, "[ %s ] : %s",command,raChSys.colors_name[k]);
-
-			WFIFOHEAD(fd,msg_len + 12);
-			WFIFOW(fd,0) = 0x2C1;
-			WFIFOW(fd,2) = msg_len + 12;
-			WFIFOL(fd,4) = 0;
-			WFIFOL(fd,8) = raChSys.colors[k];
-			safestrncpy((char*)WFIFOP(fd,12), mout, msg_len);
-			WFIFOSET(fd, msg_len + 12);
+		for( k = 0; k < Channel_Config.colors_count; k++ ) {
+			sprintf(mout, "[ %s ] : %s",command,Channel_Config.colors_name[k]);
+			clif_colormes(sd,k,mout);
 		}
 		return -1;
 	}
@@ -9059,11 +8928,8 @@ ACMD_FUNC(fontcolor)
 		return 0;
 	}
 
-	for( k = 0; k < raChSys.colors_count; k++ ) {
-		if( strcmpi(message,raChSys.colors_name[k]) == 0 )
-			break;
-	}
-	if( k == raChSys.colors_count ) {
+	ARR_FIND(0,Channel_Config.colors_count,k,( strcmpi(message,Channel_Config.colors_name[k]) == 0 ));
+	if( k == Channel_Config.colors_count ) {
 		sprintf(atcmd_output, msg_txt(sd,1411), message);// Unknown color '%s'.
 		clif_displaymessage(fd, atcmd_output);
 		return -1;

+ 1168 - 0
src/map/channel.c

@@ -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);
+	}
+}

+ 94 - 0
src/map/channel.h

@@ -0,0 +1,94 @@
+// Copyright (c) rAthena Dev Teams - Licensed under GNU GPL
+// For more information, see LICENCE in the main folder
+
+#ifndef CHANNEL_H
+#define	CHANNEL_H
+
+#include "pc.h"
+
+#ifdef	__cplusplus
+extern "C" {
+#endif
+
+#define CHAN_NAME_LENGTH 20
+#define CHAN_MSG_LENGTH 150
+
+enum Channel_Opt {
+	CHAN_OPT_BASE		= 0,
+	CHAN_OPT_ANNOUNCE_JOIN	= 1,	//display message when join or leave
+	CHAN_OPT_MSG_DELAY	= 2,
+};
+
+enum Channel_Type {
+	CHAN_TYPE_PUBLIC	= 0,	//config file made
+	CHAN_TYPE_PRIVATE	= 1,	//user made
+	CHAN_TYPE_MAP		= 2,	//made by map
+	CHAN_TYPE_ALLY		= 3,	//guild
+};
+
+struct {
+	unsigned long *colors;		//color avail int list
+	char **colors_name;		//colors avail name list
+	unsigned char colors_count;	//color avail count
+	unsigned char map_chcolor, ally_chcolor; //msg color for map, ally
+	bool map_enable, ally_enable, user_chenable; //map, ally, users channels enable ?
+	bool map_autojoin, ally_autojoin;	//do user auto join in mapchange, guildjoin ?
+	char map_chname[CHAN_NAME_LENGTH], ally_chname[CHAN_NAME_LENGTH]; //channel name for map and ally
+	bool closing;			//server is closing
+} Channel_Config;
+
+struct Channel {
+	char name[CHAN_NAME_LENGTH];	//channel name
+	char pass[CHAN_NAME_LENGTH];	//channel password
+	unsigned char color;		//msg color
+	DBMap *users;			//users in channel charid list
+	DBMap *banned;			//users banned from channel charid list
+	enum Channel_Opt opt;		//flag for some treatement
+	enum Channel_Type type;		//type of channel
+	unsigned int owner;		//if chan_type private charid of creator
+	uint16 m;			//if chan_type map guild_id
+	int gid;			//if chan_type guild type guild_id
+	unsigned char msg_delay;	//delay in second if opt_msg_delay
+};
+
+DBMap* channel_get_db(void);
+
+struct Channel* channel_create(char *name, char *pass, unsigned char color, enum Channel_Type chantype, int val);
+int channel_delete(struct Channel *channel);
+
+int channel_join(struct Channel *channel, struct map_session_data *sd);
+int channel_mjoin(struct map_session_data *sd);
+int channel_gjoin(struct map_session_data *sd, int flag);
+int channel_ajoin(struct guild *g);
+int channel_clean(struct Channel *channel, struct map_session_data *sd, int flag);
+int channel_pcquit(struct map_session_data *sd, int type);
+
+int channel_send(struct Channel *channel, struct map_session_data *sd, const char *msg);
+void channel_read_config(void);
+
+int channel_chk(char *name, char *pass, int type);
+struct Channel* channel_name2channel(char *chname, struct map_session_data *sd, int flag);
+int channel_haspc(struct Channel *channel,struct map_session_data *sd);
+int channel_haspcbanned(struct Channel *channel,struct map_session_data *sd);
+int channel_pc_haschan(struct map_session_data *sd, struct Channel *channel);
+int channel_display_list(struct map_session_data *sd, char *option);
+
+int channel_pccreate(struct map_session_data *sd, char *chname, char *pass);
+int channel_pcdelete(struct map_session_data *sd, char *chname);
+int channel_pcjoin(struct map_session_data *sd, char *chname, char *pass);
+int channel_pcleave(struct map_session_data *sd, char *chname);
+int channel_pccolor(struct map_session_data *sd, char *chname, char *color);
+int channel_pcbind(struct map_session_data *sd, char *chname);
+int channel_pcunbind(struct map_session_data *sd);
+int channel_pcban(struct map_session_data *sd, char *chname, struct map_session_data *tsd, int flag);
+int channel_pcsetopt(struct map_session_data *sd, char *chname, const char *option, const char *val);
+
+int do_init_channel(void);
+void do_final_channel(void);
+
+#ifdef	__cplusplus
+}
+#endif
+
+#endif	/* CHANNEL_H */
+

+ 23 - 332
src/map/clif.c

@@ -44,6 +44,7 @@
 #include "mail.h"
 #include "quest.h"
 #include "cashshop.h"
+#include "channel.h"
 
 #include <stdio.h>
 #include <stdlib.h>
@@ -53,8 +54,6 @@
 
 /* for clif_clearunit_delayed */
 static struct eri *delay_clearunit_ers;
-static DBMap* channel_db; // channels
-DBMap* clif_get_channel_db(void){ return channel_db; }
 
 //#define DUMP_UNKNOWN_PACKET
 //#define DUMP_INVALID_PACKET
@@ -5490,52 +5489,11 @@ void clif_broadcast2(struct block_list* bl, const char* mes, int len, unsigned l
 		aFree(buf);
 }
 
-/*==========================================
- * Channel System
- *------------------------------------------*/
-void clif_chsys_create(struct raChSysCh *channel, char *name, char *pass, unsigned char color) {
-	channel->users = idb_alloc(DB_OPT_BASE);
-	if( name )
-		safestrncpy(channel->name, name, RACHSYS_NAME_LENGTH);
-	channel->color = color;
-	if( !pass )
-		channel->pass[0] = '\0';
-	else
-		safestrncpy(channel->pass, pass, RACHSYS_NAME_LENGTH);
-
-	channel->opt = raChSys_OPT_BASE;
-
-	if( channel->type != raChSys_MAP && channel->type != raChSys_ALLY )
-		strdb_put(channel_db, channel->name, channel);
-}
-
-void clif_chsys_join(struct raChSysCh *channel, struct map_session_data *sd) {
-	RECREATE(sd->channels, struct raChSysCh *, ++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 & raChSys_OPT_ANNOUNCE_JOIN ) {
-		char message[60];
-		sprintf(message, "#%s '%s' joined",channel->name,sd->status.name);
-		clif_chsys_msg(channel,sd,message);
-	}
-
-	/* someone is cheating, we kindly disconnect the bastard */
-	if( sd->channel_count > 200 ) {
-		set_eof(sd->fd);
-	}
-}
-
-void clif_chsys_send(struct raChSysCh *channel, struct map_session_data *sd, char *msg) {
-	char message[150];
-	snprintf(message, 150, "[ #%s ] %s : %s",channel->name,sd->status.name, msg);
-	clif_chsys_msg(channel,sd,message);
-}
-
-void clif_chsys_msg(struct raChSysCh *channel, struct map_session_data *sd, char *msg) {
-	DBIterator *iter = db_iterator(channel->users);
+/*
+ * Display *msg from *sd to all *users in channel
+ */
+void clif_channel_msg(struct Channel *channel, struct map_session_data *sd, char *msg) {
+	DBIterator *iter;
 	struct map_session_data *user;
 	unsigned short msg_len = strlen(msg) + 1;
 
@@ -5543,9 +5501,10 @@ void clif_chsys_msg(struct raChSysCh *channel, struct map_session_data *sd, char
 	WFIFOW(sd->fd,0) = 0x2C1;
 	WFIFOW(sd->fd,2) = msg_len + 12;
 	WFIFOL(sd->fd,4) = 0;
-	WFIFOL(sd->fd,8) = raChSys.colors[channel->color];
+	WFIFOL(sd->fd,8) = Channel_Config.colors[channel->color];
 	safestrncpy((char*)WFIFOP(sd->fd,12), msg, msg_len);
 
+	iter = db_iterator(channel->users);
 	for( user = dbi_first(iter); dbi_exists(iter); user = dbi_next(iter) ) {
 		if( user->fd == sd->fd )
 			continue;
@@ -5553,231 +5512,11 @@ void clif_chsys_msg(struct raChSysCh *channel, struct map_session_data *sd, char
 		memcpy(WFIFOP(user->fd,0), WFIFOP(sd->fd,0), msg_len + 12);
 		WFIFOSET(user->fd, msg_len + 12);
 	}
-
-	WFIFOSET(sd->fd, msg_len + 12);
-
 	dbi_destroy(iter);
-}
-
-void clif_chsys_mjoin(struct map_session_data *sd) {
-	if( !map[sd->bl.m].channel ) {
-		CREATE(map[sd->bl.m].channel, struct raChSysCh , 1);
-		safestrncpy(map[sd->bl.m].channel->name, raChSys.local_name, RACHSYS_NAME_LENGTH);
-		map[sd->bl.m].channel->type = raChSys_MAP;
-		map[sd->bl.m].channel->m = sd->bl.m;
-
-		clif_chsys_create(map[sd->bl.m].channel,NULL,NULL,raChSys.local_color);
-	}
-	clif_chsys_join(map[sd->bl.m].channel,sd);
-
-	if( !( map[sd->bl.m].channel->opt & raChSys_OPT_ANNOUNCE_JOIN ) ) {
-		char mout[60];
-		sprintf(mout, msg_txt(sd,1435),raChSys.local_name,map[sd->bl.m].name); // You're now in the '#%s' channel for '%s'.
-		clif_disp_onlyself(sd, mout, strlen(mout));
-	}
-}
-
-void clif_chsys_left(struct raChSysCh *channel, struct map_session_data *sd) {
-	unsigned char i;
-	idb_remove(channel->users,sd->status.char_id);
-
-	if( channel == sd->gcbind )
-		sd->gcbind = NULL;
-
-	if( !db_size(channel->users) && channel->type == raChSys_PRIVATE ) {
-		clif_chsys_delete(channel);
-	} else if( !raChSys.closing && (channel->opt & raChSys_OPT_ANNOUNCE_JOIN) ) {
-		char message[60];
-		sprintf(message, "#%s '%s' left",channel->name,sd->status.name);
-		clif_chsys_msg(channel,sd,message);
-	}
-
-	ARR_FIND(0, sd->channel_count, i, sd->channels[i] == channel);
-	if( i < sd->channel_count ) {
-		unsigned char cursor = 0;
-		sd->channels[i] = NULL;
-		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;
-		}
-	}
-
-}
-
-void clif_chsys_delete(struct raChSysCh *channel) {
-	if( db_size(channel->users) && !raChSys.closing ) {
-		struct map_session_data *sd;
-		unsigned char i;
-		DBIterator *iter = db_iterator(channel->users);
-		for( sd = dbi_first(iter); dbi_exists(iter); sd = dbi_next(iter) ) { //for all users
-			ARR_FIND(0, sd->channel_count, i, sd->channels[i] == channel); //found cur chan
-			if( i < sd->channel_count ) {
-				unsigned char cursor = 0;
-				sd->channels[i] = NULL;
-				for( i = 0; i < sd->channel_count; i++ ) { //move down links
-					if( sd->channels[i] == NULL )
-						continue;
-					if( cursor != i )
-						sd->channels[cursor] = sd->channels[i];
-					cursor++;
-				}
-				if ( !(sd->channel_count = cursor) ) { //news chan count = total
-					aFree(sd->channels);
-					sd->channels = NULL;
-				}
-			}
-		}
-		dbi_destroy(iter);
-	}
-	db_destroy(channel->users);
-	if( channel->m ) {
-		map[channel->m].channel = NULL;
-		aFree(channel);
-	} else if ( channel->type == raChSys_ALLY )
-		aFree(channel);
-	else if( !raChSys.closing )
-		strdb_remove(channel_db, channel->name);
-}
-
-void clif_read_channels_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 *local_name, *ally_name,
-					*local_color, *ally_color;
-		int ally_enabled = 0, local_enabled = 0,
-			local_autojoin = 0, ally_autojoin = 0,
-			allow_user_channel_creation = 0;
-
-		if( !config_setting_lookup_string(settings, "map_local_channel_name", &local_name) )
-			local_name = "map";
-		safestrncpy(raChSys.local_name, local_name, RACHSYS_NAME_LENGTH);
-
-		if( !config_setting_lookup_string(settings, "ally_channel_name", &ally_name) )
-			ally_name = "ally";
-		safestrncpy(raChSys.ally_name, ally_name, RACHSYS_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 )
-			raChSys.local = true;
-		if( ally_enabled )
-			raChSys.ally = 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 )
-			raChSys.local_autojoin = true;
-		if( ally_autojoin )
-			raChSys.ally_autojoin = true;
 
-		config_setting_lookup_bool(settings, "allow_user_channel_creation", &allow_user_channel_creation);
-
-		if( allow_user_channel_creation )
-			raChSys.allow_user_channel_creation = true;
-
-		if( (colors = config_setting_get_member(settings, "colors")) != NULL ) {
-			int color_count = config_setting_length(colors);
-			CREATE( raChSys.colors, unsigned long, color_count );
-			CREATE( raChSys.colors_name, char *, color_count );
-			for(i = 0; i < color_count; i++) {
-				config_setting_t *color = config_setting_get_elem(colors, i);
-
-				CREATE( raChSys.colors_name[i], char, RACHSYS_NAME_LENGTH );
-
-				safestrncpy(raChSys.colors_name[i], config_setting_name(color), RACHSYS_NAME_LENGTH);
-
-				raChSys.colors[i] = strtoul(config_setting_get_string_elem(colors,i),NULL,0);
-				raChSys.colors[i] = (raChSys.colors[i] & 0x0000FF) << 16 | (raChSys.colors[i] & 0x00FF00) | (raChSys.colors[i] & 0xFF0000) >> 16;//RGB to BGR
-			}
-			raChSys.colors_count = color_count;
-		}
-
-		config_setting_lookup_string(settings, "map_local_channel_color", &local_color);
-
-		for (k = 0; k < raChSys.colors_count; k++) {
-			if( strcmpi(raChSys.colors_name[k],local_color) == 0 )
-				break;
-		}
-
-		if( k < raChSys.colors_count ) {
-			raChSys.local_color = k;
-		} else {
-			ShowError("channels.conf: unknown color '%s' for channel 'map_local_channel_color', disabling '#%s'...\n",local_color,local_name);
-			raChSys.local = false;
-		}
-
-		config_setting_lookup_string(settings, "ally_channel_color", &ally_color);
-
-		for (k = 0; k < raChSys.colors_count; k++) {
-			if( strcmpi(raChSys.colors_name[k],ally_color) == 0 )
-				break;
-		}
-
-		if( k < raChSys.colors_count ) {
-			raChSys.ally_color = k;
-		} else {
-			ShowError("channels.conf: unknown color '%s' for channel 'ally_channel_color', disabling '#%s'...\n",local_color,ally_name);
-			raChSys.ally = 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 *name = config_setting_name(channel);
-				const char *color = config_setting_get_string_elem(channels,i);
-				struct raChSysCh *chd;
-
-				for (k = 0; k < raChSys.colors_count; k++) {
-					if( strcmpi(raChSys.colors_name[k],color) == 0 )
-						break;
-				}
-				if( k == raChSys.colors_count ) {
-					ShowError("channels.conf: unknown color '%s' for channel '%s', skipping channel...\n",color,name);
-					continue;
-				}
-				if( strcmpi(name,raChSys.local_name) == 0 || strcmpi(name,raChSys.ally_name) == 0 || strdb_exists(channel_db, name) ) {
-					ShowError("channels.conf: duplicate channel '%s', skipping channel...\n",name);
-					continue;
-
-				}
-				CREATE( chd, struct raChSysCh, 1 );
-
-				safestrncpy(chd->name, name, RACHSYS_NAME_LENGTH);
-				chd->type = raChSys_PUBLIC;
-
-				clif_chsys_create(chd,NULL,NULL,k);
-			}
-		}
-
-		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);
-	}
+	WFIFOSET(sd->fd, msg_len + 12);
 }
 
-
 /// Displays heal effect (ZC_RECOVERY).
 /// 013d <var id>.W <amount>.W
 /// var id:
@@ -9564,8 +9303,8 @@ void clif_parse_LoadEndAck(int fd,struct map_session_data *sd)
 		status_calc_pc(sd, false); // Some conditions are map-dependent so we must recalculate
 		sd->state.changemap = false;
 
-		if( raChSys.local && raChSys.local_autojoin && !map[sd->bl.m].flag.chsysnolocalaj ) {
-			clif_chsys_mjoin(sd);
+		if( Channel_Config.map_enable && Channel_Config.map_autojoin && !map[sd->bl.m].flag.chmautojoin) {
+			channel_mjoin(sd); //join new map
 		}
 	}
 
@@ -9867,23 +9606,12 @@ void clif_parse_GlobalMessage(int fd, struct map_session_data* sd)
 	}
 
 	if( sd->gcbind ) {
-		clif_chsys_send(sd->gcbind,sd,message);
+		channel_send(sd->gcbind,sd,message);
 		return;
 	} else if ( sd->fontcolor && !sd->chatID ) {
 		char mout[200];
-		unsigned char mylen = 1;
-
-		mylen += snprintf(mout, 200, "%s : %s",sd->fakename[0]?sd->fakename:sd->status.name,message);
-
-		WFIFOHEAD(fd,mylen + 12);
-		WFIFOW(fd,0) = 0x2C1;
-		WFIFOW(fd,2) = mylen + 12;
-		WFIFOL(fd,4) = sd->bl.id;
-		WFIFOL(fd,8) = raChSys.colors[sd->fontcolor - 1];
-		safestrncpy((char*)WFIFOP(fd,12), mout, mylen);
-		clif_send(WFIFOP(fd,0), WFIFOW(fd,2), &sd->bl, AREA_WOS);
-		WFIFOL(fd,4) = -sd->bl.id;
-		WFIFOSET(fd, mylen + 12);
+		snprintf(mout, 200, "%s : %s",sd->fakename[0]?sd->fakename:sd->status.name,message);
+		clif_colormes(sd,sd->fontcolor-1,mout);
 		return;
 	}
 
@@ -10256,30 +9984,18 @@ void clif_parse_WisMessage(int fd, struct map_session_data* sd)
 			return;
 		}
 	} else if( target[0] == '#' ) {
-		struct raChSysCh *channel = NULL;
+		struct Channel *channel = NULL;
 		char* chname = target;
 
-		chname++;
-
-		if( raChSys.local && strcmpi(chname, raChSys.local_name) == 0 ) {
-			if( !map[sd->bl.m].channel ) {
-				clif_chsys_mjoin(sd);
+		channel = channel_name2channel(chname,sd,3);
+		if(channel){
+			if(channel_pc_haschan(sd,channel)>=0){ //we are in the chan
+				channel_send(channel,sd,message);
 			}
-			channel = map[sd->bl.m].channel;
-		} else if( raChSys.ally && sd->status.guild_id && strcmpi(chname, raChSys.ally_name) == 0 ) {
-			struct guild *g = sd->guild;
-			if( !g ) return;
-			channel = (struct raChSysCh *)g->channel;
-		}
-		if( channel || (channel = strdb_get(channel_db,chname)) ) {
-			unsigned char k;
-			ARR_FIND(0, sd->channel_count, k, sd->channels[k] == channel);
-			if( k < sd->channel_count ) {
-				clif_chsys_send(channel,sd,message);
-			} else if( channel->pass[0] == '\0' ) {
-				clif_chsys_join(channel,sd);
-				clif_chsys_send(channel,sd,message);
-			} else {
+			else if( channel->pass[0] == '\0') { //no pass needed
+				if (channel_join(channel,sd)==0) channel_send(channel,sd,message); //join success
+			}
+			else {
 				clif_displaymessage(fd, msg_txt(sd,1402)); //You're not in that channel, type '@join <#channel_name>'
 			}
 			return;
@@ -17374,34 +17090,9 @@ int do_init_clif(void) {
 	add_timer_func_list(clif_delayquit, "clif_delayquit");
 
 	delay_clearunit_ers = ers_new(sizeof(struct block_list),"clif.c::delay_clearunit_ers",ERS_OPT_CLEAR);
-
-	channel_db = stridb_alloc(DB_OPT_DUP_KEY|DB_OPT_RELEASE_DATA, RACHSYS_NAME_LENGTH);
-	raChSys.ally = raChSys.local = raChSys.ally_autojoin = raChSys.local_autojoin = false;
-	clif_read_channels_config();
-
 	return 0;
 }
 
 void do_final_clif(void) {
-	DBIterator *iter = db_iterator(channel_db);
-	struct raChSysCh *channel;
-	unsigned char i;
-
-	for( channel = dbi_first(iter); dbi_exists(iter); channel = dbi_next(iter) ) {
-		clif_chsys_delete(channel);
-	}
-
-	dbi_destroy(iter);
-
-	for(i = 0; i < raChSys.colors_count; i++) {
-		aFree(raChSys.colors_name[i]);
-	}
-
-	if( raChSys.colors_count ) {
-		aFree(raChSys.colors_name);
-		aFree(raChSys.colors);
-	}
-
-	db_destroy(channel_db);
 	ers_destroy(delay_clearunit_ers);
 }

+ 3 - 49
src/map/clif.h

@@ -6,7 +6,9 @@
 
 #include "../common/cbasetypes.h"
 #include "../common/db.h" //dbmap
+
 //#include "../common/mmo.h"
+struct Channel;
 struct item;
 struct storage_data;
 struct guild_storage;
@@ -775,55 +777,7 @@ enum clif_colors {
 unsigned long color_table[COLOR_MAX];
 int clif_colormes(struct map_session_data * sd, enum clif_colors color, const char* msg);
 
-/**
- * Channel System
- **/
-#define RACHSYS_NAME_LENGTH 20
-
-enum raChSysChOpt {
-	raChSys_OPT_BASE				= 0,
-	raChSys_OPT_ANNOUNCE_JOIN	= 1,
-};
-
-enum raChSysChType {
-	raChSys_PUBLIC	= 0,
-	raChSys_PRIVATE	= 1,
-	raChSys_MAP		= 2,
-	raChSys_ALLY		= 3,
-};
-
-struct {
-	unsigned long *colors;
-	char **colors_name;
-	unsigned char colors_count;
-	bool local, ally;
-	bool local_autojoin, ally_autojoin;
-	char local_name[RACHSYS_NAME_LENGTH], ally_name[RACHSYS_NAME_LENGTH];
-	unsigned char local_color, ally_color;
-	bool closing;
-	bool allow_user_channel_creation;
-} raChSys;
-
-struct raChSysCh {
-	char name[RACHSYS_NAME_LENGTH];
-	char pass[RACHSYS_NAME_LENGTH];
-	unsigned char color;
-	DBMap *users;
-	unsigned int opt;
-	unsigned int owner;
-	enum raChSysChType type;
-	uint16 m;
-};
-
-struct DBMap* clif_get_channel_db(void);
-void clif_chsys_create(struct raChSysCh *channel, char *name, char *pass, unsigned char color);
-void clif_chsys_msg(struct raChSysCh *channel, struct map_session_data *sd, char *msg);
-void clif_chsys_send(struct raChSysCh *channel, struct map_session_data *sd, char *msg);
-void clif_chsys_join(struct raChSysCh *channel, struct map_session_data *sd);
-void clif_chsys_left(struct raChSysCh *channel, struct map_session_data *sd);
-void clif_chsys_delete(struct raChSysCh *channel);
-void clif_chsys_mjoin(struct map_session_data *sd);
-void clif_read_channels_config(void);
+void clif_channel_msg(struct Channel *channel, struct map_session_data *sd, char *msg);
 
 #define clif_menuskill_clear(sd) (sd)->menuskill_id = (sd)->menuskill_val = (sd)->menuskill_val2 = 0;
 

+ 33 - 74
src/map/guild.c

@@ -21,6 +21,7 @@
 #include "mob.h"
 #include "intif.h"
 #include "clif.h"
+#include "channel.h"
 #include "skill.h"
 #include "log.h"
 
@@ -499,7 +500,7 @@ int guild_recv_info(struct guild *sg)
 	DBData data;
 	struct map_session_data *sd;
 	bool guild_new = false;
-	void *aChSysSave = NULL;
+	struct Channel *channel;
 
 	nullpo_ret(sg);
 
@@ -507,40 +508,6 @@ int guild_recv_info(struct guild *sg)
 		guild_new = true;
 		g=(struct guild *)aCalloc(1,sizeof(struct guild));
 		idb_put(guild_db,sg->guild_id,g);
-		if( raChSys.ally ) {
-			struct raChSysCh *channel;
-
-			CREATE(channel, struct raChSysCh , 1);
-			safestrncpy(channel->name, raChSys.ally_name, RACHSYS_NAME_LENGTH);
-			channel->type = raChSys_ALLY;
-
-			clif_chsys_create(channel,NULL,NULL,raChSys.ally_color);
-			if( raChSys.ally_autojoin ) {
-				struct s_mapiterator* iter = mapit_getallusers();
-
-				for( sd = (TBL_PC*)mapit_first(iter); mapit_exists(iter); sd = (TBL_PC*)mapit_next(iter) ) {
-					if( sd->status.guild_id ) {
-						if( sd->status.guild_id == sg->guild_id ) {
-							clif_chsys_join(channel,sd);
-							sd->guild = g;
-							continue;
-						}
-
-						for (i = 0; i < MAX_GUILDALLIANCE; i++) {
-							if( sg->alliance[i].guild_id == sd->status.guild_id ) {
-								clif_chsys_join(channel,sd);
-								break;
-							}
-						}
-					}
-				}
-
-				mapit_free(iter);
-			}
-
-			aChSysSave = (void*)channel;
-
-		}
 		before=*sg;
 		//Perform the check on the user because the first load
 		guild_check_member(sg);
@@ -558,13 +525,9 @@ int guild_recv_info(struct guild *sg)
 		}
 	} else {
 		before=*g;
-		if( g->channel )
-			aChSysSave = g->channel;
 	}
 	memcpy(g,sg,sizeof(struct guild));
 
-	g->channel = aChSysSave;
-
 	if(g->max_member > MAX_GUILD) {
 		ShowError("guild_recv_info: Received guild with %d members, but MAX_GUILD is only %d. Extra guild-members have been lost!\n", g->max_member, MAX_GUILD);
 		g->max_member = MAX_GUILD;
@@ -581,11 +544,22 @@ int guild_recv_info(struct guild *sg)
 			bm++;
 	}
 
+	if(Channel_Config.ally_enable){
+		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);
+		}
+	}
 	for (i = 0; i < g->max_member; i++) { //Transmission of information at all members
 		sd = g->member[i].sd;
 		if( sd==NULL )
 			continue;
 		sd->guild = g;
+		if(Channel_Config.ally_autojoin ) {
+			channel_gjoin(sd,3); //make all member join guildchan+allieschan
+		}
 
 		if (before.guild_lv != g->guild_lv || bm != m ||
 			before.max_member != g->max_member) {
@@ -755,16 +729,8 @@ void guild_member_joined(struct map_session_data *sd)
 		g->member[i].sd = sd;
 		sd->guild = g;
 
-		if( raChSys.ally && raChSys.ally_autojoin ) {
-			struct guild* sg = NULL;
-			clif_chsys_join((struct raChSysCh*)g->channel,sd);
-
-			for (i = 0; i < MAX_GUILDALLIANCE; i++) {
-				if( g->alliance[i].guild_id && (sg = guild_search(g->alliance[i].guild_id) ) ) {
-					clif_chsys_join((struct raChSysCh*)sg->channel,sd);
-					break;
-				}
-			}
+		if( Channel_Config.ally_enable && Channel_Config.ally_autojoin ) {
+			channel_gjoin(sd,3);
 		}
 	}
 }
@@ -909,13 +875,7 @@ int guild_member_withdraw(int guild_id, int account_id, int char_id, int flag, c
 		if (sd->state.storage_flag == 2) //Close the guild storage.
 			storage_guild_storageclose(sd);
 		guild_send_dot_remove(sd);
-		if( raChSys.ally ) {
-			uint8 ch_count = sd->channel_count;
-			for (i = 0; i < ch_count; i++) {
-				if( sd->channels[i] && sd->channels[i]->type == raChSys_ALLY )
-					clif_chsys_left(sd->channels[i],sd);
-			}
-		}
+		channel_pcquit(sd,3); //leave guild and ally chan
 		sd->status.guild_id = 0;
 		sd->guild = NULL;
 		sd->guild_emblem_id = 0;
@@ -1578,9 +1538,9 @@ int guild_opposition(struct map_session_data *sd,struct map_session_data *tsd)
 		return 0;
 	}
 
-    for (i = 0; i < MAX_GUILDALLIANCE; i++) { // checking relations
+	for (i = 0; i < MAX_GUILDALLIANCE; i++) { // checking relations
 		if(g->alliance[i].guild_id==tsd->status.guild_id){
-            if (g->alliance[i].opposition == 1) { // check if not already hostile
+			if (g->alliance[i].opposition == 1) { // check if not already hostile
 				clif_guild_oppositionack(sd,2);
 				return 0;
 			}
@@ -1592,7 +1552,7 @@ int guild_opposition(struct map_session_data *sd,struct map_session_data *tsd)
 		}
 	}
 
-    // inform other serv
+	// inform other serv
 	intif_guild_alliance( sd->status.guild_id,tsd->status.guild_id,
 			sd->status.account_id,tsd->status.account_id,1 );
 	return 0;
@@ -1624,14 +1584,14 @@ int guild_allianceack(int guild_id1,int guild_id2,int account_id1,int account_id
 		sd[0]->guild_alliance_account=0;
 	}
 
-    if (flag & 0x70) { // failure
+	if (flag & 0x70) { // failure
 		for(i=0;i<2-(flag&1);i++)
 			if( sd[i]!=NULL )
 				clif_guild_allianceack(sd[i],((flag>>4)==i+1)?3:4);
 		return 0;
 	}
 
-    if (!(flag & 0x08)) { // new relationship
+	if (!(flag & 0x08)) { // new relationship
 		for(i=0;i<2-(flag&1);i++)
 		{
 			if(g[i]!=NULL)
@@ -1645,35 +1605,38 @@ int guild_allianceack(int guild_id1,int guild_id2,int account_id1,int account_id
 				}
 			}
 		}
-    } else { // remove relationship
+	} else { // remove relationship
 		for(i=0;i<2-(flag&1);i++)
 		{
 			if(g[i]!=NULL)
 			{
+				for(j=0;j<g[i]->max_member;j++) channel_pcquit(g[i]->member[j].sd,2); //leave all alliance chan
 				ARR_FIND( 0, MAX_GUILDALLIANCE, j, g[i]->alliance[j].guild_id == guild_id[1-i] && g[i]->alliance[j].opposition == (flag&1) );
 				if( j < MAX_GUILDALLIANCE )
 					g[i]->alliance[j].guild_id = 0;
 			}
-            if (sd[i] != NULL) // notify players
+		if (sd[i] != NULL) // notify players
 				clif_guild_delalliance(sd[i],guild_id[1-i],(flag&1));
 		}
 	}
 
-    if ((flag & 0x0f) == 0) { // alliance notification
+	if ((flag & 0x0f) == 0) { // alliance notification
 		if( sd[1]!=NULL )
 			clif_guild_allianceack(sd[1],2);
-    } else if ((flag & 0x0f) == 1) { // enemy notification
+	} else if ((flag & 0x0f) == 1) { // enemy notification
 		if( sd[0]!=NULL )
 			clif_guild_oppositionack(sd[0],0);
 	}
 
 
-    for (i = 0; i < 2 - (flag & 1); i++) { // Retransmission of the relationship list to all members
+	for (i = 0; i < 2 - (flag & 1); i++) { // Retransmission of the relationship list to all members
 		struct map_session_data *sd;
 		if(g[i]!=NULL)
 			for(j=0;j<g[i]->max_member;j++)
-				if((sd=g[i]->member[j].sd)!=NULL)
+				if((sd=g[i]->member[j].sd)!=NULL){
 					clif_guild_allianceinfo(sd);
+					channel_gjoin(sd,2); //join ally join
+				}
 	}
 	return 0;
 }
@@ -1751,10 +1714,8 @@ int guild_broken(int guild_id,int flag)
 	guild_db->foreach(guild_db,guild_broken_sub,guild_id);
 	castle_db->foreach(castle_db,castle_guild_broken_sub,guild_id);
 	guild_storage_delete(guild_id);
-	if( raChSys.ally ) {
-		if( g->channel != NULL ) {
-			clif_chsys_delete(( struct raChSysCh * )g->channel);
-		}
+	if( Channel_Config.ally_enable ) {
+		channel_delete(g->channel);
 	}
 	idb_remove(guild_db,guild_id);
 	return 0;
@@ -2203,10 +2164,8 @@ void do_final_guild(void) {
 	struct guild *g;
 
 	for( g = dbi_first(iter); dbi_exists(iter); g = dbi_next(iter) ) {
-		if( g->channel != NULL )
-			clif_chsys_delete((struct raChSysCh *)g->channel);
+		channel_delete(g->channel);
 	}
-
 	dbi_destroy(iter);
 
 	db_destroy(guild_db);

+ 6 - 3
src/map/map.c

@@ -48,6 +48,8 @@
 #include "log.h"
 #include "mail.h"
 #include "cashshop.h"
+#include "channel.h"
+
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
@@ -3549,7 +3551,7 @@ void do_final(void)
 	struct s_mapiterator* iter;
 
 	ShowStatus("Terminating...\n");
-	raChSys.closing = true;
+	Channel_Config.closing = true;
 
 	//Ladies and babies first.
 	iter = mapit_getallusers();
@@ -3565,8 +3567,7 @@ void do_final(void)
 		ShowStatus("Cleaning up maps [%d/%d]: %s..."CL_CLL"\r", i+1, map_num, map[i].name);
 		if (map[i].m >= 0) {
 			map_foreachinmap(cleanup_sub, i, BL_ALL);
-			if( map[i].channel != NULL )
-				clif_chsys_delete((struct raChSysCh *)map[i].channel);
+			channel_delete(map[i].channel);
 		}
 	}
 	ShowStatus("Cleaned up %d maps."CL_CLL"\n", map_num);
@@ -3597,6 +3598,7 @@ void do_final(void)
 	do_final_duel();
 	do_final_elemental();
 	do_final_cashshop();
+	do_final_channel(); //should be called after final guild
 
 	map_db->destroy(map_db, map_db_final);
 
@@ -3881,6 +3883,7 @@ int do_init(int argc, char *argv[])
 	do_init_atcommand();
 	do_init_battle();
 	do_init_instance();
+	do_init_channel();
 	do_init_chrif();
 	do_init_clif();
 	do_init_script();

+ 3 - 3
src/map/map.h

@@ -17,7 +17,7 @@
 
 struct npc_data;
 struct item_data;
-struct raChSysCh;
+struct Channel;
 
 enum E_MAPSERVER_ST {
 	MAPSERVER_ST_RUNNING = CORE_ST_LAST,
@@ -567,7 +567,7 @@ struct map_data {
 		unsigned guildlock :1;
 		unsigned src4instance : 1; // To flag this map when it's used as a src map for instances
 		unsigned reset :1; // [Daegaladh]
-		unsigned chsysnolocalaj : 1;
+		unsigned chmautojoin : 1; //prevent to auto join map channel
 	} flag;
 	struct point save;
 	struct npc_data *npc[MAX_NPC_PER_MAP];
@@ -595,7 +595,7 @@ struct map_data {
 	int instance_src_map;
 
 	/* rAthena Local Chat */
-	struct raChSysCh *channel;
+	struct Channel *channel;
 };
 
 /// Stores information about a remote map (for multi-mapserver setups).

+ 3 - 3
src/map/npc.c

@@ -2150,7 +2150,7 @@ static const char* npc_parse_warp(char* w1, char* w2, char* w3, char* w4, const
 		return strchr(start,'\n');// skip and continue
 	}
 
-	if( m != -1 && ( x < 0 || x >= map[m].xs || y < 0 || y >= map[m].ys ) ) { 
+	if( m != -1 && ( x < 0 || x >= map[m].xs || y < 0 || y >= map[m].ys ) ) {
 		ShowWarning("npc_parse_warp: coordinates %d/%d are out of bounds in map %s(%dx%d), in file '%s', line '%d'\n", x, y, map[m].name, map[m].xs, map[m].ys,filepath,strline(buffer,start-buffer));
 	}
 
@@ -2221,7 +2221,7 @@ static const char* npc_parse_shop(char* w1, char* w2, char* w3, char* w4, const
 
 	if( m != -1 && ( x < 0 || x >= map[m].xs || y < 0 || y >= map[m].ys ) ) {
 		ShowWarning("npc_parse_shop: coordinates %d/%d are out of bounds in map %s(%dx%d), in file '%s', line '%d'\n", x, y, map[m].name, map[m].xs, map[m].ys,filepath,strline(buffer,start-buffer));
-	} 
+	}
 
 	if( !strcasecmp(w2,"cashshop") )
 		type = CASHSHOP;
@@ -3441,7 +3441,7 @@ static const char* npc_parse_mapflag(char* w1, char* w2, char* w3, char* w4, con
 	else if (!strcmpi(w3,"reset"))
 		map[m].flag.reset=state;
 	else if (!strcmpi(w3,"nomapchannelautojoin"))
-		map[m].flag.chsysnolocalaj = state;
+		map[m].flag.chmautojoin = state;
 	else
 		ShowError("npc_parse_mapflag: unrecognized mapflag '%s' (file '%s', line '%d').\n", w3, filepath, strline(buffer,start-buffer));
 

+ 5 - 6
src/map/pc.c

@@ -16,6 +16,7 @@
 #include "atcommand.h" // get_atcommand_level()
 #include "battle.h" // battle_config
 #include "battleground.h"
+#include "channel.h"
 #include "chrif.h"
 #include "clif.h"
 #include "date.h" // is_day_of_*()
@@ -4783,9 +4784,7 @@ int pc_setpos(struct map_session_data* sd, unsigned short mapindex, int x, int y
 			vending_closevending(sd);
 		}
 
-		if( raChSys.local && map[sd->bl.m].channel && idb_exists(map[sd->bl.m].channel->users, sd->status.char_id) ) {
-			clif_chsys_left(map[sd->bl.m].channel,sd);
-		}
+		channel_pcquit(sd,4); //quit map chan
 	}
 
 	if( m < 0 )
@@ -9609,7 +9608,7 @@ int pc_readdb(void)
 	FILE *fp;
 	char line[24000],*p;
 
-    //reset
+	//reset
 	memset(exp_table,0,sizeof(exp_table));
 	memset(max_level,0,sizeof(max_level));
 
@@ -9656,7 +9655,7 @@ int pc_readdb(void)
 		//Reverse check in case the array has a bunch of trailing zeros... [Skotlex]
 		//The reasoning behind the -2 is this... if the max level is 5, then the array
 		//should look like this:
-	   //0: x, 1: x, 2: x: 3: x 4: 0 <- last valid value is at 3.
+		//0: x, 1: x, 2: x: 3: x 4: 0 <- last valid value is at 3.
 		while ((ui = max_level[job][type]) >= 2 && exp_table[job][type][ui-2] <= 0)
 			max_level[job][type]--;
 		if (max_level[job][type] < maxlv) {
@@ -9767,7 +9766,7 @@ int pc_readdb(void)
 	fclose(fp);
 	ShowStatus("Done reading '"CL_WHITE"%s"CL_RESET"'.\n","attr_fix.txt");
 
-    // reset then read statspoint
+	 // reset then read statspoint
 	memset(statp,0,sizeof(statp));
 	i=1;
 

+ 4 - 3
src/map/pc.h

@@ -243,7 +243,7 @@ struct map_session_data {
 	unsigned int canskill_tick; // used to prevent abuse from no-delay ACT files
 	unsigned int cansendmail_tick; // [Mail System Flood Protection]
 	unsigned int ks_floodprotect_tick; // [Kill Steal Protection]
-	 unsigned int bloodylust_tick; // bloodylust player timer [out/in re full-heal protection]
+	unsigned int bloodylust_tick; // bloodylust player timer [out/in re full-heal protection]
 
 	struct {
 		short nameid;
@@ -507,11 +507,12 @@ struct map_session_data {
 	int shadowform_id;
 
 	/* Channel System [Ind] */
-	struct raChSysCh **channels;
+	struct Channel **channels;
 	unsigned char channel_count;
-	struct raChSysCh *gcbind;
+	struct Channel *gcbind;
 	bool stealth;
 	unsigned char fontcolor; /* debug-only */
+	unsigned int channel_tick;
 
 	// temporary debugging of bug #3504
 	const char* delunit_prevfile;

+ 2 - 24
src/map/unit.c

@@ -18,6 +18,7 @@
 #include "mercenary.h"
 #include "elemental.h"
 #include "skill.h"
+#include "channel.h"
 #include "clif.h"
 #include "duel.h"
 #include "npc.h"
@@ -2294,30 +2295,7 @@ int unit_free(struct block_list *bl, clr_type clrtype)
 			if( sd->duel_invite > 0 )
 				duel_reject(sd->duel_invite, sd);
 
-			if( raChSys.ally && sd->status.guild_id ) {
-				struct guild *g = sd->guild, *sg;
-				if( g ) {
-					if( idb_exists(((struct raChSysCh *)g->channel)->users, sd->status.char_id) )
-						clif_chsys_left((struct raChSysCh *)g->channel,sd);
-					for (i = 0; i < MAX_GUILDALLIANCE; i++) {
-						if( g->alliance[i].guild_id && (sg = guild_search(g->alliance[i].guild_id) ) ) {
-							if( idb_exists(((struct raChSysCh *)sg->channel)->users, sd->status.char_id) )
-								clif_chsys_left((struct raChSysCh *)sg->channel,sd);
-							break;
-						}
-					}
-				}
-			}
-
-			if( sd->channel_count ) {
-				uint8 ch_count = sd->channel_count;
-				for( i = 0; i < ch_count; i++ ) {
-					if( sd->channels[i] != NULL )
-						clif_chsys_left(sd->channels[i],sd);
-				}
-				if( raChSys.closing )
-					aFree(sd->channels);
-			}
+			channel_pcquit(sd,0xF); //leave all chan
 
 			// Notify friends that this char logged out. [Skotlex]
 			map_foreachpc(clif_friendslist_toggle_sub, sd->status.account_id, sd->status.char_id, 0);