September 2009 Archives

Valve's Double Entendre

Crash Course poster

Today's the big launch day of Valve's much an­tic­i­pated Left 4 Dead up­date in­clud­ing the two-level cam­paign Crash Course… sigh.

I re­ally won­der if Valve has a QA de­part­ment. It seems to break in one of three ways—ei­ther it silently launches a local server (de­spite lobby set­tings), puts the server you con­nect to into a live­lock re­quir­ing a man­ual restart, or makes each player con­nect to their own local IP. It's not an iso­lated prob­lem, with their fo­rums being full of com­plaints. It makes me yearn for the days when patches weren't forced down your throat by some crap like Steam.

It ac­tu­ally works in sin­gle-player, so I tried the new cam­paign there. It uses a Death Toll theme, look­ing a lot like the level where you start in the church. In what seems to be a stor­age area, you jump from room to room or walk down the al­leys con­nect­ing the rooms. Each level is un­in­ven­tive, look­ing like one big area in­stead of smaller ones that vary to keep it fresh. Every room and alley looks the same, just a ran­dom scat­ter­ing of boxes and cars.

Re­ally, there's not much to left say about Crash Course. Given how short it is and how un­fin­ished and mo­not­o­nous it looks, I can't imag­ine my­self play­ing it much in ver­sus. Much rather be play­ing Ab­solute Zero or Death Aboard. If ver­sus ac­tu­ally worked, that is.

Windows 7 to support non-OEM CableCARD

TV-on-PC users re­joice! Ca­ble­CARD sup­port is fi­nally com­ing to PC ex­pan­sion cards avail­able through re­tail chan­nels.

Win­dows has long used the Broad­cast Dri­ver Ar­chi­tec­ture (BDA) to com­mu­ni­cate with TV tuner cards, but the folks in charge of Ca­ble­CARD had a major prob­lem with it: there's no DRM sup­port. Be­cause of this they for­bade sell­ing any add-on cards alone, and any TV tuners you could buy would only work with ana­log or Clear­QAM (un­en­crypted) chan­nels, which typ­i­cally means low-def or local chan­nels only. The only way to get Ca­ble­CARD sup­port on a PC was to buy a full OEM setup that in­cluded the tuners.

One of the new fea­tures in Win­dows 7 is the new PBDA (Pro­tected BDA) API which, you guessed it, sup­ports DRM. With PBDA, WDDM, and HDCP, the sig­nal can be pro­tected from the tuner all the way to the mon­i­tor. Mi­crosoft kept quiet and avoided ac­knowl­edg­ing any ques­tions about it dur­ing the test, but many testers spec­u­lated it would be part of a big­ger push from Mi­crosoft to open up Ca­ble­CARD add-on sup­port, and it turns out we were right. I wouldn't be sur­prised to see an­nounce­ments of new hard­ware from Haup­pauge and other tuner man­u­fac­tur­ers.

I watch a lot of TV—usu­ally in the form of a small box in the cor­ner of the screen while I'm cod­ing, so I've got plenty of time. I cur­rently have two Haup­pauge HVR-2250 cards for a total of four tuners. This works great for my local chan­nels like NBC and FOX but there are al­ways some shows I like on cable chan­nels, so I'll be look­ing for­ward to some of the new hard­ware, like Ceton's new 6-tuner Ca­ble­CARD be­he­moth.

Playing a lot of Section 8

Section 8

A month or so ago, I learned that an ac­quain­tance of mine from a few years ago had got a job at TimeGate Stu­dios mak­ing maps for their new title, Sec­tion 8. I missed the closed beta, but had enough fun with the open one to make me want to ac­quire the full game when it came out.

Ap­par­ently about half of their dev team are hard­core Tribes play­ers, and to a de­gree it shows. The maps, while not as big as the ones in Tribes, are vast and open com­pared to most mod­ern games. Play­ers are able to cus­tomize their sol­dier with two guns, two sec­ondary util­i­ties, and ten points to dis­trib­ute across a num­ber of pas­sive powerups. Play­ers also get a shield, a jet pack, su­per-sprint­ing, and lock-on.

