Jump to content
  • 0

Compile Error


chowking

Question


  • Group:  Members
  • Topic Count:  32
  • Topics Per Day:  0.01
  • Content Count:  178
  • Reputation:   16
  • Joined:  06/25/12
  • Last Seen:  

This error just came to be when i was re-compiling without changing anything since last compile,

using Visual C++ 2010 express, though it compiles successfully in linux (server host compiler) and on Visual C++ 2009,

 

1>c:\users\cyson\desktop\tro\rathena\rathena\src\map\channel.c(129): warning C4133: '=' : incompatible types - from 'Channel **' to 'raChSysCh **'
1>c:\users\cyson\desktop\tro\rathena\rathena\src\map\channel.c(130): warning C4133: '=' : incompatible types - from 'Channel *' to 'raChSysCh *'
1>c:\users\cyson\desktop\tro\rathena\rathena\src\map\channel.c(138): warning C4013: 'clif_channel_msg' undefined; assuming extern returning int
1>c:\users\cyson\desktop\tro\rathena\rathena\src\map\channel.c(161): warning C4133: '=' : incompatible types - from 'Channel *' to 'raChSysCh *'
1>c:\users\cyson\desktop\tro\rathena\rathena\src\map\channel.c(170): warning C4133: 'function' : incompatible types - from 'raChSysCh *' to 'Channel *'
1>c:\users\cyson\desktop\tro\rathena\rathena\src\map\channel.c(251): warning C4133: '==' : incompatible types - from 'raChSysCh *' to 'Channel *'
1>c:\users\cyson\desktop\tro\rathena\rathena\src\map\channel.c(254): warning C4133: '==' : incompatible types - from 'Channel *' to 'raChSysCh *'
1>c:\users\cyson\desktop\tro\rathena\rathena\src\map\channel.c(313): warning C4133: 'function' : incompatible types - from 'raChSysCh *' to 'Channel *'
1>c:\users\cyson\desktop\tro\rathena\rathena\src\map\channel.c(314): warning C4133: 'function' : incompatible types - from 'raChSysCh *' to 'Channel *'
1>c:\users\cyson\desktop\tro\rathena\rathena\src\map\channel.c(319): warning C4133: 'function' : incompatible types - from 'raChSysCh *' to 'Channel *'
1>c:\users\cyson\desktop\tro\rathena\rathena\src\map\channel.c(337): error C2039: 'channel_tick' : is not a member of 'map_session_data'
1>          c:\users\cyson\desktop\tro\rathena\rathena\src\map\pc.h(111) : see declaration of 'map_session_data'
1>c:\users\cyson\desktop\tro\rathena\rathena\src\map\channel.c(349): error C2039: 'channel_tick' : is not a member of 'map_session_data'
1>          c:\users\cyson\desktop\tro\rathena\rathena\src\map\pc.h(111) : see declaration of 'map_session_data'
1>c:\users\cyson\desktop\tro\rathena\rathena\src\map\channel.c(401): warning C4133: '=' : incompatible types - from 'raChSysCh *' to 'Channel *'
1>c:\users\cyson\desktop\tro\rathena\rathena\src\map\channel.c(405): warning C4133: '=' : incompatible types - from 'Channel *' to 'raChSysCh *'
1>c:\users\cyson\desktop\tro\rathena\rathena\src\map\channel.c(495): warning C4133: '=' : incompatible types - from 'raChSysCh *' to 'Channel *'
1>c:\users\cyson\desktop\tro\rathena\rathena\src\map\channel.c(765): warning C4133: '=' : incompatible types - from 'Channel *' to 'raChSysCh *'

 

 

channel.c

// 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; }

struct chan_banentry {
	int char_id;
	char char_name[NAME_LENGTH];
} chan_banentry;

