/***************************************************************************
* Mud Telopt Handler 1.4 by Igor van den Hoven. 10 Jun 2011 *
***************************************************************************/
#include "mud.h"
#include "telnet.h"
// Set table size and check for errors. Call once at startup.
void init_msdp_table(void)
{
int index;
for (index = 0 ; *msdp_table[index].name ; index++)
{
if (strcmp(msdp_table[index].name, msdp_table[index+1].name) > 0)
{
if (*msdp_table[index+1].name)
{
log_printf("init_msdp_table: Improperly sorted variable: %s.", msdp_table[index]);
}
}
}
mud->msdp_table_size = index;
}
// Binary search on the msdp_table.
int msdp_find(char *var)
{
int val, bot, top, srt;
bot = 0;
top = mud->msdp_table_size - 1;
val = top / 2;
while (bot <= top)
{
srt = strcmp(var, msdp_table[val].name);
if (srt < 0)
{
top = val - 1;
}
else if (srt > 0)
{
bot = val + 1;
}
else
{
return val;
}
val = bot + (top - bot) / 2;
}
return -1;
}
// Update a variable and queue it if it's being reported.
void msdp_update_var(DESCRIPTOR_DATA *d, char *var, char *fmt, ...)
{
char buf[MAX_STRING_LENGTH];
int index;
va_list args;
if (d->msdp_data == NULL)
{
return;
}
index = msdp_find(var);
if (index == -1)
{
log_printf("msdp_update_var: Unknown variable: %s.", var);
return;
}
va_start(args, fmt);
vsprintf(buf, fmt, args);
va_end(args);
if (strcmp(d->msdp_data[index]->value, buf))
{
if (HAS_BIT(d->msdp_data[index]->flags, MSDP_FLAG_REPORTED))
{
SET_BIT(d->msdp_data[index]->flags, MSDP_FLAG_UPDATED);
SET_BIT(d->comm_flags, COMM_FLAG_MSDPUPDATE);
}
RESTRING(d->msdp_data[index]->value, buf);
}
}
// Update a variable and send it instantly.
void msdp_update_var_instant(DESCRIPTOR_DATA *d, char *var, char *fmt, ...)
{
char buf[MAX_STRING_LENGTH];
int index;
va_list args;
if (d->msdp_data == NULL)
{
return;
}
index = msdp_find(var);
if (index == -1)
{
log_printf("msdp_update_var_instant: Unknown variable: %s.", var);
return;
}
va_start(args, fmt);
vsprintf(buf, fmt, args);
va_end(args);
if (HAS_BIT(d->msdp_data[index]->flags, MSDP_FLAG_REPORTED))
{
descriptor_printf(d, "%c%c%c%c%s%c%s%c%c", IAC, SB, TELOPT_MSDP, MSDP_VAR, msdp_table[index].name, MSDP_VAL, buf, IAC, SE);
}
if (strcmp(d->msdp_data[index]->value, buf))
{
RESTRING(d->msdp_data[index]->value, buf);
}
}
// Send all reported variables that have been updated.
void msdp_send_update(DESCRIPTOR_DATA *d)
{
char buf[MAX_STRING_LENGTH];
int index, size;
if (d->msdp_data == NULL)
{
return;
}
size = sprintf(buf, "%c%c%c", IAC, SB, TELOPT_MSDP);
for (index = 0 ; index < mud->msdp_table_size ; index++)
{
if (HAS_BIT(d->msdp_data[index]->flags, MSDP_FLAG_UPDATED))
{
size += cat_sprintf(buf, "%c%s%c%s", MSDP_VAR, msdp_table[index].name, MSDP_VAL, d->msdp_data[index]->value);
DEL_BIT(d->msdp_data[index]->flags, MSDP_FLAG_UPDATED);
}
if (size > MAX_STRING_LENGTH - MAX_INPUT_LENGTH)
{
break;
}
}
cat_sprintf(buf, "%c%c", IAC, SE);
descriptor_printf(d, "%s", buf);
DEL_BIT(d->comm_flags, COMM_FLAG_MSDPUPDATE);
}
char *msdp_get_var(DESCRIPTOR_DATA *d, char *var)
{
int index;
if (d->msdp_data == NULL)
{
return NULL;
}
index = msdp_find(var);
if (index == -1)
{
log_printf("msdp_get_var: Unknown variable: %s.", var);
return NULL;
}
return d->msdp_data[index]->value;
}
void process_msdp_varval( DESCRIPTOR_DATA *d, char *var, char *val )
{
int var_index, val_index;
if (d->msdp_data == NULL)
{
return;
}
var_index = msdp_find(var);
if (var_index == -1)
{
return;
}
if (HAS_BIT(msdp_table[var_index].flags, MSDP_FLAG_CONFIGURABLE))
{
RESTRING(d->msdp_data[var_index]->value, val);
if (msdp_table[var_index].fun)
{
msdp_table[var_index].fun(d, var_index);
}
return;
}
// Commands only take variables as arguments.
if (HAS_BIT(msdp_table[var_index].flags, MSDP_FLAG_COMMAND))
{
val_index = msdp_find(val);
if (val_index == -1)
{
return;
}
if (msdp_table[var_index].fun)
{
msdp_table[var_index].fun(d, val_index);
}
return;
}
}
void msdp_command_list(DESCRIPTOR_DATA *d, int index)
{
int flag;
char buf[MAX_STRING_LENGTH];
if (!HAS_BIT(msdp_table[index].flags, MSDP_FLAG_LIST))
{
return;
}
flag = msdp_table[index].flags;
sprintf(buf, "%c%c%c%c%s", IAC, SB, TELOPT_MSDP, MSDP_VAR, msdp_table[index].name);
for (index = 0 ; index < mud->msdp_table_size ; index++)
{
if (flag != MSDP_FLAG_LIST)
{
if (HAS_BIT(d->msdp_data[index]->flags, flag) && !HAS_BIT(d->msdp_data[index]->flags, MSDP_FLAG_LIST))
{
cat_sprintf(buf, "%c%s", MSDP_VAL, msdp_table[index].name);
}
}
else
{
if (HAS_BIT(d->msdp_data[index]->flags, MSDP_FLAG_LIST))
{
cat_sprintf(buf, "%c%s", MSDP_VAL, msdp_table[index].name);
}
}
}
cat_sprintf(buf, "%c%c", IAC, SE);
descriptor_printf(d, "%s", buf);
}
void msdp_command_report(DESCRIPTOR_DATA *d, int index)
{
if (!HAS_BIT(msdp_table[index].flags, MSDP_FLAG_REPORTABLE))
{
return;
}
SET_BIT(d->msdp_data[index]->flags, MSDP_FLAG_REPORTED);
if (!HAS_BIT(msdp_table[index].flags, MSDP_FLAG_SENDABLE))
{
return;
}
SET_BIT(d->msdp_data[index]->flags, MSDP_FLAG_UPDATED);
}
void msdp_command_reset(DESCRIPTOR_DATA *d, int index)
{
int flag;
if (!HAS_BIT(msdp_table[index].flags, MSDP_FLAG_LIST))
{
return;
}
flag = msdp_table[index].flags &= ~MSDP_FLAG_LIST;
for (index = 0 ; index < mud->msdp_table_size ; index++)
{
if (HAS_BIT(d->msdp_data[index]->flags, flag))
{
d->msdp_data[index]->flags = msdp_table[index].flags;
}
}
}
void msdp_command_send(DESCRIPTOR_DATA *d, int index)
{
if (HAS_BIT(d->msdp_data[index]->flags, MSDP_FLAG_SENDABLE))
{
SET_BIT(d->msdp_data[index]->flags, MSDP_FLAG_UPDATED);
SET_BIT(d->comm_flags, COMM_FLAG_MSDPUPDATE);
}
}
void msdp_command_unreport(DESCRIPTOR_DATA *d, int index)
{
if (!HAS_BIT(msdp_table[index].flags, MSDP_FLAG_REPORTABLE))
{
return;
}
DEL_BIT(d->msdp_data[index]->flags, MSDP_FLAG_REPORTED);
}
// Comment out if you don't want Arachnos Intermud support
void msdp_configure_arachnos(DESCRIPTOR_DATA *d, int index)
{
char var[MAX_INPUT_LENGTH], val[MAX_INPUT_LENGTH];
char mud_name[MAX_INPUT_LENGTH], mud_host[MAX_INPUT_LENGTH], mud_port[MAX_INPUT_LENGTH];
char msg_user[MAX_INPUT_LENGTH], msg_time[MAX_INPUT_LENGTH], msg_body[MAX_INPUT_LENGTH];
char mud_players[MAX_INPUT_LENGTH], mud_uptime[MAX_INPUT_LENGTH], mud_update[MAX_INPUT_LENGTH];
char *pti, *pto;
struct tm timeval_tm;
time_t timeval_t;
var[0] = val[0] = mud_name[0] = mud_host[0] = mud_port[0] = msg_user[0] = msg_time[0] = msg_body[0] = mud_players[0] = mud_uptime[0] = mud_update[0] = 0;
pti = d->msdp_data[index]->value;
while (*pti)
{
switch (*pti)
{
case MSDP_VAR:
pti++;
pto = var;
while (*pti > MSDP_ARRAY_CLOSE)
{
*pto++ = *pti++;
}
*pto = 0;
break;
case MSDP_VAL:
pti++;
pto = val;
while (*pti > MSDP_ARRAY_CLOSE)
{
*pto++ = *pti++;
}
*pto = 0;
if (!strcmp(var, "MUD_NAME"))
{
strcpy(mud_name, val);
}
else if (!strcmp(var, "MUD_HOST"))
{
strcpy(mud_host, val);
}
else if (!strcmp(var, "MUD_PORT"))
{
strcpy(mud_port, val);
}
else if (!strcmp(var, "MSG_USER"))
{
strcpy(msg_user, val);
}
else if (!strcmp(var, "MSG_TIME"))
{
timeval_t = (time_t) atoll(val);
timeval_tm = *localtime(&timeval_t);
strftime(msg_time, 20, "%T %D", &timeval_tm);
}
else if (!strcmp(var, "MSG_BODY"))
{
strcpy(msg_body, val);
}
else if (!strcmp(var, "MUD_UPTIME"))
{
timeval_t = (time_t) atoll(val);
timeval_tm = *localtime(&timeval_t);
strftime(mud_uptime, 20, "%T %D", &timeval_tm);
}
else if (!strcmp(var, "MUD_UPDATE"))
{
timeval_t = (time_t) atoll(val);
timeval_tm = *localtime(&timeval_t);
strftime(mud_update, 20, "%T %D", &timeval_tm);
}
else if (!strcmp(var, "MUD_PLAYERS"))
{
strcpy(mud_players, val);
}
break;
default:
pti++;
break;
}
}
if (*mud_name && *mud_host && *mud_port)
{
if (!strcmp(msdp_table[index].name, "ARACHNOS_DEVEL"))
{
if (*msg_user && *msg_time && *msg_body)
{
arachnos_devel("%s %s@%s:%s devtalks: %s", msg_time, msg_user, mud_host, mud_port, msg_body);
}
}
else if (!strcmp(msdp_table[index].name, "ARACHNOS_MUDLIST"))
{
if (*mud_uptime && *mud_update && *mud_players)
{
arachnos_mudlist("%18.18s %14.14s %5.5s %17.17s %17.17s %4.4s", mud_name, mud_host, mud_port, mud_update, mud_uptime, mud_players);
}
}
}
}
struct msdp_type msdp_table[] =
{
{ "ALIGNMENT", MSDP_FLAG_SENDABLE|MSDP_FLAG_REPORTABLE, NULL },
{ "ARACHNOS_DEVEL", MSDP_FLAG_CONFIGURABLE|MSDP_FLAG_REPORTABLE, msdp_configure_arachnos },
{ "ARACHNOS_MUDLIST", MSDP_FLAG_CONFIGURABLE, msdp_configure_arachnos },
{ "COMMANDS", MSDP_FLAG_COMMAND|MSDP_FLAG_LIST, NULL },
{ "CONFIGURABLE_VARIABLES", MSDP_FLAG_CONFIGURABLE|MSDP_FLAG_LIST, NULL },
{ "HEALTH", MSDP_FLAG_SENDABLE|MSDP_FLAG_REPORTABLE, NULL },
{ "EXPERIENCE", MSDP_FLAG_SENDABLE|MSDP_FLAG_REPORTABLE, NULL },
{ "EXPERIENCE_MAX", MSDP_FLAG_SENDABLE|MSDP_FLAG_REPORTABLE, NULL },
{ "HEALTH_MAX", MSDP_FLAG_SENDABLE|MSDP_FLAG_REPORTABLE, NULL },
{ "LEVEL", MSDP_FLAG_SENDABLE|MSDP_FLAG_REPORTABLE, NULL },
{ "LIST", MSDP_FLAG_COMMAND, msdp_command_list },
{ "LISTS", MSDP_FLAG_LIST, NULL },
{ "MANA", MSDP_FLAG_SENDABLE|MSDP_FLAG_REPORTABLE, NULL },
{ "MANA_MAX", MSDP_FLAG_SENDABLE|MSDP_FLAG_REPORTABLE, NULL },
{ "MONEY", MSDP_FLAG_SENDABLE|MSDP_FLAG_REPORTABLE, NULL },
{ "MOVEMENT", MSDP_FLAG_SENDABLE|MSDP_FLAG_REPORTABLE, NULL },
{ "MOVEMENT_MAX", MSDP_FLAG_SENDABLE|MSDP_FLAG_REPORTABLE, NULL },
{ "REPORT", MSDP_FLAG_COMMAND, msdp_command_report },
{ "REPORTABLE_VARIABLES", MSDP_FLAG_REPORTABLE|MSDP_FLAG_LIST, NULL },
{ "REPORTED_VARIABLES", MSDP_FLAG_REPORTED|MSDP_FLAG_LIST, NULL },
{ "RESET", MSDP_FLAG_COMMAND, msdp_command_reset },
{ "ROOM", MSDP_FLAG_REPORTABLE, NULL },
{ "ROOM_EXITS", MSDP_FLAG_SENDABLE|MSDP_FLAG_REPORTABLE, NULL },
{ "SEND", MSDP_FLAG_COMMAND, msdp_command_send },
{ "SENDABLE_VARIABLES", MSDP_FLAG_SENDABLE|MSDP_FLAG_LIST, NULL },
{ "SPECIFICATION", MSDP_FLAG_SENDABLE, NULL },
{ "UNREPORT", MSDP_FLAG_COMMAND, msdp_command_unreport },
{ "", 0, NULL } // End of table marker
};