Sec­tion 8 is a cap­ture-the-point game with a twist—as play­ers start to earn points for var­i­ous achieve­ments, the game au­to­mat­i­cally starts up mini-ob­jec­tives to com­plete. This turns out to be a great way to keep things chal­leng­ing and fresh, while giv­ing play­ers a good rea­son to come out of their bases. If you tur­tle in a base and don't com­plete your ob­jec­tives, the other team will win. This gives the game a higher learn­ing curve than most other games, but most should only take a few days to get it down.

Sec­tion 8 is a mul­ti­player game, so I'd cau­tion you against buy­ing it if you're ex­pect­ing a good sin­gle-player story. Some sites men­tions that it has a sin­gle-player cam­paign, but it re­ally only con­sists of mul­ti­player with bots tied to­gether in an hour long tu­to­r­ial story where an un­seen gen­eral is yelling rea­sons to com­plete all the ob­jec­tives you'd nor­mally com­plete in mul­ti­player. But that's okay—the real fun is in the mul­ti­player.

Spawn­ing is a unique ex­pe­ri­ence in this game. You get hurled out of ships in orbit and are able to break mid-air to ad­just your land­ing po­si­tion. With a bit of skill and luck, you can ac­tu­ally land on en­e­mies for a very sat­is­fy­ing in­stant kill. To com­bat you land­ing in enemy ter­ri­tory, anti-air comes stan­dard in all bases and play­ers can de­ploy more if they choose. Anti-air be­comes cru­cial to game­play—if yours gets taken down, the en­e­mies will start to swarm in right on top of you. Play­ers drop­ping down within an anti-air ra­dius will ei­ther be shot down or take heavy dam­age be­fore ever see­ing an­other player.

The maps will re­mind you a lot of Tribes. They are big and open, with 2-4 bases scat­tered around them. They all fea­ture dead zones defin­ing their bound­aries, which can change de­pend­ing on the max­i­mum num­ber of play­ers. The bases are pretty good, with an in­tri­cate fu­tur­is­tic de­sign. De­spite the large maps, the area in be­tween the bases are for the most part also well very de­tailed. The mini-ob­jec­tives will usu­ally take place in these areas, so you may end up spend­ing more time out­side of a base than in one.

Char­ac­ter cus­tomiza­tion is one of the cru­cial areas of the game. You get ten points to spread across var­i­ous pas­sive power-ups mod­i­fy­ing your armor, shield, at­tack strength, lock-on du­ra­tion and re­sis­tance, ac­cu­racy, and a lot of other things. This is prob­a­bly the biggest area to mas­ter—even after three weeks play­ing (two in the beta, one in final), I am still tweak­ing my pas­sives to bet­ter sup­port my play style. Sev­eral of them are very ob­vi­ous in their use­ful­ness, but oth­ers take a bit of play time to fully grasp.

Part of your load-out is two weapons. Un­like most games, Sec­tion 8 makes no dis­tinc­tion be­tween pri­mary and sec­ondary weapons—it lets you choose what­ever com­bi­na­tion you want, be it a pis­tol and knife or a ma­chine gun and mis­sile launcher. There are sev­eral weapons to choose from, but un­for­tu­nately there isn't much di­ver­sity be­tween them. If there is one area this game doesn't shine in, it's this. What we have now are ba­si­cally all your bor­ing stan­dard bul­let-based fu­tur­is­tic army weapons. Each varies in ac­cu­racy, shield pierc­ing abil­ity, and armor dam­age, but they're all just bor­ing stuff we've seen a thou­sand times be­fore. I would have liked to have seen some Tribes-in­spired en­ergy weapons.

One thing the game's weaponry took from Tribes is the pro­jec­tile-based guns, com­pared to games like Quake where the shot­gun was hitscan (in­stant-hit). Ask any Quake Cus­tom-TF player, and they will con­firm the $25 shot­gun can often be for­mi­da­ble against the $3000 rocket launcher if the wielder has good enough aim. In Sec­tion 8, all of the bul­lets fired are ac­tual pro­jec­tile tracer rounds that take some small time to reach their tar­get. This is one of my fa­vorite fea­tures, and I'm often dis­ap­pointed that more games don't use it. Forc­ing play­ers to lead their shots in­tro­duces a whole new di­men­sion of skill to the game.

