Feb
27
2013

Why you should Indent with Spaces

I avoid tabs in all code I write. That’s why you can read my code in your browser with the exact same formatting as it had in my IDE – no matter what browser or device you are using: Nuclex Framework sources in TRAC. Yet from time to time, I encounter people evangelizing tabs.

While I am fine with working on projects that use tab characters and I neither argue about the decision to use tabs nor intentionally divert from their style, I do have an opinion and it is that you should avoid tabs wherever you can.

Even the one, lonely, supposed advantage of tabs – users being able to choose their preferred indentation level – is a drawback in disguise because you no longer can set a safe limit for line lengths (see below).

Read the rest of this entry »

Oct
28
2012

First SSD Killed. Yay.

At some point in 2009, I bought an Intel x25 Postville G2. Everyone was scared that SSDs would break down too quickly and I almost made the error of buying one of OCZ’s Vertex drives, but in the end I picked the more expensive Intel drive and it worked brilliantly.

This weekend, 3 years later, it did break down. It had no bad sectors and the SMART status showed only few internal reallocations due weak blocks. But from one reboot to the next, it prevented my BIOS screen from even showing up (and what options do you have left if, with the SSD plugged in, you can’t even turn on your PC…). In one word: bricked.

Of course, like any part of computer hardware, it failed at the most inappropriate moment. It stood by while I installed Windows 8. Kept working while I configured my email client and accounts, browser and bookmarks, VoIP connection and productivity apps. It endured while I installed and configured Visual Studio 2012, setting up all my preferences. And neither did it break when I installed my SSL certificates or began to organize my task bar and start screen.

No, the moment it failed was when I had set everything up to completion and rebooted with my Acronis TrueImage CD to make a backup of my system drive :)

Read the rest of this entry »

Oct
05
2012

Spell and Grammar Checkers

I’m not a native English speaker, so given the task of writing something a bit more involved than just a blog post, I decided to seek out a spell checker which could also analyze my grammar (things such as punctuation rules, mixed up tense and so on come to mind).

The easiest route would probably have been to just install Word since it has both a spell checker and a grammar checker built-in, but I’m a bit allergic to having Office on my system, so I hesitated for a few days and looked at several other options. Which ended up motivating me to write this little rant…

Read the rest of this entry »

Sep
08
2012

