Latest articles

Review: El Shaddai: Ascension of the Metatron

The gameplay of El Shaddai is that of a watered-down God of War or Devil May Cry...and God of War isn't exactly an example of deep gameplay anyway. It isn't terrible, or even badly-controlled, but it is simplistic and derivative.

The game also leaves me wondering: Why is there a button dedicated to changing the weapon's color? Apparently it's something about "purifying" the weapon, but I don't see what difference that makes. Does that make it work better? I can't tell.

Due to the basis of the story, there are constantly repeated bible references and themes. These can make the game feel dangerously close to being a production of a religiously-affiliated content house, even though it actually isn't. This can be fairly irritating, particularly during the first (roughly) half-hour which consists primarily of unskippable cutscenes.

However...El Shaddai is worth playing simply for the environments alone.

I'm not a particularly graphics-driven gamer these days. I'll pick an ugly or low-tech title with solid gameplay over a shallow cinematic polygon-pushing powerhouse nearly every time. But the art direction in El Shaddai is incredible. These are the most original, inspired abstract environments I've ever seen - let alone with interactive motion.

Various other games certainly have more raw horsepower under their hoods. But due to the pure artistry here, and very creative use of pixel shaders, I don't hesitate at saying this is the most beautiful looking game out there.

Though notably imperfect, El Shaddai isn't a videogame with graphics and story: It's an interactive painting you can battle through.

Read more


Do Not Use git status --ignored

Sometimes it's useful to find all files in a git working copy that aren't being tracked in the repository. There's an --ignored switch for git status that's supposed to make it show the ignored files. Combined with --untracked-files, that should do the trick nicely. But the problem: The --ignored switch doesn't fucking work. Seriously.

To be more accurate, it only shows some of the ignored files (despite no mention of this fact in the man page). It's easy fooled: Just try adding someUntrackedDirWithUntrackedFiles/* to your .gitignore, and see if you can get git status to mention either the directory or the files inside it. Note that none of the settings for --untracked-files= fix this.

Wanna know the best part? There is no git command known to reliably do what --ignored claims to do. There are various alleged solutions floating around, but unfortunately they don't work. The author in that last link offers a suggestion, but still admits to a lack of confidence in it. Great.

