Logo Search packages:      
Sourcecode: xblast-tnt version File versions  Download package

chat.c

/*
 * file chat.c - manage chat data for both client and server
 *
 * $Id: chat.c,v 1.13 2005/03/02 15:04:16 lodott Exp $
 *
 * Program XBLAST
 * (C) by Oliver Vogel (e-mail: m.vogel@ndh.net)
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published
 * by the Free Software Foundation; either version 2; or (at your option)
 * any later version
 *
 * This program is distributed in the hope that it will be entertaining,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILTY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
 * Public License for more details.
 *
 * You should have received a copy of the GNU General Public License along
 * with this program; if not, write to the Free Software Foundation, Inc.
 * 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */

#include "chat.h"
#include "network.h"
#include "status.h"
#include "server.h"
#include "client.h"

/* max size for names */
#define MAX_CHAT_NAME_DISP 4
/* chat description size, at least 5 */
#define CHAT_DESCR_SIZE 5

#if CHAT_DESCR_SIZE<=MAX_CHAT_NAME_SIZE
#error "CHAT_DESCR_SIZE must be at least as large as MAX_CHAT_NAME_SIZE!"
#endif

#if CHAT_DESCR_SIZE<5
#error "CHAT_DESCR_SIZE must be at least 5!"
#endif

/* chat structure */
struct _xb_chat {
  XBChat     *next;
  unsigned char  fh;    /* sending host */
  unsigned char  fp;    /* sending local player */
  unsigned char  th;    /* receiving host */
  unsigned char  tp;    /* receiving local player */
  XBChatMode     how;   /* 0..numofplayers-1 (private), public, team */
  char           txt[CHAT_LINE_SIZE];
  size_t         len;
  XBChatStatus   status;
};

/* input lines for local players */
static XBChat *input[NUM_LOCAL_PLAYER+1];
static XBEventCode codes[NUM_LOCAL_PLAYER+1];
static XBBool initialized = XBFalse;
static XBBool listening = XBFalse;
static int active = -1;
/* list of all chats */
static XBChat *listFirst = NULL;
static XBChat *listLast = NULL;

/****************************
 * initialization, creation *
 ****************************/

/*
 * clear all chat data
 */
void
Chat_Clear() {
  XBChat *next;
  memset(input, 0, sizeof(input));
  memset(codes, 0, sizeof(codes));
  Dbg_Chat("clearing\n");
  while (listFirst != NULL) {
    next = listFirst->next;
    free(listFirst);
    listFirst = next;
  }
  listLast = NULL;
  initialized = XBTrue;
  listening = XBFalse;
  active = -1;
} /* Chat_Clear*/

/*
 * listen to chat events
 */
void
Chat_Listen(XBBool flag)
{
  if (!initialized) {
    Chat_Clear();
  }
  listening = flag;
  Dbg_Chat("%s\n", listening ? "active" : "inactive");
} /* Chat_Listen */

/*
 * return if listening
 */
XBBool
Chat_isListening()
{
  if (! initialized) {
    return XBFalse;
  }
  return listening;
} /* Chat_isListening */

/*
 * create a chat structure
 */
XBChat *
Chat_Create()
{
  XBChat *dat;
  Dbg_Chat("creating\n");
  /* get memory */
  dat = calloc(1,sizeof(XBChat));
  assert (dat != NULL);
  /* append to list */
  if (listLast == NULL) {
    listFirst = dat;
  } else {
    assert (listLast->next == NULL);
    listLast->next = dat;
  }
  listLast = dat;
  /* set successor */
  dat->next = NULL;
  /* mark as created */
  dat->status = XBCS_Created;
  return dat;
} /* Chat_Create */

/*
 * set chat parameters
 */