The two util­i­ties you pick for your class are also pretty im­por­tant. These in­clude grenades, mor­tars, sen­sors, re­pair kits, and some oth­ers. Some of these pro­vide a ser­vice to your whole team, so with some good or­ga­ni­za­tion you could get a re­ally un­stop­pable squad. The grenades are ba­si­cally proxy mines that you throw. They stick to walls, ve­hi­cles, and blow up if they get near an enemy. The mor­tars are like pre­ci­sion MIRVs, let­ting you drop con­cen­trated groups of ex­plo­sions that are great against pretty much every­thing.

There are a few things all play­ers get. The first is a su­per-sprint, let­ting you fairly quickly travel the long dis­tances of the map. You can use it to ram en­e­mies, tak­ing off their shield. The sec­ond is a jet pack with about five sec­onds of use be­fore recharg­ing. It is ba­si­cally only use­ful for jump­ing small hur­dles, or a quick large jump onto build­ings in con­junc­tion with sprint.

The third is prob­a­bly the most con­tro­ver­sial fea­ture of the game: lock-on. While many mul­ti­player games have aim-bot cheats made for them, it's ac­tu­ally built into Sec­tion 8 as a slow charg­ing 5-10 sec­ond lock-on abil­ity. I've no­ticed a lot of mediocre play­ers have grown to de­pend on it, and all the good play­ers take ad­van­tage of this by de­vel­op­ing strate­gies to make oth­ers waste their lock-on be­fore jump­ing in with a good aim. They de­serve some major props for cre­at­ing a well bal­anced aim-bot that doesn't feel to­tally lame.

As you com­plete ob­jec­tives, frag en­e­mies, and cap­ture points, you will be awarded with money to spend on de­ploy­ables. You can buy sup­ply de­pots, tur­rets, mechs, tanks, and anti-air. All of these are very ef­fec­tive in their own ways, but for some rea­son many play­ers seem to for­get to de­ploy any­thing until the match is al­most over.

The game does have some flaws that will hope­fully be patched soon. Like sev­eral Games For Win­dows Live games be­fore it, Sec­tion 8 has plenty of peo­ple un­able to launch it due to out­dated GFWL in­stalls. The game pulls down servers from the mas­ter list very slowly over an XBox-en­crypted link. The in-game voice chat doesn't fea­ture au­to­matic gain, mak­ing most voices get drowned out by the ac­tion. The per­sis­tent stats sys­tem looks pretty cool but has been plagued with is­sues since the launch. Servers seem to be in­fre­quently un­sta­ble, some­times crash­ing or boot­ing play­ers. A few of the servers I've con­nected to seemed to lose sync, caus­ing jumps as every­thing cor­rected it­self every few sec­onds. Oddly enough none of these flaws ex­isted in the open beta, mak­ing me won­der if the GFWL in­te­gra­tion, which wasn't in the beta, had any­thing to do with it.

Flaws aside, I'm very happy with this game. There's a lot of fun to be had, and it de­liv­ers one of things I want most in a game: a large scale of skill that isn't reach­able after only a few weeks of play­ing. Is it worth $50? I'm not sure there's enough con­tent—8 maps—for me to say so. Maybe wait until it's $30 or $40. I'm hop­ing they re­lease the map ed­i­tor and en­able mods. It's got a lot of po­ten­tial for some good player-made con­tent.

Efficient stream parsing in C++

A while ago I wrote about cre­at­ing a good parser and while the non-block­ing idea was spot-on, the rest of it re­ally isn’t very good in C++ where we have the power of tem­plates around to help us.

I’m cur­rently fin­ish­ing up a HTTP li­brary and have been re­vis­ing my views on stream pars­ing be­cause of it. I’m still not en­tirely set on my over­all im­ple­men­ta­tion, but I’m near­ing com­ple­tion and am ready to share my ideas. First, I’ll list my re­quire­ments:

To ac­com­plish this I broke this out into three lay­ers: a core parser, a buffer, and a buffer parser.

The core parser

