From aea2d4e833c4139ecef772850ffa79f373a5bc67 Mon Sep 17 00:00:00 2001 From: Rick L Bird Date: Fri, 6 May 2011 00:40:52 -0400 Subject: [PATCH] Re-enabled chat system equivalent with features of PennMUSH 1.8.3p12.. The standard CobraMUSH channel object features are not back yet, but at least we have a chat system while we're in the testing phase. --- hdrs/extchat.h | 31 ++- options.h.dist | 3 +- src/extchat.c | 580 +++++++++++++++++++++++++++++++++---------------- src/function.c | 2 +- 4 files changed, 418 insertions(+), 198 deletions(-) diff --git a/hdrs/extchat.h b/hdrs/extchat.h index a011309..493b7f5 100644 --- a/hdrs/extchat.h +++ b/hdrs/extchat.h @@ -65,9 +65,15 @@ struct chanuser { #define CU_DEFAULT_FLAGS 0x0 /* channel_broadcast flags */ -#define CB_CHECKQUIET 0x1 /* Check for quiet flag on recipients */ -#define CB_NOSPOOF 0x2 /* Use nospoof emits */ -#define CB_PRESENCE 0x4 /* This is a presence message, not sound */ +#define CB_CHECKQUIET 0x01 /* Check for quiet flag on recipients */ +#define CB_NOSPOOF 0x02 /* Use nospoof emits */ +#define CB_PRESENCE 0x04 /* This is a presence message, not sound */ +#define CB_QUIET 0x08 /* Do not prepend the name */ +#define CB_SPEECH 0x10 /* This is player speech */ +#define CB_POSE 0x20 /* This is a pose */ +#define CB_SEMIPOSE 0x40 /* This is a semipose */ +#define CB_EMIT 0x80 /* This is an emit */ +#define CB_TYPE 0xF0 /* Type of a message. */ #define CUdbref(u) ((u)->who) #define CUtype(u) ((u)->type) @@ -90,6 +96,7 @@ struct channel { privbits type; /**< Channel flags */ int cost; /**< What it cost to make this channel */ dbref creator; /**< This is who paid the cost for the channel */ + dbref mogrifier; /**< This is the object that mogrifies the channel text. */ int num_users; /**< Number of connected users */ int max_users; /**< Maximum allocated users */ struct chanuser *users; /**< Linked list of current users */ @@ -177,11 +184,12 @@ struct na_cpass { ((IsPlayer(o) && Channel_Player(c)) || \ (IsThing(o) && Channel_Object(c))) #define Chan_Can(p,t) \ - (!(t & CHANNEL_DISABLED) && (!(t & CHANNEL_DIRECTOR) || Director(p)) || \ - (!(t & CHANNEL_ADMIN) || Admin(p) || div_powover(p,p,"Chat"))) + (!(t & CHANNEL_DISABLED) && (!(t & CHANNEL_DIRECTOR) || Director(p)) && \ + (!(t & CHANNEL_ADMIN) || Admin(p) || (div_powover(p,p,"Chat")))) /* Who can change channel privileges to type t */ #define Chan_Can_Priv(p,t) (Chan_Can(p,t)) #define Chan_Can_Access(c,p) (Chan_Can(p,ChanType(c))) +#define ChanMogrifier(c) ((c)->mogrifier) #define Chan_Can_Join(c,p) \ (Chan_Can_Access(c,p) && \ (eval_chan_lock(c,p, CLOCK_JOIN))) @@ -194,15 +202,12 @@ struct na_cpass { (Director(p) || (ChanCreator(c) == (p)) || \ (!Guest(p) && Chan_Can_Access(c,p) && \ (eval_chan_lock(c,p, CLOCK_MOD)))) -#define Chan_Can_See(c,p) \ - ((Admin(p) || See_All(p)) || (Chan_Can_Access(c,p) && \ - (eval_chan_lock(c,p,CLOCK_SEE)))) +#define Chan_Can_See(c,p) (Admin(p) || CanSee(p,ChanCreator(c)) || (Chan_Can_Access(c,p) && (eval_chan_lock(c,p,CLOCK_SEE)))) #define Chan_Can_Hide(c,p) \ (Can_Hide(p) || (Channel_CanHide(c) && Chan_Can_Access(c,p) && \ (eval_chan_lock(c,p, CLOCK_HIDE)))) #define Chan_Can_Nuke(c,p) (ChanCreator(c) == (p) || div_powover(p, ChanCreator(c), "Chat")) -#define Chan_Can_Decomp(c,p) (Director(p) || (ChanCreator(c) == (p))) - +#define Chan_Can_Decomp(c,p) ((div_powover(p,ChanCreator(c), "Chat") && CanSee(p,ChanCreator(c))) || (ChanCreator(c) == (p))) /* For use in channel matching */ @@ -217,8 +222,8 @@ extern void WIN32_CDECL channel_chat __attribute__ ((__format__(__printf__, 6, 7))); extern CHANUSER *onchannel(dbref who, CHAN *c); extern void init_chatdb(void); -extern int load_chatdb(FILE * fp); -extern int save_chatdb(FILE * fp); +extern int load_chatdb(PENNFILE * fp); +extern int save_chatdb(PENNFILE * fp); extern void do_cemit (dbref player, const char *name, const char *msg, int flags); extern void do_chan_user_flags @@ -258,5 +263,7 @@ enum chan_match_type { PMATCH_ON /**< Match channels user is on */ }; +/* Chat db flags */ +#define CDB_SPIFFY 0x01 /* Has mogrifier and buffer */ #endif /* __EXTCHAT_H */ diff --git a/options.h.dist b/options.h.dist index a713edb..920b6c1 100644 --- a/options.h.dist +++ b/options.h.dist @@ -113,9 +113,8 @@ /* Standard PennMUSH chat system. Allows user to talk cross-MUSH * to each other, without needing to be in the same room. Wether or * not you want this depends on what type of MUSH you want. - * **RECOMMENDED NOT TO ENABLE THIS DURING 0.80 DEVELOPMENT!** */ -/* #define CHAT_SYSTEM /* */ +#define CHAT_SYSTEM /* */ /* Many MUSHes want to change the +channels to =channels. That's * annoying. So we've got this CHAT_TOKEN_ALIAS, which allows + as well diff --git a/src/extchat.c b/src/extchat.c index df738c7..e0bff52 100644 --- a/src/extchat.c +++ b/src/extchat.c @@ -44,11 +44,11 @@ static CHANUSER *new_user(dbref who, const void *hint); static void free_channel(CHAN *c); static void free_chanlist(CHANLIST *cl); static void free_user(CHANUSER *u); -static int load_chatdb_oldstyle(FILE * fp); -static int load_channel(FILE * fp, CHAN *ch); -static int load_chanusers(FILE * fp, CHAN *ch); -static int load_labeled_channel(FILE * fp, CHAN *ch); -static int load_labeled_chanusers(FILE * fp, CHAN *ch); +static int load_chatdb_oldstyle(PENNFILE *fp); +static int load_channel(PENNFILE *fp, CHAN *ch); +static int load_chanusers(PENNFILE *fp, CHAN *ch); +static int load_labeled_channel(PENNFILE *fp, CHAN *ch, int dbflags); +static int load_labeled_chanusers(PENNFILE *fp, CHAN *ch); static void insert_channel(CHAN **ch); static void remove_channel(CHAN *ch); static void insert_obj_chan(dbref who, CHAN **ch); @@ -58,8 +58,8 @@ static void chan_chown(CHAN *c, dbref victim); void chan_chownall(dbref old, dbref new); static int insert_user(CHANUSER *user, CHAN *ch); static int remove_user(CHANUSER *u, CHAN *ch); -static int save_channel(FILE * fp, CHAN *ch); -static int save_chanuser(FILE * fp, CHANUSER *user); +static int save_channel(PENNFILE *fp, CHAN *ch); +static int save_chanuser(PENNFILE *fp, CHANUSER *user); static void channel_wipe(dbref player, CHAN *chan); static int yesno(const char *str); static int canstilladd(dbref player); @@ -73,8 +73,12 @@ static void channel_leave_self(dbref player, const char *name); static void do_channel_who(dbref player, CHAN *chan); void chat_player_announce(dbref player, char *msg, int ungag); static int ok_channel_name(const char *n); +/* static void format_channel_chat(CHAN *chan, CHANUSER *u, dbref victim, int flags, const char *msg, const char *extra); + */ +static void channel_send(CHAN *channel, dbref player, int flags, + const char *origmessage); static void list_partial_matches(dbref player, const char *name, enum chan_match_type type); @@ -88,9 +92,9 @@ slab *channel_slab; /**< slab for 'struct channel' allocations */ slab *chanlist_slab; /**< slab for 'struct chanlist' allocations */ slab *chanuser_slab; /**< slab for 'struct chanuser' allocations */ -#define YES 1 /**< An affirmative. */ -#define NO 0 /**< A negative. */ -#define ERR -1 /**< An error. Clever, eh? */ +#define CHAN_YES 1 /**< An affirmative. */ +#define CHAN_NO 0 /**< A negative. */ +#define CHAN_ERR -1 /**< An error. Clever, eh? */ /** Wrapper for insert_user() that generates a new CHANUSER and inserts it */ #define insert_user_by_dbref(who,chan) \ @@ -110,6 +114,7 @@ static PRIV priv_table[] = { {"Admin", 'A', CHANNEL_ADMIN | CHANNEL_PLAYER, CHANNEL_ADMIN}, {"Director", 'W', CHANNEL_DIRECTOR | CHANNEL_PLAYER, CHANNEL_DIRECTOR}, {"Player", 'P', CHANNEL_PLAYER, CHANNEL_PLAYER}, + {"Thing", 'T', CHANNEL_OBJECT, CHANNEL_OBJECT}, {"Object", 'O', CHANNEL_OBJECT, CHANNEL_OBJECT}, {"Quiet", 'Q', CHANNEL_QUIET, CHANNEL_QUIET}, {"Open", 'o', CHANNEL_OPEN, CHANNEL_OPEN}, @@ -217,7 +222,7 @@ init_chatdb(void) * \retval 0 failure */ static int -load_chatdb_oldstyle(FILE * fp) +load_chatdb_oldstyle(PENNFILE *fp) { int i; CHAN *ch; @@ -230,7 +235,7 @@ load_chatdb_oldstyle(FILE * fp) /* Load all channels */ for (i = 0; i < num_channels; i++) { - if (feof(fp)) + if (penn_feof(fp)) break; ch = new_channel(); if (!ch) @@ -245,7 +250,7 @@ load_chatdb_oldstyle(FILE * fp) num_channels = i; /* Check for **END OF DUMP*** */ - if (!fgets(buff, sizeof buff, fp)) + if (!penn_fgets(buff, sizeof buff, fp)) do_rawlog(LT_ERR, T("CHAT: No end-of-dump marker in the chat database.")); else if (strcmp(buff, EOD) != 0) do_rawlog(LT_ERR, T("CHAT: Trailing garbage in the chat database.")); @@ -261,23 +266,23 @@ extern char db_timestamp[]; * \retval 0 failure */ int -load_chatdb(FILE * fp) +load_chatdb(PENNFILE *fp) { int i, flags; CHAN *ch; char buff[20]; char *chat_timestamp; - i = fgetc(fp); + i = penn_fgetc(fp); if (i == EOF) { do_rawlog(LT_ERR, T("CHAT: Invalid database format!")); longjmp(db_err, 1); } else if (i != '+') { - ungetc(i, fp); + penn_ungetc(i, fp); return load_chatdb_oldstyle(fp); } - i = fgetc(fp); + i = penn_fgetc(fp); if (i != 'V') { do_rawlog(LT_ERR, T("CHAT: Invalid database format!")); @@ -303,7 +308,7 @@ load_chatdb(FILE * fp) ch = new_channel(); if (!ch) return 0; - if (!load_labeled_channel(fp, ch)) { + if (!load_labeled_channel(fp, ch, flags)) { do_rawlog(LT_ERR, T("Unable to load channel %d."), i); free_channel(ch); return 0; @@ -313,7 +318,7 @@ load_chatdb(FILE * fp) num_channels = i; /* Check for **END OF DUMP*** */ - if (!fgets(buff, sizeof buff, fp)) + if (!penn_fgets(buff, sizeof buff, fp)) do_rawlog(LT_ERR, T("CHAT: No end-of-dump marker in the chat database.")); else if (strcmp(buff, EOD) != 0) do_rawlog(LT_ERR, T("CHAT: Trailing garbage in the chat database.")); @@ -335,6 +340,7 @@ new_channel(void) ch->title[0] = '\0'; ChanType(ch) = CHANNEL_DEFAULT_FLAGS; ChanCreator(ch) = NOTHING; + ChanMogrifier(ch) = NOTHING; ChanCost(ch) = CHANNEL_COST; ChanNext(ch) = NULL; ChanNumMsgs(ch) = 0; @@ -401,14 +407,15 @@ free_user(CHANUSER *u) * successful, 0 otherwise. */ static int -load_channel(FILE * fp, CHAN *ch) +load_channel(PENNFILE *fp, CHAN *ch) { mush_strncpy(ChanName(ch), getstring_noalloc(fp), CHAN_NAME_LEN); - if (feof(fp)) + if (penn_feof(fp)) return 0; mush_strncpy(ChanTitle(ch), getstring_noalloc(fp), CHAN_TITLE_LEN); ChanType(ch) = (privbits) getref(fp); ChanCreator(ch) = getref(fp); + ChanMogrifier(ch) = NOTHING; ChanCost(ch) = getref(fp); ChanNumMsgs(ch) = 0; ChanJoinLock(ch) = getboolexp(fp, chan_join_lock); @@ -428,7 +435,7 @@ load_channel(FILE * fp, CHAN *ch) * successful, 0 otherwise. */ static int -load_labeled_channel(FILE * fp, CHAN *ch) +load_labeled_channel(PENNFILE *fp, CHAN *ch, int dbflags) { char *tmp; int i; @@ -445,6 +452,13 @@ load_labeled_channel(FILE * fp, CHAN *ch) ChanCreator(ch) = d; db_read_this_labeled_int(fp, "cost", &i); ChanCost(ch) = i; + if (dbflags & CDB_SPIFFY) { + db_read_this_labeled_int(fp, "buffer", &i); + if (i) + ChanBufferQ(ch) = allocate_bufferq(i); + db_read_this_labeled_dbref(fp, "mogrifier", &d); + ChanMogrifier(ch) = d; + } ChanNumMsgs(ch) = 0; while (1) { db_read_labeled_string(fp, &label, &value); @@ -472,7 +486,7 @@ load_labeled_channel(FILE * fp, CHAN *ch) /* Load the *channel's user list. Return number of users on success, or 0 */ static int -load_chanusers(FILE * fp, CHAN *ch) +load_chanusers(PENNFILE *fp, CHAN *ch) { int i, num = 0; CHANUSER *user; @@ -500,7 +514,7 @@ load_chanusers(FILE * fp, CHAN *ch) /* Load the *channel's user list. Return number of users on success, or 0 */ static int -load_labeled_chanusers(FILE * fp, CHAN *ch) +load_labeled_chanusers(PENNFILE *fp, CHAN *ch) { int i, num = 0, n; char *tmp; @@ -682,6 +696,9 @@ remove_all_obj_chan(dbref thing) CHANLIST *p, *nextp; for (p = Chanlist(thing); p; p = nextp) { nextp = p->next; + if (ChanMogrifier(p->chan) == thing) { + ChanMogrifier(p->chan) = NOTHING; + } remove_user_by_dbref(thing, p->chan); } return; @@ -782,26 +799,26 @@ remove_user(CHANUSER *u, CHAN *ch) * \retval 0 failure */ int -save_chatdb(FILE * fp) +save_chatdb(PENNFILE *fp) { CHAN *ch; int default_flags = 0; /* How many channels? */ - OUTPUT(fprintf(fp, "+V%d\n", default_flags)); + penn_fprintf(fp, "+V%d\n", default_flags); db_write_labeled_string(fp, "savedtime", show_time(mudtime, 1)); db_write_labeled_int(fp, "channels", num_channels); for (ch = channels; ch; ch = ch->next) { save_channel(fp, ch); } - OUTPUT(fputs(EOD, fp)); + penn_fputs(EOD, fp); return 1; } /* Save a single channel. Return 1 if successful, 0 otherwise. */ static int -save_channel(FILE * fp, CHAN *ch) +save_channel(PENNFILE *fp, CHAN *ch) { CHANUSER *cu; @@ -810,6 +827,8 @@ save_channel(FILE * fp, CHAN *ch) db_write_labeled_int(fp, " flags", ChanType(ch)); db_write_labeled_dbref(fp, " creator", ChanCreator(ch)); db_write_labeled_int(fp, " cost", ChanCost(ch)); + db_write_labeled_int(fp, " buffer", bufferq_blocks(ChanBufferQ(ch))); + db_write_labeled_dbref(fp, " mogrifier", ChanMogrifier(ch)); db_write_labeled_string(fp, " lock", "join"); putboolexp(fp, ChanJoinLock(ch)); db_write_labeled_string(fp, " lock", "speak"); @@ -828,7 +847,7 @@ save_channel(FILE * fp, CHAN *ch) /* Save the channel's user list. Return 1 on success, 0 on failure */ static int -save_chanuser(FILE * fp, CHANUSER *user) +save_chanuser(PENNFILE *fp, CHANUSER *user) { db_write_labeled_dbref(fp, " dbref", CUdbref(user)); db_write_labeled_int(fp, " flags", CUtype(user)); @@ -1240,11 +1259,11 @@ do_channel(dbref player, const char *name, const char *target, const char *com) T("CHAT: You join %s to channel <%s>."), Name(victim), ChanName(chan)); u = onchannel(victim, chan); + ChanNumUsers(chan)++; if (!Channel_Quiet(chan) && !DarkLegal(victim)) { - format_channel_chat(chan, u, victim, CB_CHECKQUIET | CB_PRESENCE, - T("%s has joined this channel."), NULL); + channel_send(chan, victim, CB_CHECKQUIET | CB_PRESENCE | CB_POSE, + T("%s has joined this channel.")); } - ChanNumUsers(chan)++; } else { notify_format(player, T("%s is already on channel <%s>."), Name(victim), @@ -1266,9 +1285,9 @@ do_channel(dbref player, const char *name, const char *target, const char *com) strcpy(title, (u && CUtitle(u)) ? CUtitle(u) : ""); if (remove_user(u, chan)) { if (!Channel_Quiet(chan) && !DarkLegal(victim)) { - format_channel_chat(chan, NULL, victim, - CB_CHECKQUIET | CB_PRESENCE, - T("%s has left this channel."), title); + channel_send(chan, victim, + CB_CHECKQUIET | CB_PRESENCE | CB_POSE, + T("has left this channel.")); } notify_format(victim, T("CHAT: %s removes you from channel <%s>."), @@ -1326,10 +1345,10 @@ channel_join_self(dbref player, const char *name) /* Does victim pass the joinlock? */ if (!Chan_Can_Join(chan, player)) { if (Director(player)) { - /* Directors can override join locks */ + /* Wizards can override join locks */ notify(player, T - ("CHAT: Warning: You don't meet channel join permissions (joining anyway)")); + ("CHAT: Warning: You don't meet channel join permissions! (joining anyway)")); } else { notify(player, T("Permission to join denied.")); return; @@ -1338,10 +1357,11 @@ channel_join_self(dbref player, const char *name) if (insert_user_by_dbref(player, chan)) { notify_format(player, T("CHAT: You join channel <%s>."), ChanName(chan)); u = onchannel(player, chan); - if (!Channel_Quiet(chan) && !DarkLegal(player)) - format_channel_chat(chan, u, player, CB_CHECKQUIET | CB_PRESENCE, - T("%s has joined this channel."), NULL); ChanNumUsers(chan)++; + if (!Channel_Quiet(chan) && !DarkLegal(player)) + channel_send(chan, player, + CB_CHECKQUIET | CB_PRESENCE | CB_POSE, + T("has joined this channel.")); } else { /* Should never happen */ notify_format(player, @@ -1381,8 +1401,9 @@ channel_leave_self(dbref player, const char *name) strcpy(title, (u && CUtitle(u)) ? CUtitle(u) : ""); if (remove_user(u, chan)) { if (!Channel_Quiet(chan) && !DarkLegal(player)) - format_channel_chat(chan, NULL, player, CB_CHECKQUIET | CB_PRESENCE, - T("%s has left this channel."), title); + channel_send(chan, player, + CB_CHECKQUIET | CB_PRESENCE | CB_POSE, + T("has left this channel.")); notify_format(player, T("CHAT: You leave channel <%s>."), ChanName(chan)); } else { /* Should never happen */ @@ -1503,9 +1524,6 @@ do_chat(dbref player, CHAN *chan, const char *arg1) CHANUSER *u; const char *gap; char type; - const char *someone = "Someone"; - char *title; - const char *name; bool canhear; if (!Chan_Ok_Type(chan, player)) { @@ -1542,17 +1560,6 @@ do_chat(dbref player, CHAN *chan, const char *arg1) return; } - if (!Channel_NoTitles(chan) && u && CUtitle(u) && *CUtitle(u)) - title = CUtitle(u); - else - title = NULL; - if (Channel_NoNames(chan)) - name = NULL; - else - name = accented_name(player); - if (!title && !name) - name = someone; - /* figure out what kind of message we have */ gap = " "; type = ':'; @@ -1563,27 +1570,12 @@ do_chat(dbref player, CHAN *chan, const char *arg1) /* FALLTHRU */ case POSE_TOKEN: arg1 = arg1 + 1; - channel_chat(chan, player, 0, arg1, type, "<%s> %s%s%s%s%s%s", - ChanName(chan), title ? title : "", title ? ANSI_ENDALL : "", - (title && name) ? " " : "", name ? name : "", gap, arg1); - if (!canhear) - notify_format(player, T("To channel %s: %s%s%s%s%s%s"), ChanName(chan), - title ? title : "", title ? ANSI_ENDALL : "", - (title && name) ? " " : "", name ? name : "", gap, arg1); + channel_send(chan, player, type == ';' ? CB_SEMIPOSE : CB_POSE, arg1); break; default: if (CHAT_STRIP_QUOTE && (*arg1 == SAY_TOKEN)) arg1 = arg1 + 1; - channel_chat(chan, player, 0, arg1, '"', T("<%s> %s%s%s%s says, \"%s\""), - ChanName(chan), title ? title : "", - title ? ANSI_ENDALL : "", (title && name) ? " " : "", - name ? name : "", arg1); - if (!canhear) - notify_format(player, - T("To channel %s: %s%s%s%s says, \"%s\""), - ChanName(chan), title ? title : "", - title ? ANSI_ENDALL : "", (title && name) ? " " : "", - name ? name : "", arg1); + channel_send(chan, player, CB_SPEECH, arg1); break; } @@ -1605,7 +1597,6 @@ do_cemit(dbref player, const char *name, const char *msg, int flags) CHAN *chan = NULL; CHANUSER *u; int canhear; - int override_checks = 0; if (!name || !*name) { notify(player, T("That is not a valid channel.")); @@ -1626,18 +1617,15 @@ do_cemit(dbref player, const char *name, const char *msg, int flags) notify(player, T("CHAT: I don't recognize that channel.")); return; } - /* If the cemitter is both See_All and Pemit_All, always allow them - * to @cemit, as they could iterate over connected players, examine - * their channels, and pemit to them anyway. - */ - if (!override_checks && !Chan_Ok_Type(chan, player)) { + + if (!Chan_Ok_Type(chan, player)) { notify_format(player, T ("Sorry, you're not the right type to be on channel <%s>."), ChanName(chan)); return; } - if (!override_checks && !Chan_Can_Cemit(chan, player)) { + if (!Chan_Can_Cemit(chan, player)) { notify_format(player, T("Sorry, you're not allowed to @cemit on channel <%s>."), ChanName(chan)); @@ -1646,7 +1634,7 @@ do_cemit(dbref player, const char *name, const char *msg, int flags) u = onchannel(player, chan); canhear = u ? !Chanuser_Gag(u) : 0; /* If the channel isn't open, you must hear it in order to speak */ - if (!override_checks && !Channel_Open(chan)) { + if (!Channel_Open(chan)) { if (!u) { notify(player, T("You must be on that channel to speak on it.")); return; @@ -1661,11 +1649,9 @@ do_cemit(dbref player, const char *name, const char *msg, int flags) return; } if (!(flags & PEMIT_SILENT)) - channel_chat(chan, player, (flags & PEMIT_SPOOF) ? 0 : CB_NOSPOOF, - msg, '|', "<%s> %s", ChanName(chan), msg); + channel_send(chan, player, CB_EMIT, msg); else - channel_chat(chan, player, (flags & PEMIT_SPOOF) ? 0 : CB_NOSPOOF, - msg, '|', "%s", msg); + channel_send(chan, player, CB_EMIT | CB_QUIET, msg); if (!canhear) notify_format(player, T("Cemit to channel %s: %s"), ChanName(chan), msg); ChanNumMsgs(chan)++; @@ -1768,6 +1754,7 @@ do_chan_admin(dbref player, char *name, const char *perms, int flag) if (type) ChanType(chan) = type; ChanCreator(chan) = Owner(player); + ChanMogrifier(chan) = NOTHING; mush_strncpy(ChanName(chan), name, CHAN_NAME_LEN); insert_channel(&chan); notify_format(player, T("CHAT: Channel <%s> created."), ChanName(chan)); @@ -1817,9 +1804,8 @@ do_chan_admin(dbref player, char *name, const char *perms, int flag) insert_channel(&chan); snprintf(announcebuff, BUFFER_LEN, "has renamed %s to %s", old, ChanName(chan)); - channel_chat(chan, player, 0, announcebuff, '@', - "<%s> %s has renamed channel %s to %s.", - ChanName(chan), Name(player), old, ChanName(chan)); + channel_send(chan, player, + CB_CHECKQUIET | CB_PRESENCE | CB_POSE, announcebuff); notify(player, T("Channel renamed.")); break; case 3: @@ -2502,6 +2488,61 @@ do_chan_wipe(dbref player, const char *name) return; } +/** Change the mogrifier of a channel. + * \verbatim + * This is the top-level function for @channel/mogrifier + * \endverbatim + * \param player the enactor. + * \param name name of the channel. + * \param newobj name of the new mogrifier object. + */ +void +do_chan_set_mogrifier(dbref player, const char *name, const char *newobj) +{ + CHAN *c; + dbref it = NOTHING; + /* Find the channel */ + test_channel(player, name, c); + + /* Only a channel modifier can do this. */ + if (!Chan_Can_Modify(c, player)) { + notify(player, T("CHAT: Only a channel modifier can do that.")); + return; + } + + /* Find the mogrifying object */ + if (newobj && *newobj) { + if ((it = match_result(player, newobj, NOTYPE, MAT_EVERYTHING)) < 0) { + if (it == NOTHING) + notify(player, T("I can't see that here.")); + else if (it == AMBIGUOUS) + notify(player, T("I don't know which thing you mean.")); + return; + } + } else if (ChanMogrifier(c) != NOTHING) { + notify_format(player, + T("CHAT: Channel <%s> no longer mogrified by %s."), + ChanName(c), Name(ChanMogrifier(c))); + ChanMogrifier(c) = NOTHING; + return; + } else { + notify_format(player, + T("CHAT: Channel <%s> isn't being mogrified."), ChanName(c)); + return; + } + + /* The player must be able to *control* the mogrifier. */ + if (!controls(player, it)) { + notify(player, T("CHAT: You must control the mogrifier.")); + return; + } + ChanMogrifier(c) = it; + notify_format(player, + T("CHAT: Channel <%s> now mogrified by %s."), ChanName(c), + Name(it)); + return; +} + /** Change the owner of a channel. * \verbatim * This is the top-level function for @channel/chown, which changes @@ -2684,6 +2725,10 @@ do_chan_what(dbref player, const char *partname) notify(player, ChanName(c)); notify_format(player, T("Description: %s"), ChanTitle(c)); notify_format(player, T("Owner: %s"), Name(ChanCreator(c))); + if (ChanMogrifier(c) != NOTHING) { + notify_format(player, T("Mogrifier: %s (#%d)"), + Name(ChanMogrifier(c)), ChanMogrifier(c)); + } notify_format(player, T("Flags: %s"), privs_to_string(priv_table, ChanType(c))); if (ChanBufferQ(c)) @@ -2723,7 +2768,7 @@ do_chan_decompile(dbref player, const char *name, int brief) strcpy(cleanp, remove_markup(ChanName(c), NULL)); if (string_prefix(cleanp, cleanname)) { found++; - if (!(See_All(player) || Chan_Can_Modify(c, player) + if (!(CanSee(player, ChanCreator(c)) || Chan_Can_Modify(c, player) || (ChanCreator(c) == player))) { if (Chan_Can_See(c, player)) notify_format(player, T("CHAT: No permission to decompile <%s>"), @@ -2734,6 +2779,10 @@ do_chan_decompile(dbref player, const char *name, int brief) privs_to_string(priv_table, ChanType(c))); notify_format(player, "@channel/chown %s = %s", ChanName(c), Name(ChanCreator(c))); + if (ChanMogrifier(c) != NOTHING) { + notify_format(player, "@channel/mogrifier %s = #%d", ChanName(c), + ChanMogrifier(c)); + } if (ChanModLock(c) != TRUE_BOOLEXP) notify_format(player, "@clock/mod %s = %s", ChanName(c), unparse_boolexp(player, ChanModLock(c), UB_MEREF)); @@ -2808,6 +2857,9 @@ do_channel_who(dbref player, CHAN *chan) FUNCTION(fun_cwho) { int first = 1; + int matchcond = 0; + int priv = 0; + int show; CHAN *chan = NULL; CHANUSER *u; dbref who; @@ -2823,6 +2875,19 @@ FUNCTION(fun_cwho) break; } + if (nargs == 2) { + if (!strcasecmp(args[1], "on")) + matchcond = 0; + else if (!strcasecmp(args[1], "off")) + matchcond = 1; + else if (!strcasecmp(args[1], "all")) + matchcond = 2; + else { + safe_str(T("#-1 INVALID ARGUMENT"), buff, bp); + return; + } + } + /* Feh. We need to do some sort of privilege checking, so that * if mortals can't do '@channel/who wizard', they can't do * 'think cwho(wizard)' either. The first approach that comes to @@ -2840,16 +2905,25 @@ FUNCTION(fun_cwho) safe_str(T("#-1 NO PERMISSIONS FOR CHANNEL"), buff, bp); return; } + + priv = Priv_Who(executor); + for (u = ChanUsers(chan); u; u = u->next) { who = CUdbref(u); - if ((IsThing(who) || Connected(who)) && - (!Chanuser_Hide(u) || Priv_Who(executor))) { - if (first) - first = 0; + show = 1; + if (!IsThing(who) && matchcond != 2) { + if (matchcond) + show = !Connected(who) || (Chanuser_Hide(u) && !priv); else - safe_chr(' ', buff, bp); - safe_dbref(who, buff, bp); + show = Connected(who) && (!Chanuser_Hide(u) || priv); } + if (!show) + continue; + if (first) + first = 0; + else + safe_chr(' ', buff, bp); + safe_dbref(who, buff, bp); } } @@ -2897,28 +2971,28 @@ static int yesno(const char *str) { if (!str || !*str) - return ERR; + return CHAN_ERR; switch (str[0]) { case 'y': case 'Y': - return YES; + return CHAN_YES; case 'n': case 'N': - return NO; + return CHAN_NO; case 'o': case 'O': switch (str[1]) { case 'n': case 'N': - return YES; + return CHAN_YES; case 'f': case 'F': - return NO; + return CHAN_NO; default: - return ERR; + return CHAN_ERR; } default: - return ERR; + return CHAN_ERR; } } @@ -2955,8 +3029,7 @@ chat_player_announce(dbref player, char *msg, int ungag) if (u) { if (!Channel_Quiet(c) && (Channel_Admin(c) || Channel_Wizard(c) || (!Chanuser_Hide(u) && !Dark(player)))) { - format_channel_chat(c, u, player, CB_CHECKQUIET | CB_PRESENCE, - msg, NULL); + channel_send(c, player, CB_CHECKQUIET | CB_PRESENCE | CB_POSE, msg); } if (ungag) CUtype(u) &= ~CU_GAG; @@ -3262,7 +3335,8 @@ FUNCTION(fun_crecall) COMMAND(cmd_cemit) { - int spflags = !strcmp(cmd->name, "@NSCEMIT") ? PEMIT_SPOOF : 0; + int spflags = (!strcmp(cmd->name, "@NSCEMIT") + && Can_Nspemit(player) ? PEMIT_SPOOF : 0); SPOOF(player, cause, sw); if (SW_ISSET(sw, SWITCH_SILENT)) spflags |= PEMIT_SILENT; @@ -3295,6 +3369,8 @@ COMMAND(cmd_channel) do_chan_desc(player, arg_left, args_right[1]); else if (SW_ISSET(sw, SWITCH_TITLE)) do_chan_title(player, arg_left, args_right[1]); + else if (SW_ISSET(sw, SWITCH_MOGRIFIER)) + do_chan_set_mogrifier(player, arg_left, args_right[1]); else if (SW_ISSET(sw, SWITCH_CHOWN)) do_chan_chown(player, arg_left, args_right[1]); else if (SW_ISSET(sw, SWITCH_WIPE)) @@ -3369,88 +3445,253 @@ na_channel(dbref current, void *data) return current; } +/** + * Mogrify a value using u(/,) + * + * \param mogrifier The object doing the mogrification + * \param attrname The attribute on mogrifier to call. + * \param channel The active channel. (%1) + * \param value The value to mogrify (%0) + * \retval Mogrified text. + */ + +char * +mogrify(dbref mogrifier, char *attrname, + dbref player, int numargs, char *argv[], char *orig) +{ + static char buff[BUFFER_LEN]; + const char *wenv[10] = { 0 }; + int i; + buff[0] = '\0'; + for (i = 0; i < numargs; i++) { + wenv[i] = argv[i]; + } + + if (call_attrib(mogrifier, attrname, wenv, numargs, buff, player, NULL)) { + if (buff[0]) { + return buff; + } + } + + snprintf(buff, BUFFER_LEN, "%s", orig); + + return buff; +} + /** Broadcast a message to a channel, using @chatformat if it's - * available. + * available, and mogrifying. * \param channel pointer to channel to broadcast to. * \param player message speaker. * \param flags broadcast flag mask (see CB_* constants in extchat.h) * \param fmt message format string. */ -void WIN32_CDECL -channel_chat(CHAN *channel, dbref player, int flags, - const char *message, char type, const char *fmt, ...) -/* flags: 0x1 = checkquiet, 0x2 = nospoof */ +void +channel_send(CHAN *channel, dbref player, int flags, const char *origmessage) { - va_list args; -#ifdef HAS_VSNPRINTF - char tbuf1[BUFFER_LEN]; -#else - char tbuf1[BUFFER_LEN * 2]; /* Safety margin as per tprintf */ -#endif +/* flags: + * CB_CHECKQUIET CB_NOSPOOF CB_PRESENCE CB_POSE CB_EMIT CB_SPEECH + */ + + /* These are static only to prevent them from chewing up + * time to malloc/free, and chewing up space. + */ + static char channame[BUFFER_LEN]; + static char title[BUFFER_LEN]; + static char playername[BUFFER_LEN]; + static char message[BUFFER_LEN]; + static char buff[BUFFER_LEN]; + static char speechtext[BUFFER_LEN]; + CHANUSER *u; + CHANUSER *speaker; dbref current; - const char *cname; - const char *name, *title; - char ctype[2]; - char ctitle[CU_TITLE_LEN]; + char *bp; + char *blockstr = ""; int na_flags = NA_INTER_LOCK; const char *someone = "Someone"; + dbref mogrifier = NOTHING; + char *ctype = NULL; + char *argv[10]; + int override_chatformat = 0; + memset(argv, 0, sizeof(argv)); - ctype[0] = type; - ctype[1] = '\0'; - - /* Make sure we can write to the channel before doing so */ + /* Make sure we can write to the channel before doing anything */ if (Channel_Disabled(channel)) return; - va_start(args, fmt); + speaker = onchannel(player, channel); -#ifdef HAS_VSNPRINTF - (void) vsnprintf(tbuf1, sizeof tbuf1, fmt, args); -#else - (void) vsprintf(tbuf1, fmt, args); -#endif - va_end(args); - tbuf1[BUFFER_LEN - 1] = '\0'; + snprintf(channame, BUFFER_LEN, "<%s>", ChanName(channel)); - cname = ChanName(channel); - u = onchannel(player, channel); - strcpy(ctitle, (u && CUtitle(u)) ? CUtitle(u) : ""); + if (!Channel_NoTitles(channel) && speaker && + CUtitle(speaker) && *CUtitle(speaker)) { + snprintf(title, BUFFER_LEN, "%s", CUtitle(speaker)); + } else { + title[0] = '\0'; + } - if (!Channel_NoTitles(channel) || !*ctitle) - title = ctitle; - else - title = NULL; - if (Channel_NoNames(channel)) - name = NULL; - else - name = accented_name(player); - if (!title && !name) - name = someone; + if (Channel_NoNames(channel)) { + playername[0] = '\0'; + } else { + snprintf(playername, BUFFER_LEN, "%s", accented_name(player)); + } + if (!title[0] && !playername[0]) { + snprintf(playername, BUFFER_LEN, "%s", someone); + } + + if (flags & CB_PRESENCE) { + ctype = "@"; + } else if (flags & CB_POSE) { + ctype = ":"; + } else if (flags & CB_SEMIPOSE) { + ctype = ";"; + } else if (flags & CB_EMIT) { + ctype = "|"; + } else { + ctype = "\""; + } + + snprintf(speechtext, BUFFER_LEN, "says"); + + snprintf(message, BUFFER_LEN, "%s", origmessage); + + if (GoodObject(ChanMogrifier(channel))) { + if (eval_lock(player, ChanMogrifier(channel), Use_Lock)) { + mogrifier = ChanMogrifier(channel); + + argv[0] = ctype; + argv[1] = ChanName(channel); + argv[2] = message; + argv[3] = playername; + argv[4] = title; + + blockstr = mogrify(mogrifier, "MOGRIFY`BLOCK", player, 6, argv, ""); + if (blockstr && *blockstr) { + notify(player, blockstr); + return; + } + // Do we override chatformats? + if (parse_boolean + (mogrify(mogrifier, "MOGRIFY`OVERRIDE", player, 6, argv, ""))) { + override_chatformat = 1; + } + + argv[1] = ChanName(channel); + argv[2] = ctype; + argv[3] = message; + argv[4] = title; + argv[5] = playername; + + argv[0] = channame; + snprintf(channame, BUFFER_LEN, "%s", + mogrify(mogrifier, "MOGRIFY`CHANNAME", player, 6, argv, + channame)); + + argv[0] = title; + snprintf(title, BUFFER_LEN, "%s", + mogrify(mogrifier, "MOGRIFY`TITLE", player, 6, argv, title)); + + argv[0] = playername; + snprintf(playername, BUFFER_LEN, "%s", + mogrify(mogrifier, "MOGRIFY`PLAYERNAME", player, 6, argv, + playername)); + + if (flags & CB_SPEECH) { + argv[0] = speechtext; + snprintf(speechtext, BUFFER_LEN, "%s", + mogrify(mogrifier, "MOGRIFY`SPEECHTEXT", player, 6, argv, + speechtext)); + } + + argv[0] = message; + snprintf(message, BUFFER_LEN, "%s", + mogrify(mogrifier, "MOGRIFY`MESSAGE", player, 6, argv, message)); + } + } + + bp = buff; + + *bp = '\0'; + + if (!(flags & CB_QUIET)) { + safe_str(channame, buff, &bp); + safe_chr(' ', buff, &bp); + } + + if (flags & CB_EMIT) { + safe_str(message, buff, &bp); + } else { + if (!(flags & CB_PRESENCE)) { + if (title[0]) { + safe_str(title, buff, &bp); + safe_chr(' ', buff, &bp); + } + } + safe_str(playername, buff, &bp); + switch (flags & CB_TYPE) { + case CB_POSE: + safe_chr(' ', buff, &bp); + case CB_SEMIPOSE: + safe_str(message, buff, &bp); + break; + case CB_SPEECH: + safe_format(buff, &bp, " %s, \"%s\"", T(speechtext), message); + break; + } + } + *bp = '\0'; - if (Channel_Interact(channel)) + // @chatformat + if (flags & CB_PRESENCE) { + snprintf(title, BUFFER_LEN, "%s", message); + snprintf(message, BUFFER_LEN, "%s %s", Name(player), title); + title[0] = '\0'; + } + + if (GoodObject(mogrifier)) { + argv[0] = ctype; + argv[1] = ChanName(channel); + argv[2] = message; + argv[3] = playername; + argv[4] = title; + argv[5] = buff; + snprintf(buff, BUFFER_LEN, "%s", + mogrify(mogrifier, "MOGRIFY`FORMAT", player, 6, argv, buff)); + } + + if (Channel_Interact(channel)) { na_flags |= (flags & CB_PRESENCE) ? NA_INTER_PRESENCE : NA_INTER_HEAR; + } + + if (!(flags & CB_NOSPOOF)) { + na_flags |= NA_SPOOF; + } for (u = ChanUsers(channel); u; u = u->next) { current = CUdbref(u); - if (!(((flags & 1) && Chanuser_Quiet(u)) || + if (!(((flags & CB_CHECKQUIET) && Chanuser_Quiet(u)) || Chanuser_Gag(u) || (IsPlayer(current) && !Connected(current)))) { - if (!vmessageformat(current, "CHATFORMAT", player, - na_flags | ((flags & CB_NOSPOOF) ? 0 : NA_SPOOF), - 6, ctype, cname, message, name, title, tbuf1)) { - notify_anything(player, na_one, ¤t, ns_esnotify, - na_flags | ((flags & CB_NOSPOOF) ? 0 : NA_SPOOF), - tbuf1); + if (override_chatformat + || !vmessageformat(current, "CHATFORMAT", player, + na_flags, + 6, + ctype, + ChanName(channel), + message, playername, title, buff)) { + notify_anything(player, na_one, ¤t, ns_esnotify, na_flags, buff); } } } if (ChanBufferQ(channel)) add_to_bufferq(ChanBufferQ(channel), 0, - (flags & CB_NOSPOOF) ? player : NOTHING, tbuf1); -} + (flags & CB_NOSPOOF) ? player : NOTHING, buff); + if (!(flags & CB_PRESENCE) && !speaker) { + notify_format(player, T("To channel %s: %s"), ChanName(channel), buff); + } +} /** Recall past lines from the channel's buffer. * We try to recall no more lines that are requested by the player, @@ -3648,33 +3889,6 @@ do_chan_buffer(dbref player, const char *name, const char *lines) } } -/* msg is a printf-style format that has exactly and only 2 %s specifiers - in it. */ -static void -format_channel_chat(CHAN *chan, CHANUSER *u, - dbref victim, int flags, const char *msg, const char *extra) -{ - const char *title = NULL; - char fmtbuff[BUFFER_LEN]; - if (extra && *extra) - title = extra; - else if (u && CUtitle(u)) - title = CUtitle(u); - if (Channel_NoNames(chan)) { - if (Channel_NoTitles(chan) || !title) { - channel_chat(chan, victim, flags, msg, '@', ChanName(chan), "Someone"); - snprintf(fmtbuff, BUFFER_LEN, msg, "Someone"); - } else { - snprintf(fmtbuff, BUFFER_LEN, msg, title); - } - } else { - snprintf(fmtbuff, BUFFER_LEN, msg, Name(victim)); - } - channel_chat(chan, victim, flags, fmtbuff, '@', "<%s> %s", - ChanName(chan), fmtbuff); -} - - /** Evaluate a channel lock with %0 set to the channel name. * \param c the channel to test. * \param p the object trying to pass the lock. @@ -3710,7 +3924,7 @@ eval_chan_lock(CHAN *c, dbref p, enum clock_type type) global_eval_context.wenv[0] = ChanName(c); for (n = 1; n < 10; n++) global_eval_context.wenv[n] = NULL; - retval = eval_boolexp(p, b, p); + retval = eval_boolexp(p, b, p, NULL); restore_global_env("eval_chan_lock", oldenv); return retval; } diff --git a/src/function.c b/src/function.c index 29aebb9..6823ed1 100644 --- a/src/function.c +++ b/src/function.c @@ -372,7 +372,7 @@ FUNTAB flist[] = { {"CHECKPASS", fun_checkpass, 2, 2, FN_REG | FN_DIRECTOR}, {"CLONE", fun_clone, 1, 1, FN_REG}, {"CMDS", fun_cmds, 1, 1, FN_REG}, -#ifdef CHAT_SYSTEM +#ifdef NVCHAT_SYSTEM {"COBJ", fun_cobj, 1, 1, FN_REG}, #endif /* CHAT_SYSTEM */ {"COMP", fun_comp, 2, 3, FN_REG}, -- 2.30.2