void
Chat_Set (XBChat *chat, unsigned char fh, unsigned char fp, unsigned char th, unsigned char tp, unsigned char how, const char *txt)
{
  assert (chat != NULL);
  /* store sender */
  chat->fh = fh;
  chat->fp = fp;
  /* store target */
  chat->th = th;
  chat->tp = tp;
  /* store mode */
  chat->how = how;
  /* store length of message, truncate if necessary */
  chat->len = strlen(txt);
  if (chat->len > CHAT_LINE_SIZE-1) {
    chat->len = CHAT_LINE_SIZE-1;
  }
  /* copy message */
  memcpy(chat->txt, txt, chat->len);
  chat->txt[chat->len] = (char)'\0';
  assert (chat != NULL);
  /* mark as set */
  chat->status = XBCS_Inactive;
} /* Chat_Set */

/*
 * make a chat line visible
 */
void
Chat_Receive(XBChat *chat)
{
  static char buf[CHAT_LINE_SIZE + 2* CHAT_DESCR_SIZE + 6];
  static char snd[20];
  static char snd0[CHAT_DESCR_SIZE+1];
  static char sep[3];
  static char trg[20];
  XBBool own;
  XBBool any;
  XBAtom from;
  XBAtom to;
  assert(NULL != chat);
  /* clear strings */
  memset(snd, 0, sizeof(snd));
  memset(snd0, 0, sizeof(snd0));
  memset(trg, 0, sizeof(trg));
  /* chat originated on local machine? */
  own = (Network_LocalHostId() == chat->fh);
  /* chat didn't come from a specific player? */
  any = (chat->fp == NUM_LOCAL_PLAYER);
  /* get name atom from sending player */
  from = any ? ATOM_INVALID : Network_GetPlayer2(chat->fh, chat->fp);
  /* determine sender string */
  if (from == ATOM_INVALID) {
    /* sender is host */
    sprintf(snd, "#%u", chat->fh);  /* length<=5 */
  } else {
    /* sender is player, truncate full name if necessary */
    strncpy(snd, GUI_AtomToString(from), sizeof(snd)-1);
  }
  /* truncate sender for display */
  strncpy(snd0, snd, MAX_CHAT_NAME_DISP);
  /* set separator */
  sprintf(sep, "->");
  /* now check mode for target string */
  switch (chat->how) {
  case XBCM_Public:
    sprintf(trg, "%s", "all"); /* length<=5 */
    break;
  case XBCM_Team:
    sprintf(trg, "%s", "team"); /* length<=5 */
    break;
  case XBCM_Private:
    to = Network_GetPlayer2(chat->th, chat->tp);
    if (to == ATOM_INVALID) {
      /* target is host */
      sprintf(trg, "#%u", chat->th); /* length<=5 */
    } else {
      /* target is player */
      strncpy(trg, GUI_AtomToString(to), sizeof(trg)-1);
    }
    break;
  case XBCM_System:
    sprintf(snd, "SYS");
    sprintf(snd0, "SYS");
    sprintf(sep, "-");
    sprintf(trg, "#%u", chat->fh);
    break;
  }
  /* full print to stdout */
  fprintf(stdout, "CHAT %s%s%s: %s\n", snd, sep, trg, chat->txt);
  /* truncated display in GUI */
  sprintf(buf,"%s:%s", snd0, chat->txt);
  SetChat(buf, XBTrue);
  /* mark as received */
  chat->status = XBCS_Received;
} /* Chat_Receive */

/*
 * flush out all messages with status 0
 */
void
Chat_Flush () {
  XBChat *next;
  while (listFirst != NULL) {
    if (listFirst->status != 0) {
      return;
    }
    next = listFirst->next;
    free(listFirst);
    listFirst = next;
  }
  if (listFirst == NULL) {
    listLast = NULL;
  }
} /* Chat_Flush */

/*
 * remove and return first line
 */
XBChat *
Chat_Pop() {
  XBChat *ret = NULL;
  Chat_Flush();
  if (listFirst != NULL) {
    ret = listFirst;
    listFirst = listFirst->next;
    if (listFirst == NULL) {
      listLast = NULL;
    }
  }
  return(ret);
} /* Chat_Pop */

/*********************
 * packing/unpacking *
 *********************/

/*
 * pack chat data for transmission
 */