De­sign­ing the core parser was sim­ple. I be­lieve I al­ready have a solid C++ parser de­sign in my XML li­brary, so I’m reusing that con­cept. This is fully in-situ pull parser that op­er­ates on a range of bidi­rec­tional it­er­a­tors and re­turns back a sub-range of those it­er­a­tors. The pull func­tion re­turns ok when it parses a new el­e­ment, done when it has reached a point that could be con­sid­ered an end of the stream, and need_more when an el­e­ment can’t be ex­tracted from the passed in it­er­a­tor range. Using this parser is pretty sim­ple:

typedef std::deque<char> buffer_type;
typedef http::parser<buffer_type::iterator> parser_type;

buffer_type buffer;

parser_type p;
parser_type::node_type n;
parser_type::result_type r;

do
{
  push_data(buffer); // add data to buffer from whatever I/O source.

  std::deque<char>::iterator first = buffer.begin();

  while((r = p.parse(first, buffer.end(), n)) == http::result_types::ok)
  {
    switch(n.type)
    {
      case http::node_types::method:
      case http::node_types::uri:
      case http::node_types::version:
    }
  }

  buffer.erase(buffer.begin(), first); // remove all the used
                                       // data from the buffer.
} while(r == http::result_types::need_more);

By let­ting the user pass in a new range of it­er­a­tors to parse each time, we have the op­tion of up­dat­ing the stream with more data when need_more is re­turned. The parse() func­tion also up­dates the first it­er­a­tor so that we can pop any data prior to it from the data stream.

By de­fault the parser will throw an ex­cep­tion when it en­coun­ters an error. This can be changed by call­ing an over­load and han­dling the error re­sult type:

typedef std::deque<char> buffer_type;
typedef http::parser<buffer_type::iterator> parser_type;

buffer_type buffer;

parser_type p;
parser_type::node_type n;
parser_type::error_type err;
parser_type::result_type r;

do
{
  push_data(buffer); // add data to buffer from whatever I/O source.

  std::deque<char>::iterator first = buffer.begin();

  while((r = p.parse(first, buffer.end(), n, err)) == http::result_types::ok)
  {
    switch(n.type)
    {
      case http::node_types::method:
      case http::node_types::uri:
      case http::node_types::version:
    }
  }

  buffer.erase(buffer.begin(), first); // remove all the used
                                       // data from the buffer.
} while(r == http::result_types::need_more);

if(r == http::result_types::error)
{
  std::cerr
    << "an error occured at "
    << std::distance(buffer.begin(), err.position())
    << ": "
    << err.what()
    << std::endl;
}

The buffer

Ini­tially I was test­ing my parser with a deque<char> like above. This let me test the it­er­a­tor-based parser very eas­ily by in­cre­men­tally push­ing data on, pars­ing some of it, and pop­ping off what was used. Un­for­tu­nately, using a deque means we al­ways have an extra copy, from an I/O buffer into the deque. It­er­at­ing a deque is also a lot slower than it­er­at­ing a range of point­ers be­cause of the way deque is usu­ally im­ple­mented. This in­ef­fi­ciency is ac­cept­able for test­ing, but just won't work in a live app.

My buffer class is I/O- and pars­ing-op­ti­mized, op­er­at­ing on pages of data. It al­lows pages to be in­serted di­rectly from I/O with­out copy­ing. Ones that weren't filled en­tirely can still be filled later, al­low­ing the user to com­mit more bytes of a page as read­able. One can use scat­ter/gather I/O to make op­er­a­tions span mul­ti­ple pages con­tained in a buffer.

The buffer ex­poses two types of it­er­a­tors. The first type is what we are used to in deque: just a gen­eral byte stream it­er­a­tor. But this in­curs the same cost as deque: each in­cre­ment to the it­er­a­tor must check if it's at the end of the cur­rent page and move to the next. A pro­to­col like HTTP can fit a lot of el­e­ments into a sin­gle 4KiB page, so it doesn't make sense to have this cost. This is where the sec­ond it­er­a­tor comes in: the page it­er­a­tor. A page can be thought of as a Range rep­re­sent­ing a sub­set of the data in the full buffer. Over­all the buffer class looks some­thing like this:

struct page
{
  const char *first;    // the first byte of the page.
  const char *last;     // one past the last byte of the page.
  const char *readpos;  // the first readable byte of the page.
  const char *writepos; // the first writable byte of the page,
                        // one past the last readable byte.
};

class buffer
{
public:
  typedef ... size_type;
  typedef ... iterator;
  typedef ... page_iterator;

  void push(page *p); // pushes a page into the buffer.  might
                      // be empty, semi-full, or full.

  page* pop(); // pops the first fully read page from from the buffer.

  void commit_write(size_type numbytes); // merely moves writepos
                                         // by some number of bytes.

  void commit_read(size_type numbytes); // moves readpos by
                                        // some number of bytes.

  iterator begin() const;
  iterator end() const;

  page_iterator pages_begin() const;
  page_iterator pages_end() const;
};

One thing you may no­tice is it ex­pects you to push() and pop() pages di­rectly onto it, in­stead of al­lo­cat­ing its own. I re­ally hate classes that al­lo­cate mem­ory – in terms of scal­a­bil­ity the fewer places that al­lo­cate mem­ory, the eas­ier it will be to op­ti­mize. Be­cause of this I al­ways try to de­sign my code to – if it makes sense – have the next layer up do al­lo­ca­tions. When it doesn't make sense, I doc­u­ment it. Hid­den al­lo­ca­tions are the root of evil.

The buffer parser

Un­like the core parser, the buffer parser isn't a tem­plate class. The buffer parser ex­poses the same func­tion­al­ity as a core parser, but using a buffer in­stead of it­er­a­tor ranges.

This is where C++ gives me a big ad­van­tage. The buffer parser is ac­tu­ally im­ple­mented with two core parsers. The first is a very fast http::parser<const char*>. It uses this to parse as much of a sin­gle page as pos­si­ble, stop­ping when it en­coun­ters need_more and no more data can be added to the page. The sec­ond is a http::parser<buffer::iterator>. This gets used when the first parser stops, which will hap­pen very in­fre­quently – only when a HTTP el­e­ment spans mul­ti­ple pages.

This is fairly easy to im­ple­ment, but re­quired a small change to my core parser con­cept. Be­cause each has sep­a­rate in­ter­nal state, I needed to make it so I could move the state be­tween two parsers that use dif­fer­ent it­er­a­tors. The amount of state is ac­tu­ally very small, mak­ing this a fast op­er­a­tion.

The buffer parser works with two dif­fer­ent it­er­a­tor types in­ter­nally, so I chose to al­ways re­turn a buffer::iterator range. The choice was ei­ther that or silently copy el­e­ments span­ning mul­ti­ple pages, and this way lets the user of the code de­cide how they want to han­dle it.

Using the buffer parser is just as easy as be­fore:

http::buffer buffer;
http::buffer_parser p;
http::buffer_parser::node_type n;
http::buffer_parser::result_type r;

do
{
  push_data(buffer); // add data to buffer from whatever I/O source.

  while((r = p.parse(buffer, n)) == http::result_types::ok)
  {
    switch(n.type)
    {
      case http::node_types::method:
      case http::node_types::uri:
      case http::node_types::version:
    }
  }

  pop_used(buffer); // remove all the used
                    // data from the buffer.
} while(r == http::result_types::need_more);

The I/O layer

I'm leav­ing out an I/O layer for now. I will prob­a­bly write sev­eral small I/O sys­tems for it once I'm sat­is­fied with the parser. Per­haps one using asio, one using I/O com­ple­tion ports, and one using epoll. I've de­signed this from the start to be I/O ag­nos­tic but with op­ti­miza­tions that fa­cil­i­tate ef­fi­cient forms of all I/O, so I think it could be an good bench­mark of the var­i­ous I/O sub­sys­tems that dif­fer­ent plat­forms pro­vide.

One idea I've got is to use Winsock Ker­nel to im­ple­ment a ker­nel-mode HTTPd. Not a very good idea from a se­cu­rity stand­point, but would still be in­ter­est­ing to see the ef­fects on per­for­mance. Be­cause the parser per­forms no al­lo­ca­tion, no I/O calls, and doesn't force the use of ex­cep­tions, it should ac­tu­ally be very sim­ple to use in ker­nel-mode.