/*
 * 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|DB_OPT_RELEASE_DATA);
	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 : success
 *  -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 a channel *channel
 * - add charid to channel user list
 * - add *channel to user channel list
 * return
 *  0 : success
 * -1 : invalid channel or sd
 * -2 : sd already in channel
 * -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)
		return -2;

	if(channel_haspcbanned(channel,sd)==1){
		sprintf(output, msg_txt(sd,1438),channel->name); //You're currently banned from the '%s' channel.
		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' has joined.",channel->name,sd->status.name);
		clif_channel_msg(channel,sd,message,channel->color);
	}

	/* 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 : sd already in channel (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 : success
 *  -1 : invalid 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 their 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 : success
 *  -1 : invalid sd
 *  -2 : sd has no guild attached
 */
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->type != CHAN_TYPE_PUBLIC )
		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 : success
 *  -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_table[COLOR_RED],msg_txt(sd,1455)); //You're talking too fast!
		return -2;
	}
	else {
		char message[CHAN_MSG_LENGTH], color;
		if((channel->opt)&CHAN_OPT_COLOR_OVERRIDE && sd->fontcolor)
			color = sd->fontcolor;
		else
			color = channel->color;
		snprintf(message, CHAN_MSG_LENGTH, "[ #%s ] %s : %s",channel->name,sd->status.name, msg);
		clif_channel_msg(channel,sd,message,color);
		sd->channel_tick = gettick();
	}
	return 0;
}

/*
 * Check parameters for channel creation
 * @type (bitflag)
 *	1 : check name # + length
 *	2 : check if already exist, need 1
 *	4 : check password length
 * return
 *  0 : success
 *  -1 : bad chan name
 *  -2 : bad chan name length
 *  -3 : pass given too long
 *  -4 : chan already exists
 */
int channel_chk(char *chname, char *chpass, int type){
	if(type&1){ //check name
		if( chname[0] != '#' )
			return -1;
		if ( strlen(chname) < 3 || strlen(chname) > CHAN_NAME_LENGTH )
			return -2;
		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;
		}
	}
	if (type&4 && (chpass != '\0' && strlen(chpass) > CHAN_NAME_LENGTH ) ) {
		return -3;
	}

	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 : invalid 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 : invalid 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 : success
 *  -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];
		clif_displaymessage(sd->fd, msg_txt(sd,1444)); // ---- Available Colors ----
		for( k = 0; k < Channel_Config.colors_count; k++ ) {
			sprintf(msg, msg_txt(sd,1445),Channel_Config.colors_name[k]);// - '%s'
			clif_colormes(sd,Channel_Config.colors[k],msg);
		}
	}
	else if( options[0] != '\0' && strcmpi(options,"mine") == 0 ) { //display chan I'm into
		clif_displaymessage(sd->fd, msg_txt(sd,1475)); // ---- My Channels ----
		if(!sd->channel_count)
			clif_displaymessage(sd->fd, msg_txt(sd,1476)); // You have not joined any channels.
		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;
}