Console Ports :-(

Decided to buy the game “I am Alive” from Steam today because it was only $15 and the setting sounded interesting: a psychological survival horror adventure taking place after some unnamed apocalypse.

The first thing that greeted me was this:

Screenshot of an error message from the game "I am Alive"

The moment I see such 1990s-typical “System Check” idiocy I’m already appalled by its developers. You can’t check anything useful that way and it will only serve to break your game in the future. Like those 1990s games complaining about not enough RAM when they check 16 GB as a 32 bit integer, this one complained about Windows 8 being an unsupported operating system – unthinkable that future OSes might run the game.

Of course, no care has been taken to support anything but 96 DPI, so I don’t know if there is an ignore button or just a close button in that dialog. There also seems to be some text on the very bottom which I can’t read since the dialog is not resizeable.

Gameplay

The gameplay seemed sort-of interesting for a while, but like any game these days, you’re running through cordoned off tubes of scenery so the developers can put scripted scenes in your way. Good to lower production costs, bad for immersion.

Also typical for consoles is the lack of direct control you have over your character. You can only jump when you stand at the edge of something you’re supposed to jump onto or off from. You can only slash with your machete if you are in the right position and at the right distance from the enemy so a cool-looking prepared animation can be played.

Even worse than the "press space to play animation" control style is that the mouse look is badly broken. I often find myself unable to change direction because it seems mouse movements get lost whenever the game is loading assets in the background, forcing me to pick up the mouse every so often.

 

Then there was the novel psychological aspect which might have made the game worthwhile: you start with an empty gun that you can still use to threaten enemies. The game requires you to decide whether it’s wise to pull that gun, or if that would start a firefight you can’t win. Also, whether it’s wise to back away, run away or make a surprise attack using other means. And which enemies to pick first – if you do have a bullet and shoot the guy with a machete, but the second guy has a gun, that’s a bad choice obviously.

Sadly, I found the actual gameplay to be more annoying than thrilling, controls doing sometimes this, sometimes that. It’s best explained with an example:

(small spoiler)

At the beginning a guy comes at you with a machete and you have to put him in check with your empty gun. You make him back away to the edge of the broken road, then, as instructed by the game, press E to kick him down.

A few steps further, you’re put in check yourself by two other guys, one wielding a gun and another wielding a machete. When the gun guy walks up to you, the game tells you to slash his throat by pressing E to trigger a surprise attack. Then you hold the other guy off with your gun, this time the game tells you knock him out. Which key? E of course.

Two more machete-wielding guys come at you. Using your gun, you drive them back to the same edge you kicked the first guy into but – surprise – E won’t kick anymore. It also won’t make you knock the guy out. Now E starts a struggle machete vs. machete (hey, I’ve got a gun in my hand!) that gives the other machete guy time to cut you up nicely.

In short: instead of having a bunch of moves available to you, you have to guess how the level designers wanted you to resolve a confrontation and you better guess right.

Combine that with uninterruptible cutscenes and a checkpoint save system and you find yourself doing the same 5 minutes over and over and over.

Conclusion

Uninstalled.

Pro: Nice idea. Does not include Ubisoft launcher, so I’m not forced to read their marketing department’s tweets, patch the game or join some community.

Contra: badly ported console game, mouse look broken, tube levels, controls do random things depending on scene, lack of direct control over the character, no immersion, game feels like an interactive movie, forced to repeat scenes endlessly.

Jul
24
2012

Simple Main Window Class

Here’s another fairly trivial code snippet. I’ve stumbled across some borked attempts at initializing and maintaining rendering windows for games lately. Most failed to properly respond to window messages, either ignoring WM_CLOSE outright or letting DefWindowProc() call DestroyWindow() when WM_CLOSE was received, thereby not giving the rest of the game’s code any time to cleanly shut down before the window handle becomes invalid.

So I’ll provide a clean and well-behaved window class here. It doesn’t use any global variables – in fact, you could create any number of windows from any number of threads. WM_CLOSE simply causes the class’ WasCloseRequested() method to return true, so by polling this method you can first shut down graphics and input devices and then destroy the window in an orderly fashion.

For your convenience I also added some helper methods: one resizes the window in a way that ensures the client area will actually end up with the exact pixel size requested. Another will center the window on the screen without messing up if the user has extended his desktop over multiple monitors.

Read the rest of this entry »

Jun
23
2012

Thread-Safe Random Access to Zip Archives

Many games choose to store their resources in packages instead of shipping the potentially thousands of individual files directly. This is sometimes an attempt at tamper-proofing, but mostly it is about performance. Try copying a thousand 1 KiB files from one drive to another, then copy a single 1 MiB file on the same way – the former operation will take many times longer.

A good choice for a package format is the well known .zip archive. It’s not necessarily well-designed, but it decompresses fast and you definitely won’t have any problems finding tools to create, modify and extract .zip archives. Thus, when I started work on a file system abstraction layer for my future games, seamless .zip support was one of my main goals (I may also add 7-Zip at a later time just for fun).

Here is the design I came up with after exploring the file system APIs available on Windows, WinRT and Linux:

UML diagram showing the design of my file system abstraction layer

You may notice some rather radical design choices in my File class: there are no Open() or Close() methods and there is no Seek() method, either – each read or write specifies the absolute position without requiring the file to be opened.

Read the rest of this entry »

Jun
18
2012

Ogre 1.8.0 for WinRT/Metro

Ogre 3D Logo

In March I provided some binaries of Ogre 1.8.0 RC1 that were based on Eugene’s Metro port of Ogre, allowing Ogre to run as a native Metro App, using the Direct3D 11 renderer and RTShaderSystem for dynamic shader generation.

Those binaries no longer work with the Windows 8 Release Preview and Visual Studio 2012 RC, so I thought I’d provide an updated package!

Screenshot of Ogre 1.8.0 on Windows 8 Release Preview running as a Metro app

This time I went a bit further: while the last package was compiled with multithreading disabled, I have in the meantime ported Boost 1.50.0 to compile on WinRT (using a slightly modified version of Shawn Hargreaves’ WinRT CreateThread emulation code. Thus, this Ogre build has full support for multithreading and includes Boost!

Read the rest of this entry »

Jun
15
2012

Code Better: Headers without Hidden Dependencies

When you work on a larger project, you cannot easily keep track of which header depends on which other header. You can (and should) do your best to keep the number of other headers referenced inside your headers low (to speed up compilation) and move as many header dependencies as you can into your source files, but this still doesn’t prevent you from building headers that implicitly depend on another header being included before them.

Take this example:

#ifndef MAP_H
#define MAP_H
 
/// <summary>Stores the tiles of a 2D map<summary>
struct Map {};
 
#endif // MAP_H
#ifndef WORLD_H
#define WORLD_H
 
#include "Actor.h"
#include <vector>
// Oops, forgot Map.h, but won't notice since World.cpp includes Map.h before World.h
 
/// <summary>Maintains the state of the entire world<summary>
struct World {
  /// <summary>Stores the map as a grid of 2D tiles</summary>
  public: Map Map;
  /// <summary>Actors (player, monsters, etc.) currently active in the world</summary>
  public: std::vector<Actor *> Actors;
};
 
#endif // WORLD_H

Throughout your project, map.h might always end up being included before world.h and you might never notice that if someone included world.h on its own, a nasty compilation error would be the result.

So what can you do ensure this situation never happens?

Read the rest of this entry »

Jun
12
2012

How to Delete Directories Recursively with Win32

Well, while I’m at it, here’s the counterpart to the recursive directory creation function from my last post, a function that recursively deletes a directory and all its contents.

Ordinarily, you could just use the shell API to achieve this on classic Win32:

/// <summary>Deletes a directory and everything in it</summary>
/// <param name="path">Path of the directory that will be deleted</param>
void deleteDirectory(const std::wstring &path) {
  SHFILEOPSTRUCTW fileOperation;
  fileOperation.wFunc = FO_DELETE;
  fileOperation.pFrom = path.c_str();
  fileOperation.fFlags = FOF_NO_UI | FOF_NOCONFIRMATION;
 
  int result = ::SHFileOperationW(&fileOperation);
  if(result != 0) {
    throw std::runtime_error("Could not delete directory");
  }
}

But WinRT/Metro applications cannot use the shell API and have to do it manually. So here’s a piece of code that takes care of directory deletion using nothing but Win32 API calls that are also available to WinRT/Metro applications:

/// <summary>Automatically closes a search handle upon destruction</summary>
class SearchHandleScope {
 
  /// <summary>Initializes a new search handle closer</summary>
  /// <param name="searchHandle">Search handle that will be closed on destruction</param>
  public: SearchHandleScope(HANDLE searchHandle) :
    searchHandle(searchHandle) {}
 
  /// <summary>Closes the search handle</summary>
  public: ~SearchHandleScope() {
    ::FindClose(this->searchHandle);
  }
 
  /// <summary>Search handle that will be closed when the instance is destroyed</summary>
  private: HANDLE searchHandle;
 
};
 
/// <summary>Recursively deletes the specified directory and all its contents</summary>
/// <param name="path">Absolute path of the directory that will be deleted</param>
/// <remarks>
///   The path must not be terminated with a path separator.
/// </remarks>
void recursiveDeleteDirectory(const std::wstring &path) {
  static const std::wstring allFilesMask(L"\\*");
 
  WIN32_FIND_DATAW findData;
 
  // First, delete the contents of the directory, recursively for subdirectories
  std::wstring searchMask = path + allFilesMask;
  HANDLE searchHandle = ::FindFirstFileExW(
    searchMask.c_str(), FindExInfoBasic, &findData, FindExSearchNameMatch, nullptr, 0
  );
  if(searchHandle == INVALID_HANDLE_VALUE) {
    DWORD lastError = ::GetLastError();
    if(lastError != ERROR_FILE_NOT_FOUND) { // or ERROR_NO_MORE_FILES, ERROR_NOT_FOUND?
      throw std::runtime_error("Could not start directory enumeration");
    }
  }
 
  // Did this directory have any contents? If so, delete them first
  if(searchHandle != INVALID_HANDLE_VALUE) {
    SearchHandleScope scope(searchHandle);
    for(;;) {
 
      // Do not process the obligatory '.' and '..' directories
      if(findData.cFileName[0] != '.') {
        bool isDirectory = 
          ((findData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0) ||
          ((findData.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) != 0);
 
        // Subdirectories need to be handled by deleting their contents first
        std::wstring filePath = path + L'\\' + findData.cFileName;
        if(isDirectory) {
          recursiveDeleteDirectory(filePath);
        } else {
          BOOL result = ::DeleteFileW(filePath.c_str());
          if(result == FALSE) {
            throw std::runtime_error("Could not delete file");
          }
        }
      }
 
      // Advance to the next file in the directory
      BOOL result = ::FindNextFileW(searchHandle, &findData);
      if(result == FALSE) {
        DWORD lastError = ::GetLastError();
        if(lastError != ERROR_NO_MORE_FILES) {
          throw std::runtime_error("Error enumerating directory");
        }
        break; // All directory contents enumerated and deleted
      }
 
    } // for
  }
 
  // The directory is empty, we can now safely remove it
  BOOL result = ::RemoveDirectory(path.c_str());
  if(result == FALSE) {
    throw std::runtime_error("Could not remove directory");
  }
}

This code is free for the taking and you can use it however you want.

THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

Jun
12
2012

How to Create Directories Recursively with Win32

As I just found out, the CreateDirectory function on Win32 can only create one directory at a time. If one, for example, specifies C:\Users\All Users\FirstNew\SecondNew as the directory to create, and both FirstNew and SecondNew do not exist, then CreateDirectory() fails.

That’s less than ideal for some cases. Recently, for example, I wanted my game to create the C:\Users\<Whoever>\Documents\My Games\<GameName> directory, where both My Games and <GameName> may not yet exist. Here’s a workaround:

/// <summary>Creates all directories down to the specified path</summary>
/// <param name="directory">Directory that will be created recursively</param>
/// <remarks>
///   The provided directory must not be terminated with a path separator.
/// </remarks>
void createDirectoryRecursively(const std::wstring &directory) {
  static const std::wstring separators(L"\\/");
 
  // If the specified directory name doesn't exist, do our thing
  DWORD fileAttributes = ::GetFileAttributesW(directory.c_str());
  if(fileAttributes == INVALID_FILE_ATTRIBUTES) {
 
    // Recursively do it all again for the parent directory, if any
    std::size_t slashIndex = directory.find_last_of(separators);
    if(slashIndex != std::wstring::npos) {
      createDirectoryRecursively(directory.substr(0, slashIndex));
    }
 
    // Create the last directory on the path (the recursive calls will have taken
    // care of the parent directories by now)
    BOOL result = ::CreateDirectoryW(directory.c_str(), nullptr);
    if(result == FALSE) {
      throw std::runtime_error("Could not create directory");
    }
 
  } else { // Specified directory name already exists as a file or directory
 
    bool isDirectoryOrJunction =
      ((fileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0) ||
      ((fileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) != 0);
 
    if(!isDirectoryOrJunction) {
      throw std::runtime_error(
        "Could not create directory because a file with the same name exists"
      );
    }
 
  }
}

This code is free for the taking and you can use it however you want.

THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

Older posts «

Social Widgets powered by AB-WebLog.com.