Fix them CTF say_team bugs!


 

Posted by Riviera (62.20.148.*) on July 05, 1999 at 06:46:51:

Author: Peter 'Riviera' Engstrom


Who needs it: If say_team %a%a%a%a%a%a%a%a%a%a%a%a%a%a%a%a%a%a%a%a%a%a%a%a%a%a%a%a%a%a%a%a%a%a%a%a%a%a%a%a%a%a%a%a%a%a%a%a%a%a%a%a%a%a%a%a%a%a%a%a%a%a%a%a%a%a%a%a%a%a%a%a crashes your server

This code fixes two bugs in the CTF say_team code tha can crash your server. One bug is that there were no length checks during the parsing and the other that the maximum message length was about 1024 characters. Fear no more. There's also some optimizations and the parsing has it's own function (to be used for radio messages or similar).


vec_t
VectorLengthSqr(vec3_t v)
{
        int            i;
        float   length;
       
        length = 0.0f;
        for (i=0; i<3; i++)
               length += v[i]*v[i];
       
        return length;
}

static void
CTFSay_Team_Location(edict_t * who, char *buf)
{
    edict_t *what = NULL;
    edict_t *hot = NULL;
    float hotdist = 222222222.2, newdist;
    vec3_t v;
    int hotindex = 999;
    int i;
    gitem_t *item;
    int nearteam = -1;
    edict_t *flag1, *flag2;
    qboolean hotsee = false;
    qboolean cansee;
    int ibuf = 0;
       
    while ((what = loc_findradius(what, who->s.origin, 1024)) != NULL) {
               // find what in loc_classnames
               for (i = 0; loc_names[i].classname; i++)
                       if (!strcmp(what->classname, loc_names[i].classname))
                               break;
                       if (!loc_names[i].classname)
                               continue;
                       // something we can see get priority over something we can't
                       cansee = loc_CanSee(what, who);
                       if (cansee && !hotsee) {
                               hotsee = true;
                               hotindex = loc_names[i].priority;
                               hot = what;
                               VectorSubtract(what->s.origin, who->s.origin, v);
                               hotdist = VectorLengthSqr(v);
                               continue;
                       }
                       // if we can't see this, but we have something we can see, skip it
                       if (hotsee && !cansee)
                               continue;
                       if (hotsee && hotindex < loc_names[i].priority)
                               continue;
                       VectorSubtract(what->s.origin, who->s.origin, v);
                       newdist = VectorLengthSqr(v);
                       if (newdist < hotdist ||
                               (cansee && loc_names[i].priority < hotindex)) {
                               hot = what;
                               hotdist = newdist;
                               hotindex = i;
                               hotsee = loc_CanSee(hot, who);
                       }
    }
       
    if (!hot) {
               memcpy(buf, "nowhere", 8);
               return;
    }
       
    // we now have the closest item
    // see if there's more than one in the map, if so
    // we need to determine what team is closest
    what = NULL;
    while ((what = G_Find(what, FOFS(classname), hot->classname)) != NULL) {
               if (what == hot)
                       continue;
               // if we are here, there is more than one, find out if hot
               // is closer to red flag or blue flag
               if ((flag1 = G_Find(NULL, FOFS(classname), "item_flag_team1")) != NULL &&
                       (flag2 = G_Find(NULL, FOFS(classname), "item_flag_team2")) != NULL) {
                       VectorSubtract(hot->s.origin, flag1->s.origin, v);
                       hotdist = VectorLengthSqr(v);
                       VectorSubtract(hot->s.origin, flag2->s.origin, v);
                       newdist = VectorLengthSqr(v);
                       if (hotdist < newdist)
                               nearteam = CTF_TEAM1;
                       else if (hotdist > newdist)
                               nearteam = CTF_TEAM2;
               }
               break;
    }
       
    if ((item = FindItemByClassname(hot->classname)) == NULL) {
               memcpy(buf, "nowhere", 8);
               return;
    }
       
    // in water?
    if (who->waterlevel) {
               memcpy(buf, "in the water ", 13);
               ibuf = 13;
    }
    else
               *buf = 0;
       
    // near or above
    VectorSubtract(who->s.origin, hot->s.origin, v);
    if (fabs(v[2]) > fabs(v[0]) && fabs(v[2]) > fabs(v[1])) {
               if (v[2] > 0) {
                       memcpy(buf + ibuf, "above ", 6);
                       ibuf += 6;
               }
               else {
                       memcpy(buf + ibuf, "below ", 6);
                       ibuf += 6;
               }
    }
    else {
               memcpy(buf + ibuf, "near ", 5);
               ibuf += 5;
    }
       
    if (nearteam == CTF_TEAM1) {
               memcpy(buf + ibuf, "the red ", 8);
               ibuf += 8;
    }
    else if (nearteam == CTF_TEAM2) {
               memcpy(buf + ibuf, "the blue ", 9);
               ibuf += 9;
    }
    else {
               memcpy(buf + ibuf, "the ", 4);
               ibuf += 4;
    }
       
    strcpy(buf + ibuf, item->pickup_name);
}

