<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>Dan Hulton&#039;s blog</title>
	<atom:link href="http://blog.danhulton.com/feed/" rel="self" type="application/rss+xml" />
	<link>http://blog.danhulton.com</link>
	<description>Coffee, code, contemplation.</description>
	<lastBuildDate>Mon, 14 May 2012 23:36:32 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.3.2</generator>
		<item>
		<title>So Much Fail.</title>
		<link>http://blog.danhulton.com/2012/05/14/so-much-fail/?utm_source=rss&#038;utm_medium=rss&#038;utm_campaign=so-much-fail</link>
		<comments>http://blog.danhulton.com/2012/05/14/so-much-fail/#comments</comments>
		<pubDate>Mon, 14 May 2012 23:36:32 +0000</pubDate>
		<dc:creator>Dan Hulton</dc:creator>
				<category><![CDATA[Games]]></category>

		<guid isPermaLink="false">http://blog.danhulton.com/?p=815</guid>
		<description><![CDATA[The only thing worse than this video: Is the comments beneath it. lol, is this supposed to be &#8220;hot&#8221; or something? I guess this kind of marketing works on virgins or something, but it&#8217;s funny how much this shit doesn&#8217;t even faze you once you get a girlfriend. Try again, Ubisoft &#8212; this bitch is [...]]]></description>
			<content:encoded><![CDATA[<p>The only thing worse than this video:</p>
<p><iframe width="520" height="294" src="http://www.youtube.com/embed/2K0eRtLeFiM" frameborder="0" allowfullscreen></iframe></p>
<p>Is the comments beneath it.</p>
<blockquote><p>lol, is this supposed to be &#8220;hot&#8221; or something? I guess this kind of marketing works on virgins or something, but it&#8217;s funny how much this shit doesn&#8217;t even faze you once you get a girlfriend. Try again, Ubisoft &#8212; this bitch is hideous, your game looks generic, and you should﻿ probably fire your marketing department.<br />
BTW, it&#8217;s pretty obvious that your marketing bots gave you 600 of those likes.</p></blockquote>
<div style="text-align: right; margin-bottom: 25px;">&#8211; SolipsistGrifter</div>
<blockquote><p>Wow, this chick(?) is hideous&#8230; What terrible marketing. She looks like some Las Vegas tranny with bad plastic surgery (sorry) and this is supposed to make me want to play this game? Even if you didn&#8217;t objectify ugly women to sell your shit game I probably still wouldn&#8217;t play it; looks like every﻿ other generic brown shooter.</p></blockquote>
<div style="text-align: right; margin-bottom: 25px;">&#8211; phubans</div>
<p>I love how they purport to attack the sexism in the ad, while throwing out phrases like &#8220;this bitch is hideous&#8221; and &#8220;Las Vegas tranny&#8221;.  My god, the irony in the sentence &#8220;Even if you didn&#8217;t objectify ugly women to sell your shit game&#8221;&#8230;  So hold on, objectifying women upsets you, and you focus on whether the woman in the ad is <i>pretty</i> or not to judge her value?</p>
<p>The internet makes me so sad some days.</p>
]]></content:encoded>
			<wfw:commentRss>http://blog.danhulton.com/2012/05/14/so-much-fail/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Run Apache As Your User For Fewer Conflicts</title>
		<link>http://blog.danhulton.com/2012/05/08/run-apache-as-your-user/?utm_source=rss&#038;utm_medium=rss&#038;utm_campaign=run-apache-as-your-user</link>
		<comments>http://blog.danhulton.com/2012/05/08/run-apache-as-your-user/#comments</comments>
		<pubDate>Tue, 08 May 2012 20:03:58 +0000</pubDate>
		<dc:creator>Dan Hulton</dc:creator>
				<category><![CDATA[Coding]]></category>

		<guid isPermaLink="false">http://blog.danhulton.com/?p=809</guid>
		<description><![CDATA[When writing code, so many things can go wrong. So many little things can be forgotten or overlooked and cause bugs and interruption. However, your time is finite and your patience is limited. Removing all these little causes of frustration pays incredible dividends down the line. One of the frustrations I regularly encounter is failing [...]]]></description>
			<content:encoded><![CDATA[<p>When writing code, so many things can go wrong.  So many little things can be forgotten or overlooked and cause bugs and interruption.  However, your time is finite and your patience is limited.  Removing all these little causes of frustration pays incredible dividends down the line.</p>
<p>One of the frustrations I regularly encounter is failing to save a file in our backend tool after making modifications in SVN, which is caused by a permissions conflict.  The user I run SVN as (me) is different from the user I run Apache as (_www by default), which means that if when <i>I</i> own a file, Apache can run into trouble trying to save it (such as through our backend tool).</p>
<p>For a while, I depended on a custom bash script that would recursively modify the permissions in my development folder, but I still have to remember to run that script every time I make certain modifications via SVN.  Every once in a while I&#8217;ll forget and get glaring error messages.  It breaks my flow, which slows me down and frustrates me.  Thankfully, there is an easy solution.</p>
<h3>Be someone else</h3>
<p>If Apache runs as the same user that SVN does, suddenly these problems dissappear.  They both have the same rights and access to the files, and no longer need I worry about running scripts to fix things.</p>
<p>Kick open a console:</p>
<pre class="prettyprint lang-bsh">$ sudo nano /etc/apache2/httpd.conf</pre>
<p>Find the line that says:</p>
<pre class="prettyprint">User _www</pre>
<p>And change &#8220;_www&#8221; to your username.  If you&#8217;re not sure what that is, type &#8220;whoami&#8221; at the console to find out.  With that done, save and restart Apache:</p>
<pre class="prettyprint lang-bsh">$ sudo apachectl restart</pre>
<p>From here on out, your problems should be solved!  (Well, you may need to change permissions on or remove certain files created by _www, like session files.)  Apache will open and save files as you, so as long as you have access, so too will your web server.</p>
]]></content:encoded>
			<wfw:commentRss>http://blog.danhulton.com/2012/05/08/run-apache-as-your-user/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>Spreadsheets: A Game Designers Best Friend</title>
		<link>http://blog.danhulton.com/2012/05/04/spreadsheets-a-game-designers-best-friend/?utm_source=rss&#038;utm_medium=rss&#038;utm_campaign=spreadsheets-a-game-designers-best-friend</link>
		<comments>http://blog.danhulton.com/2012/05/04/spreadsheets-a-game-designers-best-friend/#comments</comments>
		<pubDate>Fri, 04 May 2012 13:15:18 +0000</pubDate>
		<dc:creator>Dan Hulton</dc:creator>
				<category><![CDATA[Design]]></category>
		<category><![CDATA[Games]]></category>

		<guid isPermaLink="false">http://blog.danhulton.com/?p=779</guid>
		<description><![CDATA[Seriously, if you haven&#8217;t fallen in love with spreadsheets yet, you&#8217;re doin&#8217; it wrong. I&#8217;m currently in the middle of figuring out a multiple-week game loop with really tight targets. How the heck would you ever get this done without a well-constructed spreadsheet? Trial and error would take forever. Guesstimates wouldn&#8217;t get you anywhere near [...]]]></description>
			<content:encoded><![CDATA[<p>Seriously, if you haven&#8217;t fallen in love with spreadsheets yet, you&#8217;re doin&#8217; it wrong.  I&#8217;m currently in the middle of figuring out a multiple-week game loop with really tight targets.  How the heck would you ever get this done without a well-constructed spreadsheet?  Trial and error would take forever.  Guesstimates wouldn&#8217;t get you anywhere near what you wanted. No, you need constantly-updating formulae and graphs and colours and stuff.  The difference it makes in your capabilities is insane.</p>
<h3>A quick example</h3>
<p>Say you have an enemy &#8211; a cute little slime-creature.  And when he&#8217;s defeated, he drops a macguffin &#8211; but occasionally!  Players need ten of these macguffins to progress to the next area, where there&#8217;s a boar who drops a similar macguffin.  And in the next area, there&#8217;s the final creature &#8211; a gargoyle &#8211; and ten more macguffins to collect.</p>
<p>Three monsters, three macguffins.  We want this area to be challenging, but not <i>too</i> frustrating, so let&#8217;s set some base assumptions: If it takes less than 100 encounters, it&#8217;s not challenging enough.  If it takes more than 300 encounters, it&#8217;s too frustrating.</p>
<p>Now, we could just throw some numbers onto those creatures and hope, but I&#8217;ve got a better way.  Let&#8217;s set up a spreadsheet with some intuition-based numbers and see how close we come.</p>
<h3>Table 1: Drop Rates</h3>
<p>The first thing we need to figure out is how often a player is going to get a macguffin drop from a creature.  To do that, I&#8217;ve created the following table:</p>
<p><a href="http://blog.danhulton.com/wp-content/uploads/2012/05/table1-1.png"><img src="http://blog.danhulton.com/wp-content/uploads/2012/05/table1-1.png" alt="" title="Drop Rates - First Try" width="329" height="108" class="aligncenter size-full wp-image-782" /></a></p>
<p>Column A is the name of the creature the player will be encountering.  B is the chance they will beat that creature when fighting them.  C is the chance they drop the macguffin they are looking for.  And finally D is the number of expected macguffins from each encounter.  So far, we can see that for every time you fight a slime, you can expect to get 0.07 of a macguffin.  Seems pretty low, but we&#8217;ll find out just how low in a second.  Let&#8217;s make a second table.</p>
<h3>Table 2: Encounters Needed</h3>
<p><a href="http://blog.danhulton.com/wp-content/uploads/2012/05/table2-1.png"><img src="http://blog.danhulton.com/wp-content/uploads/2012/05/table2-1.png" alt="" title="Encounters Needed Table" width="321" height="213" class="aligncenter size-full wp-image-786" /></a></p>
<p>Column A has the same creature names as before.  B is the amount we need to collect before we can progress.  And C is the number of expected encounters a player will need to go through to get the amount listed in column B.</p>
<p>And WOW are those numbers high!  Seems our intuition was off, and it&#8217;s time to play with the numbers a bit to balance this out.  We have some options:</p>
<ul>
<li>We could increase the drop rate of the macguffins.  This will not only speed up the collection, but also reduce variance for players.  (Variance is a topic for another time, but in short, you want less of it.  High variance means that you&#8217;ll have a lot of players who fall outside of your expected numbers, which is either exceedingly frustrating or boring for those players.)</li>
<li>We could decrease the difficulty of the encounters.  This will allow players to collect secondary assets (gold, experience) more quickly while speeding up collection as well.</li>
<li>We could decrease the number of macguffins needed.  Psychologically, collecting 10 macguffins at a 10% drop rate feels a lot less like a chore than collecting 100 macguffins at a 100% drop rate.</li>
</ul>
<p>And that&#8217;s outside of doing something crazy tricksy, like adding or removing more creatures, allowing players to buy or craft macguffins, etc.  So let&#8217;s play with the numbers some, and see if we can come up with something a little more <i>fun</i>.</p>
<h3>Table 3: Some Interesting Numbers</h3>
<p><a href="http://blog.danhulton.com/wp-content/uploads/2012/05/table3.png"><img src="http://blog.danhulton.com/wp-content/uploads/2012/05/table3.png" alt="" title="Some Interesting Numbers" width="298" height="175" class="aligncenter size-full wp-image-789" /></a></p>
<p>It only took me about five minutes of tweaking to come up with this, and I can already tell that it&#8217;s a lot better.  For one, it only takes 191 encounters to resolve &#8211; almost exactly in the middle of our 100-300 range.  For another, see how the Encounters column in the second table gently rises?  It didn&#8217;t when I first started playing around with the numbers, but I quickly saw how going from 50 encounters to 25 encounters to 125 encounters would be very weird, and altered my values to suit.</p>
<p>Of course, you&#8217;ll still want to play-test this to ensure it&#8217;s actually fun when you <i>play it</i>, but this is a much better starting point for your playtest than our original numbers, and it only took five minutes to find out, and five minutes to improve it a vast amount.</p>
<p>Hopefully you can see how much of a difference a good spreadsheet can bring to a game developer&#8217;s toolkit.  I&#8217;ve still got a few more concepts I want to cover regarding spreadsheets (the LOOKUP function, conditional formatting, etc.), so stay tuned!</p>
]]></content:encoded>
			<wfw:commentRss>http://blog.danhulton.com/2012/05/04/spreadsheets-a-game-designers-best-friend/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Female Game Developers</title>
		<link>http://blog.danhulton.com/2012/02/29/female-game-developers/?utm_source=rss&#038;utm_medium=rss&#038;utm_campaign=female-game-developers</link>
		<comments>http://blog.danhulton.com/2012/02/29/female-game-developers/#comments</comments>
		<pubDate>Thu, 01 Mar 2012 01:10:16 +0000</pubDate>
		<dc:creator>Dan Hulton</dc:creator>
				<category><![CDATA[Coding]]></category>
		<category><![CDATA[Design]]></category>
		<category><![CDATA[Life]]></category>

		<guid isPermaLink="false">http://blog.danhulton.com/?p=772</guid>
		<description><![CDATA[I follow a bunch of female game developers on Twitter. Now, I also follow a bunch of male game developers on Twitter, too. In fact, it&#8217;s fairer to say that I just follow a bunch of game developers on Twitter. But that&#8217;s exactly the issue, isn&#8217;t it? What are you talking about? The point is, [...]]]></description>
			<content:encoded><![CDATA[<p>I follow a bunch of female game developers on Twitter.  Now, I also follow a bunch of male game developers on Twitter, too.  In fact, it&#8217;s fairer to say that I just follow a bunch of game developers on Twitter.  But that&#8217;s exactly the issue, isn&#8217;t it?</p>
<h3>What are you talking about?</h3>
<p>The point is, a few days ago, <a href="https://twitter.com/#!/whitney">@whitney</a> &#8211; one of the game devs I follow &#8211; tweeted the following:</p>
<blockquote><p><a href="https://twitter.com/#!/whitney/status/173567185762009089">Folks contact me b/c they need a female game designer. Makes me want to relabel a giant jar of mayo with MAGIC GIRL SAUCE &#038; carry it around.</a></p></blockquote>
<p>Then followed it up with:</p>
<blockquote><p><a href="https://twitter.com/#!/whitney/status/173568126649253888">Was also going to say I can&#8217;t wait until &#8220;female game designer&#8221; sounds as antiquated as &#8220;female doctor&#8221; but uh, people still say the latter.</a></p></blockquote>
<p>And then:</p>
<blockquote><p><a href="https://twitter.com/#!/whitney/status/173568517378019328">Pilotess! Doctorette! Police officerina! Game designelle?</a></p></blockquote>
<h3>Well she&#8217;s right, you know</h3>
<p>Well, maybe.  I&#8217;m sure that in many cases &#8211; and probably the original case she mentioned &#8211; it&#8217;s just hiring managers ticking a checkbox.  &#8220;Must have X number of female developers on staff.&#8221;  Or worse, clueless wanna-be Zyngas thinking they can hire <a href="https://twitter.com/#!/whitney/">@whitney</a> and have her sell them The Secret Behind Bilking Ladies Out Of Money.</p>
<p>But on the other hand, there can be an <i>incredible</i> value in adding a female to an already all-male team.</p>
<h3>Doctorette!</h3>
<p>Imagine you have to go into the doctor&#8217;s office and have your, ah&#8230; sensitive areas examined &#8211; you think your partner might have been cheating on you, and that rash down there is <i>hella</i> embarrassing and worrying.  But the only doctors available are of the opposite gender.</p>
<p>Sure, maybe <i>you&#8217;re</i> strong enough and un-self-conscious enough to go get it done anyway, but there are going to be plenty of people out there who are embarrassed to the point where they may skip having the examination done at all.</p>
<p>Having a female doctor on-staff may <i>save lives</i>.</p>
<h3>Police officerina!</h3>
<p>10-16!  10-16!  We have a domestic disturbance!  Car 460, you are closest, please respond!</p>
<p>And yet car 416 has only male officers.  Arriving on-scene, they&#8217;re able to separate the squabbling couple.  The man insists that his &#8220;stupid bitch&#8221; of a wife is trying to ruin his life and he doesn&#8217;t remember who started swinging first.  The lady is too intimidated by the large male police officer to say anything.  In this case, having officers of both genders on-site ensures that both parties get fair, unbiased, caring, and un-intimidating treatment.</p>
<h3>Yeah, but Pilotess?</h3>
<p>You got me on that one.  That IS ridiculous.</p>
<h3>Okay, so&#8230; Female Game Developer, then?</h3>
<p>I still think this one has merit.  I went to a presentation at PAX East last year on &#8220;Expanding Your Game&#8217;s Market&#8221;, and expected tips about SEO, or cross-brand promotion, etc.  How wrong I was.  The entire presentation &#8211; the <i>entire</i> presentation &#8211; was about finding entirely different markets <i>for</i> your game.  Not how to advertise better, but how to think of and appeal to entirely different people.</p>
<p>A great deal was said about transgender gamers.  Transgender people typically tend to be <i>very</i> brand-loyal &#8211; when they find a company that values them and values their values, they make go out of their way to reward that effort with increased patronage.  Many of the people on the panel were blown away by the fact that there were no games or game companies falling over themselves to appeal to transgender people.</p>
<p>But really, how can they without having someone on-staff who understands the position of transgender people?  I&#8217;ll be honest, I don&#8217;t know the first thing about what it&#8217;s like to identify as transgender.  I don&#8217;t have any transgender friends, or even acquaintances!  So how am I supposed to make a game that appeals to that market without hiring from within that market?</p>
<p>Now, a market that games companies <i>are</i> falling all over themselves to appeal to is <i>women</i>.  Yet a great many game companies are still composed entirely of men!  Now while we all (generally) know some women that we can consult with or at least mentally model, how much more beneficial would it be to actually <i>have</i> a lady on-staff that can contribute regularly and meaningfully?</p>
<h3>Hypocrite!</h3>
<p>A little.  We don&#8217;t have many female game developers at <a href="http://www.hitgrab.com">Hitgrab</a>, true.  And yet we somehow manage to put out a game that <i>does</i> appeal to women.</p>
<p>But it&#8217;s <i>hard.</i></p>
<p>Putting yourself in someone else&#8217;s shoes &#8211; successfully &#8211; is difficult.  We&#8217;ve lamented a few times the difficulties we face in trying to appeal to the various play styles and tastes of the people who play <a href="http://www.mousehuntgame.com">MouseHunt</a>, especially when we don&#8217;t share those same tastes.  We do our best to put the stuff <i>we&#8217;d</i> like to do in the game, as well as listen to our community, and put in the things that <i>they</i> request.</p>
<p>But it&#8217;s <i>hard.</i></p>
<p>Many great, game-changing ideas fall by the wayside because they don&#8217;t have a champion.  Many incredible ideas never get raised because nobody with that point of view is there.  Many good, cross-gender-appealing ideas <i>do</i> make it in the game, but they have rough edges that only get polished off once the game is live and a full cross-section of our players have the chance to realize what does and doesn&#8217;t work.</p>
<p>Like I said, it&#8217;s <i>hard</i>.</p>
<p>So would I hire a female game dev as a magic bullet to make my game appeal to women?  Heck no.</p>
<p>But would I hire a game dev with insight into the mind of a wider market &#8211; all other qualifications being equal?  Sure.  And that dev would have an excellent chance of being female, I&#8217;m sure.</p>
]]></content:encoded>
			<wfw:commentRss>http://blog.danhulton.com/2012/02/29/female-game-developers/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Broken Glass</title>
		<link>http://blog.danhulton.com/2012/02/16/broken-glass/?utm_source=rss&#038;utm_medium=rss&#038;utm_campaign=broken-glass</link>
		<comments>http://blog.danhulton.com/2012/02/16/broken-glass/#comments</comments>
		<pubDate>Fri, 17 Feb 2012 00:14:14 +0000</pubDate>
		<dc:creator>Dan Hulton</dc:creator>
				<category><![CDATA[Coding]]></category>

		<guid isPermaLink="false">http://blog.danhulton.com/?p=761</guid>
		<description><![CDATA[Imagine, for a moment&#8230; You&#8217;re the new guy at BlokkStone Games, and you&#8217;ve had a pretty good first month. You fit in with the other programmers, and your team has made some excellent progress towards releasing their new title: Carolina Jess And The Temple Of Gloom. It&#8217;s a retro-graphics cell-based adventure game that is looking [...]]]></description>
			<content:encoded><![CDATA[<p>Imagine, for a moment&#8230;</p>
<p>You&#8217;re the new guy at BlokkStone Games, and you&#8217;ve had a pretty good first month.  You fit in with the other programmers, and your team has made some excellent progress towards releasing their new title: Carolina Jess And The Temple Of Gloom.  It&#8217;s a retro-graphics cell-based adventure game that is looking pretty sweet.</p>
<p>Now occasionally &#8211; when a player is hit, or an enemy dies &#8211; you need to add a glow to a shape.  You do this by calling an internal library function written long ago: &#8220;addGlowToShape()&#8221;.  Everything was working fine&#8230; until you tried using shapes that weren&#8217;t exact squares.  Everything still ends up being an exact square, every time!</p>
<p>Dutifully, you dig up the library file, find the function in question, and are greeted with&#8230; this:</p>
<p><code>
<pre class="prettyprint">
/**
        add a glow to a shape
    */
function addGlowToShape(&#038;$shape) {

    // getstartloc
    $x = getStartX($shape);
    $y = getStartY($shape);
    global $world;
    $cells = $world->getCells();
    $shape_name = $shape->getName();
    $cellsAffected = 0;

    // do it all
    for ($rowY = $y; $rowY < $y + $shape->getHeight(); $rowY++) {
        for ($rowX = $x; $rowX < $x + $shape->getRowLength($y); $rowX++)
            addGlowToCell($cells[$rowX][$rowY]);
            $cellsAffected++;
    }

    // @todo: return false on an error
    return $cellsAffected;
}
</pre>
<p></code></p>
<p>Augh!  Oh god!  What is&#8230; Is that comment supposed to <b>mean</b> anything?  And&#8230; and you don&#8217;t even <b>use</b> $shape_name!  What is <b>going on</b> in there?</p>
<p>This, my friends, is broken glass.  Broken glass all over the place.  And I bet you it didn&#8217;t start out like this.  I bet you it started out very simple, very maintainable &#8211; like everyone&#8217;s favourite pet code &#8211; and then evolved into this horrid mess.  But how, you ask?  Easily.  Someone broke a window.</p>
<h3>The Broken Windows Theory</h3>
<p>Alright, enough dancing around metaphors here.  Let me explain what I actually mean.</p>
<p>In 1982, social scientists James Q. Wilson and George L. Kelling wrote an article containing the following example:</p>
<blockquote><p>Consider a building with a few broken windows. If the windows are not repaired, the tendency is for vandals to break a few more windows. Eventually, they may even break into the building, and if it&#8217;s unoccupied, perhaps become squatters or light fires inside. Or consider a sidewalk. Some litter accumulates. Soon, more litter accumulates. Eventually, people even start leaving bags of trash from take-out restaurants there or breaking into cars.</p></blockquote>
<p>So influential was this article that it was used to justify a crackdown on vandalism, fare-dodging, and lead to all kinds of &#8220;zero-tolerance&#8221; policing in the city of New York.  The theory was that if the police cleaned up all the &#8220;broken windows&#8221; (i.e. worked tirelessly to clean up the streets and absolutely prevent lesser crimes), that the overall crime rate would improve.  And from 1984 (when the program began) onwards, the rate of serious crime fell dramatically. Today, the City of New York City is not the scary, drug-and-disease-infested hellhole predicted in 1980&#8242;s movies.  It&#8217;s not even comparable.</p>
<p>This is known as <a href="http://en.wikipedia.org/wiki/Broken_windows_theory">The Broken Windows Theory</a>, and it is easily applicable to programming as well.  In fact, it could be rewritten as such:</p>
<blockquote><p>Consider a function with a few coding standard violations. If the violations are not cleaned up, the tendency is for maintainers to ignore a few more standards while editing. Eventually, they may even leave unused variables from previous revisions, or forgo commenting entirely. Or consider a class. Some uncommented functions accumulate. Soon, more functions are added with no comments to describe them. Eventually, people even start copying and pasting whole sections of uncommented code elsewhere.</p></blockquote>
<p>Sure, it&#8217;s a <i>bit</i> of a reach, but I&#8217;m sure you&#8217;ve all seen some version of it happen in the projects you&#8217;ve worked on.</p>
<h3>See what happens?</h3>
<p>Okay, so back to our example.  We can do one of two things here.  One, we can just slip in quietly, fix the bug, and get the hell out of there.  Or two, we can clean up the function while we fix the bug.  Let&#8217;s just see what happens.</p>
<p>First, we just fix the bug:</p>
<p><code>
<pre class="prettyprint">
/**
        add a glow to a shape
    */
function addGlowToShape(&#038;$shape) {

    // getstartloc
    $x = getStartX($shape);
    $y = getStartY($shape);
    global $world;
    $cells = $world->getCells();
    $shape_name = $shape->getName();
    $cellsAffected = 0;

    // do it all
    for ($rowY = $y; $rowY < $y + $shape->getHeight(); $rowY++) {
        for ($rowX = $x; $rowX < $x + $shape->getRowLength($rowY); $rowX++)
            addGlowToCell($cells[$rowX][$rowY]);
            $cellsAffected++;
    }

    // @todo: return false on an error
    return $cellsAffected;
}
</pre>
<p></code></p>
<p>Not so bad.  I mean, we fixed that bug &#8211; we&#8217;re checking the length of each row instead of just the original row.  But that function is still a mess for the next person to come by.  Why don&#8217;t we try cleaning it up?</p>
<p><code>
<pre class="prettyprint">
/**
 * Add a glow effect to all cells in a shape.
 *
 * @param ModelShape $shape The shape to add the glow effect to.
 * @param array      $cells The cells from the world to change.
 *
 * @return mixed The number of cells updated or false on error.
 */
function addGlowToShape($shape, &#038; $cells) {

    $start_x = getStartX($shape);
    $start_y = getStartY($shape);

    // Get the row to stop at
    $end_y = $start_y + $shape->getHeight();

    $cells_affected = 0;

    $success = true;

    for ($cell_y = $start_y; $cell_y < $end_y; $cell_y++) {

        // Get the cell to stop at for this row
        $end_x = $start_x + $shape->getRowLength($cell_y);

        for ($cell_x = $start_x; $cell_x < $end_x; $cell_x++) {
            $success &#038;= addGlowToCell($cells[$cell_x][$cell_y]);
            $cells_affected++;
        }
    }

    return $success ? $cells_affected : false;
}
</pre>
<p></code></p>
<p>See?  See what we did there?  We fixed the actual bug, did the todo, <b>and</b> fixed another, subtler bug that was hidden until we started going in and doing simple things like adding missing brackets.</p>
<p>Granted, yes, this is also a significant change that you'd need to go over with your team - we changed the signature which means we need to change everywhere that it is called from.  But that's why any modern IDE will have a global find-and-replace in it.  Or even just a global find-and-mark if you're the paranoid type (and I am).</p>
<p>And the second-order effects from this are enormous.  The next person to approach this method will likely see that it has been cared for, and not want to be the first person to mess things up.  Code quality goes up, code <i>comprehension</i> goes up, and bugs go down.</p>
<p>So the next time you see "broken glass" in your project, don't just walk around it - <i>clean it up</i>.</p>
<p><span style="text-align:center; display: block;"><a href="http://blog.danhulton.com/2012/02/16/broken-glass/"><img src="http://img.youtube.com/vi/y25stK5ymlA/2.jpg" alt="" /></a></span></p>
]]></content:encoded>
			<wfw:commentRss>http://blog.danhulton.com/2012/02/16/broken-glass/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Creating a Development Virtual Machine &#8211; Part One: Ubuntu, Apache, MySQL, PHP</title>
		<link>http://blog.danhulton.com/2011/11/27/creating-a-development-virtual-machine-part-one-ubuntu-apache-mysql-php/?utm_source=rss&#038;utm_medium=rss&#038;utm_campaign=creating-a-development-virtual-machine-part-one-ubuntu-apache-mysql-php</link>
		<comments>http://blog.danhulton.com/2011/11/27/creating-a-development-virtual-machine-part-one-ubuntu-apache-mysql-php/#comments</comments>
		<pubDate>Sun, 27 Nov 2011 17:58:32 +0000</pubDate>
		<dc:creator>Dan Hulton</dc:creator>
				<category><![CDATA[Coding]]></category>

		<guid isPermaLink="false">http://blog.danhulton.com/?p=731</guid>
		<description><![CDATA[If you do web development on Windows, you&#8217;re probably used to making do. XAMPP can go a long way, as can Cygwin, but once you want to start playing around with anything state-of-the-art, you&#8217;ll start feeling left behind. The solution? Set up a virtual machine running Linux, so you can take advantage of the latest [...]]]></description>
			<content:encoded><![CDATA[<p>If you do web development on Windows, you&#8217;re probably used to making do.  <a href="http://www.apachefriends.org/en/xampp-windows.html">XAMPP</a> can go a long way, as can <a href="http://www.cygwin.com/">Cygwin</a>, but once you want to start playing around with anything state-of-the-art, you&#8217;ll start feeling left behind.  The solution?  Set up a virtual machine running Linux, so you can take advantage of the latest and greatest in their native environments.</p>
<h3>Set up Ubuntu</h3>
<p>For this purpose, we&#8217;re going to be using <a href="https://www.virtualbox.org/">VirtualBox</a> &#8211; originally by Sun, recently(ish) purchased by Oracle.  For my money, it still has the best mix of ease-of-use, features, and price (free).  <a href="https://www.virtualbox.org/wiki/Downloads">You can download the latest version here.</a></p>
<p>And while you&#8217;re at it, go download the latest version of <a href="http://www.ubuntu.com/business/server/overview">Ubuntu Server</a>.  For this article, we&#8217;re using 11.10 (Oneiric Ocelot), which you can <a href="http://www.ubuntu.com/download/server/download">download here</a>.  Grab the 64-bit version.</p>
<p>Install VirtualBox.  Before you run it the first time, you&#8217;ll want to right-click the icon and choose &#8220;Properties&#8221;.  Then go to the &#8220;Compatability&#8221; tab and click the checkbox next to &#8220;Run this program as an administrator&#8221;.  If you want any symlinks you create in Ubuntu to be recognized by Windows, you MUST be running the program as an administrator.  Windows is funny like that.</p>
<p>Launch VirtualBox, then create a new virtual machine and set the Ubuntu ISO as the CD/DVD drive.  (Under the Storage menu in Settings, click on the CD-looking thing under the IDE controller).  Also, make sure to set up Networking as &#8220;Bridged&#8221;.  Start up the virtual machine, then follow Ubuntu&#8217;s really slick install process.  The only application you&#8217;ll want to install during setup is the OpenSSH server.  We&#8217;ll install the rest ourselves.</p>
<p>When all is said and done, restart it and log in.  Now the fun begins!</p>
<h3>Set up your networking</h3>
<p>Let&#8217;s set up a static IP address so we don&#8217;t have to keep figuring out what dynamic IP the machine obtained.</p>
<pre class="prettyprint lang-bsh">$ sudo nano /etc/network/interfaces</pre>
<p>Edit the last part to look like this:</p>
<pre class="prettyprint lang-bsh"># The primary network interface
auto eth0
iface eth0 inet static
        address 192.168.0.151
        netmask 255.255.255.0
        network 192.168.0.0
        broadcast 192.168.0.255
        gateway 192.168.0.1</pre>
<p>You&#8217;ll have to fiddle with those numbers a bit depending on your networking configuration.  Ensure primarily that the address you supply isn&#8217;t taken by anyone else on your network, and that the gateway matches the one you get when you type &#8220;ipconfig&#8221; in Windows.  Restart your network to get the changes recognized.</p>
<pre class="prettyprint lang-bsh">$ sudo /etc/init.d/networking restart</pre>
<p>Once that&#8217;s working, start up Notepad as Administrator in Windows (right-click and &#8220;Run as administrator&#8221;).  Then open up &#8220;C:\Windows\System32\drivers\etc\hosts&#8221; (no extension) and add your new machine&#8217;s IP address.  Now you can do things like SSH directly to the hostname or type the hostname in your browser instead of remembering the IP address.</p>
<h3>Install Apache</h3>
<p>This is easy, and can be handled entirely through automated utilities built-in to Ubuntu.</p>
<pre class="prettyprint lang-bsh">$ sudo apt-get install --reinstall language-pack-en
$ sudo dpkg-reconfigure locales
$ sudo apt-get install gcc make wget cron curl
$ sudo apt-get install apache2 apache2-mpm-prefork apache2-prefork-dev apache2-utils apache2.2-common</pre>
<p>One quick thing, to avoid getting the error &#8220;Could not reliably determine the server’s fully qualified domain name, using 127.0.1.1 for ServerName&#8221; every time we restart, we need to make a small change.</p>
<pre class="prettyprint lang-bsh">$ sudo nano /etc/apache2/httpd.conf</pre>
<p>Add the following line and save.</p>
<pre class="prettyprint lang-bsh">ServerName localhost</pre>
<h3>Install MySQL</h3>
<p>Nearly as easy.  Start with the automated utilities.</p>
<pre class="prettyprint lang-bsh">$ sudo apt-get install mysql-server-5.1</pre>
<p>No need to give MySQL a root password on your development box.  Then edit the MySQL config file.</p>
<pre class="prettyprint lang-bsh">$ sudo nano /etc/mysql/my.cnf</pre>
<p>Change the &#8220;bind-address&#8221; line in that file to point the the local server IP.  (192.168.0.151 in the example above.)</p>
<p>Then, it&#8217;s worthwhile allowing root to log in from machines other than localhost.  That way you can use whatever Windows GUI tool you like to log in to your virtual machine&#8217;s MySQL installation.</p>
<pre class="prettyprint lang-bsh">$ mysql -u root
GRANT ALL ON *.* TO 'root'@'%';
exit;
$ sudo service mysql restart</pre>
<h3>Install PHP</h3>
<p>Now we could just use the built-in version of PHP, if we felt like taking the easy road.  But that&#8217;s no fun.  Let&#8217;s install PHP 5.4 RC2, so we can play around with all the new features.  As an added bonus, if you get comfortable with compiling PHP <i>now</i>, you can recompile with whatever version and options you want <i>later</i> for when there&#8217;s a new release of PHP, and you don&#8217;t have to wait for your package maintainers to update.</p>
<p>First we need a bunch of build tools.</p>
<pre class="prettyprint lang-bsh">$ sudo apt-get build-dep php5
$ sudo apt-get install libxml2 libxml2-dev libzip-dev libbz2-dev curl libcurl4-openssl-dev libcurl3 libcurl3-gnutls libjpeg62 libjpeg62-dev libpng12-0 libpng12-dev libmcrypt-dev libmcrypt4 libxslt1-dev libxml2-dev</pre>
<p>Next, grab and extract PHP 5.4.</p>
<pre class="prettyprint lang-bsh">$ cd ~/
$ mkdir tmp
$ cd tmp
$ wget http://downloads.php.net/stas/php-5.4.0RC2.tar.gz -O php-5.4.0RC2.tar.gz
$ tar xvfz php-5.4.0RC2.tar.gz</pre>
<p>Now, we configure and compile.  This takes a while.  Feel free to grab a sandwich or something.</p>
<pre class="prettyprint lang-bsh">$ cd php-5.4.0RC2
$ ./configure --with-apxs2=/usr/bin/apxs2 --with-config-file-path=/etc/php5 --with-mysql=mysqlnd --enable-inline-optimization --disable-debug --enable-bcmath --enable-calendar --enable-ctype --enable-force-cgi-redirect --enable-ftp --with-gd --enable-memory-limit --disable-sigchild --enable-trans-sid --with-ftp --with-jpeg-dir=/usr --with-png-dir=/usr --with-zlib=yes --with-zlib-dir=/usr --with-openssl --with-xsl=/usr --with-gd --with-mcrypt=/usr --with-mhash=/usr --enable-mbstring=all --with-curl=/usr/bin --with-curlwrappers --enable-mbregex --enable-zend-multibyte --with-bz2=/usr --with-mime-magic --with-iconv --with-pdo-mysql=mysqlnd --enable-fileinfo --with-pear --enable-exif
$ make
$ sudo make -i install</pre>
<p>Of course, now we have to ensure that Apache knows about PHP.</p>
<pre class="prettyprint lang-bsh">$ sudo ln -s /usr/local/bin/php /usr/bin/php
$ sudo nano /etc/apache2/mods-available/php5.conf</pre>
<p>Add the following lines and save.</p>
<pre class="prettyprint lang-bsh">AddType application/x-httpd-php .php .phtml .php3
AddType application/x-httpd-php-source .phps</pre>
<p>Now enable PHP (and mod-rewrite, while we&#8217;re at it).</p>
<pre class="prettyprint lang-bsh">$ sudo a2enmod php5
$ sudo a2enmod rewrite</pre>
<p>It wouldn&#8217;t hurt to ensure that PHP is configured how we like it, either.</p>
<pre class="prettyprint lang-bsh">$ cd /etc
$ sudo mkdir php5
$ cd php5
$ sudo cp ~/tmp/php-5.4.0RC2/php.ini-production php.ini
$ sudo nano php.ini</pre>
<p>Ensure the following are set:</p>
<pre class="prettyprint lang-bsh">date.timezone = America/Toronto (Or whatever is closest to you.)
short_open_tag = On
error_reporting = E_ALL
display_errors = On
log_errors = On
error_log = /var/log/php.log
max_execution_time = 30
memory_limit = 128M
mysql.default_socket = /var/run/mysqld/mysqld.sock</pre>
<p>Now make sure that the logfile we specified exists and is writable.</p>
<pre class="prettyprint lang-bsh">$ sudo touch /var/log/php.log
$ sudo chmod a+rw /var/log/php.log</pre>
<p>Finally, restart Apache!</p>
<pre class="prettyprint lang-bsh">$ sudo service apache2 restart</pre>
<p>You should be able to visit the address of your new virtual machine (or the alias you set up in hosts) now and get your basic Apache display.</p>
<h3>Extra step &#8211; VirtualHosts</h3>
<p>I presume you&#8217;re going to want to develop multiple projects without having to create separate virtual machines for them all.  That&#8217;s easy!  We&#8217;ll just set up an Apache VirtualHost for each one.</p>
<pre class="prettyprint lang-bsh">$ sudo nano /etc/apache2/sites-available/{development url}</pre>
<p>Add the following, then save.</p>
<pre class="prettyprint lang-bsh">&lt;VirtualHost *:80&gt;
    ServerName {development url}
    DocumentRoot /home/{user}{/{development url}/

    &lt;Directory /home/{user}/{development url}/&gt;
        AllowOverride All
    &lt;/Directory&gt;
&lt;/VirtualHost&gt;</pre>
<p>Now enable your site and reload Apache.</p>
<pre class="prettyprint lang-bsh">$ sudo a2ensite {development url}
$ sudo service apache2 reload</pre>
<h3>Extra step &#8211; Shared folders</h3>
<p>I bet you&#8217;re wondering how you&#8217;re supposed to get files on and off that machine, huh?  Well, since you installed OpenSSH, you have options there, but there&#8217;s an easier way.  You can just share that folder you were referencing above with a specific folder on your host machine.  Make changes in Windows, and it will (usually) be reflected in the virtual machine.  I&#8217;ve noticed a few times that when I&#8217;ve created a file, the Ubuntu host flips out and doesn&#8217;t properly recognize it, but a quick restart fixes it, and machine restarts are usually like ten seconds, max.</p>
<p>First, make sure that folder above exists on your virtual machine, then click on the &#8220;Devices&#8221; menu in VirtualBox, then click &#8220;Install Guest Additions&#8221;.  This makes a virtual CD available to your virtual machine that contains the actual guest additions we&#8217;re going to install.</p>
<pre class="prettyprint lang-bsh">$ sudo mount /dev/cdrom /media/cdrom
$ cd /media/cdrom
$ sudo ./VBoxLinuxAdditions.run</pre>
<p>Ignore the crap about x.org failing &#8211; we don&#8217;t have any GUI installed on the server, so whatever.  Now shut down the machine (&#8220;sudo shutdown -h now&#8221;), and open up the Settings menu and click on Shared Folders.  Click the little Folder/Plus icon on the right, and choose a Folder Path from your Windows box, and remember the Folder Name it gives you (or make up your own).  Hit &#8220;OK&#8221; and start that machine back up.</p>
<p>We&#8217;re going to set up this machine to automatically mount that folder every time it boots up.</p>
<pre class="prettyprint lang-bsh">$ sudo nano /etc/init.d/rc.local</pre>
<p>Add the following just after &#8220;do_start() {&#8220;:</p>
<pre class="prettyprint lang-bsh">    mount -t vboxsf {Folder Name} /home/{user}/{development url}</pre>
<p>Now restart to check it out.</p>
<pre class="prettyprint lang-bsh">$ sudo restart -r now
# After restarting...
$ cd /home/{user}/{development url}
$ ls</pre>
<p>You should see all the files from your Windows machine.  If you don&#8217;t, well&#8230; ugh.  Okay, I got this fixed by re-running the VBoxLinuxAdditions.run script, the re-mounting.  No clue why that worked, but it worked, so&#8230; yeah.  Not questioning it.</p>
<h3>What Else Could We Possibly Do?</h3>
<p>Hot damn, that&#8217;s a lot of stuff, isn&#8217;t it?  Well, we still have Memcache, Memcached (yes, both), and Redis to install.  These are all optional, but super-neat tools, and practically essential if you want to write anything that scales well these days.</p>
<p>For now though, you should have a good base to work from.  If anything doesn&#8217;t work, let me know, but I followed along with my own VM while I was writing this, so I&#8217;m pretty confident I didn&#8217;t skip anything.</p>
]]></content:encoded>
			<wfw:commentRss>http://blog.danhulton.com/2011/11/27/creating-a-development-virtual-machine-part-one-ubuntu-apache-mysql-php/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>The Curious Case of The Missing Memcached::casMulti()</title>
		<link>http://blog.danhulton.com/2011/11/20/the-curious-case-of-the-missing-memcached-casmulti/?utm_source=rss&#038;utm_medium=rss&#038;utm_campaign=the-curious-case-of-the-missing-memcached-casmulti</link>
		<comments>http://blog.danhulton.com/2011/11/20/the-curious-case-of-the-missing-memcached-casmulti/#comments</comments>
		<pubDate>Sun, 20 Nov 2011 17:48:15 +0000</pubDate>
		<dc:creator>Dan Hulton</dc:creator>
				<category><![CDATA[Coding]]></category>

		<guid isPermaLink="false">http://blog.danhulton.com/?p=719</guid>
		<description><![CDATA[I understand I&#8217;m a little late to the game, but Memcached is friggin&#8217; awesome. (No, I&#8217;m not talking about Memcache, I&#8217;ve known about that for a while. I&#8217;m talking about Memcached, the younger, beefier, stupidly-named cousin of Memcache. Crazy new features, but named in a way that&#8217;s sure to guarantee dozens of Google search mismatches. [...]]]></description>
			<content:encoded><![CDATA[<p>I understand I&#8217;m a little late to the game, but Memcached is friggin&#8217; <i>awesome</i>.</p>
<p>(No, I&#8217;m not talking about Memcache, I&#8217;ve known about that for a while.  I&#8217;m talking about Memcache<b>d</b>, the younger, beefier, stupidly-named cousin of Memcache.  Crazy new features, but named in a way that&#8217;s sure to guarantee dozens of Google search mismatches.  Nice.)</p>
<p>Previously, if you wanted to be sure that your cache writes weren&#8217;t clobbering each other, you had to implement some form of locking system, which introduces the very healthy chance of deadlocks and locking timeouts.  This kills the concurrency.</p>
<p>But with Memcached, you can do this really sweet thing called &#8220;cas()&#8221; which stands for &#8220;compare and set&#8221;.  (Maybe.  Depending on who you&#8217;re talking to.)</p>
<p>Simple usage:</p>
<p><code>
<pre class="prettyprint">
$cas = null;
$m = new Memcached();
do {
    // Get user, get cas token byref
    $user = $m->get('user::4099', null, $cas);

    // Do something with your user
    $m->cas($cas, 'user::4099', $user);
} while ($m->getResultCode() != Memcached::RES_SUCCESS);
</pre>
<p></code></p>
<p>So when you&#8217;re getting your $user, you&#8217;re also getting a $cas token.  This token is updated every time the cache entry is changed.  When you go to write your user later, instead of calling set(), you call cas() and pass in your $cas token.  Memcached will only update the user if $cas is equal to the cas token in memory for that cache entry.  If it fails, it sets Memcached&#8217;s internal result code to RES_DATA_EXISTS.</p>
<p>Now obviously you&#8217;ll want to limit that do-while loop there to prevent infinite looping, and if you really wanted to be a speed demon, you could change that &#8220;null&#8221; in there to a callback function so this function would block less, but still &#8211; it&#8217;s pretty goddamn quick, and no need to wait for locks.</p>
<p>There&#8217;s just one problem.  What if you need to work with MULTIPLE objects?</p>
<p>See, there&#8217;s a getMulti() and a setMulti(), but no casMulti().  This is even weirder, because getMulti() even passes back an array of $cas tokens.  Googling for it produces complaints about its lack, or worse, people confused about the difference between Memcache and Memcached.  Apparently it&#8217;s not even IN the Memcached server, which sort of blows my mind.  I can think of all kinds of situations where you&#8217;d want two or more objects to be updated in-step.</p>
<p>So anyway, since this thing doesn&#8217;t exist natively, I wrote it.</p>
<h3>Memcached::casMulti()</h3>
<p><code>
<pre class="prettyprint">
/**
 * Set an array of Memcached keys with cas tokens, fail on all if any don't match.
 *
 * @param array   $items      An array of items to store.  All items require "key", "value", and "cas" entries.
 * @param integer $expiration The expiration time, defaults to 0.
 */
function casMulti(array $items, int $expiration = 0) {
    $newCasToken = null;
    $successfullySet = array();

    // First load old versions of data, ensure cas values remain sound
    foreach ($items as $item) {
        $item['old_value'] = $this->get($item['key'], null, $newCasToken);

        // If any tokens have changed since we loaded them, WHOOPS
        if ($newCasToken !== $item['cas']) {
            throw new DataChangedException("Data changed while we were messing about.");
        }
    }

    foreach ($items as $item) {
        // On successful set
        if ($this->cas($item['cas'], $item['key'], $item['value'], $expiration)) {
            // Get new cas token
            $this->get($item['key'], null, $item['cas']);

            // Keep track of our successes
            $successfullySet[] = $item;

        // Failed set?
        } else {
            // Data has been modified, crap
            if ($this->getResultCode() === Memcached::RES_DATA_EXISTS) {
                // Loop through "successful" sets
                foreach ($successfullySet as $item) {
                    // Get new cas token
                    $this->get($item['key'], null, $newCasToken);

                    // If this item hasn't changed on us
                    if ($newCasToken !== $item['cas']) {
                        // Undo our changes
                        $this->cas($newCasToken, $item['key'], $item['old_value'], $expiration);
                    }
                }

                throw new DataChangedException("Data changed while we were messing about.");

            // Some other reason?
            } else {
                // Do something appropriate
                throw new Exception("Something else messed up.");
            }
        }
    }
}</pre>
<p></code></p>
<p>Some caveats:</p>
<ul>
<li>This function assumes it&#8217;s part of a Memcached library, ideally one that extends PHP&#8217;s Memcached object, and thus $this is equal to a valid Memcached instance.</li>
<li>Since I don&#8217;t have access to Memcached&#8217;s Result Object (there&#8217;s no setResultObject), it relies on Exceptions to report failures, and those pretty obviously haven&#8217;t been fleshed out.</li>
<li>This is the biggie: <b>I haven&#8217;t tested it yet.</b>  Like, not at all.  I&#8217;m busy trying to set up a Virtual Machine to replace the shitty XAMPP-on-Windows thing I&#8217;ve been fighting with for years, but it&#8217;s a slow process.  This will of course be updated once I get a chance to test it out, but I had an itch one evening and just had to scratch it and write this code.  (I&#8217;ll also be updating this blog with the process used to create said Virtual Machine, should you choose to try it out yourself.)</li>
</ul>
<p>With that said, let&#8217;s walk through it quickly.</p>
<p>You&#8217;re going to be passing in an array of items to compare-and-set, and that array should look something like:</p>
<p><code>
<pre class="prettyprint">
array(
    [0] => array(
        "key" => "user::4099",
        "value" => "json-user-data-goes-here",
        "cas" => "big-long-cas-token-taken-from-get-or-getMulti",
    ),
    [1] => array(
        "key" => "user::4105",
        "value" => "json-user-data-goes-here",
        "cas" => "big-long-cas-token-taken-from-get-or-getMulti",
    )
)
</pre>
<p></code></p>
<p>I could have put $expiration in each of the items too, and maybe that&#8217;s a good idea?  I figured keeping as close to Memcached&#8217;s existing methods was worthwhile, and how often are you going to need to have different expirations for everything?</p>
<p>Next, we get the old values of everything we&#8217;re changing, just in case we need to roll back.  If any values have changed since before this function was called, <a href="http://img181.imageshack.us/img181/481/1240980788386.png" target="_blank">SHUT DOWN EVERYTHING</a>.</p>
<p>Then we try to cas each key individually.  If a key is successfully set, we save it for later.  If they&#8217;re all successfully set, no biggie.  If any fail though, we go into recovery mode.</p>
<p>In recovery mode, we examine every previously-set item.  If it hasn&#8217;t changed, we use cas() again to change it back to the way we found it before we tried anything.  If it HAS changed, we just leave it &#8211; no sense in messing with anyone else&#8217;s work.</p>
<h3>Yeah, so&#8230;</h3>
<p>Now like I said, this is absolutely untested.  And not in an &#8220;untested under load&#8221; kind of way &#8211; I mean I haven&#8217;t even run it yet.  It might not even friggin&#8217; compile.  I&#8217;ll update this blog post once I have tested it sure, but for now, exercise caution.</p>
<p>And even if it does compile and seems to work, exercise caution.  I wrote this late at night after a day of frustratedly poking at the Memcached docs and Google.  There could be all kinds of situations where this Just Doesn&#8217;t Work.</p>
<p>And even if it <i>does</i> work in all situations as expected?  It may not even be a good idea.  There&#8217;s a LOT of cache reads going on here, and I really don&#8217;t know what that does to concurrency.  There may very well be much more efficient ways of doing this.</p>
<p>So be warned.  Have fun playing around with this, but try not to use it in production code.</p>
]]></content:encoded>
			<wfw:commentRss>http://blog.danhulton.com/2011/11/20/the-curious-case-of-the-missing-memcached-casmulti/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Nope.</title>
		<link>http://blog.danhulton.com/2011/10/16/nope/?utm_source=rss&#038;utm_medium=rss&#038;utm_campaign=nope</link>
		<comments>http://blog.danhulton.com/2011/10/16/nope/#comments</comments>
		<pubDate>Sun, 16 Oct 2011 15:08:03 +0000</pubDate>
		<dc:creator>Dan Hulton</dc:creator>
				<category><![CDATA[Uncategorized]]></category>

		<guid isPermaLink="false">http://blog.danhulton.com/?p=712</guid>
		<description><![CDATA[Alternate title: &#8220;Welp&#8221;. Alternate, actually informative title: &#8220;Indie Stone Still Doesn&#8217;t Get It&#8221; Or at the very least, Lemmy still doesn&#8217;t get it. Barely hours after I posted, hoping that Indie Stone would learn from this latest disaster, developer Lemmy has posted an apology, and announced that he&#8217;s removing himself from further community involvement. But [...]]]></description>
			<content:encoded><![CDATA[<p>Alternate title: &#8220;Welp&#8221;.</p>
<p>Alternate, actually informative title: &#8220;Indie Stone Still Doesn&#8217;t Get It&#8221;</p>
<p>Or at the very least, Lemmy still doesn&#8217;t get it.</p>
<p>Barely hours after I posted, hoping that Indie Stone would learn from this latest disaster, developer Lemmy <a href="http://www.theindiestone.com/lemmy/index.php/2011/10/16/final-post-and-apology/">has posted an apology</a>, and announced that he&#8217;s removing himself from further community involvement.</p>
<p>But while that may or may not represent a good idea, the big news in the article is the following excerpt:</p>
<blockquote><p>No one put ‘must have thick skin’ (or ‘must make nightly off-site backups, for that matter) in my game programmer job description.</p></blockquote>
<p>Really?  <b>Really?</b></p>
<p>First, I mean, you went indie.  You made your own job description, so you can&#8217;t really complain that you&#8217;re required to do more than you were told &#8211; it was <i>your</i> job in the first place to find out what was required, and if it was acceptable.</p>
<p>Second &#8211; and this is the killer &#8211; it&#8217;s pretty obvious that &#8220;nightly off-site backups&#8221; are still a distasteful, onerous thing to him, that the entirety of the development has been a hack job from the start, and will continue to be so until yet another slapdash solution fails catastrophically.  And when that happens, all the people who supported him and supported PZ&#8217;s development can expect is more hand-wringing, and more &#8220;in hindsight, it&#8217;s obvious I shouldn&#8217;t have stored credit cards unencrypted on our web server&#8221; responses.</p>
<p>If they ever finish Project Zomboid, I <i>might</i> buy it (if I&#8217;m convinced my payment information isn&#8217;t being handled by them) because it looks like it&#8217;ll be a really cool game on completion.  But I&#8217;m certainly not going to invest in a pre-order, given that they can&#8217;t be arsed to exhibit a bare minimum level of professionalism.</p>
<p>Hell, my <i>college homework</i> was in source control.</p>
]]></content:encoded>
			<wfw:commentRss>http://blog.danhulton.com/2011/10/16/nope/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>There&#8217;s Professionalism, and then there&#8217;s Professionalism.</title>
		<link>http://blog.danhulton.com/2011/10/16/theres-professionalism-and-then-theres-professionalism/?utm_source=rss&#038;utm_medium=rss&#038;utm_campaign=theres-professionalism-and-then-theres-professionalism</link>
		<comments>http://blog.danhulton.com/2011/10/16/theres-professionalism-and-then-theres-professionalism/#comments</comments>
		<pubDate>Sun, 16 Oct 2011 13:27:49 +0000</pubDate>
		<dc:creator>Dan Hulton</dc:creator>
				<category><![CDATA[Coding]]></category>
		<category><![CDATA[Games]]></category>
		<category><![CDATA[News]]></category>

		<guid isPermaLink="false">http://blog.danhulton.com/?p=707</guid>
		<description><![CDATA[So Project Zomboid was stolen last night. Literally. Someone broke into the developer&#8217;s flat, stole two laptops containing the source code, and booked it. And these two laptops contained the ONLY recent copies of the code. All other backups are at least a month old. Seriously. Reddit got angry about this, and Lemmy got angry [...]]]></description>
			<content:encoded><![CDATA[<p><a href="http://projectzomboid.com/blog/index.php/2011/10/project-zomboid-burglary-statement/">So Project Zomboid was stolen last night.</a>  Literally.  Someone broke into the developer&#8217;s flat, stole two laptops containing the source code, and booked it.  And these two laptops contained the ONLY recent copies of the code.  All other backups are at least a month old.</p>
<p>Seriously.</p>
<p><a href="http://www.reddit.com/r/gaming/comments/ldkwg/the_indie_stone_project_zomboid_has_been_struck/">Reddit got angry about this</a>, and Lemmy got angry back over Twitter (account since removed, wisely).</p>
<p>So far, this was all something of an unfortunate morality play, but then another PZ developer popped up this morning to elucidate on the subject of <a href="http://theindiestone.com/binky/2011/10/16/professionalism-and-indies/">Professionalism and Indies</a>.</p>
<p>In as much as he is talking about people being angry because Lemmy raged at Reddit over Twitter, he&#8217;s right.  (Hypocritical, of course, since they pulled his Twitter account, but whatever.)  One of the differences between indies and commercial studios is that you get a closer relationship with the developers than you would if you had a PR department between you.  If that relationship exposes a personality you don&#8217;t like, well, tough.  Don&#8217;t invest in that game, simple.</p>
<p>But he also draws an arbitrary line in the sand in that article, declaring that some indies would rather just be commercial studios.  Here, in his words:</p>
<blockquote><p>There’s the type of studio that really, if they were honest, they’d like to be the first type of indie [independent studios, working for commercial clients] – everything they do is onwards an upwards to this goal. Legitimacy. A proper company. And then there’s the other type.</p>
<p>This type of indie never pretends to be professional. The “company” is probably just a name. It technically exists, but really what it boils down to is a guy in his underpants making a daft game. Sometimes that guy has a day-job, sometimes he’s taken a whoppping gamble and is working full-time from home.</p>
<p>The point is, if you’re going to throw your money at one of these indies, it’s important to know who you’re throwing money at and why. You can’t necessarily have your cake and eat it too.</p></blockquote>
<p>You can just <i>hear</i> the derision there.  Ugh, commercial studios, right?  All that process and overhead and accountability, bleh.  When PZ was first getting popular, I read <a href="http://theindiestone.com/binky/2011/04/15/why-indie-games-development-trumps-commercial-development/">a blog post from one of their developers about how he left the commercial games industry because it was the most miserable period of his life.</a>  Which is cool &#8211; we&#8217;ve all heard the gory details exposed by EASpouse and others.  Fleeing that environment is a positive, healthy step.  But there was always something about the way it was written that never sat well with me.</p>
<p>I guess last night I figured out what it was.  At Indie Stone, &#8220;professionalism&#8221; is a dirty word.</p>
<p>But being a professional doesn&#8217;t mean having to give up your indie cred, or bow to the demands of marketing folk or whatever.  It just means that you treat this shit like Your Job, which it now is.  It means taking all the appropriate precautions to prevent shit like this from sinking you.  Every instance of &#8220;bad luck&#8221; that&#8217;s struck Indie Stone so far has been entirely preventable with just the bare minimum of professionalism.  If they&#8217;d read the PayPal terms that said they didn&#8217;t accept pre-orders, they wouldn&#8217;t have lost all those money and orders.  Ditto for Google Checkout.  And ditto for backing up your code appropriately.</p>
<p>You wouldn&#8217;t hire an independent caterer who didn&#8217;t wash their kitchen first.  You wouldn&#8217;t hire an independent electrical contractor who wasn&#8217;t certified.  These are just little one or two-person outfits, without any dreams of gaining &#8220;Legitimacy&#8221;.  But they take the time to know their craft and be professionals about it.</p>
<p>And the dirtiest, scariest part of this all is that setting up an offsite backup in this day and age isn&#8217;t hard.  At all.  It&#8217;s almost harder NOT to, actually.  See, source control tools were invented way back in the dark ages, and you have a million options nowadays.  CVS.  Subversion.  Git.  Mercurial.  Veracity.  And the interesting thing is, they all make development EASIER, not harder.  First, if you ever made a mistake, you can roll your code back to ANY POINT in the development history to figure out what went wrong.  Second, when working with more than one developer, it makes it really difficult to accidentally clobber changes that they&#8217;ve been working on.  Finally, if your computer ever catches on fire, your source control is generally safe on another server somewhere in another city, and even that&#8217;s backed up in case that server catches on fire.</p>
<p>The fact that Indie Stone was charging people for pre-orders for a game, and then not expending the requisite effort to protect people&#8217;s investment is absolutely, completely unprofessional.  And not in the &#8220;Commercial Studio&#8221; sense that these guys seem to dread, but in the &#8220;Basic Respect For Your Customers And For Your Trade&#8221; sense.</p>
<p>Now, they&#8217;re planning on continuing on (good for them!), and hopefully they&#8217;ll be able to.  I know personally that losing a month&#8217;s worth of work would be <i>devastating</i> &#8211; moreso than the theft itself.  But hopefully they learn from this, and not just to back up their code remotely.  They&#8217;ve had three serious hits due to a lack of professionalism.  This is the time to start looking into all their shit that they&#8217;ve been giving short shrift.  Check their security for XSS, SQL injection, change every password (because you now have people who own their laptops, and a whole bunch of people on the internet upset at them).  Ensure payments are still flowing properly, ensure refunds are processed immediately (because there&#8217;s going to be a bunch of those, and having their payment processing yanked on them now is going to suck BIGTIME) &#8211; double-down on this stuff.</p>
<p>I&#8217;d love to see Project Zomboid survive, because it&#8217;s a thrilling, ambitious project.  But I hate how they&#8217;re badmouthing professionalism.  You can be a professional indie without succumbing to all the soul-deadening crap that happens in a commercial studio.  Long-term, it&#8217;s even easier to do, because then you don&#8217;t have to worry about having your payment processing yanked <i>twice</i> and having months of work stolen from you.  Swear all you want on Twitter, but respect your craft, and respect your customers.</p>
]]></content:encoded>
			<wfw:commentRss>http://blog.danhulton.com/2011/10/16/theres-professionalism-and-then-theres-professionalism/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Slave Leias Get No Respect!</title>
		<link>http://blog.danhulton.com/2011/08/24/slave-leias-get-no-respect/?utm_source=rss&#038;utm_medium=rss&#038;utm_campaign=slave-leias-get-no-respect</link>
		<comments>http://blog.danhulton.com/2011/08/24/slave-leias-get-no-respect/#comments</comments>
		<pubDate>Wed, 24 Aug 2011 22:30:48 +0000</pubDate>
		<dc:creator>Dan Hulton</dc:creator>
				<category><![CDATA[Life]]></category>

		<guid isPermaLink="false">http://blog.danhulton.com/?p=695</guid>
		<description><![CDATA[This is a reaction to this post: “Geek girls” and the problem of self-objectification I guess my biggest problem with this article is the following: Too often, women in geek cultures are only welcomed if they are decoration, sexy versions of the things geek men love, not equal participants or fellow fans. Maybe I live [...]]]></description>
			<content:encoded><![CDATA[<p>This is a reaction to this post: <a href="http://geekfeminism.org/2011/08/23/geek-girls-and-the-problem-of-self-objectification/">“Geek girls” and the problem of self-objectification</a></p>
<p>I guess my biggest problem with this article is the following:</p>
<blockquote><p>Too often, women in geek cultures are only welcomed if they are decoration, sexy versions of the things geek men love, not equal participants or fellow fans.</p></blockquote>
<p>Maybe I live in a weird, tiny bubble, but with the folks I hang around with, this just ain&#8217;t so.  My group of friends in Oakville is fully of nerdy girls, and I hang around with them because they are smart and fun and into awesome nerdy things, not because they are &#8220;decoration.&#8221;  Fully half the awesome friends I made at PAX Prime last year were nerdy girls &#8211; at no time dressed up or flaunting themselves.  We met on the Harry Potter Pubcrawl and talked about nerdy shit and exchanged numbers and hung out for the rest of the con.</p>
<p>Of all of the nerdy girls I am friends with, the emphasis is on the &#8220;nerdy&#8221; &#8211; they&#8217;re into comic books, or games, or loose-leaf tea, or reading.  That they&#8217;re girls makes no more difference than their hair colour, or height.  Are other nerdy dudes so fucking shallow that ladies are shunned if they&#8217;re not eye candy?  For shame.</p>
<p><a href="http://blog.danhulton.com/wp-content/uploads/2011/08/leeloo-cold.png"><img src="http://blog.danhulton.com/wp-content/uploads/2011/08/leeloo-cold.png" alt="Yup. Definitely cold." title="Yup.  Definitely cold." width="261" height="205" class="alignright size-full wp-image-699" /></a>Now on the flip side of that &#8211; if you are dressed in costume AT ALL, you are emphasizing yourself as decoration and shouldn&#8217;t be surprised when you&#8217;re treated as such.  I&#8217;ve never been able to have a meaningful conversation with a lady dressed as Leeloo, BUT the same goes for dudes dressed as Master Chief or whatever.  The most I ever get out is &#8220;Oh hey, sweet costume, you look great/badass/cold&#8221; and then I mosey on.  Seriously, I can&#8217;t connect with you when you&#8217;re kitted out like that, it&#8217;s intimidating.</p>
<p>If you want to connect with and gain acceptance within a community, be as you are.  Sure, there will be some sexist assholes (and perhaps the nerd community has more than its fair share due to natural high levels of social anxiety in its members), but as with all things, the cream will rise to the top and you will be welcomed.  There&#8217;s no need to lower yourself to fit in.</p>
]]></content:encoded>
			<wfw:commentRss>http://blog.danhulton.com/2011/08/24/slave-leias-get-no-respect/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
	</channel>
</rss>