size_t
Chat_PackData (XBChat *chat, char **data, unsigned *iob) {
  static char buf[CHAT_LINE_SIZE+2];
  unsigned char from;
  assert (chat != NULL);
  Dbg_Chat("packing (%u,%u)->(%u,%u)-%u-%s(%u)\n", chat->fh, chat->fp, chat->th, chat->tp, chat->how, chat->txt, chat->len);
  /* redefine local sender */
  from = (chat->fp == NUM_LOCAL_PLAYER) ? 0xFF : chat->fp;
  /* build buffer */
  buf[0] = 0xFF & ( (chat->fh << 4) + (from & 0x0F) );
  buf[1] = 0xFF & ( (chat->th << 4) + (chat->tp & 0x0F) );
  memcpy(buf+2, chat->txt, chat->len);
  buf[chat->len+2] = (char)'\0';
  /* return data */
  *data = buf;
  *iob = chat->how & 0xFF;
  return(chat->len+3);
} /* Chat_PackData */

/*
 * unpack chat data
 */
XBChat *
Chat_UnpackData (const char* data, size_t len, unsigned iob) {
  XBChat *chat = NULL;
  unsigned char from;
  if (len>2) {
    chat = Chat_Create();
    from = data[0] & 0x0F;
    if (from == 0x0F) {
      from = NUM_LOCAL_PLAYER;
    }
    Chat_Set(chat, data[0] >> 4, from, data[1] >> 4, data[1] & 0x0F, iob, data+2);
    Dbg_Chat("unpacking (%u,%u)->(%u,%u)-%u-%s(%u)\n", chat->fh, chat->fp, chat->th, chat->tp, chat->how, chat->txt, chat->len);
  }
  return chat;
} /* Chat_UnpackData */

/*********
 * input *
 *********/

/*
 * deactivate all input
 */
void
Chat_Deactivate()
{
  int p;
  assert(initialized);
  /* deactivating all input */
  for (p = 0; p <= NUM_LOCAL_PLAYER; p++) {
    if (input[p] != NULL) {
      input[p]->status = XBCS_Inactive;
    }
  }
  Dbg_Chat("deactivating all current input\n");
  active = -1;
} /* Chat_Deactivate */

/*
 * activate chat input for a player
 */
void
Chat_ActivateInput(unsigned int local)
{
  assert (local >= 0);
  assert (local <= NUM_LOCAL_PLAYER);
  assert (initialized);
  assert (input[local] != NULL);
  Chat_Deactivate();
  /* activate */
  input[local]->status = XBCS_Input;
  active = local;
} /* Chat_Activate */

/*
 * start chat input for a local player
 */
void
Chat_StartInput(unsigned int local)
{
  unsigned char id = Network_LocalHostId();
  if (id >= MAX_HOSTS) {
    Dbg_Chat("cannot start input, no host id\n");
    return;
  }
  assert (local <= NUM_LOCAL_PLAYER);
  assert (initialized);
  /* activating input for local */
  if (input[local] == NULL) {
    input[local] = Chat_Create();
    Chat_Set(input[local], id, local, 0, 0, XBCM_Public, "");
    Dbg_Chat("initializing chat input for player %u\n", local);
  }
  SetGet("Start Chatting",XBTrue);
  Chat_ActivateInput(local);
} /* Chat_StartInput */

/*
 * send input for a local player
 */
void
Chat_SendInput(unsigned int local)
{
  assert (local <= NUM_LOCAL_PLAYER);
  assert (initialized);
  if (input[local] == NULL) {
    Dbg_Chat("no chat input for player %u, cannot send\n", local);
    return;
  }
  switch (input[local]->status) {
  case XBCS_Inactive:
    Dbg_Chat("activating chat input for player %u before send\n", local);
    SetGet(input[local]->txt, XBTrue);
    Chat_ActivateInput(local);
    break;
  case XBCS_Input:
    Dbg_Chat("sending chat input #%u\n", local);
    switch (Network_GetType()) {
    case XBNT_Server: Server_ReceiveChat(input[local]); break;
    case XBNT_Client: Client_SendChat(input[local]); break;
    default:
      Dbg_Chat("failed to send, invalid net type\n");
      return;
    }
    input[local]->status = XBCS_Sent;
    SetGet(NULL, XBTrue);
    input[local] = NULL;
    active = -1;
    break;
  default:
    Dbg_Chat("failed to send chat input #%u, invalid status %u\n", local, input[local]->status);
    break;
  }
} /* Chat_SendInput */