static void
CTFSay_Team_Armor(edict_t *who, char *buf)
{
        gitem_t        *item;
        int                    index, cells;
        int                    power_armor_type;
       
        *buf = 0;
       
        power_armor_type = PowerArmorType(who);
        if (power_armor_type)
        {
               cells = who->client->pers.inventory[ITEM_INDEX(FindItem("cells"))];
               if (cells)
                       sprintf(buf+strlen(buf), "%s with %i cells ",
                       (power_armor_type == POWER_ARMOR_SCREEN) ?
                       "Power Screen" : "Power Shield", cells);
        }
       
        index = ArmorIndex(who);
        if (index)
        {
               item = GetItemByIndex(index);
               if (item) {
                       if (*buf)
                               strcat(buf, "and ");
                       sprintf(buf+strlen(buf), "%i units of %s",
                               who->client->pers.inventory[index], item->pickup_name);
               }
        }
       
    if (!*buf)
               memcpy(buf, "no armor", 9);
}

static void
CTFSay_Team_Health(edict_t * who, char *buf)
{
    if (who->health <= 0)
               memcpy(buf, "dead", 5);
    else
               sprintf(buf, "%i health", who->health);
}

static void
CTFSay_Team_Tech(edict_t *who, char *buf)
{
        gitem_t *tech;
        int i;
       
        // see if the player has a tech powerup
        i = 0;
        while (tnames[i]) {
               if ((tech = FindItemByClassname(tnames[i])) != NULL &&
                       who->client->pers.inventory[ITEM_INDEX(tech)]) {
                       sprintf(buf, "the %s", tech->pickup_name);
                       return;
               }
               i++;
        }
    memcpy(buf, "no powerup", 11);
}

static void
CTFSay_Team_Weapon(edict_t *who, char *buf)
{
        if (who->client->pers.weapon)
               strcpy(buf, who->client->pers.weapon->pickup_name);
        else
               memcpy(buf, "none", 5);
}

static void
CTFSay_Team_Sight(edict_t *who, char *buf)
{
        int i;
        edict_t *targ;
        int n = 0;
        char s[1024];
        char s2[1024];
       
        *s = *s2 = 0;
        for (i = 1; i <= game.maxclients; i++) {
               targ = g_edicts + i;
               if (!targ->inuse ||
                       targ->svflags & SVF_NOCLIENT ||
                       targ == who ||
                       !loc_CanSee(targ, who))
                       continue;
               if (*s2) {
                       if (strlen(s) + strlen(s2) + 3 < sizeof(s)) {
                               if (n)
                                      strcat(s, ", ");
                               strcat(s, s2);
                               *s2 = 0;
                       }
                       n++;
               }
               strcpy(s2, targ->client->pers.netname);
        }
        if (*s2) {
               if (strlen(s) + strlen(s2) + 6 < sizeof(s)) {
                       if (n)
                               strcat(s, " and ");
                       strcat(s, s2);
               }
               strcpy(buf, s);
        }
        else
               memcpy(buf, "no one", 7);
}

void
CTFParseTeamMessage(edict_t * who, char *msg, char *outmsg, int size)
{
    char buf[1024];
    char *p;
        int len;
       
    outmsg[0] = 0;
       
    if (*msg == '\"') {
               msg[strlen(msg) - 1] = 0;
               msg++;
    }
       
    for (p = outmsg; *msg && (p - outmsg) < size - 1; msg++) {
               if (*msg == '%') {
                       switch (*++msg) {
                       case 'l':
                       case 'L':
                               CTFSay_Team_Location(who, buf);
                               len = strlen(buf);
                               if (p-outmsg+len < size) {
                                      memcpy(p, buf, len);
                                       p += len;
                               }
                               break;
                       case 'a':
                       case 'A':
                               CTFSay_Team_Armor(who, buf);
                               len = strlen(buf);
                               if (p-outmsg+len < size) {
                                      memcpy(p, buf, len);
                                      p += len;
                               }
                               break;
                       case 'h':
                       case 'H':
                               CTFSay_Team_Health(who, buf);
                               len = strlen(buf);
                               if (p-outmsg+len < size) {
                                      memcpy(p, buf, len);
                                      p += len;
                               }
                               break;
                       case 't':
                       case 'T':
                               CTFSay_Team_Tech(who, buf);
                               len = strlen(buf);
                               if (p-outmsg+len < size) {
                                      memcpy(p, buf, len);
                                      p += len;
                               }
                               break;
                       case 'w':
                       case 'W':
                               CTFSay_Team_Weapon(who, buf);
                               len = strlen(buf);
                               if (p-outmsg+len < size) {
                                      memcpy(p, buf, len);
                                      p += len;
                               }
                               break;
                       case 'n':
                       case 'N':
                               CTFSay_Team_Sight(who, buf);
                               len = strlen(buf);
                               if (p-outmsg+len < size) {
                                      memcpy(p, buf, len);
                                      p += len;
                               }
                               break;
                       case '%':
                               *p++ = '%';
                               break;
                       case '\0':
                               --msg;
                               break;
                       default:
                               break;
                       }
               }
               else
                       *p++ = *msg;
    }
    *p = 0;
}

void
CTFSay_Team(edict_t * who, char *msg)
{
    char outmsg[160];
    int i;
    edict_t *cl_ent;
       
        //FIXME: Insert flood protection here
       
    CTFParseTeamMessage(who, msg, outmsg, sizeof (outmsg));
       
        for (i = 1; i <= game.maxclients; i++) {
               cl_ent = g_edicts + i;
               if (!cl_ent->inuse)
                       continue;
               if (cl_ent->client->resp.ctf_team == who->client->resp.ctf_team)
                       gi.cprintf(cl_ent, PRINT_CHAT, "(%s): %s\n",
                       who->client->pers.netname, outmsg);
        }
}


Good luck!!!