My suggestion is what I recommend for git usage in general: Minimize your reliance on any features beyond the basics. For such an amazingly useful tool (I highly recommend switching to git if you're still on subversion - or worse, CVS or VSS), it's amazing how shitty git gets the deeper you dive into it.

In this case, I suggest using git ls-files to generate a list of the files that are under version control, stick them into a hash set (for quick lookup), then iterate over all the files in your working copy, outputting only the files that aren't in the hash set.

Here's how to do it in D:

/++ Show all unversioned (both untracked and ignored) files and directories in the current git working copy. Much like "git status --ignored --untracked-files=all" except this actually works (although you must run it from the root of the working copy). Further explanation: https://semitwist.com/articles/article/view/do-not-use-git-status-ignored Tested on DMD v2.063.2 Licensed under the WTFPL - Do What the Fuck You Want to Public License: http://www.wtfpl.net/ +/ import std.algorithm; import std.array; import std.file; import std.path; import std.process; import std.stdio; import std.string; string run(string cmd) { auto result = executeShell(cmd); if(result.status != 0) throw new Exception("Command failed: " ~ cmd); return result.output; } void main() { if(!exists(".git")) throw new Exception("Must run from the root of the git working copy"); bool[string] versionedFiles; auto gitOutput = run("git ls-files"); foreach(filename; gitOutput.splitter("\n")) { versionedFiles[filename] = true; // Also include the file's parent directories, since git likes to // pretend there's no such thing as directories: while(true) { auto parentDir = dirName(filename); if(parentDir == filename) break; // Done versionedFiles[parentDir] = true; filename = parentDir; } } foreach(DirEntry entry; dirEntries(".", SpanMode.breadth)) { // The output of "git ls-files" doesn't include the "./" prefix // (like DirEntry does) and uses forward slashes on all platforms. auto filename = entry.name.replace("\\", "/").chompPrefix("./"); if(filename !in versionedFiles) { if(entry.isDir) writeln(filename, "/"); else writeln(filename); } } }

Read more


Review: The Walking Dead

It's been awhile since I've done one of my videogame "reviews" (for lack of a better word). Clearly I'm past due. So here's my review of Telltale Game's "The Walking Dead":

*Ahem*...

Didn't I already play this twenty years ago?

Read more


Debian's Package System is Shit

It's two-thousand-goddamn-thirteen: Why the crap are we manually screwing around with arcane instructions, some arbitrary config file, obtuse meaningless substitute names for the real fucking version number, and the whole disabled-by-default "backports" bullshit, all just to install a vaguely recent version of some program or lib, that's already been backported (really? Shit needs to be ported from Debian to Debian?!?) to whatever version of Debian we're using? What the fuck is this, 90's Slackware?

This is how sane shit works:

% sudo apt-get install libevent2 Ok boss, done.

Or even this:

% sudo apt-get install libevent2 ERROR: That's only available in the 'backports' repo which is disabled by default. You must use the flag '--backports' to install software from the 'backports' repo. % sudo apt-get install libevent2 --backports Ok boss, done.

(I've been writing software nearly all my life and I can tell you right now there is no technical reason why that can't work.)

On the other hand, this is how obtuse moronic bullshit works:

% sudo apt-get install libevent2 'libevent2'? Never heard of it. % sudo apt-get install libevent Installing some useless ancient version...done % Gee, thanks error: Piss off % websearch "how to install libevent2 on debian" Results: Whole bunch of useless crap and some vague buried mention of needing some "backports". % websearch "how to use backports on debian" Results: Bunch of crap and some mention of adding "deb http://YOURMIRROR.debian.org/debian-backports squeeze-backports(-sloppy) main" to some 'sources.list' file. If you're lucky it tells you where fuck 'sources.list' actually is. % gksudo kate /etc/apt/sources.list > /dev/null 2> /dev/null & (Isn't launching a GUI program from the Unix cmdline fun? Almost makes Windows look good. Sort of...) % sudo apt-get update Barf. You were clever enough to guess what YOURMIRROR was supposed to be changed to by blindly copy-pasting from some other random line in the same file, but I'm still gonna crap out for you with unhelpful errors anyway. % remove the "(-sloppy)" bullshit from the new line, because by dumb luck I just happen to know that doesn't belong there. Ok. % sudo apt-get update Could not resolve 'ftp.us.debian.org' (Fucking SERIOUSLY?!?! Yes, I really got that.) % sudo apt-get update (Partially works this time.) % sudo apt-get install libevent2 Never heard of it. % sudo apt-get install libevent2-dev Never heard of it. % sudo aptitude update Sure dude, whatever I guess... % sudo aptitude install libevent2 Never heard of it. % sudo aptitude install libevent2-dev Never heard of it. % websearch "It didn't fucking work, asshole!" Ehh, ok, well what version of Debian you using? % debian 6 Never heard of Debian 6 % What the fuck, you stupid...&*(@#$&^*(?!? Never heard of Debian 6. Please enter the name of some random-fuck Toy Story character instead. % ugh...fine...ummm, a=1, b=2...e=5... And no cheating by using any sane ordering, we've made sure that won't work, either. % FUCK YOU error: command is unrelated to Toy Story. Go screw off and come back when you care more about Pixar. % post-rant "Debian's Package System is Shit..."

Guess which of the above most closely resembles Debian.

It's no &*$^@# wonder that MS-slave corporate suits are such douchebag snobs towards Unix.

If there is already active work in progress at fixing this mess, then sincerely: KUDOS!

Read more


Downloading Files From Plain Batch - With Zero Dependencies

Background

I've been mulling over some ideas on a bootstrapper for D. I'd like users and contributors, on either Windows or Unixy systems, to be able to go from nothing to anything they need as easily as possible.

In a best case scenario, these would be the ideal qualities:

  • No prerequisites, other than what's likely to be already installed on nearly any system.
  • One-step setup: No "download this archive, extract it somewhere, and run the file named blahblahblah".
  • No prebuilt binaries for the custom tools: Because then that would need to be bootstrapped, too. Not a big problem on Windows, but prebuilt binaries in the Unix words can be tricky due to all the varieties (not impossible of course, but non-trivial).
  • Scriptable
  • As little shell/batch scripting as possible: Get straight to a bootstrapped compiler so everything else can be written directly in D.
  • Maximum compatibility

Those are lofty, conflicting, and perhaps unrealistic goals, but I'd like to get as close to that ideal as possible.

One interesting sub-problem of this is how to handle Windows:

Due to the first two goals, it's clear some way will probably be needed to automate downloading files from the internet. On Unixy systems this is easy - most already have either wget or curl installed (and the ones that don't are generally very expert-oriented anyway, so it's not too terrible to expect those users to install wget or curl themselves).

But Windows has no built-in command-line tool to download files. You can get curl and wget for Windows, but most Windows developers won't already have them installed. So what to do on Windows? It's not like we can just wget a copy of wget itself...

Step One: Download Tool in VBScript

We can't download files on just any run-of-the-mill Windows box using batch scripting. But we can do it using VBScript, which is a standard part of Windows:

'File: download.vbs Option Explicit Dim args, http, fileSystem, adoStream, url, target, status Set args = Wscript.Arguments Set http = CreateObject("WinHttp.WinHttpRequest.5.1") url = args(0) target = args(1) WScript.Echo "Getting '" & target & "' from '" & url & "'..." http.Open "GET", url, False http.Send status = http.Status If status <> 200 Then WScript.Echo "FAILED to download: HTTP Status " & status WScript.Quit 1 End If Set adoStream = CreateObject("ADODB.Stream") adoStream.Open adoStream.Type = 1 adoStream.Write http.ResponseBody adoStream.Position = 0 Set fileSystem = CreateObject("Scripting.FileSystemObject") If fileSystem.FileExists(target) Then fileSystem.DeleteFile target adoStream.SaveToFile target adoStream.Close

Run it like this:

> cscript //Nologo download.vbs urlToGet pathAndFilenameToSaveAs > cscript //Nologo download.vbs http://example.com blah.html

Not bad. In fact, with this tool, we already have what we need to download files from within batch. Just invoke the script from a batch file, and you're good.

But there are still some problems:

First of all, I'd like to be able to give users instructions that aren't prepended with cscript. Plus, I don't particularly feel like continuing down this path of trying to remember how to use VBScript. So, even though batch is hardly a VB-killing language, I would like to be invoking any VBScript code from a batch file.

But that leads to another problem. Now I have, at a minimum, two distinct files: A batch file and a VBScript file. This means I'll need the user to download two files, or make them download and extract a zip, or make a self-extracting archive which violates the "no custom prebuilt binaries" goal. Is there any way out? Without sacrificing any of my goals?

Step Two: Downloader Ex Machina

Like Unix, the Windows command line can redirect output to files. Use > to overwrite a file, or >> to append:

> echo First line > file.txt > echo Second line >> file.txt > type file.txt First line Second line

Do you see where I'm going with this?

A common trick in the Unix world, we can use echo and file redirection to generate a text file on-the-fly. A text file such as...a VBScript!:

@echo off rem This is file: hello.bat echo WScript.Echo "Hello world" > hello.vbs cscript //Nologo hello.vbs

Run that batch, and it will create the following VBScript hello world:

WScript.Echo "Hello world"

Then the batch file invokes the VBScript file it just created, and "Hello world" is printed! We've just embedded a VBScript tool into a batch script.

Now we have everything we need to download a file using nothing more than a one-file batch script. All we do is embed the VBScript downloader tool into batch (using the caret ^ to escape any characters echo has trouble with):

@echo off rem Windows has no built-in wget or curl, so generate a VBS script to do it: rem ------------------------------------------------------------------------- set DLOAD_SCRIPT=download.vbs echo Option Explicit > %DLOAD_SCRIPT% echo Dim args, http, fileSystem, adoStream, url, target, status >> %DLOAD_SCRIPT% echo. >> %DLOAD_SCRIPT% echo Set args = Wscript.Arguments >> %DLOAD_SCRIPT% echo Set http = CreateObject("WinHttp.WinHttpRequest.5.1") >> %DLOAD_SCRIPT% echo url = args(0) >> %DLOAD_SCRIPT% echo target = args(1) >> %DLOAD_SCRIPT% echo WScript.Echo "Getting '" ^& target ^& "' from '" ^& url ^& "'..." >> %DLOAD_SCRIPT% echo. >> %DLOAD_SCRIPT% echo http.Open "GET", url, False >> %DLOAD_SCRIPT% echo http.Send >> %DLOAD_SCRIPT% echo status = http.Status >> %DLOAD_SCRIPT% echo. >> %DLOAD_SCRIPT% echo If status ^<^> 200 Then >> %DLOAD_SCRIPT% echo WScript.Echo "FAILED to download: HTTP Status " ^& status >> %DLOAD_SCRIPT% echo WScript.Quit 1 >> %DLOAD_SCRIPT% echo End If >> %DLOAD_SCRIPT% echo. >> %DLOAD_SCRIPT% echo Set adoStream = CreateObject("ADODB.Stream") >> %DLOAD_SCRIPT% echo adoStream.Open >> %DLOAD_SCRIPT% echo adoStream.Type = 1 >> %DLOAD_SCRIPT% echo adoStream.Write http.ResponseBody >> %DLOAD_SCRIPT% echo adoStream.Position = 0 >> %DLOAD_SCRIPT% echo. >> %DLOAD_SCRIPT% echo Set fileSystem = CreateObject("Scripting.FileSystemObject") >> %DLOAD_SCRIPT% echo If fileSystem.FileExists(target) Then fileSystem.DeleteFile target >> %DLOAD_SCRIPT% echo adoStream.SaveToFile target >> %DLOAD_SCRIPT% echo adoStream.Close >> %DLOAD_SCRIPT% echo. >> %DLOAD_SCRIPT% rem ------------------------------------------------------------------------- cscript //Nologo %DLOAD_SCRIPT% http://example.com blah.html echo File downloaded via batch!

Nice!

Read more


Microsoft Fails at Counting To Three

In sanity-ville, counting works like this: One, Two, Three. But at Microsoft, it apparently goes: One, Three Hundred Sixty, One. (And here I thought Verizon was horrible at grade-school math.)

That's right: As the latest stunt in Microsoft's groundbreaking new line of "practical jokes as a serious business strategy", Microsoft's upcoming third XBox is officially called "XBox One".

Naturally, I get the whole "What's in a name?" deal. Obviously the third XBox doesn't need to be called XBox 3. They can certainly call it whatever the hell they want. Like "Poop Box Hepatitis Supreme". But that doesn't mean certain names aren't colossally stupid. Case in point: The XBox One won't be released until later this year, and yet somehow, I already had an XBox 1 over ten years ago. Go figure.

Side note: I can't believe none of the game news outlets (from what I've seen) have even touched on this. I know it's just a name, but still, absurdity is absurdity.

I used to lament the passing of the days when game systems had cool names (Genesis, TurboGraphix 16, Jaguar). Now I just miss the days of generic names (GameCube, XBox). Right, Nintendo Weeyu?

Read more