/*
 * cancel input for local player
 */
void
Chat_CancelInput(unsigned int local)
{
  assert (local <= NUM_LOCAL_PLAYER);
  assert (initialized);
  if (input[local] == NULL) {
    Dbg_Chat("no chat input for player %u, cannot cancel\n", local);
    return;
  }
  switch (input[local]->status) {
  case XBCS_Inactive:
    Dbg_Chat("activating chat input for player %u before cancel\n", local);
    SetGet(input[local]->txt, XBTrue);
    Chat_ActivateInput(local);
    break;
  case XBCS_Input:
    Dbg_Chat("canceling chat input #%u\n", local);
    memset(input[local]->txt, 0, sizeof(input[local]->txt));
    input[local]->len = 0;
    SetGet("Chat canceled", XBTrue);
    input[local]->status = XBCS_Inactive;
    active = -1;
    break;
  default:
    Dbg_Chat("failed to send chat input #%u, invalid status %u\n", local, input[local]->status);
    break;
  }
} /* Chat_CancelInput */

/*
 * choose next target
 */
void
Chat_NextTarget(unsigned int local) {
  XBAtom atom;
  XBChat *chat;
  assert (local <= NUM_LOCAL_PLAYER);
  assert (initialized);
  if (input[local] == NULL) {
    Chat_StartInput(local);
  }
  chat = input[local];
  switch (chat->status) {
  case XBCS_Inactive:
    Dbg_Chat("activating chat input #%u\n", local);
    SetGet(input[local]->txt, XBTrue);
    Chat_ActivateInput(local);
  case XBCS_Input:
    switch (chat->how) {
    case XBCM_Public:
      chat->how = XBCM_Team;
      SetGet("Team message", XBTrue);
      return;
    case XBCM_Team:
      chat->how = XBCM_Private;
      if (Network_GetFirstOtherPlayer(chat->fh, local, &chat->th, &chat->tp)) {
      atom = Network_GetPlayer2(chat->th, chat->tp);
      SetGet((char *)GUI_AtomToString(atom), XBTrue);
      Dbg_Chat("first other player = %s (%u,%u)\n", GUI_AtomToString(atom), chat->th, chat->tp);
      } else {
      Dbg_Chat("no other players found, skipping private target\n");
      Chat_NextTarget(local);
      }
      break;
    case XBCM_Private:
      if (Network_GetNextOtherPlayer(chat->fh, local, &chat->th, &chat->tp)) {
      atom = Network_GetPlayer2(chat->th, chat->tp);
      SetGet((char *)GUI_AtomToString(atom), XBTrue);
      Dbg_Chat("next other player = %s (%u,%u)\n", GUI_AtomToString(atom), chat->th, chat->tp);
      } else {
      chat->how = XBCM_Public;
      SetGet("Public message", XBTrue);
      }
      break;
    default:
      break;
    }
    Dbg_Chat("how = %u\n", chat->how);
    break;
  default:
    Dbg_Chat("failed to change target for input #%u, invalid status %u\n", local, input[local]->status);
    break;
  }
} /* Chat_NextTarget */

/*
 * handle backspace for active input
 */
void
Chat_Backspace()
{
  assert (initialized);
  if (active < 0) {
    Dbg_Chat("no active input, backspace failed\n");
    return;
  }
  assert(active <= NUM_LOCAL_PLAYER);
  if (input[active] != NULL && input[active]->status == XBCS_Input) {
    size_t len = input[active]->len;
    if (len > 0) {
      input[active]->txt[--len] = (char) 0;
      input[active]->len = len;
      Dbg_Chat("backspacing chat input #%u\n", active);
      SetGet(input[active]->txt, XBTrue);
    } else {
      Dbg_Chat("chat input #%u empty, backspace failed\n", active);
    }
    return;
  }
} /* Chat_Backspace*/