/*
 * A user *sd is attempting to create a channel named *chname with pass *chpass
 * return
 *  0 : success
 *  -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){ //success
		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 password can't be over %d characters.
		case -4: sprintf(output, msg_txt(sd,1407), chname);// Channel '%s' is not available.
		}
		clif_displaymessage(sd->fd, output);
		return -1;
	}
	return 0;
}

/*
 * A user *sd is attempting to delete a channel named *chname
 * return
 *  0 : success
 *  -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, 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
	}
	channel_delete(channel);

	sprintf(output, msg_txt(sd,1448),chname); // Channel '%s' deleted.
	clif_displaymessage(sd->fd, output);

	return 0;
}

/*
 * A user *sd is attempting to leave a channel named *chname
 * return
 *  0 : success
 *  -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,channel->color);
	}
	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;
}

/*
 * A user *sd is attempting to join a channel named *chname
 * return
 *  0 : success
 *  -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"); // Channel '%s' 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;
}

/*
 * A user *sd is attempting to change color with *color of  a channel named *chname
 * return
 *  0 : success
 *  -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;
}

/*
 * A user *sd is attempting to bind (make default message output display chan talk)
 * from a channel named *chname
 * return
 *  0 : success
 *  -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;
}

/*
 * A user *sd is attempting to unbind
 * return
 *  0 : success
 *  -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;
}

/*
 * A user *sd is attempting to do something with the banlist
 * @flag == 0 : ban
 * @flag == 1 : unban
 * @flag == 2 : unbanall
 * @flag == 3 : banlist
 *  return
 *  0 : success
 *  -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) ) {
			sprintf(output, msg_txt(sd,1464), tsd->status.name);// Ban failed for player '%s'.
			clif_displaymessage(sd->fd, output);
			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);// 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' contains no banned players.
			clif_displaymessage(sd->fd, output);
			return -1;
		}
	}

	//let properly alter the list now
	switch(flag){
	case 0: {
		struct chan_banentry *cbe;
		CREATE(cbe, struct chan_banentry, 1);
		cbe->char_id = tsd->status.char_id;
		strcpy(cbe->char_name,tsd->status.name);
		idb_put(channel->banned, tsd->status.char_id, cbe);
		channel_clean(channel,tsd,0);
		sprintf(output, msg_txt(sd,1437),tsd->status.name,chname); // Player '%s' is banned from the '%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' is unbanned from the '%s' channel.
		break;
	case 2:
		db_clear(channel->banned);
		sprintf(output, msg_txt(sd,1442),chname); // Cleared all bans from the '%s' channel.
		break;
	case 3: {
		DBIterator *iter = db_iterator(channel->banned);
		struct chan_banentry *cbe;
		sprintf(output, msg_txt(sd,1443), channel->name);// ---- '#%s' Ban List:
		clif_displaymessage(sd->fd, output);
		for( cbe = dbi_first(iter); dbi_exists(iter); cbe = dbi_next(iter) ) { //for all users
			sprintf(output, "%d: %s",cbe->char_id,cbe->char_name);
			clif_displaymessage(sd->fd, output);
		}
		dbi_destroy(iter);
		}
		return 0;
	}
	clif_displaymessage(sd->fd, output);

	return 0;
}

/*
 * A 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 : success
 *  -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[] = {
		"None",
		"JoinAnnounce",
		"MessageDelay",
		"ColorOverride",
	};

	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 || option == '\0' ) {
		clif_displaymessage(sd->fd, msg_txt(sd,1446));// You need to input an 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 >= s ) {
		sprintf(output, msg_txt(sd,1447), option);// Unknown channel option '%s'.
		clif_displaymessage(sd->fd, output);
		clif_displaymessage(sd->fd, msg_txt(sd,1414));// ---- 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]);// Input the number of seconds (0-10) for the '%s' option.
			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 (use '@channel setopt %s 0' to disable).
			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 enabled for channel '#%s'.
			clif_displaymessage(sd->fd, output);
		}
	} 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 0-10).
				clif_displaymessage(sd->fd, output);
				return -1;
			}
			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 disabled for channel '#%s'.
				clif_displaymessage(sd->fd, output);
			} else {
				channel->opt |= k;
				channel->msg_delay = v;
				sprintf(output, msg_txt(sd,1452), opt_str[k],channel->name,v);// Option '%s' is enabled for channel '#%s' at %d seconds.
				clif_displaymessage(sd->fd, output);
			}
		} else {
			if( v ) {
				if( channel->opt & k ) {
					sprintf(output, msg_txt(sd,1449), opt_str[k],opt_str[k]); // Option '%s' is already enabled (use '@channel setopt %s 0' to disable).
					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 enabled for channel '#%s'.
					clif_displaymessage(sd->fd, output);
				}
			} else {
				if( !(channel->opt & k) ) {
					sprintf(output, msg_txt(sd,1450), opt_str[k],channel->name); // Option '%s' is enabled for channel '#%s'.
					clif_displaymessage(sd->fd, output);
					return -1;
				} else {
					channel->opt &=~ k;
					sprintf(output, msg_txt(sd,1453), opt_str[k],channel->name);// Option '%s' is disabled for channel '#%s'.
					clif_displaymessage(sd->fd, output);
				}
			}
		}
	}
	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 '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 'ally_channel_color', disabling '#%s'...\n",ally_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;
	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);
	}
}
 

 

channel.h

// 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,
	CHAN_OPT_COLOR_OVERRIDE = 3,
};

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 */

these files were not touched ever since the clean trunk has been downloaded

 

don't have a clue on how to fix this since i generally edit skill.c battle.c atcommand.c and status.c only, please help

Edited by chowking
Link to comment
Share on other sites

0 answers to this question

Recommended Posts

There have been no answers to this question yet

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Answer this question...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

×
×
  • Create New...