<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/xsl" media="screen" href="/~d/styles/rss2full.xsl"?><?xml-stylesheet type="text/css" media="screen" href="http://feeds.feedburner.com/~d/styles/itemcontent.css"?><rss xmlns:atom="http://www.w3.org/2005/Atom" xmlns:openSearch="http://a9.com/-/spec/opensearch/1.1/" xmlns:georss="http://www.georss.org/georss" xmlns:gd="http://schemas.google.com/g/2005" xmlns:thr="http://purl.org/syndication/thread/1.0" xmlns:feedburner="http://rssnamespace.org/feedburner/ext/1.0" version="2.0"><channel><atom:id>tag:blogger.com,1999:blog-46689162362346050</atom:id><lastBuildDate>Sun, 15 Jan 2012 08:26:37 +0000</lastBuildDate><category>linux</category><category>computer problems</category><category>guitar hero</category><category>login2009</category><category>technology</category><category>cwa</category><category>quizzes</category><category>personal</category><category>movies</category><category>zune</category><category>solvers</category><category>programming</category><category>legos</category><category>wii</category><category>games</category><category>fires</category><category>hacking</category><category>fan faire</category><category>commentary</category><category>rock band</category><category>opinions</category><category>star wars</category><category>game programming</category><category>cell phones</category><category>iphone</category><category>scams</category><category>fun stuff</category><category>eq2</category><category>irritations</category><category>memes</category><category>ipod</category><category>holidays</category><category>shadow odyssey</category><category>ultima online</category><category>family</category><category>voice chat</category><category>interviews</category><category>rise of kunark</category><category>repair</category><category>cycling</category><category>psp</category><category>soe</category><category>star trek</category><category>xbox</category><category>conferences</category><category>nvidia</category><category>friends</category><title>The Joshua Tree</title><description /><link>http://kriegshauser.blogspot.com/</link><managingEditor>noreply@blogger.com (Joshua Kriegshauser)</managingEditor><generator>Blogger</generator><openSearch:totalResults>109</openSearch:totalResults><openSearch:startIndex>1</openSearch:startIndex><openSearch:itemsPerPage>25</openSearch:itemsPerPage><atom10:link xmlns:atom10="http://www.w3.org/2005/Atom" rel="self" type="application/rss+xml" href="http://feeds.feedburner.com/kriegshauser" /><feedburner:info uri="kriegshauser" /><atom10:link xmlns:atom10="http://www.w3.org/2005/Atom" rel="hub" href="http://pubsubhubbub.appspot.com/" /><item><guid isPermaLink="false">tag:blogger.com,1999:blog-46689162362346050.post-5438437953410185370</guid><pubDate>Fri, 06 Jan 2012 06:03:00 +0000</pubDate><atom:updated>2012-01-11T23:20:16.095-08:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">programming</category><category domain="http://www.blogger.com/atom/ns#">solvers</category><title>1 in 600,000</title><description>&lt;div style="text-align: left;"&gt;For Christmas this year, my wife gave me (among other things) &lt;a href="http://www.amazon.com/Great-American-Puzzle-Factory-GA135/dp/B0052WBHXU/ref=cm_cr_pr_product_top" style="text-align: left; "&gt;Another Tough Puzzle&lt;/a&gt;&lt;span class="Apple-style-span" style="text-align: left; "&gt;. This puzzle claims that "with millions of wrong solutions, the chance of solving this puzzle is ONE in 600,000!"&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;There are 16 triangular pieces and each piece has three connections each being one of four possible shapes. The completed puzzle should be a pyramid with six connections on the edges pointing in and six pointed out.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;The puzzle also claims that "it looks easy...but just when you think you've solved it, the last piece proves you wrong." This is very true and very frustrating. I (and others) played around with the puzzle for a while always ending up with one piece that didn't fit.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Then I thought, "I write software for a living; why don't I do that here?" I've enjoyed writing solvers in the past (my Minesweeper solver would actually play the GUI for you on previous Windows versions). I mostly use C++, so I decided to use it here as well. Certainly there are languages more suited to rapid development, but I'm a glutton for punishment.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;The first step was to describe the pieces. We noticed pretty quickly that half of the pieces have two outward-facing connectors (outputs) and one inward-facing connector (input) and the other half are opposite (two inputs and one output). There are no pieces with three inputs or outputs.&lt;/div&gt;&lt;div&gt;&lt;pre&gt;enum FaceType&lt;br /&gt;{&lt;br /&gt;    eFaceTypeClover = 0,&lt;br /&gt;    eFaceTypeHeart,&lt;br /&gt;    eFaceTypeSpade,&lt;br /&gt;    eFaceTypeDiamond,&lt;br /&gt;};&lt;br /&gt;&lt;br /&gt;enum Direction&lt;br /&gt;{&lt;br /&gt;    eDirIn = 0,&lt;br /&gt;    eDirOut,&lt;br /&gt;};&lt;br /&gt;&lt;br /&gt;struct Node&lt;br /&gt;{&lt;br /&gt;    FaceType face;&lt;br /&gt;    Direction dir;&lt;br /&gt;&lt;br /&gt;    bool fits(const Node&amp;amp; rhs) const&lt;br /&gt;    {&lt;br /&gt;        return face == rhs.face &amp;&amp; dir != rhs.dir;&lt;br /&gt;    }&lt;br /&gt;};&lt;br /&gt;struct Piece&lt;br /&gt;{&lt;br /&gt;    Node node[eNumSides];&lt;br /&gt;&lt;br /&gt;    Piece(FaceType in, FaceType out1, FaceType out2, bool)&lt;br /&gt;    {&lt;br /&gt;        node[0].face = in;&lt;br /&gt;        node[1].face = out1;&lt;br /&gt;        node[2].face = out2;&lt;br /&gt;        node[0].dir = eDirIn;&lt;br /&gt;        node[1].dir = node[2].dir = eDirOut;&lt;br /&gt;    }&lt;br /&gt;    Piece(FaceType in1, FaceType in2, FaceType out)&lt;br /&gt;    {&lt;br /&gt;        node[0].face = in1;&lt;br /&gt;        node[1].face = in2;&lt;br /&gt;        node[2].face = out;&lt;br /&gt;        node[0].dir = node[1].dir = eDirIn;&lt;br /&gt;        node[2].dir = eDirOut;&lt;br /&gt;    }&lt;br /&gt;};&lt;/pre&gt;Using these structures, all of the pieces can be declared:&lt;/div&gt;&lt;div&gt;&lt;pre&gt;Piece pieces[eNumPieces] =&lt;br /&gt;{&lt;br /&gt;// one input, two outputs&lt;br /&gt;Piece(eFaceTypeSpade, eFaceTypeDiamond, eFaceTypeHeart),        // 0&lt;br /&gt;Piece(eFaceTypeHeart, eFaceTypeDiamond, eFaceTypeSpade),        // 1&lt;br /&gt;Piece(eFaceTypeDiamond, eFaceTypeClover, eFaceTypeSpade),       // 2&lt;br /&gt;Piece(eFaceTypeClover, eFaceTypeSpade, eFaceTypeHeart),         // 3&lt;br /&gt;Piece(eFaceTypeSpade, eFaceTypeHeart, eFaceTypeClover),         // 4&lt;br /&gt;Piece(eFaceTypeHeart, eFaceTypeSpade, eFaceTypeClover),         // 5&lt;br /&gt;Piece(eFaceTypeSpade, eFaceTypeClover, eFaceTypeDiamond),       // 6&lt;br /&gt;Piece(eFaceTypeDiamond, eFaceTypeClover, eFaceTypeHeart),       // 7&lt;br /&gt;&lt;br /&gt;// two inputs, one output&lt;br /&gt;Piece(eFaceTypeClover, eFaceTypeDiamond, eFaceTypeHeart, true), // 8&lt;br /&gt;Piece(eFaceTypeClover, eFaceTypeDiamond, eFaceTypeSpade, true), // 9&lt;br /&gt;Piece(eFaceTypeHeart, eFaceTypeDiamond, eFaceTypeSpade, true),  // a&lt;br /&gt;Piece(eFaceTypeHeart, eFaceTypeClover, eFaceTypeDiamond, true), // b&lt;br /&gt;Piece(eFaceTypeHeart, eFaceTypeClover, eFaceTypeSpade, true),   // c&lt;br /&gt;Piece(eFaceTypeDiamond, eFaceTypeClover, eFaceTypeHeart, true), // d&lt;br /&gt;Piece(eFaceTypeDiamond, eFaceTypeClover, eFaceTypeSpade, true), // e&lt;br /&gt;Piece(eFaceTypeSpade, eFaceTypeHeart, eFaceTypeDiamond, true)   // f&lt;br /&gt;};&lt;/pre&gt;&lt;a href="http://2.bp.blogspot.com/-YM_AL-iAwWA/Tw6AAVQ8dlI/AAAAAAAAAJw/ViL6XZnGZqc/s1600/gameboard.png" onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}"&gt;&lt;img src="http://2.bp.blogspot.com/-YM_AL-iAwWA/Tw6AAVQ8dlI/AAAAAAAAAJw/ViL6XZnGZqc/s400/gameboard.png" border="0" alt="" id="BLOGGER_PHOTO_ID_5696631321866827346" style="float: left; margin-top: 0px; margin-right: 10px; margin-bottom: 10px; margin-left: 0px; cursor: pointer; width: 254px; height: 298px; " /&gt;&lt;/a&gt;Now that the pieces are all set up, the gameboard must be described. The end result should be a pyramid made up of all 16 pieces (see diagram). Therefore, I had to describe how the pieces relate to each other in the gameboard. This will allow the solver algorithm to test that a piece 'fits' if its connectors match the adjacent pieces. In order to do this, each position in the desired puzzle shape is marked with a number and each side of the triangular pieces are numbered as well. The sides are always numbered clockwise starting at the horizontal side so that adjacent pieces in the puzzle have sides matching (for instance, pieces 7 and 8 both share their side number 2).&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt; The array of PieceLoc structures define how each piece relates to adjacent pieces:&lt;/div&gt;&lt;div&gt;&lt;pre&gt;PieceLoc locs[eNumPieces] =&lt;br /&gt;{&lt;br /&gt;// Sides:     0               1               2          // Pieces&lt;br /&gt;PieceLoc(eInvalidPiece,  eInvalidPiece,              1), // 0&lt;br /&gt;PieceLoc(            7,              2,              0), // 1&lt;br /&gt;PieceLoc(eInvalidPiece,              1,              3), // 2&lt;br /&gt;PieceLoc(            9,              4,              2), // 3&lt;br /&gt;PieceLoc(eInvalidPiece,              3,              5), // 4&lt;br /&gt;PieceLoc(           11,              6,              4), // 5&lt;br /&gt;PieceLoc(eInvalidPiece,              5,  eInvalidPiece), // 6&lt;br /&gt;PieceLoc(            1,  eInvalidPiece,              8), // 7&lt;br /&gt;PieceLoc(           12,              9,              7), // 8&lt;br /&gt;PieceLoc(            3,              8,             10), // 9&lt;br /&gt;PieceLoc(           14,             11,              9), // a&lt;br /&gt;PieceLoc(            5,             10,  eInvalidPiece), // b&lt;br /&gt;PieceLoc(            8,  eInvalidPiece,             13), // c&lt;br /&gt;PieceLoc(           15,             14,             12), // d&lt;br /&gt;PieceLoc(           10,             13,  eInvalidPiece), // e&lt;br /&gt;PieceLoc(           13,  eInvalidPiece,  eInvalidPiece)  // f&lt;br /&gt;};&lt;/pre&gt;The fitting function is recursive. The first time it is called it has all 16 pieces remaining to try and fit. It loops through all remaining pieces rotating them in all possible positions each time checking to see if the piece fits. If the piece fits, it calls itself again this time with 15 pieces remaining. When a pieces is found that fits, it calls itself again with 14 pieces remaining. And so on, and so forth until all pieces have been used. If a piece doesn't fit, it goes on to the next piece. If a solution is found or no more pieces fit, the function returns and tries the next piece.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Since we only have 16 pieces, I use a bitmask to determine which pieces are already used.&lt;/div&gt;&lt;div&gt;&lt;pre&gt;void FitRemaining(int remain)&lt;br /&gt;{&lt;br /&gt;    if (remain &lt;= 0)&lt;br /&gt;    {&lt;br /&gt;        ShowSolution();&lt;br /&gt;    }&lt;br /&gt;    else&lt;br /&gt;    {&lt;br /&gt;        int index = eNumPieces-remain;&lt;br /&gt;        for (int i = 0; i &lt; eNumPieces; ++i)&lt;br /&gt;        {&lt;br /&gt;            if ((used &amp; (1 &lt;&lt; i)) == 0)&lt;br /&gt;            {&lt;br /&gt;                used |= (1 &lt;&lt; i); // Mark in-use&lt;br /&gt;                pieces[index] = i;&lt;br /&gt;                for (int r = 0; r &lt; eNumRotations; ++r)&lt;br /&gt;                {&lt;br /&gt;                    rotation[index] = r;&lt;br /&gt;                    if (validate(index))&lt;br /&gt;                    {&lt;br /&gt;                        FitRemaining(remain - 1);&lt;br /&gt;                    }&lt;br /&gt;                }&lt;br /&gt;                pieces[index] = eInvalidPiece;&lt;br /&gt;                used &amp;= ~(1 &lt;&lt; i); // No longer in-use&lt;br /&gt;            }&lt;br /&gt;        }&lt;br /&gt;    }&lt;br /&gt;}&lt;/pre&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;The last part is the validation function. We really only need to validate the most recent piece added. Figuring out which sides are facing each other is the most difficult part, but even that is pretty easy. Because of how the gameboard is organized we already know that side indexes on adjacent pieces always face each other. All we need to do is take the rotation into account and ensure that the shapes match:&lt;/div&gt;&lt;div&gt;&lt;pre&gt;bool validate(int piece) const&lt;br /&gt;{&lt;br /&gt;    assert(piece &gt;= 0 &amp;&amp; piece &lt; eNumPieces);&lt;br /&gt;    const Piece* cur = Get(piece);&lt;br /&gt;    int rot1 = rotation[piece];&lt;br /&gt;    const PieceLoc&amp; fit = locs[piece];&lt;br /&gt;    for (int f = 0; f &lt; eNumSides; ++f)&lt;br /&gt;    {&lt;br /&gt;        const Piece* p = Get(fit.neighbors[f]);&lt;br /&gt;        if (p)&lt;br /&gt;        {&lt;br /&gt;            int rot2 = rotation[fit.neighbors[f]];&lt;br /&gt;            if (!cur-&gt;node[(rot1 + f) % 3].fits(p-&gt;node[(rot2 + f) % 3]))&lt;br /&gt;            {&lt;br /&gt;                return false;&lt;br /&gt;            }&lt;br /&gt;        }&lt;br /&gt;    }&lt;br /&gt;    return true;&lt;br /&gt;}&lt;/pre&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;That's all there is to it! I probably spent less time writing this program than manually (and frustratingly) trying to find the solution.&lt;br /&gt;&lt;br /&gt;In fact with this program I found all 518,469 solutions to the puzzle. Here's the first one:&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;a href="http://2.bp.blogspot.com/-bdYHFBV3yU4/TwaSdhXbc2I/AAAAAAAAAJk/vbGoY7q-v0Q/s1600/puzzle.jpg" onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}"&gt;&lt;img src="http://2.bp.blogspot.com/-bdYHFBV3yU4/TwaSdhXbc2I/AAAAAAAAAJk/vbGoY7q-v0Q/s400/puzzle.jpg" border="0" alt="" id="BLOGGER_PHOTO_ID_5694399814727070562" style="display: block; margin-top: 0px; margin-right: auto; margin-bottom: 10px; margin-left: auto; text-align: center; cursor: pointer; width: 400px; height: 299px; " /&gt;&lt;/a&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/46689162362346050-5438437953410185370?l=kriegshauser.blogspot.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/kriegshauser/~4/v-UHo4iLkQE" height="1" width="1"/&gt;</description><link>http://feedproxy.google.com/~r/kriegshauser/~3/v-UHo4iLkQE/1-in-600000.html</link><author>noreply@blogger.com (Joshua Kriegshauser)</author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="http://2.bp.blogspot.com/-YM_AL-iAwWA/Tw6AAVQ8dlI/AAAAAAAAAJw/ViL6XZnGZqc/s72-c/gameboard.png" height="72" width="72" /><thr:total>2</thr:total><feedburner:origLink>http://kriegshauser.blogspot.com/2012/01/1-in-600000.html</feedburner:origLink></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-46689162362346050.post-4660979636036289593</guid><pubDate>Thu, 21 Jul 2011 21:44:00 +0000</pubDate><atom:updated>2011-07-21T15:00:54.340-07:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">game programming</category><category domain="http://www.blogger.com/atom/ns#">eq2</category><category domain="http://www.blogger.com/atom/ns#">conferences</category><title>GDC Online 2011</title><description>I haven't done a speaking engagement since &lt;a href="http://www.loginconference.com/"&gt;LOGIN&lt;/a&gt; 2009, but I really enjoyed it, so I figured it was high time to give it another shot.&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;(And no, acting out Beyoncé's &lt;a href="http://www.youtube.com/watch?v=4m1EFMoRFvY"&gt;Single Ladies dance&lt;/a&gt; on stage at the Developer War Stories panel of &lt;a href="http://soefanfaire.com/"&gt;Fan Faire 2011&lt;/a&gt; doesn't count.)&lt;br /&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;I've always enjoyed attending &lt;a href="http://gdconline.com/"&gt;GDC Online&lt;/a&gt; (formerly GDC Austin or Austin Game Developers' Conference) and since it has always catered to a large crowd of &lt;i&gt;online&lt;/i&gt; game developers (and I do miss Austin a bit), it's as good as any a place to foist my shenanigans on the somnolent audience.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;This year I'll be talking about the trials and tribulations of taking a square peg never designed for streaming, &lt;a href="http://everquest2.com/"&gt;EverQuest II&lt;/a&gt; and inserting it into a round hole. If you've read this blog, I've &lt;a href="http://kriegshauser.blogspot.com/search/label/eq2"&gt;mentioned things&lt;/a&gt; about this before, but this is your opportunity to see the veil ripped away and the nitty-gritty of the good and bad choices that were made.&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;If you're heading to GDC Online (and you should for this lecture alone!), &lt;a href="http://schedule.gdconline.com/session/5916/EverQuest_II_Extended%3A_Streaming_a_Non-Streaming_Game"&gt;click here&lt;/a&gt; to see the lecture in their Schedule Builder.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Also (and completely to my surprise), Gamasutra &lt;a href="http://www.gamasutra.com/view/news/35344/GDC_Online_2011_Reveals_Riot_Games_EverQuest_II_Gamification_Talks.php"&gt;listed my lecture&lt;/a&gt; as a 'highlight' of GDC Online. Fortunately they don't know me that well yet. ;D&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/46689162362346050-4660979636036289593?l=kriegshauser.blogspot.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/kriegshauser/~4/W8kCWTxckxo" height="1" width="1"/&gt;</description><link>http://feedproxy.google.com/~r/kriegshauser/~3/W8kCWTxckxo/gdc-online-2011.html</link><author>noreply@blogger.com (Joshua Kriegshauser)</author><thr:total>6</thr:total><feedburner:origLink>http://kriegshauser.blogspot.com/2011/07/gdc-online-2011.html</feedburner:origLink></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-46689162362346050.post-1888133255660618139</guid><pubDate>Fri, 01 Apr 2011 01:09:00 +0000</pubDate><atom:updated>2011-03-31T23:40:53.845-07:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">game programming</category><category domain="http://www.blogger.com/atom/ns#">programming</category><title>Philosophy of Code</title><description>&lt;div&gt;Programming in a business environment requires creating a product or service that is intended to produce a higher profit than cost. However, I believe that few programmers understand this or think about it on a daily basis. At the end of the day, we must be working towards making a profit.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;I'll be the first to tell you that I'm not a business major. I'm not an economist. I love programming and was best friends with a keyboard in the third grade. I can write code like the dickens but I'm no Wizard of Business. However, throughout the past year working on &lt;a href="http://www.clonewarsadventures.com/"&gt;Clone Wars Adventures&lt;/a&gt; I've had time (while my &lt;a href="http://xkcd.com/303/"&gt;code is compiling&lt;/a&gt;) and inclination to reflect on the nature of programming in a business environment.&lt;br /&gt;&lt;br /&gt;I've basically come to the conclusion that programming is a balance of three distinct yet intertwined facets: Developer Efficiency, Code Correctness and Performance. I call this the Philosophy of Code:&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div style="text-align: center;"&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://4.bp.blogspot.com/-b7jWOujDb3w/TZUl7lLODxI/AAAAAAAAAHc/wG-YiMKCJBI/s1600/Trifecta.PNG"&gt;&lt;img style="cursor: pointer; width: 392px; height: 340px;" src="http://4.bp.blogspot.com/-b7jWOujDb3w/TZUl7lLODxI/AAAAAAAAAHc/wG-YiMKCJBI/s400/Trifecta.PNG" alt="" id="BLOGGER_PHOTO_ID_5590416217972018962" border="0" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;span style="font-weight: bold;"&gt;Performance &lt;/span&gt;- How quickly the code executes.&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;Developer Efficiency&lt;/span&gt; - How quickly developers are able to complete tasks.&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;Code Correctness&lt;/span&gt; - Adherence to best practices (avoiding global memory, avoiding macros, namespaces, templates, etc.).&lt;br /&gt;&lt;br /&gt;These three aspects are not necessarily mutually exclusive. Every programming task that we work on has some balance of the above. However, when evaluating this philosophy from a business perspective, I've come to believe that the most important element of the triad is &lt;span style="font-style: italic;"&gt;Developer Efficiency&lt;/span&gt;. Developing a Philosophy of Code that puts emphasis on Developer Efficiency will allow your programmers (and incidentally your entire development team) to work faster and smarter. It can also require less code to be written and fewer bugs.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;Mutual Exclusivity&lt;/span&gt;&lt;br /&gt;In some cases, an Efficiency-based Philosophy of Code will compromise the Performance and Code Correctness aspects. For instance, &lt;a href="http://www.everquest2.com/"&gt;EverQuest II&lt;/a&gt; has a console-variable system. With &lt;span style="font-weight: bold;"&gt;one line of code&lt;/span&gt; in an implementation file I can add a semi-constant: a named value that can be changed by typing a slash-command into the chat window:&lt;br /&gt;&lt;pre&gt;CV_FLOAT(max_radius, 30.0);&lt;/pre&gt;Presto. This is like saying &lt;code&gt;float max_radius = 30.0;&lt;/code&gt; but I can also change it in real-time by typing "/max_radius 25" into the chat window. This typically is frowned upon from Code Correctness aspects for a few reasons:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;It uses macros&lt;/li&gt;&lt;li&gt;It uses global memory&lt;/li&gt;&lt;li&gt;It causes code to execute before &lt;code&gt;main()&lt;/code&gt;&lt;/li&gt;&lt;/ul&gt;If your Philosophy of Code leans towards Code Correctness, doing it the Right Way™ would entail at least:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;Creating a manager object as a member of your application manager class&lt;br /&gt;&lt;/li&gt;&lt;li&gt;Writing explicit code to register a member of your class with the manager object&lt;/li&gt;&lt;li&gt;Writing explicit code to unregister that member when your class instance is destroyed&lt;/li&gt;&lt;li&gt;A lot more than one line of code&lt;br /&gt;&lt;/li&gt;&lt;/ul&gt;Leaning towards Code Correctness over Developer Efficiency in this case would require that your programmers spend more time writing code and compiling (since members are typically declared in header files) to accomplish a very simple task.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;Compile Times&lt;/span&gt;&lt;br /&gt;In some cases, Code Correctness philosophies can institute policies that work to the detriment of Developer Efficiency:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;Extensive use of templates&lt;/li&gt;&lt;li&gt;Excessive class declarations&lt;/li&gt;&lt;li&gt;Lack of forward declarations&lt;/li&gt;&lt;/ul&gt;Excessive templatization typically means that header files are included in more places and more code is written in header files. When header files are changed, they require more of the code to be rebuilt. More code written in header files can also contribute to longer link times.&lt;br /&gt;&lt;br /&gt;Time spent compiling and linking can't necessarily be taken at face value. As programmers wait for a rebuild they tend to do something else which probably takes longer than the rebuild. The shorter the rebuild time, the more likely the programmer is going to stay focused. From a Developer Efficiency perspective, it's in your best interest to keep compile times as low as possible.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;Code Bloat&lt;/span&gt;&lt;br /&gt;The less code that is required to perform a task, the more efficiently a developer can implement said code. I'll give another practical example from EverQuest II. I've &lt;a href="http://kriegshauser.blogspot.com/2009/10/keep-designer-data-in-files-not.html"&gt;previously mentioned&lt;/a&gt; a bit about how the designer data system works. Every server-side data file uses the same data format.  As such, no special code is required to load each type of file. Weapons, quests, characters, everything is all defined by the same generic data description language. To load data from any data file uses a simple interface:&lt;br /&gt;&lt;pre&gt;DataObject* pObject = DataLoader::Load("weapons/sword_of_awesomesauce");&lt;br /&gt;if (pObject-&amp;gt;IsA("Weapon"))&lt;br /&gt;{&lt;br /&gt;  String name = pObject-&amp;gt;GetField("Name").AsString();&lt;br /&gt;}&lt;/pre&gt;Due to data object inheritance, &lt;code&gt;pObject&lt;/code&gt; may &lt;span style="font-style: italic;"&gt;actually&lt;/span&gt; be a Sword, but inherits from Weapon. The server's object model is further generified so that instantiating any object requires just providing the type:&lt;br /&gt;&lt;pre&gt;Item* pItem = ObjectFactory::Spawn(Item::Type, "weapons/sword_of_awesomesauce");&lt;br /&gt;if (pItem &amp;amp;&amp;amp; pItem-&amp;gt;IsA(Weapon::Type))&lt;br /&gt;{&lt;br /&gt; // Successfully spawned and is actually a Weapon.&lt;br /&gt;}&lt;/pre&gt;In other words, there is very little code that needs to be written to add a new game object type.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;Conclusion&lt;br /&gt;&lt;/span&gt;Programmers should consider their Philosophy of Code. I believe that focusing on Developer Efficiency in a business setting makes the most sense given the deadlines, the complexity of the tasks at hand, and the goal to make a profit.&lt;span style="font-weight: bold;"&gt;&lt;span style="font-weight: bold;"&gt;&lt;/span&gt;&lt;br /&gt;&lt;/span&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/46689162362346050-1888133255660618139?l=kriegshauser.blogspot.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/kriegshauser/~4/wWoPsHXwJGs" height="1" width="1"/&gt;</description><link>http://feedproxy.google.com/~r/kriegshauser/~3/wWoPsHXwJGs/philosophy-of-code.html</link><author>noreply@blogger.com (Joshua Kriegshauser)</author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="http://4.bp.blogspot.com/-b7jWOujDb3w/TZUl7lLODxI/AAAAAAAAAHc/wG-YiMKCJBI/s72-c/Trifecta.PNG" height="72" width="72" /><thr:total>2</thr:total><feedburner:origLink>http://kriegshauser.blogspot.com/2011/03/philosophy-of-code.html</feedburner:origLink></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-46689162362346050.post-1961461251101742304</guid><pubDate>Wed, 23 Mar 2011 16:50:00 +0000</pubDate><atom:updated>2011-03-23T13:56:26.460-07:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">game programming</category><category domain="http://www.blogger.com/atom/ns#">programming</category><title>Debugging information cannot be found? No Problem!</title><description>In my &lt;a href="http://kriegshauser.blogspot.com/2011/02/current-bane-of-my-existence.html"&gt;last post&lt;/a&gt;, I discussed the frustrations of Visual Studio 2005 (and later versions) not being able to locate debugging information, even though it was present and fully usable by other tools.&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;I believe I have discovered a workaround that works nearly all the time: Remote Debugging.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;It turns out that running the Remote Debugging Monitor on the same machine and setting up the project to use Remote Debugging seems to work around whatever issue Visual Studio is experiencing.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Remote Debugging is really useful if another machine is experiencing a problem that you can't reproduce locally. The Remote Debugging Monitor is primarily intended to run on a different machine than Visual Studio, but can also run on the same machine.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Here is how to set up Remote Debugging:&lt;/div&gt;&lt;div&gt;&lt;ol&gt;&lt;li&gt;Generally, the machine running the Remote Debugging Monitor will need the files. These are found under the Visual Studio install location: &lt;a href="file:///C:/Program%20Files%20(x86)/Microsoft%20Visual%20Studio%208/Common7/IDE/Remote%20Debugger/x86"&gt;C:\Program Files (x86)\Microsoft Visual Studio 8\Common7\IDE\Remote Debugger\x86&lt;/a&gt; (for 32-bit) or &lt;a href="file:///C:/Program%20Files/Microsoft%20Visual%20Studio%208/Common7/IDE/Remote%20Debugger/x64"&gt;C:\Program Files\Microsoft Visual Studio 8\Common7\IDE\Remote Debugger\x64&lt;/a&gt; (for 64-bit).&lt;/li&gt;&lt;li&gt;Copy the files from the above directory to the machine that will run the Remote Debugging Monitor (skip if you are running the Monitor locally).&lt;/li&gt;&lt;li&gt;To start the Remote Debugging Monitor, run msvsmon.exe. If you are running locally, there should be shortcuts in the start menu:&lt;br /&gt;&lt;a href="http://1.bp.blogspot.com/-zEQDndiLc-s/TYotNuYC_WI/AAAAAAAAAGs/jLqitF9bzu0/s1600/RemoteDebugging1.png"&gt;&lt;img src="http://1.bp.blogspot.com/-zEQDndiLc-s/TYotNuYC_WI/AAAAAAAAAGs/jLqitF9bzu0/s320/RemoteDebugging1.png" border="0" alt="" id="BLOGGER_PHOTO_ID_5587328001516961122" style="cursor: pointer; width: 320px; height: 311px; " /&gt;&lt;/a&gt;&lt;/li&gt;&lt;li&gt;Configure the options if necessary. Take note of 'Server name'.&lt;br /&gt;&lt;a href="http://4.bp.blogspot.com/-nVNsbsi9Kb4/TYo0UNN_0fI/AAAAAAAAAHM/alKFt4uuVLQ/s1600/RemoteDebugging4.png"&gt;&lt;img src="http://4.bp.blogspot.com/-nVNsbsi9Kb4/TYo0UNN_0fI/AAAAAAAAAHM/alKFt4uuVLQ/s320/RemoteDebugging4.png" border="0" alt="" id="BLOGGER_PHOTO_ID_5587335809456919026" style="cursor: pointer; width: 320px; height: 167px; " /&gt;&lt;/a&gt;&lt;br /&gt;&lt;a href="http://3.bp.blogspot.com/-Nel8SXdekiM/TYouw8E_TgI/AAAAAAAAAG8/16asBjtkSi4/s1600/RemoteDebugging2.png"&gt;&lt;img src="http://3.bp.blogspot.com/-Nel8SXdekiM/TYouw8E_TgI/AAAAAAAAAG8/16asBjtkSi4/s320/RemoteDebugging2.png" border="0" alt="" id="BLOGGER_PHOTO_ID_5587329706002173442" style="cursor: pointer; width: 320px; height: 234px; " /&gt;&lt;/a&gt;&lt;/li&gt;&lt;li&gt;Configure your project to do Remote Debugging. Change the 'Debugger to launch' to 'Remote Windows Debugger'. The 'Remote Server Name' must match the 'Server name' configured above if using Windows Authentication. If you are running the Monitor on a remote machine, the Remote Command and Working Directory will be paths on the remote machine.&lt;br /&gt;&lt;a href="http://3.bp.blogspot.com/-piDUIZn6N5E/TYpebKOy0hI/AAAAAAAAAHU/l_dRcLh5-gY/s1600/RemoteDebugging3.png"&gt;&lt;img src="http://3.bp.blogspot.com/-piDUIZn6N5E/TYpebKOy0hI/AAAAAAAAAHU/l_dRcLh5-gY/s320/RemoteDebugging3.png" border="0" alt="" id="BLOGGER_PHOTO_ID_5587382108402405906" style="cursor: pointer; width: 320px; height: 222px; " /&gt;&lt;/a&gt;&lt;/li&gt;&lt;/ol&gt;&lt;div&gt;When you Start Debugging, Step Into or Step Over to start debugging the application, Visual Studio will now connect to the Remote Debugging Monitor. You should be able to verify this by looking at the Remote Debugging Monitor and seeing how many connections it has.&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;So far this appears (for me at least) to be a successful work around to the 'Debugging information for 'X.exe' cannot be found or does not match. No symbols loaded.' error message.  There have only been about two times in the past month where a problem occurred. In those cases, a restart of Visual Studio (and killing mspdbsrv.exe) fixed it.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Please let me know if this worked for you!&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/46689162362346050-1961461251101742304?l=kriegshauser.blogspot.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/kriegshauser/~4/mRB4WWngw9o" height="1" width="1"/&gt;</description><link>http://feedproxy.google.com/~r/kriegshauser/~3/mRB4WWngw9o/debugging-information-cannot-be-found.html</link><author>noreply@blogger.com (Joshua Kriegshauser)</author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="http://1.bp.blogspot.com/-zEQDndiLc-s/TYotNuYC_WI/AAAAAAAAAGs/jLqitF9bzu0/s72-c/RemoteDebugging1.png" height="72" width="72" /><thr:total>2</thr:total><feedburner:origLink>http://kriegshauser.blogspot.com/2011/03/debugging-information-cannot-be-found.html</feedburner:origLink></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-46689162362346050.post-7130972975746472925</guid><pubDate>Fri, 04 Feb 2011 01:23:00 +0000</pubDate><atom:updated>2011-03-23T13:58:03.772-07:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">game programming</category><category domain="http://www.blogger.com/atom/ns#">cwa</category><category domain="http://www.blogger.com/atom/ns#">programming</category><title>The (current) Bane of my Existence</title><description>&lt;div style="text-align: center;"&gt;&lt;span class="Apple-style-span" style="color: rgb(0, 0, 238);"&gt;&lt;u&gt;&lt;br /&gt;&lt;/u&gt;&lt;/span&gt;&lt;/div&gt;Yes, &lt;a href="http://msdn.microsoft.com/en-us/library/ms950416.aspx"&gt;Microsoft Visual Studio 2005&lt;/a&gt;, I'm looking at you.&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Whenever I try to run the &lt;a href="http://www.clonewarsadventures.com/"&gt;Clone Wars Adventures&lt;/a&gt; AdminClient in the debugger, I have about an 80% chance of seeing this:&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;a href="http://3.bp.blogspot.com/_BfrMrXICM_Q/TUtVri__NHI/AAAAAAAAAGM/d_0ptH7h9Ro/s1600/NoDebuggingInfo.JPG"&gt;&lt;img src="http://3.bp.blogspot.com/_BfrMrXICM_Q/TUtVri__NHI/AAAAAAAAAGM/d_0ptH7h9Ro/s400/NoDebuggingInfo.JPG" alt="" id="BLOGGER_PHOTO_ID_5569639570791674994" style="display: block; margin: 0px auto 10px; text-align: center; cursor: pointer; width: 400px; height: 124px;" border="0" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;div&gt;This is a debug build.  I most assuredly have debug symbols turned on:&lt;/div&gt;&lt;div&gt;&lt;a href="http://3.bp.blogspot.com/_BfrMrXICM_Q/TUtWQq3rHQI/AAAAAAAAAGU/P_90rAXBS3w/s1600/DebugSettings2.JPG"&gt;&lt;img src="http://3.bp.blogspot.com/_BfrMrXICM_Q/TUtWQq3rHQI/AAAAAAAAAGU/P_90rAXBS3w/s400/DebugSettings2.JPG" alt="" id="BLOGGER_PHOTO_ID_5569640208559447298" style="display: block; margin: 0px auto 10px; text-align: center; cursor: pointer; width: 400px; height: 280px;" border="0" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;div&gt;&lt;a href="http://2.bp.blogspot.com/_BfrMrXICM_Q/TUtWYVlTyfI/AAAAAAAAAGc/yynzYApaduk/s1600/DebugSettings1.JPG"&gt;&lt;img src="http://2.bp.blogspot.com/_BfrMrXICM_Q/TUtWYVlTyfI/AAAAAAAAAGc/yynzYApaduk/s400/DebugSettings1.JPG" alt="" id="BLOGGER_PHOTO_ID_5569640340284230130" style="display: block; margin: 0px auto 10px; text-align: center; cursor: pointer; width: 400px; height: 280px;" border="0" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;div&gt;And I'm &lt;a href="http://social.msdn.microsoft.com/Forums/en/vsdebug/thread/7323de2b-3155-49a6-b4ec-955621eae6d7"&gt;not&lt;/a&gt; &lt;a href="http://social.msdn.microsoft.com/Forums/en/vsdebug/thread/4f7b3eb5-67b5-4066-8299-fe7635cc1d82"&gt;the&lt;/a&gt; &lt;a href="http://social.msdn.microsoft.com/Forums/en/vcgeneral/thread/b1a12f69-5c47-4ec6-a7d8-c1d16c5a710a"&gt;only&lt;/a&gt; &lt;a href="http://social.msdn.microsoft.com/Forums/en/vsdebug/thread/07395956-61c5-45ce-b8d0-5c35ef489393"&gt;one&lt;/a&gt; &lt;a href="http://connect.microsoft.com/VisualStudio/feedback/details/486244/debugging-information-for-exe-cannot-be-found-or-does-not-match"&gt;to&lt;/a&gt; &lt;a href="http://social.msdn.microsoft.com/Forums/en-US/Vsexpressvc/thread/503d3ded-976e-460e-b5f9-7e03454ed387"&gt;ever&lt;/a&gt; have this problem.  People have also &lt;a href="http://connect.microsoft.com/VisualStudio/feedback/details/486244/debugging-information-for-exe-cannot-be-found-or-does-not-match"&gt;reported this problem on VS2008&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;Oddly enough, no one else on my team has been complaining about this problem.  When I start VS2005, I can generally run the client in debug once or twice and then I perpetually get this error.&lt;br /&gt;&lt;br /&gt;Sometimes, exiting VS2005 and killing mspdbsrv.exe followed by a restart of VS2005 usually allows me to run a couple more times.  However, with our huge solution (112 projects), this takes a few minutes or so.  Definitely not conducive to small changes.&lt;br /&gt;&lt;br /&gt;Some of the links above have responses from MS folks who state that they aren't able to reproduce the problem, but there is most definitely a problem.&lt;br /&gt;&lt;br /&gt;I've only experienced this problem on our internal AdminClient, which, unfortunately, is the project that I'm most often trying to run.  Other projects in the solution file seem to work just fine.  I've tried several suggestions from the above links, including others like replacing dbghelp.dll with the latest version, altering the symbol search paths, trying to manually load symbols, and so on.&lt;br /&gt;&lt;br /&gt;As a last-ditch effort, some of the links above suggest re-installing VS2005.  I'll give that a shot.&lt;br /&gt;&lt;br /&gt;If that doesn't work, I may have to become &lt;a href="http://www.arrogantworms.com/music/the-last-saskatchewan-pirate-6/"&gt;a Pirate&lt;/a&gt; or something.&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;b&gt;Update: &lt;/b&gt;(3/23/2011): I believe I have found a work-around by using the Remote Debugging Monitor.  More info in &lt;a href="http://kriegshauser.blogspot.com/2011/03/debugging-information-cannot-be-found.html"&gt;this post&lt;/a&gt;.&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/46689162362346050-7130972975746472925?l=kriegshauser.blogspot.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/kriegshauser/~4/NTRwgvmzPjo" height="1" width="1"/&gt;</description><link>http://feedproxy.google.com/~r/kriegshauser/~3/NTRwgvmzPjo/current-bane-of-my-existence.html</link><author>noreply@blogger.com (Joshua Kriegshauser)</author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="http://3.bp.blogspot.com/_BfrMrXICM_Q/TUtVri__NHI/AAAAAAAAAGM/d_0ptH7h9Ro/s72-c/NoDebuggingInfo.JPG" height="72" width="72" /><thr:total>6</thr:total><feedburner:origLink>http://kriegshauser.blogspot.com/2011/02/current-bane-of-my-existence.html</feedburner:origLink></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-46689162362346050.post-4921926763862584482</guid><pubDate>Wed, 15 Sep 2010 14:14:00 +0000</pubDate><atom:updated>2010-09-15T07:17:52.491-07:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">cwa</category><title>Ladies and Gentlemen, we are LIVE!</title><description>&lt;a href="http://www.clonewarsadventures.com/"&gt;&lt;img src="http://3.bp.blogspot.com/_BfrMrXICM_Q/TAU7fr95tHI/AAAAAAAAAEw/W1fYhSHdH6A/s320/CWA.png" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;(Read the title in your best &lt;a href="http://en.wikipedia.org/wiki/Bruce_Buffer"&gt;Bruce Buffer&lt;/a&gt; voice).&lt;br /&gt;&lt;br /&gt;My new game, &lt;a href="http://www.clonewarsadventures.com/"&gt;Clone Wars Adventures&lt;/a&gt;, is now LIVE and out of Beta!  It's free to play and easy to download so head on over to the website and check it out!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/46689162362346050-4921926763862584482?l=kriegshauser.blogspot.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/kriegshauser/~4/HlO-3UB_qBQ" height="1" width="1"/&gt;</description><link>http://feedproxy.google.com/~r/kriegshauser/~3/HlO-3UB_qBQ/ladies-and-gentlemen-we-are-live.html</link><author>noreply@blogger.com (Joshua Kriegshauser)</author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="http://3.bp.blogspot.com/_BfrMrXICM_Q/TAU7fr95tHI/AAAAAAAAAEw/W1fYhSHdH6A/s72-c/CWA.png" height="72" width="72" /><thr:total>2</thr:total><feedburner:origLink>http://kriegshauser.blogspot.com/2010/09/ladies-and-gentlemen-we-are-live.html</feedburner:origLink></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-46689162362346050.post-3550022708260187651</guid><pubDate>Sat, 28 Aug 2010 05:01:00 +0000</pubDate><atom:updated>2010-08-27T22:04:48.562-07:00</atom:updated><title>Open Beta!</title><description>&lt;img src="http://3.bp.blogspot.com/_BfrMrXICM_Q/TAU7fr95tHI/AAAAAAAAAEw/W1fYhSHdH6A/s320/CWA.png" /&gt;&lt;br /&gt;&lt;br /&gt;My new game, &lt;a href="http://www.clonewarsadventures.com/"&gt;Clone Wars Adventures&lt;/a&gt;, is now in Open Beta!  Head on over, create an account and let us know what you think on the &lt;a href="http://forums.station.sony.com/cw/forums/list.m"&gt;forums&lt;/a&gt; (must sign in with your Station name)!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/46689162362346050-3550022708260187651?l=kriegshauser.blogspot.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/kriegshauser/~4/351QRaFgA-M" height="1" width="1"/&gt;</description><link>http://feedproxy.google.com/~r/kriegshauser/~3/351QRaFgA-M/open-beta.html</link><author>noreply@blogger.com (Joshua Kriegshauser)</author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="http://3.bp.blogspot.com/_BfrMrXICM_Q/TAU7fr95tHI/AAAAAAAAAEw/W1fYhSHdH6A/s72-c/CWA.png" height="72" width="72" /><thr:total>4</thr:total><feedburner:origLink>http://kriegshauser.blogspot.com/2010/08/open-beta.html</feedburner:origLink></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-46689162362346050.post-1169485340356738960</guid><pubDate>Tue, 01 Jun 2010 16:48:00 +0000</pubDate><atom:updated>2010-06-01T13:47:37.241-07:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">star wars</category><category domain="http://www.blogger.com/atom/ns#">cwa</category><category domain="http://www.blogger.com/atom/ns#">soe</category><title>Finally!</title><description>We just announced the new game that I'm working on.  Finally I can talk about it!&lt;br /&gt;&lt;br /&gt;So, without further ado:&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://www.clonewarsadventures.com/"&gt;&lt;img style="cursor: pointer; width: 320px; height: 208px;" src="http://3.bp.blogspot.com/_BfrMrXICM_Q/TAU7fr95tHI/AAAAAAAAAEw/W1fYhSHdH6A/s320/CWA.png" alt="" id="BLOGGER_PHOTO_ID_5477849937330418802" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;This is a new online free-to-play PC game targeted to kids (and adults) due out this fall.  It is built upon the technology first developed for &lt;a href="http://www.freerealms.com/"&gt;Free Realms&lt;/a&gt;.  It will be largely mini-game based allowing things from Lightsaber Dueling to Speederbike Racing, the obligatory Starfighter missions and even a 3D tower defense game called Republic Defender.  However, there is still a large social aspect with friends, housing, leaderboards and even a kid-friendly &lt;a href="http://www.facebook.com/"&gt;Facebook&lt;/a&gt;-inspired profile page.&lt;br /&gt;&lt;br /&gt;On a personal note, this is a great project to be on.  I am a huge Star Wars geek (over 15,000 LEGO bricks in my office representing different &lt;a href="http://www.youtube.com/watch?v=SqBF9mzSRss"&gt;Star&lt;/a&gt; &lt;a href="http://www.youtube.com/watch?v=3l8Kdd99fNg"&gt;Wars&lt;/a&gt; &lt;a href="http://www.youtube.com/watch?v=BvGXTAJqTWA"&gt;ships&lt;/a&gt;) and my son loves Star Wars and the Clone Wars (and, incidentally, this game too!).&lt;br /&gt;&lt;br /&gt;I hope you enjoy the teaser site and video.  I look forward to actually being able to show in-game demos (*ahem* &lt;a href="http://www.e3expo.com/"&gt;E3&lt;/a&gt; *cough*)!&lt;br /&gt;&lt;br /&gt;Update 6/1/2010 1:47PM PST:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;&lt;a href="http://kotaku.com/5552522/free-realms-meets-star-wars-in-clones-wars-adventures"&gt;Kotaku Article&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="http://www.starwars.com/games/clonewars_adventures_announced/index.html"&gt;Starwars.com article&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="http://bit.ly/dlCIsp"&gt;Gametrailers interview with Lead Designer Matt Higby&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="http://pc.ign.com/articles/109/1093853p1.html"&gt;Press Release&lt;/a&gt;&lt;br /&gt;&lt;/li&gt;&lt;/ul&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/46689162362346050-1169485340356738960?l=kriegshauser.blogspot.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/kriegshauser/~4/BJGIs51nB6A" height="1" width="1"/&gt;</description><link>http://feedproxy.google.com/~r/kriegshauser/~3/BJGIs51nB6A/finally.html</link><author>noreply@blogger.com (Joshua Kriegshauser)</author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="http://3.bp.blogspot.com/_BfrMrXICM_Q/TAU7fr95tHI/AAAAAAAAAEw/W1fYhSHdH6A/s72-c/CWA.png" height="72" width="72" /><thr:total>2</thr:total><feedburner:origLink>http://kriegshauser.blogspot.com/2010/06/finally.html</feedburner:origLink></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-46689162362346050.post-920465334908666890</guid><pubDate>Thu, 15 Apr 2010 07:51:00 +0000</pubDate><atom:updated>2010-04-15T01:31:42.755-07:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">repair</category><category domain="http://www.blogger.com/atom/ns#">iphone</category><title>/resurrect iPhone</title><description>I broke my iPhone 3G.  Again.&lt;br /&gt;&lt;br /&gt;A few months ago I dropped it in the supermarket.  It landed flat on the screen and when I picked it up I was horrified to discover a spiderweb-shaped crack in the screen.  Fortunately, everything still worked.  For $70 I had it repaired at &lt;a href="http://www.yelp.com/biz/volt-mobile-and-tech-san-diego"&gt;Volt Mobile&lt;/a&gt;.  They did pretty good work, but the touchscreen was intermittent (I'd have to flex the phone to make it work again).&lt;br /&gt;&lt;br /&gt;Last week, I dropped it again.  This time the touchscreen was completely broken--I couldn't even turn the phone off or enter my passcode to do a backup.&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://1.bp.blogspot.com/_BfrMrXICM_Q/S8bGn4uBGEI/AAAAAAAAAD4/RDKgbo_serY/s1600/DSC01977.JPG"&gt;&lt;img style="cursor: pointer; width: 320px; height: 240px;" src="http://1.bp.blogspot.com/_BfrMrXICM_Q/S8bGn4uBGEI/AAAAAAAAAD4/RDKgbo_serY/s320/DSC01977.JPG" alt="" id="BLOGGER_PHOTO_ID_5460269986775898178" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;This time, I figured I'd try my own repair.  Hey, it's worked &lt;a href="http://kriegshauser.blogspot.com/2007/06/video-card-woes.html"&gt;in&lt;/a&gt; &lt;a href="http://kriegshauser.blogspot.com/2007/07/resurrect-psp.html"&gt;the&lt;/a&gt; &lt;a href="http://kriegshauser.blogspot.com/2008/11/resurrect-xbox-360_30.html"&gt;past&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;A quick look online shows several places that sell digitizers (touchpads), LCD screens, batteries, you name it.  I ended up going with a &lt;a href="http://www.amazon.com/gp/product/B002A9PVAI"&gt;digitizer&lt;/a&gt; sold from &lt;a href="http://www.amazon.com/gp/help/seller/at-a-glance.html?ie=UTF8&amp;amp;seller=A1TB4CRIUIFO5O"&gt;AccessoryOne&lt;/a&gt; (which purports to be OEM) for less than $20 shipped and taxed.  There is also a &lt;a href="http://www.youtube.com/watch?v=8fF0DF-mbn4"&gt;three&lt;/a&gt;-&lt;a href="http://www.youtube.com/watch?v=p3xn7whpZAA"&gt;part&lt;/a&gt; &lt;a href="http://www.youtube.com/watch?v=w5LSqMoD4WE"&gt;video&lt;/a&gt; series on cracking open the iPhone and swapping out the digitizer.  The videos are made by &lt;a href="http://www.directfix.com/"&gt;http://www.directfix.com/&lt;/a&gt; who also have parts but tend to charge a bit more.&lt;br /&gt;&lt;br /&gt;This blog basically chronicles my repair.  If you're doing this yourself, watch the videos; they're much more informative.  Also, there is no guarantee that this will work, and it will void your warranty (mine has already expired).  I just figured that I'll be upgrading to the 4th-generation iPhone when it comes out anyways.&lt;br /&gt;&lt;br /&gt;The first step was pulling out the screen assembly.  Two screws from the base (near the speakers) are all that is holding the screen assembly on.  Unlike the videos, I used a suction cup on the screen near the home button to pull the screen out.  I believe this works better and doesn't molest the rubber seal around the screen assembly.  This picture shows the screen assembly removed from the base:&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://1.bp.blogspot.com/_BfrMrXICM_Q/S8bJaHNw_PI/AAAAAAAAAEA/i-pYI_r2xSw/s1600/DSC01978.JPG"&gt;&lt;img style="cursor: pointer; width: 320px; height: 240px;" src="http://1.bp.blogspot.com/_BfrMrXICM_Q/S8bJaHNw_PI/AAAAAAAAAEA/i-pYI_r2xSw/s320/DSC01978.JPG" alt="" id="BLOGGER_PHOTO_ID_5460273048683871474" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;This picture shows the new digitizer/glass on the left top, the removed LCD screen on the left bottom and the old frame/digitizer assembly on the right bottom:&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://4.bp.blogspot.com/_BfrMrXICM_Q/S8bJ11osf_I/AAAAAAAAAEI/IeAXQgpBV20/s1600/DSC01979.JPG"&gt;&lt;img style="cursor: pointer; width: 320px; height: 240px;" src="http://4.bp.blogspot.com/_BfrMrXICM_Q/S8bJ11osf_I/AAAAAAAAAEI/IeAXQgpBV20/s320/DSC01979.JPG" alt="" id="BLOGGER_PHOTO_ID_5460273525001322482" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;Removing the LCD screen is tricky and must be done carefully.  I've read posts from people who crack their LCD screens at this step and it's a much more &lt;a href="http://www.amazon.com/Apple-iPhone-Display-Assembly-Replacement/dp/B002VW3YLC/ref=sr_1_2?ie=UTF8&amp;amp;s=wireless&amp;amp;qid=1271319081&amp;amp;sr=1-2"&gt;expensive component&lt;/a&gt;.  I was very gentle.&lt;br /&gt;&lt;br /&gt;The digitizer that I ordered was just the glass/digitizer combo; it didn't include the screen frame or the components that are mounted to it (speaker, skin sensor, home button, etc).  Therefore, the frame must be removed from the original glass/digitizer and attached to the new one.  This was probably the most tricky part.  It involves using a hair dryer to weaken the glue and pry the frame off.  Then new two-sided tape must be applied to the frame and attached to the new glass/digitizer.&lt;br /&gt;&lt;br /&gt;This picture shows the removed frame (bottom center) and the old cracked digitizer on the bottom right:&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://2.bp.blogspot.com/_BfrMrXICM_Q/S8bLnX3IMlI/AAAAAAAAAEQ/jqHKxcGTJqM/s1600/DSC01981.JPG"&gt;&lt;img style="cursor: pointer; width: 320px; height: 240px;" src="http://2.bp.blogspot.com/_BfrMrXICM_Q/S8bLnX3IMlI/AAAAAAAAAEQ/jqHKxcGTJqM/s320/DSC01981.JPG" alt="" id="BLOGGER_PHOTO_ID_5460275475513881170" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;After a few minutes of work, the frame was attached to the new glass/digitizer:&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://4.bp.blogspot.com/_BfrMrXICM_Q/S8bMUI1v-KI/AAAAAAAAAEY/wx1WUWbRi0Q/s1600/DSC01982.JPG"&gt;&lt;img style="cursor: pointer; width: 320px; height: 240px;" src="http://4.bp.blogspot.com/_BfrMrXICM_Q/S8bMUI1v-KI/AAAAAAAAAEY/wx1WUWbRi0Q/s320/DSC01982.JPG" alt="" id="BLOGGER_PHOTO_ID_5460276244575680674" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;The new glass had an appropriate amount of protective film, so it was fairly easy to minimize dust getting in between the screen and the glass.  The new screen assembly with LCD installed is now shown on the bottom:&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://3.bp.blogspot.com/_BfrMrXICM_Q/S8bM9AM-AEI/AAAAAAAAAEg/eaROzayXr0E/s1600/DSC01983.JPG"&gt;&lt;img style="cursor: pointer; width: 240px; height: 320px;" src="http://3.bp.blogspot.com/_BfrMrXICM_Q/S8bM9AM-AEI/AAAAAAAAAEg/eaROzayXr0E/s320/DSC01983.JPG" alt="" id="BLOGGER_PHOTO_ID_5460276946631786562" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;After getting the screen cables connected I powered on the phone and was able to enter my passcode to unlock the phone (note that the protective film is still on the screen, hence the sticker in the middle of the screen):&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://1.bp.blogspot.com/_BfrMrXICM_Q/S8bNVPoo5nI/AAAAAAAAAEo/ODkNR3JL2zQ/s1600/DSC01984.JPG"&gt;&lt;img style="cursor: pointer; width: 240px; height: 320px;" src="http://1.bp.blogspot.com/_BfrMrXICM_Q/S8bNVPoo5nI/AAAAAAAAAEo/ODkNR3JL2zQ/s320/DSC01984.JPG" alt="" id="BLOGGER_PHOTO_ID_5460277363091236466" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;Yay!  All-in-all, a very successful repair for under $20 and about an hour.&lt;br /&gt;&lt;br /&gt;Now... to find a better case...&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/46689162362346050-920465334908666890?l=kriegshauser.blogspot.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/kriegshauser/~4/te7Q-RQh7Rw" height="1" width="1"/&gt;</description><link>http://feedproxy.google.com/~r/kriegshauser/~3/te7Q-RQh7Rw/resurrect-iphone.html</link><author>noreply@blogger.com (Joshua Kriegshauser)</author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="http://1.bp.blogspot.com/_BfrMrXICM_Q/S8bGn4uBGEI/AAAAAAAAAD4/RDKgbo_serY/s72-c/DSC01977.JPG" height="72" width="72" /><thr:total>2</thr:total><feedburner:origLink>http://kriegshauser.blogspot.com/2010/04/resurrect-iphone.html</feedburner:origLink></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-46689162362346050.post-8366223773760887500</guid><pubDate>Tue, 16 Mar 2010 06:08:00 +0000</pubDate><atom:updated>2010-03-15T23:35:33.254-07:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">game programming</category><category domain="http://www.blogger.com/atom/ns#">programming</category><category domain="http://www.blogger.com/atom/ns#">eq2</category><title>Be Careful with Default Arguments</title><description>&lt;pre&gt;CRC16::computeMemory( const char* data, unsigned length, uns16 crc = 0 );&lt;br /&gt;CRC16::computeString( const char* data, uns16 crc = 0 );&lt;/pre&gt;This little gem caused quite a headache.  Allow me to explain.&lt;br /&gt;&lt;br /&gt;&lt;a href="http://www.everquest2.com/"&gt;EQII&lt;/a&gt;'s &lt;a href="http://kriegshauser.blogspot.com/2010/02/evolution-of-streaming-client.html"&gt;streaming client&lt;/a&gt; uses 16-bit &lt;a href="http://en.wikipedia.org/wiki/Cyclic_redundancy_check"&gt;CRCs&lt;/a&gt; to tell if an asset has changed.  All 500,000 individual assets have a 16-bit CRC calculated and stored in the master asset list (manifest).  When an asset is downloaded, it is cached on disk and stored with the 16-bit CRC.  To save time, the client doesn't calculate the CRC on each asset that it downloads, it just uses the CRC stored in the manifest.&lt;br /&gt;&lt;br /&gt;Every time the client runs, we download the manifest and check the CRCs in the manifest against the stored CRCs for our cached assets to see if any changed.  If an asset has changed (the CRC is different), we delete the cached copy and request the replacement, even if it's not immediately needed.&lt;br /&gt;&lt;br /&gt;When the streaming client launched, everything worked as planned.  It worked great! Little did we know that a particularly evil bug was lurking.&lt;br /&gt;&lt;br /&gt;I discovered a problem when I wrote a utility to convert old PAK files (the game data shipped on the DVD) to cached streaming assets.  NOTHING matched the CRCs in the manifest.  Every asset was wrong.  I looked over the code several times and everything looked fine.  This bug didn't make sense.&lt;br /&gt;&lt;br /&gt;And then it hit me.&lt;br /&gt;&lt;br /&gt;The code that was building the manifests was doing this:&lt;br /&gt;&lt;pre&gt;uns16 crc = CRC16::computeString( data, dataLength );&lt;/pre&gt;Talk about &lt;a href="http://images.google.com/images?q=facepalm"&gt;/facepalm&lt;/a&gt;.  The function I &lt;span style="font-style: italic;"&gt;meant &lt;/span&gt;to call was &lt;code&gt;CRC16::computeMemory()&lt;/code&gt;.  The function I &lt;span style="font-style: italic;"&gt;actually &lt;/span&gt;called treated the input like a &lt;a href="http://en.wikipedia.org/wiki/C_string"&gt;null-terminated string&lt;/a&gt;.  This means that the CRC was only calculated up to the first &lt;code&gt;NUL&lt;/code&gt; character and the &lt;code&gt;dataLength&lt;/code&gt; parameter was actually being treated as a starting CRC value.  This was a bug that &lt;span style="font-style: italic;"&gt;had &lt;/span&gt;to get fixed.  Someday, perhaps many years from now, this would be a huge bug that would waste a lot of someone's time to hunt down.&lt;br /&gt;&lt;br /&gt;Oh, but the fun doesn't end there.  I couldn't just change the function to fix the bug.  Doing that would mean that every streaming client user would have to re-download &lt;span style="font-style: italic;"&gt;everything&lt;/span&gt; that they had already downloaded.  Every CRC would change and the naïve client would happily delete everything and start over.  To fix this properly would take a highly-synchronized effort to fix and push the manifests while deploying a one-time-only tool that would re-calculate the CRC for all assets that people had already downloaded.&lt;br /&gt;&lt;br /&gt;The moral of the story: be careful with default arguments.  They can really hurt if misused.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/46689162362346050-8366223773760887500?l=kriegshauser.blogspot.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/kriegshauser/~4/1aijMDJ2yzY" height="1" width="1"/&gt;</description><link>http://feedproxy.google.com/~r/kriegshauser/~3/1aijMDJ2yzY/be-careful-with-default-arguments.html</link><author>noreply@blogger.com (Joshua Kriegshauser)</author><thr:total>2</thr:total><feedburner:origLink>http://kriegshauser.blogspot.com/2010/03/be-careful-with-default-arguments.html</feedburner:origLink></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-46689162362346050.post-3430846014252754770</guid><pubDate>Wed, 10 Feb 2010 03:35:00 +0000</pubDate><atom:updated>2010-02-09T23:35:02.508-08:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">game programming</category><category domain="http://www.blogger.com/atom/ns#">technology</category><category domain="http://www.blogger.com/atom/ns#">eq2</category><title>Evolution of a Streaming Client</title><description>It's funny how many things in the game industry start out as "I wish..."&lt;br /&gt;... I wish we had Guild Halls.&lt;br /&gt;... I wish we had Shader 3.0 support.&lt;br /&gt;... I wish we had Battlegrounds.&lt;br /&gt;&lt;br /&gt;... I wish our game was easier to download.&lt;br /&gt;&lt;br /&gt;It's equally funny how many things are started by people in their own time just trying to make the game better.  That's how &lt;a href="http://www.everquest2.com/"&gt;EverQuest II&lt;/a&gt;'s &lt;a href="http://everquest2.com/get_the_game/free_trial"&gt;streaming client&lt;/a&gt; started out.&lt;br /&gt;&lt;br /&gt;Taking an existing game (with 12GB of client assets no less) and streaming it is no simple task.  I started off with a "proof of concept" just to prove that it could be done with EverQuest II.  As I got into it, the concept became a full fledged project.  It wasn't officially on the schedule, so it was really a labor of love on my part.  After a few weeks of silently working on it, I called the producer into my office and said, "Hey, check this out."  Needless to say he was pretty surprised.&lt;br /&gt;&lt;br /&gt;There are three major conversion steps for a streaming system.&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;&lt;br /&gt;Serving the assets&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;EverQuest II has roughly 500,000 client-side asset files: meshes, textures, collision meshes, shaders, data files, sounds, music, you name it.  Have you ever tried putting half-a-million tiny files in a directory?  Take my word for it: Don't.&lt;br /&gt;&lt;br /&gt;My first inclination was to build a custom server.  The server would run off of the PAK files that we already ship with the DVD-based game client.  I had grandiose plans about how to track files that clients were downloading and automatically send assets to clients that they didn't know they needed.&lt;br /&gt;&lt;br /&gt;But alas, it was not to be.  A custom server means that every client would have to be talking to our server.  We would have to think about where to place the server geographically, handling varying load characteristics, availability, bandwidth, etc.  These were all questions that had already been answered; we didn't need to ask them again and try to come up with our own answers.&lt;br /&gt;&lt;br /&gt;What else is great at serving files to a large number of clients all over the world?  &lt;a href="http://en.wikipedia.org/wiki/Web_server"&gt;Web servers&lt;/a&gt;!  Specifically, &lt;a href="http://en.wikipedia.org/wiki/Hypertext_Transfer_Protocol"&gt;HTTP&lt;/a&gt; servers.  We already used a &lt;a href="http://en.wikipedia.org/wiki/Content_delivery_network"&gt;CDN&lt;/a&gt; for patching purposes--we just needed to serve all the game assets individually and on-demand now.&lt;br /&gt;&lt;br /&gt;This caused another wrinkle.  The client needs to know a list of all the assets that are available and whether the assets that it has previously downloaded are out of date.  We call this the "manifest."  This manifest must be fully up-to-date before the client tries to load ANY assets.  My custom server knew how to negotiate a manifest with the client in a fairly bandwidth-friendly way because it was smart.  CDNs are less smart--they just serve files.  EQII's manifest is about 6MB, which you definitely don't want to download every time you run the game.  The solution I developed involves parts of the manifest available as separate files and an overarching CRC file that is requested first.  The CRC file is always requested, but it's only about 8KB.  Based on comparisons with the CRC file, the client reconstructs the full manifest by grabbing parts that it needs.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;&lt;br /&gt;Requesting the assets&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;Compared to everything else, serving the assets is the "easy" part.  Requesting the assets is far more difficult.  You're essentially replacing file system access with a network connection.  That sounds a lot easier than it is.  File system access is inherently goverened by the Operating System and allows any thread to open nearly any file and read data from it.  A network connection is a single pipe (or in our case, a collection of pipes) that must well-defined and tightly-controlled access.  Any thread that could just expect to read from a file at any point must now be synchronized with other threads requesting assets from a network resource.&lt;br /&gt;&lt;br /&gt;Another major difference is that file system access is &lt;span style="font-style: italic;"&gt;synchronous &lt;/span&gt;from an application's perspective.  This means that while waiting for the Operating System to read data from a file, the thread goes to sleep and allows the system to do other things.  Generally this happens so quickly that you barely notice, but network connections aren't nearly as fast as your local hard disk.  For this reason, we want most of our asset requests to be &lt;span style="font-style: italic;"&gt;asynchronous&lt;/span&gt;: we send the asset request and go about doing other things until it finishes at some later time.&lt;br /&gt;&lt;br /&gt;Unfortunately, it's much easier to do synchronous reads than asynchronous.  The EverQuest II client had many synchronous reads that you didn't even notice because the file system is fast enough.  If they weren't made asynchronous, a streaming client would appear to 'lock up' while waiting for an asset to be fetched.  Obviously, this is undesirable, and nearly unavoidable in some cases.&lt;br /&gt;&lt;br /&gt;Furthermore, network connections in games are usually given time by the main thread to do their work (colloquially referred to as "pumping").  That won't work in this system.  What if the main thread needs to synchronously load an asset (which still happens occasionally, especially on client startup)?  It would be waiting for an asset to finish loading and wouldn't be able to update the network connection that it is effectively waiting on.&lt;br /&gt;&lt;br /&gt;Clearly, a system is needed that can pump itself.  Any thread can request an asset synchronously or asynchronously and the network connection continues updating as long as the client is running.  The system should be able to determine if a request for an asset has already been sent and we don't need to waste bandwidth by requesting it again.  The system should be able to recognize and quickly send higher priority requests.  And, oh yes, let's not forget about failure cases.  This piece of technology is the very heart of the streaming client.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;Storing the assets&lt;/span&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;&lt;br /&gt;&lt;/span&gt;Obviously&lt;span style="font-weight: bold;"&gt;, &lt;/span&gt;once an asset has been downloaded, we don't want to waste bandwidth downloading that asset again.  It might take minutes to enter a zone for the first time, but we don't want to take that long every time we enter that zone.  Therefore, that asset must be stored locally.&lt;br /&gt;&lt;br /&gt;A possibility is to store each asset as its own file, but this fails in practice.  Operating Systems are not optimized for hundreds of thousands of tiny files.  No, these files must be stored in a larger file, packed together and easily accessible.&lt;br /&gt;&lt;br /&gt;EQII already has a packed file format.  Unfortunately, the way it's set up does not lend itself to modification.  When EQII's packed files are written, they're never intended to change.  With new assets being downloaded constantly, these files will be changing, and often.&lt;br /&gt;&lt;br /&gt;My solution was to develop a new type of asset database specifically suited to our needs.  These database files can store a large number of tiny assets, rapidly add and remove assets and quickly retrieve individual assets.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;Other Considerations&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;The most difficult part of building a streaming system for an existing client has been trying to change synchronous asset requests into asynchronous.  Consider the following simple example:&lt;br /&gt;&lt;pre&gt;Animation* pAnim = pAssetSystem-&gt;LoadAnimation( "animation/player_anim1" );&lt;br /&gt;if ( pAnim )&lt;br /&gt;{&lt;br /&gt;    // Do something with loaded asset&lt;br /&gt;}&lt;/pre&gt;The above example would need to fetch &lt;code&gt;player_anim1&lt;/code&gt; synchronously.  Changing this to be asynchronous might look like the following example:&lt;br /&gt;&lt;pre&gt;Asset&amp;lt;Animation&amp;gt; anim( &amp;amp;myAnimLoadHandler );&lt;br /&gt;pAssetSystem-&gt;StartLoad( &amp;amp;anim, "animation/player_anim1" );&lt;br /&gt;...&lt;br /&gt;&lt;br /&gt;void AnimLoadHandler::OnLoaded( Asset&amp;lt;Animation&amp;gt;&amp;amp; a )&lt;br /&gt;{&lt;br /&gt;    // Do something with loaded asset&lt;br /&gt;}&lt;/pre&gt;There's much complexity missing from the second example, but the point should be clear: making something asynchronous is much more difficult than making something synchronous.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;Conclusion&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;Working on the streaming client was one of the most fun projects that I've ever worked on in a technical sense.  It was challenging, but the results are a huge payoff.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/46689162362346050-3430846014252754770?l=kriegshauser.blogspot.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/kriegshauser/~4/4nIJeSFVeZA" height="1" width="1"/&gt;</description><link>http://feedproxy.google.com/~r/kriegshauser/~3/4nIJeSFVeZA/evolution-of-streaming-client.html</link><author>noreply@blogger.com (Joshua Kriegshauser)</author><thr:total>7</thr:total><feedburner:origLink>http://kriegshauser.blogspot.com/2010/02/evolution-of-streaming-client.html</feedburner:origLink></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-46689162362346050.post-6617820520036942060</guid><pubDate>Wed, 11 Nov 2009 19:54:00 +0000</pubDate><atom:updated>2009-11-11T13:14:49.161-08:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">personal</category><category domain="http://www.blogger.com/atom/ns#">commentary</category><title>Dr. Laura: Dr Jekyll and Mr. Video Gamer</title><description>&lt;a href="http://www.drlaura.com/"&gt;Dr. Laura&lt;/a&gt; has a video blog about a man who plays video games for his down time:&lt;br /&gt;&lt;object width="480" height="295"&gt;&lt;param name="movie" value="http://www.youtube.com/v/OoljFL1C0DI&amp;hl=en&amp;fs=1&amp;rel=0&amp;color1=0x006699&amp;color2=0x54abd6"&gt;&lt;/param&gt;&lt;param name="allowFullScreen" value="true"&gt;&lt;/param&gt;&lt;param name="allowscriptaccess" value="always"&gt;&lt;/param&gt;&lt;embed src="http://www.youtube.com/v/OoljFL1C0DI&amp;hl=en&amp;fs=1&amp;rel=0&amp;color1=0x006699&amp;color2=0x54abd6" type="application/x-shockwave-flash" allowscriptaccess="always" allowfullscreen="true" width="480" height="295"&gt;&lt;/embed&gt;&lt;/object&gt;&lt;br /&gt;&lt;br /&gt;Now, I actually enjoy reading most Dr. Laura articles.  Her common-sense approach to most family psychology issues provides both entertainment and self-reflection.&lt;br /&gt;&lt;br /&gt;However, sometimes she's just a little too old-fashioned for me.   She seems to come off in this video as entirely anti-video-game.  I'll grant that the guy mentioned in the video is acting like a complete idiot, but not all video games are bad.  Consoles like the &lt;a href="http://wii.com/"&gt;Wii&lt;/a&gt; consistently encourage family gameplay.&lt;br /&gt;&lt;br /&gt;Maybe it's just because I'm in the video game industry, but if a guy wants to play video games for his downtime, that seems fine.  Of course, you have to look at your priorities.  Personally, family comes first.  Always.  I will usually sit down to play games after the rest of my family has turned in for the night.  If the rest of the family is napping, occasionally my son and I will enjoy &lt;a href="http://starwars.lego.com/en-us/VideoGame/Default.aspx"&gt;LEGO Star Wars&lt;/a&gt; or some other innocuous game.&lt;br /&gt;&lt;br /&gt;Everything in moderation, but please don't demonize all video games because some idiot guy can't put down the controller and spend time with his family.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/46689162362346050-6617820520036942060?l=kriegshauser.blogspot.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/kriegshauser/~4/D4azd5o4F54" height="1" width="1"/&gt;</description><link>http://feedproxy.google.com/~r/kriegshauser/~3/D4azd5o4F54/dr-laura-dr-jekyll-and-mr-video-gamer.html</link><author>noreply@blogger.com (Joshua Kriegshauser)</author><thr:total>3</thr:total><feedburner:origLink>http://kriegshauser.blogspot.com/2009/11/dr-laura-dr-jekyll-and-mr-video-gamer.html</feedburner:origLink></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-46689162362346050.post-5444813803186398580</guid><pubDate>Sat, 24 Oct 2009 17:26:00 +0000</pubDate><atom:updated>2011-03-31T23:51:47.118-07:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">game programming</category><category domain="http://www.blogger.com/atom/ns#">eq2</category><title>Keep Designer Data in Files (not Databases)</title><description>Whoa!  Did I just fall off the deep end?!  Designer Data in ... &lt;span style="font-style: italic;"&gt;files&lt;/span&gt;?!  Data... doesn't that belong in a &lt;span style="font-style: italic;"&gt;database&lt;/span&gt;?!  There are those who say that it does, but I disagree, at least when talking about development.&lt;br /&gt;&lt;br /&gt;In MMO terms, Designer Data is data that our Design team creates: Quests, Spells, NPCs, Paths, Items, Recipes, Locations, etc. Tons and tons of data that, together with art, is really the lifeblood of the game.&lt;br /&gt;&lt;br /&gt;Designer Data is to Designers as Source Code is to Programmers. It's constantly changing and growing. Different people own different parts of it. Everyone on the dev team needs all of it and as up-to-date as possible to do their current tasks. Servers need it in a highly-optimized form to run the game. There are many parallels between Designer Data and Source Code.&lt;br /&gt;&lt;br /&gt;Which brings me to my first major point.  As files, Designer Data can be stored in Source Control (we use &lt;a href="http://perforce.com/"&gt;Perforce&lt;/a&gt;, and I love it).  This gives an amazing amount of incredibly important functionality and basically for free:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;Revision history - As text (EQII uses XML) files, anyone on the team can go back and see (through the same tool that everyone must use) all previous changes to a file. Anyone can see exactly what changed between each version or between multiple versions and who made the changes. Also, you can go back in time and grab previous file revisions.&lt;br /&gt;&lt;/li&gt;&lt;li&gt;Changelists - Most source control systems group together file changes. Perforce calls these Changelists. Grouping files together is an effective tool for seeing relative changes based on concept. One changelist might be labeled "Zone 1 population" and contain NPCs, Quests that those NPCs give out, Paths that those NPCs follow, etc. If a Changelist breaks the game, it can be rolled back wholesale (and you can see who made it and go pound them).&lt;br /&gt;&lt;/li&gt;&lt;li&gt;Integration - Changes are made in branches and we have branches for each distribution of the game. As a group of changes become ready to go Live, they are integrated into the next distribution (Main goes to QA, QA goes to Staged, Staged goes to Live). With Code and Data both in the same system, integration to the next distribution becomes a one-step process.&lt;br /&gt;&lt;/li&gt;&lt;li&gt;Code/Design in sync - Since Source Code and Design Data are both stored in the same system, they are always in sync. If a Programmer makes changes that affect both (which often happens), he can rest assured that everyone will easily get the latest in both.&lt;/li&gt;&lt;li&gt;Sandboxing - Designers are only affecting their own local version of design data until they check it in. They can do testing and make tweaks before checking in something that could potentially break everyone else.&lt;/li&gt;&lt;li&gt;Blame - It's so easy to see who made a change (and confront them if necessary).&lt;br /&gt;&lt;/li&gt;&lt;/ul&gt;If you were doing Design development in a database, you would have to develop your own solution around each of these (or use database tools that not everyone would have access to). And since the database is always up-to-date, and everyone is generally working out of the same database, problems could arise from changing formats or unexpected data.&lt;br /&gt;&lt;br /&gt;Furthermore, Designer Data in files are free to have complex formats. For instance, in EQII the Designer Data is object oriented. Data definitions specify that a Character inherits from Entity and Entity inherits from a Base type. This also allows us to do things like have a base Predicate type and more complicated types like CharacterHasQuestPredicate that inherits from Predicate but has additional data members that only make sense for CharacterHasQuestPredicate (like the Quest name). In a database, you might have a Predicate table with a type column that is a number. You would have to look up what that number means from somewhere or write special tools that understand the relationship. Additionally, that Predicate table would have to include all of the options that any types could have in a very generic fashion. So you might have columns called "StringParam1" and "NumParam1". In the database then, you might have a row with type set to 12 and StringParam1 set to a quest name (or more likely NumParam1 set to a unique ID for a particular Quest). If you were looking at raw data, what would you rather see:&lt;br /&gt;&lt;pre&gt;&amp;lt;object name="CharacterHasQuestPredicate"&amp;gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;lt;string field="sQuest"&amp;gt;quests/heritage/dwarven_work_boots&amp;lt;/string&amp;gt;&lt;br /&gt;&amp;lt;/object&amp;gt;&lt;/pre&gt;Or:&lt;br /&gt;&lt;pre&gt;Type     IntParam1    IntParam2&lt;br /&gt;12       1243         &amp;lt;null&amp;gt;&lt;/pre&gt;&lt;br /&gt;The EQII Designer Data tool is very generic as it knows how to read the game's data definition. To actually add a whole new data type (Achievement for instance) actually takes zero changes to the Designer Data tool. It just takes writing a small text file that describes the Data Definition such as this one for our aforementioned CharacterHasQuestPredicate:&lt;br /&gt;&lt;pre&gt;&amp;lt;objectdef name="CharacterHasQuestPredicate" inherits="Predicate"&amp;gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;lt;fields&amp;gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;fielddef name="sQuest" type="String" require_dir="quests/" default="" /&amp;gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;lt;/fields&amp;gt;&lt;br /&gt;&amp;lt;/objectdef&amp;gt;&lt;/pre&gt;However, reading XML files isn't the fastest thing to do. Server startup is fairly slow when reading from XML files, but EQII has a utility that converts the myriad XML files into a single file that contains all of the data in a highly optimized binary format (I've actually &lt;a href="http://kriegshauser.blogspot.com/2008/12/memory-mapping-files-for-fun-and-profit.html"&gt;written about this file&lt;/a&gt; before). This file is mapped into virtual memory allowing all server processes running on the same physical machine to share one copy of the file in memory. For development purposes, developers can run their own servers against the XML data (and both internal development servers and rapidly changed external servers such as a Beta server could run against XML as well for fast turn around).  Plus, there are enough options for converting XML data to databases that you could still use XML files for development and run production servers against a database.&lt;br /&gt;&lt;br /&gt;The only major shortcoming of keeping Designer Data in files as opposed to a database is for searching and updating large amounts of data--things that a database is designed to do.&lt;br /&gt;&lt;br /&gt;For searching data, EQII actually has a very workable solution.  The EQII team has developed a system called VooDLe (a name playing off of Google and VDL--the internal name for EQII's data library).  This is a very simple web solution that syncs to the latest data and indexes it for searching.  It also detects file references and generates links.  However, it doesn't help if you'd want to find all of the shields that have a block value higher than 300.  For this, you can search VooDLe for the field that declares shield block value and quickly scan through the results.  (Rarely is searching for a numeric value so cut and dried in EQII though with level scalars and combat scalars that affect everything.)&lt;br /&gt;&lt;br /&gt;Updating large amounts of data is actually something that (shouldn't) happen very often.  Furthermore, updating large amounts of data is error-prone and should be limited.  You might even call this an inherent benefit to having designer data in files.  When this is necessary, it's possible with a simple Perl script.  If something does go wrong (even later) the Changelist can easily be reverted.&lt;br /&gt;&lt;br /&gt;Based on all this, I feel it's more beneficial to a development team to use files for Designer Data during development rather than a database.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/46689162362346050-5444813803186398580?l=kriegshauser.blogspot.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/kriegshauser/~4/1wx9PNpCMvo" height="1" width="1"/&gt;</description><link>http://feedproxy.google.com/~r/kriegshauser/~3/1wx9PNpCMvo/keep-designer-data-in-files-not.html</link><author>noreply@blogger.com (Joshua Kriegshauser)</author><thr:total>7</thr:total><feedburner:origLink>http://kriegshauser.blogspot.com/2009/10/keep-designer-data-in-files-not.html</feedburner:origLink></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-46689162362346050.post-3313497156371752482</guid><pubDate>Tue, 25 Aug 2009 07:33:00 +0000</pubDate><atom:updated>2009-08-25T01:25:33.224-07:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">soe</category><category domain="http://www.blogger.com/atom/ns#">eq2</category><title>EverQuesting</title><description>It has been a busy time for me.  For the past few weeks I've been splitting my time at &lt;a href="http://www.station.sony.com/sonyonline/"&gt;SOE&lt;/a&gt; between three different game projects.&lt;br /&gt;&lt;br /&gt;Two of them are new projects that I can't talk about yet.  I'm very excited about both of them (and &lt;del&gt;think&lt;/del&gt; &lt;span style="font-style: italic;"&gt;know&lt;/span&gt; you will be too!) and look forward to being able to talk more freely about them.  One was a temporary post while waiting for the second to ramp up.  Now that it has I will be transitioning to it full time.&lt;br /&gt;&lt;br /&gt;The third is the project that I've been working on for over four years: &lt;a href="http://everquest2.com/"&gt;EverQuest II&lt;/a&gt;.  However, with the new project my involvement on EverQuest II is shrinking.  My official title has moved away from Technical Director on EverQuest II to being the Technical Director on one of the new projects.  Don't worry though: the EverQuest II programming team is in the hands of the very capable &lt;a href="http://www.station.sony.com/sonyonline/"&gt;Greg "Rothgar" Spence&lt;/a&gt;.  Plus I fully expect to be available to the EverQuest II team for questions and technical direction for a while to come.&lt;br /&gt;&lt;br /&gt;I'm currently finishing up my last task with EverQuest II.  This last hurrah is designed to do something very important--increase exposure to EverQuest II.  I've been personally involved with nearly every change to EQII's trial program over the past four years, but this is the one I'm the most excited about.&lt;br /&gt;&lt;br /&gt;EverQuest II is a very large game.  Overwhelming content from the get-go followed by five (Soon™ to be six) expansions have conspired to create a client footprint of about 10GB.  Yikes!  That takes hours upon hours to download even on pretty fast connections.  As my last task, I'm working on reducing the initial download down to about 60MB (which is barely a blip with broadband)  and streaming the rest as the client needs it.&lt;br /&gt;&lt;br /&gt;A few things make this possible.  First, the fetching technology that we're using was first pioneered by the incredible talent on the &lt;a href="http://www.freerealms.com/"&gt;Free Realms&lt;/a&gt; team.  This made it much faster for us to get on-demand fetching up and running since they had already done the legwork and experimentation.  Second, the fetches are nothing more than &lt;a href="http://en.wikipedia.org/wiki/Hypertext_Transfer_Protocol"&gt;HTTP&lt;/a&gt; GETs which allows us to distribute all of EverQuest II's 500,000+ individual assets over a very fast &lt;a href="http://en.wikipedia.org/wiki/Content_delivery_network"&gt;CDN&lt;/a&gt;.  Using a CDN means that we don't have to develop, test, distribute and manage our own file serving solution which obviously would take more time.  Third, EverQuest II already has a fairly robust &lt;a href="http://en.wikipedia.org/wiki/Asynchronous_I/O"&gt;asynchronous&lt;/a&gt; resource-loading system that was fairly easy to extend with remote file fetching.&lt;br /&gt;&lt;br /&gt;Getting asset fetching in place was really only the beginning of the story.  My time since then has been focused on heuristics for prefetching the next assets the client will need and reducing some dependencies on synchronous IO (which would appear to "lock up" the client until the needed assets were fetched).  These challenges are far more complicated, but the challenge is welcome!&lt;br /&gt;&lt;br /&gt;It's been a great four+ years on EverQuest II, and I look forward to getting people playing after downloading a mere 60MB.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/46689162362346050-3313497156371752482?l=kriegshauser.blogspot.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/kriegshauser/~4/1Q9CDz95y1o" height="1" width="1"/&gt;</description><link>http://feedproxy.google.com/~r/kriegshauser/~3/1Q9CDz95y1o/everquesting.html</link><author>noreply@blogger.com (Joshua Kriegshauser)</author><thr:total>4</thr:total><feedburner:origLink>http://kriegshauser.blogspot.com/2009/08/everquesting.html</feedburner:origLink></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-46689162362346050.post-6909812440205320621</guid><pubDate>Mon, 13 Apr 2009 23:25:00 +0000</pubDate><atom:updated>2009-04-13T16:30:16.543-07:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">interviews</category><category domain="http://www.blogger.com/atom/ns#">eq2</category><title>Interview of Yours Truly</title><description>I was recently interviewed (a month ago, heh) on &lt;a href="http://download.station.sony.com/patch/web/misc/podcast58_64.mp3"&gt;SOE Podcast #58&lt;/a&gt;.  The interview starts at about 1:19:44.  Whew, long podcast!&lt;br /&gt;&lt;br /&gt;SOE does a podcast about &lt;a href="http://www.station.sony.com/podcasts.vm"&gt;every two weeks&lt;/a&gt; and feature interviews, goofy ads and exciting news about current and upcoming SOE games.  You can also follow &lt;a href="http://twitter.com/SOE_Podcast"&gt;SOE_Podcast on Twitter&lt;/a&gt; for news and notification.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/46689162362346050-6909812440205320621?l=kriegshauser.blogspot.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/kriegshauser/~4/_fQ8lnKcjvA" height="1" width="1"/&gt;</description><link>http://feedproxy.google.com/~r/kriegshauser/~3/_fQ8lnKcjvA/interview-of-yours-truly.html</link><author>noreply@blogger.com (Joshua Kriegshauser)</author><thr:total>2</thr:total><feedburner:origLink>http://kriegshauser.blogspot.com/2009/04/interview-of-yours-truly.html</feedburner:origLink></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-46689162362346050.post-7349907503030166746</guid><pubDate>Thu, 09 Apr 2009 06:51:00 +0000</pubDate><atom:updated>2009-04-09T01:01:03.705-07:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">game programming</category><category domain="http://www.blogger.com/atom/ns#">eq2</category><title>Lost in Translation</title><description>&lt;a href="http://everquest2.com/"&gt;EverQuest II&lt;/a&gt; currently runs in five languages: English, German, French, Japanese and Russian.  However, all of the programmers and designers actually build the game in English.  To accomplish the translation, we use a proprietary SOE library: the Text Translation and Templating Tool, or T4 for short.  T4 is a very powerful tool that allows us to do translation through anything from dictionary lookups to macro expansion.  Our Internationalization department (i18n for short) is responsible for maintaining the T4 library and the various language modules for it.&lt;br /&gt;&lt;br /&gt;Unfortunately, sometimes we write things in a way that isn't easily language-agnostic.  Take the following example for instance:&lt;br /&gt;&lt;br /&gt;"Rothgar's Touch of Pestilence critically double attacks Autenil for 38 disease damage."&lt;br /&gt;&lt;br /&gt;It's a common bit of text that appears in the combat chat when you're fighting in EverQuest II. &lt;br /&gt;&lt;br /&gt;Let's break it down a bit.&lt;br /&gt;&lt;br /&gt;First of all, there's the actor performing the action.  This can be you or someone else; Rothgar in this example.  There's also the target which can be you or someone else; Autenil in this example.  There's an optional spell name (Touch of Pestilence) and how much of which kind of damage.  Oh, and the action -- double attack in this case -- and actions may or may not be critical.&lt;br /&gt;&lt;br /&gt;With the way T4 works with insertion macros, we use a base string like this:&lt;br /&gt;"^1 $2 critically $3 $4 for +5 $6 damage"&lt;br /&gt;&lt;br /&gt;But this really doesn't work, at least not for anything but English.  That pesky verb needs context in other languages to be conjugated properly.  We currently put in either "double attack" or "double attacks" based on who the source and target actors are.  It works fine in English but comes out in other languages like gibberish.&lt;br /&gt;&lt;br /&gt;The way to fix this is to build base strings that include the verbs, but this can explode quickly.  Say there are five different verbs that could be used in that case.  To make base strings that include the verbs with all of the other possibilities you end up with about 640 strings, and that's assuming that there is only one damage type (EQII allows multiple per attack).&lt;br /&gt;&lt;br /&gt;In the end, I went with using a generic verb for non-English languages ("hit") and put special attacks or critical status parenthetically on the end:&lt;br /&gt;Rothgar's Touch of Pestilence hits Autenil for 38 disease damage.  (critical double attack)&lt;br /&gt;&lt;br /&gt;This will be much easier on our translators and will make sense for our players.  It's been a long time coming.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/46689162362346050-7349907503030166746?l=kriegshauser.blogspot.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/kriegshauser/~4/iJWgcs8tMGM" height="1" width="1"/&gt;</description><link>http://feedproxy.google.com/~r/kriegshauser/~3/iJWgcs8tMGM/lost-in-translation.html</link><author>noreply@blogger.com (Joshua Kriegshauser)</author><thr:total>1</thr:total><feedburner:origLink>http://kriegshauser.blogspot.com/2009/04/lost-in-translation.html</feedburner:origLink></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-46689162362346050.post-8731709269107369230</guid><pubDate>Thu, 12 Feb 2009 21:42:00 +0000</pubDate><atom:updated>2009-12-13T23:50:19.552-08:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">login2009</category><category domain="http://www.blogger.com/atom/ns#">programming</category><title>LOGIN 2009</title><description>I'm going to be &lt;a href="http://www.2009.loginconference.com/session.php?id=104375"&gt;speaking&lt;/a&gt; at the &lt;a href="http://www.2009.loginconference.com/index.php"&gt;LOGIN 2009 conference&lt;/a&gt; in Seattle this year:&lt;br /&gt;&lt;br /&gt;&lt;blockquote&gt;Unfortunately, client crashes are bound to occur during the course of a live game. While prevention is definitely preferable, how quickly they are detected and resolved helps to form a positive opinion of the development team in the players’ minds. This session is a technical discussion surrounding generation, retrieval and processing of client crash logging. Though many concepts presented are fairly generic and may apply to other platforms and languages, examples will be given in C++ on the Windows platform.&lt;/blockquote&gt;&lt;br /&gt;&lt;br /&gt;Additionally, one of my &lt;a href="http://www.2009.loginconference.com/speaker.php?id=102982"&gt;programmers&lt;/a&gt; is also &lt;a href="http://www.2009.loginconference.com/session.php?id=104204"&gt;speaking&lt;/a&gt;:&lt;br /&gt;&lt;br /&gt;&lt;blockquote&gt;This lecture will be a technical discussion about the Bayesian spam filtering solution used by various SOE MMOs to block over half a million spams per day per game. The lecture will discuss SOE spam filter implementation including what can be done beyond pure Bayesian filtering to help MMOs increase their filters accuracy and reduce feedback to the spammers. We will cover the general theory behind the filter, as well as the mathematical theory behind it and real-world examples of how it is working today.&lt;br /&gt;&lt;/blockquote&gt;&lt;br /&gt;Be sure to stop by and visit our lectures if you're going to LOGIN 2009 this year!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/46689162362346050-8731709269107369230?l=kriegshauser.blogspot.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/kriegshauser/~4/afZzJERQ-Dw" height="1" width="1"/&gt;</description><link>http://feedproxy.google.com/~r/kriegshauser/~3/afZzJERQ-Dw/login-2009.html</link><author>noreply@blogger.com (Joshua Kriegshauser)</author><feedburner:origLink>http://kriegshauser.blogspot.com/2009/02/login-2009.html</feedburner:origLink></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-46689162362346050.post-5622878671240771155</guid><pubDate>Sat, 31 Jan 2009 09:23:00 +0000</pubDate><atom:updated>2009-02-01T00:45:21.798-08:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">personal</category><category domain="http://www.blogger.com/atom/ns#">memes</category><title>Meme'd</title><description>Several people have tagged me with the "25 things" meme, so I figured I'd satisfy the adoring masses:&lt;br /&gt;&lt;ol&gt;&lt;li&gt;I’m a science fiction geek.  My favorite Star Trek series is The Next Generation and I’ve seen every episode at least twice.  I also like Star Wars and Return of the Jedi is finishing up on my TV as I write this.  I’ve even gone as far as to draw up deck plans for starships…  Snow Crash by Neal Stephenson is one of my favorite books.&lt;/li&gt;&lt;li&gt;My favorite genre of music is Country, much to the chagrin of my family and my co-workers.  I do also like Classic Rock and some Oldies, so I can usually find something mutually pleasing to the people around me.&lt;/li&gt;&lt;li&gt;Tests show that I’m allergic to peanuts, but I’ve never had a problem eating them.  My kids, however...&lt;/li&gt;&lt;li&gt;My family is very involved politically, and I have some strong political convictions, though I’m not very vocal about them... usually.&lt;/li&gt;&lt;li&gt;I would play Rock Band until my arms fell off if I was ever uninterrupted for a long enough period.  I never am though (darnit).&lt;/li&gt;&lt;li&gt;I used to play first-chair alto saxophone in the high school band… while in elementary school.  I’ve done solos and performances.  I still have my sax and could probably play it, but not very well anymore.&lt;/li&gt;&lt;li&gt;I’ve driven 157mph.  In a car.  On the road.  In real life.&lt;/li&gt;&lt;li&gt;I’ve lived completely on my own since I was 19, except for a few months living with an aunt and uncle while looking for a place after moving to CO.&lt;/li&gt;&lt;li&gt;I’ve slept in the bottom of the Grand Canyon and on 14,000 ft mountain peaks in Colorado.&lt;/li&gt;&lt;li&gt;I almost never dream while sleeping.&lt;/li&gt;&lt;li&gt;Unlike my dad, mom and brother, I don't wear glasses or contacts.  My vision is 20/15.&lt;/li&gt;&lt;li&gt;I started programming computers in BASIC in the third or fourth grade.  I got my first C compiler before graduating elementary school.  I would design simple games with my brother and then write them.  One early Christmas I painstakingly programmed several Christmas carols to play over the 3-voice music synthesizer on a TI-99/4a.  My whole extended family was present for the concert.&lt;/li&gt;&lt;li&gt;I love cycling and enjoy commuting to work by bicycle when I have the time.&lt;/li&gt;&lt;li&gt;A friend and I nearly died climbing down cliffs while lost in the Gila Wilderness of New Mexico.  My Ham Radio was useless in the mountains.  We had to abandon our equipment and wade through a torrent of snow melt runoff.  After finding our way to a highway, we had to hitchhike for 17 miles to get back to my car, which had a flat tire.&lt;/li&gt;&lt;li&gt;I’ve worked at McDonalds, CompUSA, an indoor minigolf place, and as a self-employed consultant.&lt;/li&gt;&lt;li&gt;My dad, mom, sister and brother all have a college or trade degree.  I don’t, though not for lack of trying.  I just kept getting hired away from school...&lt;/li&gt;&lt;li&gt;I’ve been to Mexico several times and Canada, but I’d really love to see parts of every other continent.&lt;/li&gt;&lt;li&gt;I’ve been skydiving and loved it.  If I were independently wealthy and didn’t have kids, I’d make a hobby out of it.&lt;/li&gt;&lt;li&gt;My love of electronics goes hand-in-hand with my love of computers.  In elementary school, I walked around with a 40-pin IC in my pocket.  I etched my own circuit boards and built an adjustable power supply in junior high.  I built a turbo timer and air/fuel ratio indicator for my Supra.  I was the only kid I knew with an o-scope.&lt;/li&gt;&lt;li&gt;I’ve logged a few hours towards my private pilot’s license.  I’d love to complete it despite the impracticality.&lt;/li&gt;&lt;li&gt;My faith in Jesus Christ has affected nearly every part of my life in some way.&lt;/li&gt;&lt;li&gt;When I proposed to my (now) wife, I arrived 3 hours before I was supposed to, snuck up behind her and surprised her while holding the open ring box.  When she whirled around she knocked the ring out of my hand.  I had to crawl around on the floor for a few minutes to find it.  We’ve been married eight years as I write this.&lt;/li&gt;&lt;li&gt;I had lunch with Curt Schilling of the Boston Red Sox when he was interested in having me join the team at his game studio, 38 Studios.  Good guy, but I respectfully declined.&lt;br /&gt;&lt;/li&gt;&lt;li&gt;I have a pencil stab wound in my hand that is still visible after 15 years.&lt;br /&gt;&lt;/li&gt;&lt;li&gt;I collect Ultimate Collector Series LEGOs.  The models in my office at work total over 15,000 bricks alone.&lt;/li&gt;&lt;/ol&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/46689162362346050-5622878671240771155?l=kriegshauser.blogspot.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/kriegshauser/~4/LoQ-Lv4Bs-Q" height="1" width="1"/&gt;</description><link>http://feedproxy.google.com/~r/kriegshauser/~3/LoQ-Lv4Bs-Q/memed.html</link><author>noreply@blogger.com (Joshua Kriegshauser)</author><thr:total>0</thr:total><feedburner:origLink>http://kriegshauser.blogspot.com/2009/01/memed.html</feedburner:origLink></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-46689162362346050.post-7437582153369077764</guid><pubDate>Mon, 12 Jan 2009 19:18:00 +0000</pubDate><atom:updated>2009-01-12T11:23:48.649-08:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">interviews</category><category domain="http://www.blogger.com/atom/ns#">ultima online</category><category domain="http://www.blogger.com/atom/ns#">eq2</category><title>UOForums Interview</title><description>&lt;a href="http://www.uoforums.com/"&gt;UOForums&lt;/a&gt; asked me for an interview a while back.  I finally &lt;a href="http://www.uoforums.com/f1222/uoforums-interviews-josh-kriegshauser-aka-speedman-from-everquest-57057/"&gt;obliged&lt;/a&gt;:&lt;br /&gt;&lt;br /&gt;&lt;blockquote&gt;&lt;b&gt;What has been your most memorable accomplishment in your career? &lt;/b&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="color:DarkRed;"&gt;Making Technical Director before 30? &lt;/span&gt;:)&lt;span style="color:DarkRed;"&gt; Getting hired by Origin is probably the most memorable accomplishment as it was my first gig in the game industry. It was an amazing experience: I got to meet and work with the people behind the names on the forum that I idolized. The whole experience was surreal. &lt;/span&gt;&lt;/blockquote&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/46689162362346050-7437582153369077764?l=kriegshauser.blogspot.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/kriegshauser/~4/IaqgAQPs2ks" height="1" width="1"/&gt;</description><link>http://feedproxy.google.com/~r/kriegshauser/~3/IaqgAQPs2ks/uoforums-interview.html</link><author>noreply@blogger.com (Joshua Kriegshauser)</author><thr:total>1</thr:total><feedburner:origLink>http://kriegshauser.blogspot.com/2009/01/uoforums-interview.html</feedburner:origLink></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-46689162362346050.post-5722784117483000067</guid><pubDate>Fri, 02 Jan 2009 03:06:00 +0000</pubDate><atom:updated>2009-01-01T19:13:58.967-08:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">zune</category><category domain="http://www.blogger.com/atom/ns#">ipod</category><category domain="http://www.blogger.com/atom/ns#">iphone</category><title>Tsk, tsk, Microsoft</title><description>&lt;a href="http://www.h-i-r.net/2008/12/zune-doesnt-like-leap-years.html"&gt;This&lt;/a&gt; is &lt;a href="http://www.foxnews.com/story/0,2933,474690,00.html"&gt;awesome&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;&lt;blockquote&gt;&lt;span id="intelliTXT"&gt;Thousands of Zune portable media players made by Microsoft Corp. suddenly froze up early Wednesday, Dec. 31, labeling Internet wits to label the phenomenon "Z2K."&lt;br /&gt;&lt;/span&gt;&lt;/blockquote&gt;&lt;br /&gt;It goes without saying that I'm quite happy with my &lt;a href="http://kriegshauser.blogspot.com/2008/01/new-mp3-player-part-deux.html"&gt;iPod Touch&lt;/a&gt; and &lt;a href="http://kriegshauser.blogspot.com/2008/12/good-bye-t-mobile.html"&gt;iPhone&lt;/a&gt;, neither of which locked up at 12:01am yesterday.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/46689162362346050-5722784117483000067?l=kriegshauser.blogspot.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/kriegshauser/~4/06HKdC3YMmM" height="1" width="1"/&gt;</description><link>http://feedproxy.google.com/~r/kriegshauser/~3/06HKdC3YMmM/tsk-tsk-microsoft.html</link><author>noreply@blogger.com (Joshua Kriegshauser)</author><thr:total>0</thr:total><feedburner:origLink>http://kriegshauser.blogspot.com/2009/01/tsk-tsk-microsoft.html</feedburner:origLink></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-46689162362346050.post-1456723497246349820</guid><pubDate>Mon, 22 Dec 2008 07:47:00 +0000</pubDate><atom:updated>2008-12-22T00:59:55.953-08:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">personal</category><category domain="http://www.blogger.com/atom/ns#">iphone</category><category domain="http://www.blogger.com/atom/ns#">cell phones</category><title>Good-Bye T-Mobile (and Good Riddance)</title><description>I just received a $446 bill from &lt;a href="http://www.t-mobile.com/"&gt;T-Mobile&lt;/a&gt;.  To say that I'm disenchanted with T-Mobile would be an understatement.&lt;br /&gt;&lt;br /&gt;I suppose I should rewind a little bit.  I was a T-Mobile customer for over five years.  Back in 2003 I signed up with T-Mobile because I was moving to Austin, TX to work for the now-defunct &lt;a href="http://en.wikipedia.org/wiki/Origin_Systems"&gt;Origin Systems&lt;/a&gt; and they had the best plan for me at the time.  My wife and I mostly use our cell phones to call each other, so the cheapest no-frills family plan usually suffices for me.&lt;br /&gt;&lt;br /&gt;Fast forward to 11/27/2006.  I was sick of using the bottom-of-the-barrel free no-camera monochrome Nokia phone that I got for free when signing up in 2003.  My original two-year contract had long since expired.  &lt;a href="http://www22.verizon.com/"&gt;Verizon&lt;/a&gt; was offering the inexplicably popular &lt;a href="http://www.motorola.com/motoinfo/product/details/0,,69,00.html"&gt;RAZR&lt;/a&gt; phone for some ridiculously low price.  However, having been a loyal T-Mobile customer for over three years, I decided that I'd give them the chance to beat Verizon's deal and compete for my business.  The "Customer Loyalty" representative that I talked to jumped at the opportunity.  Not only did they give me better RAZRs than Verizon was offering at the same price (and the same two-year committment), he also changed my phone numbers and gave me some text messages for free, waived all of the upgrade fees AND put me on an "unlisted" family plan that was slightly cheaper.  Wow!  That's customer service!&lt;br /&gt;&lt;br /&gt;And then followed four months of hell.&lt;br /&gt;&lt;br /&gt;They sent me the wrong phones.  They charged me full price.  They wouldn't reimburse me to ship them back.  Every representative that I talked to showed a new previously-unheard-of level of incompetence and screwed something else up, making it all the more difficult to explain my plight to the next representative.  Untold hours on the phone forever lost arguing with people, explaining my situation and fighting to get my bill fixed and figured out.  Pages and pages of notes with a myriad of representatives' and supervisors' names weaving a trail of regret and resentment.  For &lt;span style="font-style: italic;"&gt;four months&lt;/span&gt;.&lt;br /&gt;&lt;br /&gt;Had it not been for that damned two-year contract I would've flushed them right then and there, but eventually the dust settled and things were worked out.&lt;br /&gt;&lt;br /&gt;Two years later, my contract was nearing its end, though I didn't know the exact date it was up.  I once again figured that I'd give T-Mobile every opportunity to keep my business.  I went into their stores looking at various &lt;a href="http://www.blackberry.com/"&gt;Blackberrys&lt;/a&gt; and the &lt;a href="http://www.t-mobileg1.com/"&gt;G1&lt;/a&gt;.  I was interested in a phone that would give me access to my corporate email and the Internet.  I was asking for a little bit better price on the phone and the plan.  They wouldn't budge.  I called "Customer Loyalty" again and they were resilient against my requests.  I told them that I really wanted the iPhone and was willing to go to AT&amp;amp;T unless T-Mobile would give me a better deal but instead got stuck talking to some blatherskite who insisted on hammering me on the irrelevant topic that the iPhone was available through T-Mobile--in Germany.  I feel that I gave T-Mobile every opportunity to keep my business.  Suffice to say, now I'm with AT&amp;amp;T and proud to walk around with my &lt;a href="http://www.apple.com/"&gt;Apple&lt;/a&gt; &lt;a href="http://www.apple.com/iphone/"&gt;iPhone&lt;/a&gt; (&lt;a href="http://kriegshauser.blogspot.com/2008/01/new-mp3-player-part-deux.html"&gt;I have an&lt;/a&gt; &lt;a href="http://www.apple.com/ipodtouch/"&gt;iPod Touch&lt;/a&gt; too, but adding a phone to it is just brilliant).&lt;br /&gt;&lt;br /&gt;Now, about this $446 bill.  You'll notice that I upgraded (and started a new two-year contract) on 11/27/2006.  I switched to AT&amp;amp;T on 11/23/2008.  &lt;span style="font-weight: bold;"&gt;Four days&lt;/span&gt; away from two years and T-Mobile slaps me with two $200 early terminations fees--one for each of my two lines.  Four days!  At the &lt;span style="font-style: italic;"&gt;very least&lt;/span&gt; paying the whole amount for my last month would fulfill my obligation to the two-year contract.&lt;br /&gt;&lt;br /&gt;Needless to say, I &lt;span style="font-style: italic;"&gt;immediately &lt;/span&gt;got T-Mobile customer service on the phone.  Despite the lividity coursing through my veins I managed to remain level-headed.  The "Customer Loyalty" representative offered to cut the $200 per-line fee down to $50 per-line.  "That is unacceptable," was my reply.  Her next response was cut short as I quipped, "I'll need to speak to your supervisor."  The merry Christmas tunes that filled my ear while I was waiting filled me with a sense of irony.  Finally the woman came back and told me that her supervisor was busy on another call but she could finagle it so that I'd just finish paying for the full final month, dropping $384 from my bill and returning sanity to the evening.  Another 30 minutes of my life wasted on the phone with T-Mobile to combat utter, inane stupidity.&lt;br /&gt;&lt;br /&gt;To T-Mobile, if you happen to be listening:&lt;br /&gt;&lt;ol&gt;&lt;li&gt;Either allow "customer loyalty" more authority to please customers (especially on plan prices) or lose them.&lt;br /&gt;&lt;/li&gt;&lt;li&gt;Pro-rate the early termination fee or at the very least, change your system so customers who are within the billing cycle of their end-of-contract just pay out the rest of their month to fulfill their obligations.&lt;/li&gt;&lt;li&gt;Recognize the Charlie Foxtrot cases and assign a single person be your customer's advocate and to sort the problem out.  Dealing with one &lt;span style="font-style: italic;"&gt;competent &lt;/span&gt;person who understood my problem after upgrading in 2006 would've been very nice, but alas, it was not so.&lt;br /&gt;&lt;/li&gt;&lt;li&gt;You can do better than the G1.  It doesn't even natively support Microsoft Exchange email.&lt;br /&gt;&lt;/li&gt;&lt;/ol&gt;&lt;br /&gt;To everyone else, I'd have to recommend &lt;span style="font-style: italic;"&gt;against&lt;/span&gt; T-Mobile.  By all means, use a different cell phone provider.  If you're with T-Mobile now, get out as soon as you can.  If you're not big on data, you can probably save a bundle by buying individual minutes and not having a plan.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/46689162362346050-1456723497246349820?l=kriegshauser.blogspot.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/kriegshauser/~4/XqEjw78EQw0" height="1" width="1"/&gt;</description><link>http://feedproxy.google.com/~r/kriegshauser/~3/XqEjw78EQw0/good-bye-t-mobile.html</link><author>noreply@blogger.com (Joshua Kriegshauser)</author><thr:total>8</thr:total><feedburner:origLink>http://kriegshauser.blogspot.com/2008/12/good-bye-t-mobile.html</feedburner:origLink></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-46689162362346050.post-2530079607708620906</guid><pubDate>Sat, 20 Dec 2008 06:33:00 +0000</pubDate><atom:updated>2008-12-19T23:48:55.348-08:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">game programming</category><category domain="http://www.blogger.com/atom/ns#">programming</category><title>Memory-Mapping Files for Fun and Profit</title><description>It's amazing that I still meet programmers who don't understand the benefits of &lt;a href="http://en.wikipedia.org/wiki/Memory-mapped_file"&gt;memory-mapping files&lt;/a&gt;.  If you're unaware of what that means, let me explain it a little bit.  Traditional reading from a file involves several system calls: opening the file, seeking to the desired position and then reading or writing to the file.  System calls can be slow as the operating system may have to switch to kernel mode and call into drivers to perform the IO operations.  Memory-mapping a file usually has just one call, and then you are able to access the file as if the entire file had been read into a contiguous block of memory.  But here is the greatest benefit (at least to me as a MMO server programmer): all processes accessing a file can use the same virtual memory.&lt;br /&gt;&lt;br /&gt;Take the &lt;a href="http://www.everquest2.com/"&gt;EverQuest II&lt;/a&gt; server for instance.  The (binary) data file that describes nearly everything except collision geometry runs about 460MB.  Every zone server uses that same file and we run several zone servers on one physical machine.  With memory-mapping, the OS uses 460MB of virtual memory (assuming the entire file is used) just once and all zone server instances can use that same virtual memory without having to read the file into each individual instance.&lt;br /&gt;&lt;br /&gt;Like anything, there are drawbacks.  Most modern operating systems use demand paging, so if you try and access a page of memory representing a part of the file that hasn't loaded yet, a page fault will be triggered and the OS will have to perform kernel-mode IO to load that page from the file.  Depending on how much virtual memory your application has access to (and how spread out your accesses are), this could potentially trigger a very high number of page faults.&lt;br /&gt;&lt;br /&gt;Another drawback is address space.  A 32-bit application will only have access to less than 4GB of address space (Windows has a 2GB address space by default).  If you map entire large files into memory, you are consuming this address space.  This has become more and more important on the EQII servers.  As I mentioned, our data file is 460MB.  Since shortly after the game launched, this file has always been fully mapped into memory.  That was fine back then as it was much smaller than it is today.  Now, after launching our fifth expansion, the data file is now nearly one quarter of our allotted address space (and not getting any smaller).&lt;br /&gt;&lt;br /&gt;Fortunately, the APIs for Linux (&lt;a href="http://linux.die.net/man/2/mmap"&gt;&lt;code&gt;mmap&lt;/code&gt;&lt;/a&gt;) and Windows (&lt;a href="http://msdn.microsoft.com/en-us/library/aa366761%28VS.85%29.aspx"&gt;&lt;code&gt;MapViewOfFile&lt;/code&gt;&lt;/a&gt;) allow an offset and a length to be specified.  This opens the door to &lt;span style="font-style: italic;"&gt;partial &lt;/span&gt;memory-mapped files.  However, there are some caveats.  The offset cannot be just any offset; it must be aligned.  On Linux the offset must be a multiple of the page size (found by &lt;a href="http://linux.die.net/man/2/getpagesize"&gt;&lt;code&gt;getpagesize&lt;/code&gt;&lt;/a&gt;), but on Windows the offset must be a multiple of the "allocation granularity" (found by calling &lt;a href="http://msdn.microsoft.com/en-us/library/ms724381%28VS.85%29.aspx"&gt;&lt;code&gt;GetSystemInfo&lt;/code&gt;&lt;/a&gt;).  The length of the mapping usually does not &lt;span style="font-style: italic;"&gt;need&lt;/span&gt; to be aligned, but if you're planning a generic partial mapping solution, it's probably best to use the same alignment.  Another thing to consider is that mapping parts of files as you need them will spread them all over your address space; you cannot assume that page 1 and page 2 of a file will be placed next to each other in memory if you map them separately.&lt;br /&gt;&lt;br /&gt;Here are some of the design goals for my partial memory-mapping implementation:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;Phase sections out as they are no longer used (start-up data is only needed at start-up)&lt;br /&gt;&lt;/li&gt;&lt;li&gt;Reduce thrashing (phasing out a section and then mapping it back in)&lt;/li&gt;&lt;li&gt;Similar performance to the current full-file implementation&lt;br /&gt;&lt;/li&gt;&lt;li&gt;Reduce memory usage!&lt;/li&gt;&lt;li&gt;Ability to permanently map/unmap sections (i.e. sections that don't phase out)&lt;/li&gt;&lt;li&gt;Ability to automatically re-size sections on demand&lt;br /&gt;&lt;/li&gt;&lt;/ul&gt;Allow me to touch a bit on that last point.  For my interface, given an offset and a length, I wanted to return a pointer to contiguous data rather than copy data into a provided buffer.  Say some previous access forced you to load page 10 (but not page 11) and now you want to read some data that spans pages 10 and 11.  This presents a problem for the contiguous data interface.  I can't just map in page 11 and hope that it ends up next to page 10 (I will point out that there are 'hints' that can be passed to the OS APIs to attempt address space positioning, but these are discouraged).  Do you leave page 10 mapped and create a new mapping that combines pages 10 and 11?  Since my interface always required an offset and a length for every access, I could safely unmap the existing page 10 and map pages 10 and 11 together.  Data requests for data on page 10 would now refer to the joint mapping.  However, this did greatly expand the number of test cases I needed and complicated the logic.&lt;br /&gt;&lt;br /&gt;This nifty little piece of technology is currently running on our live game servers and doing very well.  Instead of 460MB, we now keep a pool of about 20-30MB mapped from the file and generally enjoy a 0.1% or lower miss rate.  Best of all, we still have all the benefits of memory-mapped files.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/46689162362346050-2530079607708620906?l=kriegshauser.blogspot.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/kriegshauser/~4/7CfUjFezQg0" height="1" width="1"/&gt;</description><link>http://feedproxy.google.com/~r/kriegshauser/~3/7CfUjFezQg0/memory-mapping-files-for-fun-and-profit.html</link><author>noreply@blogger.com (Joshua Kriegshauser)</author><thr:total>1</thr:total><feedburner:origLink>http://kriegshauser.blogspot.com/2008/12/memory-mapping-files-for-fun-and-profit.html</feedburner:origLink></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-46689162362346050.post-6237435065257137262</guid><pubDate>Tue, 09 Dec 2008 22:26:00 +0000</pubDate><atom:updated>2008-12-09T14:32:03.487-08:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">personal</category><category domain="http://www.blogger.com/atom/ns#">rock band</category><title>(Country) Rock Band!</title><description>&lt;a href="http://kotaku.com/5105013/rock-band-country-here-are-your-hotly+anticipated-details"&gt;Oh my gosh&lt;/a&gt;:&lt;br /&gt;&lt;blockquote&gt;Consisting of five tracks, it'll be out on December 18, and will feature tracks by Brad Paisley, Brooks &amp;amp; Dunn, Dierks Bentley, Miranda Lambert and the Dixie Chicks. Pricing is fairly standard, at $2/160MS for a song or $8.50/680MS for all five.&lt;/blockquote&gt;As I've &lt;a href="http://kriegshauser.blogspot.com/2007/04/five-things-you-probably-dont-know.html"&gt;stated before&lt;/a&gt;, I love Country Music.  Maybe I'll play hooky on December 18th.&lt;br /&gt;&lt;br /&gt;Ok, I probably won't, but I'm still excited.  Harmonix, if you're listening: MOAR PLZ! KTHXBYE&lt;br /&gt;&lt;blockquote&gt;&lt;/blockquote&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/46689162362346050-6237435065257137262?l=kriegshauser.blogspot.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/kriegshauser/~4/Fm7PMmyyzjE" height="1" width="1"/&gt;</description><link>http://feedproxy.google.com/~r/kriegshauser/~3/Fm7PMmyyzjE/country-rock-band.html</link><author>noreply@blogger.com (Joshua Kriegshauser)</author><thr:total>1</thr:total><feedburner:origLink>http://kriegshauser.blogspot.com/2008/12/country-rock-band.html</feedburner:origLink></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-46689162362346050.post-1523606033876618860</guid><pubDate>Mon, 01 Dec 2008 07:42:00 +0000</pubDate><atom:updated>2010-08-10T13:33:44.297-07:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">repair</category><category domain="http://www.blogger.com/atom/ns#">xbox</category><title>/resurrect Xbox-360</title><description>I sat down on Saturday to enjoy some &lt;a href="http://starwars.lego.com/en-us/videogame/default.aspx"&gt;Lego Star Wars&lt;/a&gt; with my son, but discovered a different feeling welling up inside me instead: dread.  My Xbox-360 had fallen victim to every Xbox owner's nightmare--The &lt;a href="http://www.lansalot.net/rrod.jpg"&gt;Red&lt;/a&gt; &lt;a href="http://www.google.com/search?q=rrod"&gt;Rings&lt;/a&gt; of &lt;a href="http://en.wikipedia.org/wiki/Xbox_360_technical_problems"&gt;Death&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;He opted to play &lt;a href="http://www.supermariogalaxy.com/"&gt;Super Mario Galaxy&lt;/a&gt; on the much more reliable &lt;a href="http://wii.com/"&gt;Wii&lt;/a&gt; instead, while I googled everything I could about the RROD.&lt;br /&gt;&lt;br /&gt;It's interesting to note that Microsoft &lt;a href="http://www.xbox360fanboy.com/2007/07/05/360s-warranty-extended-to-three-years/"&gt;extended the warranty on the 360 to 3 years&lt;/a&gt;.  However, I purchased my xbox used from a friend and don't have the receipt from the original purchase.  Additionally, the date of manufacture shows as 11/03/2005, so warranty repair seemed like a non-option to me.  Plus, I've had friends send their xbox in for warranty repair and it's taken them over a month to get them back.  I'm impatient when it comes to gaming!  But if you are reading this and have a xbox that is still under warranty, please consider the options carefully before breaking that silver warranty sticker!  There is no going back!&lt;br /&gt;&lt;br /&gt;I found a &lt;a href="http://copronymous.com/wp-content/uploads/2007/05/fix-for-the-3rlod.pdf"&gt;guide&lt;/a&gt; that provided an at-home remedy for repairing the RROD.  A good theory about what causes the RROD is a &lt;a href="http://forums.xbox-scene.com/index.php?showtopic=588032&amp;amp;st=0"&gt;design flaw leading to flexing the motherboard&lt;/a&gt;.  This warping leads to microscopic breaks in the tiny surface-mount solder connections exacerbated by numerous heat cycles.  The guide directs you to fix the problem by removing the &lt;a href="http://images.anandtech.com/reviews/system/microsoft/xbox360/backplates.jpg"&gt;x-shaped brackets&lt;/a&gt; holding the heat-sinks to the CPU and GPU and replacing the mounts with screws from any old hardware store.&lt;br /&gt;&lt;br /&gt;I thought, "Eh, why not."&lt;br /&gt;&lt;br /&gt;So I &lt;a href="http://www.xboxchopshop.com/catalog/man_360_open.php"&gt;tore down my xbox&lt;/a&gt;:&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://3.bp.blogspot.com/_BfrMrXICM_Q/STOQUdjkrzI/AAAAAAAAACk/71IQwkGkmaw/s1600-h/DSC01437.JPG"&gt;&lt;img style="cursor: pointer; width: 320px; height: 240px;" src="http://3.bp.blogspot.com/_BfrMrXICM_Q/STOQUdjkrzI/AAAAAAAAACk/71IQwkGkmaw/s320/DSC01437.JPG" alt="" id="BLOGGER_PHOTO_ID_5274718269786599218" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;&lt;a href="http://www.anandtech.com/systems/showdoc.aspx?i=2610&amp;amp;p=6"&gt;Getting the heat-sinks off&lt;/a&gt; was a pain, but persistence pays off:&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://2.bp.blogspot.com/_BfrMrXICM_Q/STORFEFARcI/AAAAAAAAACs/HeED2fpL5mI/s1600-h/DSC01438.JPG"&gt;&lt;img style="cursor: pointer; width: 320px; height: 240px;" src="http://2.bp.blogspot.com/_BfrMrXICM_Q/STORFEFARcI/AAAAAAAAACs/HeED2fpL5mI/s320/DSC01438.JPG" alt="" id="BLOGGER_PHOTO_ID_5274719104761087426" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;The guide I was using didn't tell me this, but you'll also need a 1/4" socket or nut driver to get the posts out of the heat-sinks.&lt;br /&gt;&lt;br /&gt;The next step was to clean off the old thermal paste from the chips so that new thermal grease could be applied:&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://4.bp.blogspot.com/_BfrMrXICM_Q/STORe8Jn_7I/AAAAAAAAAC0/XPdTYGWf73g/s1600-h/DSC01439.JPG"&gt;&lt;img style="cursor: pointer; width: 320px; height: 240px;" src="http://4.bp.blogspot.com/_BfrMrXICM_Q/STORe8Jn_7I/AAAAAAAAAC0/XPdTYGWf73g/s320/DSC01439.JPG" alt="" id="BLOGGER_PHOTO_ID_5274719549309583282" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;To do this, I used left-over &lt;a href="http://www.arcticsilver.com/arcticlean.htm"&gt;ArctiClean&lt;/a&gt; from my &lt;a href="http://kriegshauser.blogspot.com/2007/06/video-card-woes.html"&gt;last project of this nature&lt;/a&gt;.  Using the ArctiClean produced a very high-quality mirror shine from the CPU, GPU and heat-sinks:&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://3.bp.blogspot.com/_BfrMrXICM_Q/STOSOFU5wAI/AAAAAAAAAC8/14Dd3jEyu_g/s1600-h/DSC01440.JPG"&gt;&lt;img style="cursor: pointer; width: 320px; height: 240px;" src="http://3.bp.blogspot.com/_BfrMrXICM_Q/STOSOFU5wAI/AAAAAAAAAC8/14Dd3jEyu_g/s320/DSC01440.JPG" alt="" id="BLOGGER_PHOTO_ID_5274720359226654722" border="0" /&gt;&lt;/a&gt; &lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://4.bp.blogspot.com/_BfrMrXICM_Q/STOSpdpVnDI/AAAAAAAAADE/LxNAKoEY3vw/s1600-h/DSC01441.JPG"&gt;&lt;img style="cursor: pointer; width: 320px; height: 240px;" src="http://4.bp.blogspot.com/_BfrMrXICM_Q/STOSpdpVnDI/AAAAAAAAADE/LxNAKoEY3vw/s320/DSC01441.JPG" alt="" id="BLOGGER_PHOTO_ID_5274720829611285554" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;I staged my bolts according to the guide, put the washers on the motherboard and applied the directed amount of &lt;a href="http://www.arcticsilver.com/as5.htm"&gt;Arctic Silver 5&lt;/a&gt; (about .5 - .75 the size of a grain of rice) to the GPU right before replacing the heat-sink.  I then spread the thermal paste out over the chip with the edge of an old plastic gift (credit) card:&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://1.bp.blogspot.com/_BfrMrXICM_Q/STOTVUuGahI/AAAAAAAAADM/5XGJhg2KXF0/s1600-h/DSC01442.JPG"&gt;&lt;img style="cursor: pointer; width: 320px; height: 240px;" src="http://1.bp.blogspot.com/_BfrMrXICM_Q/STOTVUuGahI/AAAAAAAAADM/5XGJhg2KXF0/s320/DSC01442.JPG" alt="" id="BLOGGER_PHOTO_ID_5274721583129586194" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;The GPU heatsink was then applied, careful to make sure that I had all screws started before allowing the heatsink to contact the surface of the GPU.  I very carefully tightened the screws until they were just snug, but &lt;span style="font-style: italic;"&gt;evenly&lt;/span&gt; snug.  Repeat the whole ordeal for the CPU.  Here's what the bottom of the motherboard looks like now without the x-brackets:&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://1.bp.blogspot.com/_BfrMrXICM_Q/STOUrfJCKdI/AAAAAAAAADU/f8HqWh9S9MM/s1600-h/DSC01444.JPG"&gt;&lt;img style="cursor: pointer; width: 320px; height: 240px;" src="http://1.bp.blogspot.com/_BfrMrXICM_Q/STOUrfJCKdI/AAAAAAAAADU/f8HqWh9S9MM/s320/DSC01444.JPG" alt="" id="BLOGGER_PHOTO_ID_5274723063395658194" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;Note the eight hex cap screws.  I recommend using normal phillips machine screws if you can find them.  Unfortunately, my local Home Depot didn't.  This was all I could find.  Originally I had washers on both sides of the motherboard, but the taller hex cap screws had major clearance problems trying to fit back in the metal case.  I had to (carefully) remove each screw, remove the washers on the bottom of the motherboard and then replace the screw.  I was very glad that I had only snugged the screws earlier.&lt;br /&gt;&lt;br /&gt;According to the guide, at this point you should place the motherboard in the case, hook up the necessities and fire it up &lt;span style="font-style: italic;"&gt;without &lt;/span&gt;the fans to &lt;span style="font-style: italic;"&gt;purposely &lt;/span&gt;overheat the system and cause the solder to re-melt back into place.  However, when I booted it up at this point, there were no more red rings!&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://1.bp.blogspot.com/_BfrMrXICM_Q/STOWUtSxR8I/AAAAAAAAADc/djhctUI8oXE/s1600-h/DSC01445.JPG"&gt;&lt;img style="cursor: pointer; width: 320px; height: 240px;" src="http://1.bp.blogspot.com/_BfrMrXICM_Q/STOWUtSxR8I/AAAAAAAAADc/djhctUI8oXE/s320/DSC01445.JPG" alt="" id="BLOGGER_PHOTO_ID_5274724871080855490" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;Apparently, between the new fastenings for the heatsinks and the better thermal grease on the chips, the problem was fixed!  I finished re-assembling the chassis and decided it was time for a real test.  I took it back downstairs and hooked it up to the TV, re-inserted the hard disk and memory card and grabbed a controller.&lt;br /&gt;&lt;br /&gt;I played &lt;a href="http://kriegshauser.blogspot.com/2008/11/braid.html"&gt;Braid&lt;/a&gt; for over a half hour with no problems, though I do wonder how permanent this solution will be.  If it does break down again, I'll come back and update.&lt;br /&gt;&lt;br /&gt;8/9/2010: After 21 months the RROD have returned.  I haven't actually pulled the machine apart again to check it out, but it might be worth buying a refurbished base model at this point!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/46689162362346050-1523606033876618860?l=kriegshauser.blogspot.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/kriegshauser/~4/52mBhrQBJ28" height="1" width="1"/&gt;</description><link>http://feedproxy.google.com/~r/kriegshauser/~3/52mBhrQBJ28/resurrect-xbox-360_30.html</link><author>noreply@blogger.com (Joshua Kriegshauser)</author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="http://3.bp.blogspot.com/_BfrMrXICM_Q/STOQUdjkrzI/AAAAAAAAACk/71IQwkGkmaw/s72-c/DSC01437.JPG" height="72" width="72" /><thr:total>3</thr:total><feedburner:origLink>http://kriegshauser.blogspot.com/2008/11/resurrect-xbox-360_30.html</feedburner:origLink></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-46689162362346050.post-4517273196719524337</guid><pubDate>Thu, 20 Nov 2008 07:01:00 +0000</pubDate><atom:updated>2008-11-19T23:34:39.775-08:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">shadow odyssey</category><category domain="http://www.blogger.com/atom/ns#">eq2</category><title>The Shadow Odyssey</title><description>Well, the launch was a success!  As &lt;a href="http://eq2dev.wordpress.com/2008/11/14/on-your-mark-get-set%e2%80%a6/"&gt;I promised&lt;/a&gt;, I tweeted (through &lt;a href="http://twitter.com/autenil"&gt;my twitter account&lt;/a&gt;) the happenings of launch day as they occurred.  Here's the full compilation:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;&lt;span style="font-weight: bold;"&gt;11/17 2:19am&lt;/span&gt;: &lt;span class="entry-content"&gt;Final green-light from QA regarding EQII:TSO is in. Critical mass building... Later today we'll be staging the code to all the servers.&lt;/span&gt;&lt;/li&gt;&lt;li&gt;&lt;span class="entry-content"&gt;&lt;span style="font-weight: bold;"&gt;11/17 9:46am&lt;/span&gt;: &lt;/span&gt;&lt;span class="entry-content"&gt;At work on the day before EQII:TSO launches.  Feels like i didn't get a weekend.  Oh wait, that would be because I didn't. :)&lt;/span&gt;&lt;/li&gt;&lt;li&gt;&lt;span class="entry-content"&gt;&lt;span style="font-weight: bold;"&gt;11/17 10:16am&lt;/span&gt;: &lt;/span&gt;&lt;span class="entry-content"&gt;Wow, 15 minute leads meeting.  I think that's the fastest it has ever been &lt;/span&gt;&lt;a href="http://hashtags.org/tag/shadowodyssey"&gt;#shadowodyssey&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;span class="entry-content"&gt;&lt;span style="font-weight: bold;"&gt;11/17 2:26pm&lt;/span&gt;: &lt;/span&gt;&lt;span class="entry-content"&gt;Leaving the office now and returning at 3am. Requests to have the Ops dept bring servers down at 12am have been set. cya@3! &lt;/span&gt;&lt;a href="http://hashtags.org/tag/shadowodyssey"&gt;#shadowodyssey&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;span class="entry-content"&gt;&lt;span style="font-weight: bold;"&gt;11/17 6:57pm&lt;/span&gt;: &lt;/span&gt;&lt;span class="entry-content"&gt;I better hop in game and finish the &lt;a href="http://eq2players.station.sony.com/news_archive_content.vm?month=102008&amp;amp;id=1918"&gt;D.I.R.T.Y.&lt;/a&gt; &lt;a href="http://eq2players.station.sony.com/news_archive_content.vm?month=102008&amp;amp;id=1949"&gt;Quests&lt;/a&gt; before &lt;a href="http://eq2players.station.sony.com/news_archive_content.vm?id=1977&amp;amp;section=News&amp;amp;locale=en_US"&gt;midnight&lt;/a&gt;! &lt;/span&gt;&lt;a href="http://hashtags.org/tag/shadowodyssey"&gt;#shadowodyssey&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;span class="entry-content"&gt;&lt;span style="font-weight: bold;"&gt;11/18 3:05am&lt;/span&gt;: &lt;/span&gt;&lt;span class="entry-content"&gt;*yawn* 3am. Servers came down at 12am and back up running TSO at about 12:45am. QA doing smoke tests now (will be a while) &lt;/span&gt;&lt;a href="http://hashtags.org/tag/shadowodyssey"&gt;#shadowodyssey&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;span class="entry-content"&gt;&lt;span style="font-weight: bold;"&gt;11/18 3:09am&lt;/span&gt;: &lt;/span&gt;&lt;span class="entry-content"&gt;i'm patching up now. luckily, I sneakily pre-copied all the expansion assets from the patcher (yay for being internal) &lt;/span&gt;&lt;a href="http://hashtags.org/tag/shadowodyssey"&gt;#shadowodyssey&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;span class="entry-content"&gt;&lt;span style="font-weight: bold;"&gt;11/18 3:28am&lt;/span&gt;: &lt;/span&gt;&lt;span class="entry-content"&gt;Unbeknownst to most, &lt;a href="http://www.vivox.com/"&gt;Vivox&lt;/a&gt; is using the opportunity of all of EQII being down to do some voice chat maintenance &lt;/span&gt;&lt;a href="http://hashtags.org/tag/shadowodyssey"&gt;#shadowodyssey&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;span class="entry-content"&gt;&lt;span style="font-weight: bold;"&gt;11/18 5:24am&lt;/span&gt;: &lt;/span&gt;&lt;span class="entry-content"&gt;Smoke test on production servers is going great.  &lt;a href="http://www.vivox.com/"&gt;Vivox&lt;/a&gt; maintenance is done. &lt;/span&gt;&lt;a href="http://hashtags.org/tag/shadowodyssey"&gt;#shadowodyssey&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;span class="entry-content"&gt;&lt;span style="font-weight: bold;"&gt;11/18 6:17am&lt;/span&gt;: &lt;/span&gt;&lt;span class="entry-content"&gt;QA still testing strong. Platform QA is testing to make sure that production keys can be consumed properly. Getting closer! &lt;/span&gt;&lt;a href="http://hashtags.org/tag/shadowodyssey"&gt;#shadowodyssey&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;span class="entry-content"&gt;&lt;span style="font-weight: bold;"&gt;11/18 7:29am&lt;/span&gt;: &lt;/span&gt;&lt;span class="entry-content"&gt;Breakfast brought in. QA is still testing strong. Funny to see everyone queued up on the login servers. Getting closer... &lt;/span&gt;&lt;a href="http://hashtags.org/tag/shadowodyssey"&gt;#shadowodyssey&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;span style="font-weight: bold;" class="entry-content"&gt;11/18 [&lt;/span&gt;&lt;span class="entry-content"&gt;&lt;span style="font-weight: bold;"&gt;8am]&lt;/span&gt;: And We're Live!  Devs are on the server (incognito) playtesting and reporting any issues. &lt;/span&gt;&lt;a href="http://hashtags.org/tag/shadowodyssey"&gt;#shadowodyssey&lt;/a&gt;&lt;span class="entry-content"&gt; is smooth so far! [&lt;a href="http://twitter.com/"&gt;twitter&lt;/a&gt; was down at 8am]&lt;/span&gt;&lt;/li&gt;&lt;li&gt;&lt;span class="entry-content"&gt;&lt;span style="font-weight: bold;"&gt;11/18 10:11am&lt;/span&gt;: &lt;/span&gt;&lt;span class="entry-content"&gt;I (and other EQII team members) are running around &lt;a href="http://maps.eq2interface.com/map.php?zone_id=361"&gt;Moors of Ykesha&lt;/a&gt; on various servers doing quests. Enjoying the expansion! &lt;/span&gt;&lt;a href="http://hashtags.org/tag/shadowodyssey"&gt;#shadowodyssey&lt;/a&gt;                                             &lt;/li&gt;&lt;li&gt;&lt;span class="entry-content"&gt;&lt;span style="font-weight: bold;"&gt;11/18 11:37am&lt;/span&gt;: &lt;/span&gt;&lt;span class="entry-content"&gt;Reports of a few minor issues with the broker.  We're looking into them. &lt;/span&gt;&lt;a href="http://hashtags.org/tag/shadowodyssey"&gt;#shadowodyssey&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;span class="entry-content"&gt;&lt;span style="font-weight: bold;"&gt;11/18 1:55pm&lt;/span&gt;: &lt;/span&gt;&lt;span class="entry-content"&gt;Been here 11 hours (and counting). May head to the &lt;a href="http://forums.station.sony.com/eq2/posts/list.m?topic_id=436053&amp;amp;post_id=4860080"&gt;launch party&lt;/a&gt;. Some minor issues with Ykesha, but I made some tweaks &lt;/span&gt;&lt;a href="http://hashtags.org/tag/shadowodyssey"&gt;#shadowodyssey&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;span class="entry-content"&gt;&lt;span style="font-weight: bold;"&gt;11/18 2:40pm&lt;/span&gt;: &lt;/span&gt;&lt;span class="entry-content"&gt;Almost 12 hours here. Heading to the launch party and then home for some Zzzz. /yawn. Great launch so far! &lt;/span&gt;&lt;a href="http://hashtags.org/tag/shadowodyssey"&gt;#shadowodyssey&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;span class="entry-content"&gt;&lt;span style="font-weight: bold;"&gt;11/18 4:59pm&lt;/span&gt;: &lt;/span&gt;&lt;span class="entry-content"&gt;Enjoyed seeing &lt;a href="http://twitter.com/tenamdar"&gt;@tenamdar&lt;/a&gt; and others at the &lt;/span&gt;&lt;a href="http://hashtags.org/tag/shadowodyssey"&gt;#shadowodyssey&lt;/a&gt;&lt;span class="entry-content"&gt; launch party! It was a very smooth launch. Now: sleep!&lt;/span&gt;&lt;/li&gt;&lt;/ul&gt;We really enjoyed working on this expansion!  Enjoy!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/46689162362346050-4517273196719524337?l=kriegshauser.blogspot.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/kriegshauser/~4/Yw30Yn353_s" height="1" width="1"/&gt;</description><link>http://feedproxy.google.com/~r/kriegshauser/~3/Yw30Yn353_s/shadow-odyssey.html</link><author>noreply@blogger.com (Joshua Kriegshauser)</author><thr:total>3</thr:total><feedburner:origLink>http://kriegshauser.blogspot.com/2008/11/shadow-odyssey.html</feedburner:origLink></item></channel></rss>