/*
 * add a character for active input, return if overflow
 */
XBBool
Chat_AddAscii(char ascii)
{ 
  size_t len;
  assert (initialized);
  if (active < 0) {
    Dbg_Chat("no active input, failed to add char\n");
    return XBFalse;
  }
  assert(active <= NUM_LOCAL_PLAYER);
  /* find active input */
  len = input[active]->len;
  if (len < CHAT_LINE_SIZE-1) {
    input[active]->txt[len++] = ascii;
    input[active]->txt[len] = (char) 0;
    input[active]->len = len;
    SetGet(input[active]->txt, XBTrue);
    Dbg_Chat("adding character to chat input #%u, length %u = %s\n", active, strlen(input[active]->txt), input[active]->txt);
    return XBTrue;
  } else {
    Dbg_Chat("ignoring character, line for display #%u too long\n", active);
    return XBFalse;
  }
} /* Chat_AddAscii */

/****************************
 * event checking for menus *
 ****************************/

/*
 * add event code
 */
void
Chat_AddEventCode(unsigned local, XBEventCode ev)
{
  assert(initialized);
  assert(local <= NUM_LOCAL_PLAYER);
  codes[local] = ev;
  Dbg_Chat("assigning event type %u to local player %u\n", ev, local);
} /* Chat_AddEventCode */

/*
 * find display to which event code corresponds to
 */
unsigned char
Chat_FindCode(XBEventCode ev)
{
  unsigned id;
  if (!initialized || ! listening) {
    return 0xFF;
  }
  for (id = 0; id <= NUM_LOCAL_PLAYER; id++) {
    if (codes[id] == ev) {
      return id;
    }
  }
  return 0xFF;
} /* Chat_FindCode */

/*
 * get current event code of active chatter
 */
XBEventCode
Chat_GetCurrentCode() {
  if (!initialized || ! listening || active < 0) {
    return XBE_NONE;
  }
  return codes[active];
} /* Chat_GetCurrentCode */

/*
 * chat keys
 */
XBBool
Chat_Event(XBEventCode event, XBEventData data)
{
  unsigned int local;
  /* check if chat mode active */
  if (! listening) {
    return XBFalse;
  }
  /* if so, grab the chat events */
  switch (event) {
    /* a fixed chat key has been entered */
  case XBE_CHAT:
    /* redefine event to currently active */
    event = Chat_GetCurrentCode();
    /* restart event routine */
    assert(event != XBE_CHAT);
    return Chat_Event(event, data);
    /* ascii character for current input, if any */
  case XBE_ASCII:
    Chat_AddAscii((char) data.value);
    break;
    /* control characters */
  case XBE_CTRL:
    return XBFalse;
    /* chat keys */
  default:
    /* TODO: find better solution than +-1000*/
    if (data.value < 1000) {
      return XBFalse;
    }
    local = Chat_FindCode(event);
    if (local > NUM_LOCAL_PLAYER) {
      return XBFalse;
    }
    Dbg_Chat("chat event %u for display %u\n", data.value, local);
    switch (data.value - 1000) {
    case XBCE_START:
      Chat_StartInput(local);
      break;
    case XBCE_ESCAPE:
    case XBCE_CANCEL:
      Chat_CancelInput(local);
      break;
    case XBCE_CHANGE:
      Chat_NextTarget(local);
      break;
    case XBCE_SEND:
    case XBCE_ENTER:
      Chat_SendInput(local);
      break;
    case XBCE_BACK:
      Chat_Backspace(local);
      break;
    default:
      return XBFalse;
    }
    break;
  }
  return XBTrue;
}

/*
 * end of file chat.c
 */

Generated by  Doxygen 1.6.0   Back to index