Setup Utility

January 31, 2011

Before I forget, I wanted to mention Inno Setup. Back in 2009, I wrote this post describing how I was using Wink as a sort of cut rate installer alternative. As I mentioned then, it was ugly, and a clearly inferior alternative to an actual installation utility. Since then, I found Inno Setup. Inno Setup is great. It’s actually easier to use it to create a real installer than it was to use my Wink-based workaround. All that’s necessary is to adapt the following script, which I used to create the installer for Forgotten Times:

[Setup]
; NOTE: The value of AppId uniquely identifies this application.
; Do not use the same AppId value in installers for other applications.
; (To generate a new GUID, click Tools | Generate GUID inside the IDE.)
AppId={{8B6CE104-25D3-40E8-8681-F2B7DA29E855}
AppName=Forgotten Times
AppVersion=1.1
;AppVerName=Forgotten Times 1.1
AppPublisher=Morriss, William S.
AppPublisherURL=http://www.example.com/
AppSupportURL=http://www.example.com/
AppUpdatesURL=http://www.example.com/
DefaultDirName={pf}\Forgotten Times
DefaultGroupName=Forgotten Times
AllowNoIcons=yes
OutputBaseFilename=setup
SetupIconFile=C:\FT2\Forgotten Times\res\GameEngine.ico
Compression=lzma
SolidCompression=yes

[Languages]
Name: “english”; MessagesFile: “compiler:Default.isl”

[Tasks]
Name: “desktopicon”; Description: “{cm:CreateDesktopIcon}”; GroupDescription: “{cm:AdditionalIcons}”; Flags: unchecked
Name: “quicklaunchicon”; Description: “{cm:CreateQuickLaunchIcon}”; GroupDescription: “{cm:AdditionalIcons}”; Flags: unchecked; OnlyBelowVersion: 0,6.1

; Set the source to your root directory
[Files]
Source: “C:\FT\Forgotten Times\GameEngine.exe”; DestDir: “{app}”; Flags: ignoreversion
Source: “C:\FT\Forgotten Times\*”; DestDir: “{app}”; Flags: ignoreversion recursesubdirs createallsubdirs
; NOTE: Don’t use “Flags: ignoreversion” on any shared system files

; For these icons, to get them to work with your GameEngine.exe you must set ‘WorkingDir:’ to ‘{app}’ because it creates
; local files
[Icons]
Name: “{group}\Forgotten Times”; Filename: “{app}\GameEngine.exe”; WorkingDir: {app}
Name: “{group}\{cm:UninstallProgram,Forgotten Times}”; Filename: “{uninstallexe}”; WorkingDir: {app}
Name: “{commondesktop}\Forgotten Times”; Filename: “{app}\GameEngine.exe”; Tasks: desktopicon; WorkingDir: {app}
Name: “{userappdata}\Microsoft\Internet Explorer\Quick Launch\Forgotten Times”; Filename: “{app}\GameEngine.exe”; Tasks: quicklaunchicon; WorkingDir: {app}

[Run]
Filename: “{app}\GameEngine.exe”; Description: “{cm:LaunchProgram,Forgotten Times}”; Flags: nowait postinstall skipifsilent

Eee Issues

January 31, 2011

At one point, I really loved my Eee. I had come to the conclusion that I needed good battery life more than I needed lots of processing power, and so the tradeoff in a netbook made a lot of sense. Unfortunately, my Eee, like the other portable computers I’ve had, didn’t prove equal to the rigors of actually being transported – after spending a year or two being transported in my backback, the screws holding its case together decided to come out. Even worse, after I replaced them, something went wrong with the connection between the battery and the processor. The Eee now works great when it’s plugged in, but turns off the moment you unplug it. The plus side is that this means the data I used to store on it isn’t lost. the minus side is that the specific reason I bought it, good battery life, is now gone. Obviously, this wasn’t good for my programming, since the main time I used to write code was on my bus ride to and from work.

However, all things come to an end, and my current hiatus is no exception. I got a shiny new laptop, and used to it pull together a good deal of the town of Ramu in Forgotten Times 2.

Fixing Forgotten Times

September 28, 2010

Maybe a month and a half ago, I decided to enter Forgotten Times in the Indie Game Challenge. I figured it would be a piece of cake. From what I had recalled, I finished the game seven or eight years ago back in law school. My thought was that I’d throw together an manual and an elevator speech (both required for the contest), put it all in an installer, and be done.

Obviously, that isn’t what happened.

Apparently, I didn’t actually finish it back in law school. I got it mostly finished back in law school. I’d say it was 98% finished. However, trying to squeeze out that last 2% reminded me of just how much work goes into these things.

Happily though, thanks in large part to my very patient wife who played through it 5-10 times as she found bugs and I fixed them, we worked out all the obvious kinks, made a couple of significant upgrades to the interface, and got it all done. Now, I just have to pull the rest of the submission package together and everything will be ready to ship well before the October 1 deadline.

Legal Stuff

January 25, 2010

Today I uploaded new versions of everything. For Forgotten Times II, this represents real progress on the game. I’ve finished the agricultural town, and gotten a good start on the mining town. For the other games though, the only change was the addition of an explicit authorization of individual non-commercial use and distribution of the games, and it was something of a pain. It wasn’t particularly difficult (writing licenses is part of my real job, after all), but at the same time it did take my energy away from writing more code, and so had an opportunity cost in terms of programming. Essentially, this is the same cost (writ small) that one of my clients faces when they hire an attorney to draft a license or a EULA for one of their commercial programs. The money that goes to paying me could alternatively be going to pay their programmers, and represents a cost of doing business which they would almost certainly rather do without.

So, was the time I spent (and, in general, the resources spent on lawyers who write software licenses) worth it? I think so. While I don’t care if people give away copies of my games, I would be more than a little annoyed if someone made a changed version and distributed it, or started selling them without my permission. By making explicit what rights I’m granting and withholding, I lay the groundwork for giving effect to my goals down the road. The same principle applies to commercial products as well. By making clear what actions are and aren’t authorized at the outset, a company can set itself up so that if it ever has to enforce its rights, it can do so at a relatively minimal cost. Even better, if rights and restrictions are clear, there’s less chance of a conflict that could lead to expensive litigation ever taking place.

Don’t write functions to return local arrays

December 23, 2009

Consider the following function (taken from Forgotten Times II):

char* CClassicRPGDoc::levelLocationID(int which_level)
{

char center_location_ID[LABELLENGTH];
int center_location_index = current_location;

char retval[LABELLENGTH];

strcpy(center_location_ID, location_IDs[current_location]);

int counter;
if(which_level > 0)
{
for(counter = 1; counter <= which_level; counter++)
{
strcpy(center_location_ID, locations[center_location_index].getTop().getLocationID());
center_location_index = getLocationIndex(center_location_ID);
}
}
if(which_level = which_level; counter–)
{
strcpy(center_location_ID, locations[center_location_index].getBase().getLocationID());
center_location_index = getLocationIndex(center_location_ID);
}
}
if(which_level != 0)
{
ASSERT(true);
}
strcpy(retval, center_location_ID);
return retval;
};

The above function is used in the display of multi-level buildings and was causing me no end of grief. For some reason, instead of returning what I expected (i.e., the ID of the specified floor of a building) it was consistently returning the ID of the floor (i.e., the one the character was in). Even worse, its behavior changed when I ran it through the debugger, and when I traced the values of the variables all the way to the end of the function, they looked the way they were supposed to.

The problem, of course, was the statement in bold text. While the retval[] string had the right value all the way to the end of the function, once the function exited, the memory that string had occupied was deallocated. As a result, rather than passing back the string I intended, I was passing back a memory location which, by that time, was being used for something else (apparently for storing the ID of the current floor, though I have no idea why). Very annoying.

While there are a number of ways to solve the problem (including reworking the logic so that the problem didn’t come up in the first place), I chose the one that was easiest for me, and made retval[] a static variable. Not the most artful way of doing it, but, since static variables are maintained between function calls, it did the job. The final results are that the last upload of Forgotten Times II has a reasonably functional implementation of multi-story building display, and that I’ve learned to avoid writing functions to return local arrays.

More progress

October 28, 2009

I finally got the basic layout (i.e., made and placed the buildings and stitched together borders) for the agricultural town in Forgotten Times 2. Wow. That represents an incredible amount of work, and is basically what I’ve been focusing on since my last post back in August.

It’s strange, but people generally seem to overlook the kind of effort I’ve been putting in over the last few months when they think of programming. When I was in law school telling a girl I knew about my efforts to put together a coherent plot for Forgotten Times 1, she remarked that she’d never thought about programming being about plot, rather than about essentially mathematical problem solving. The same sort of thing happened when I told a friend of mine here in Cincinnati about my programs – to get some sense of them, he asked to measure the number of source lines of code I had written in the engines, and completely ignored the data files.

I suppose it’s not that different from thinking of authors on the Internet as “content providers.” However, I still think it’s a mistake. Since I started writing games, judging the program by focusing on the basic mechanics has begun to seem something like judging a fine wine by looking at the bottle. True, the bottle can tell you something (e.g., where the wine came from, year, etc) but it’s what someone put into it that really makes it special.

Man…it’s been a while

August 1, 2009

I can’t believe the last post was two months ago. Actually, I can believe it, because I know why it took me so long. It took me so long because I was working on getting the pathfinding back together in Forgotten Times 2. I say back together, because when I originally wrote the AI, the characters had the power to move between any arbitrary points in an area. However, since then (circa 2003-04) I basically ripped out everyone’s brains and left them standing stationary and waiting for the player to interact with them. Now, since May, I’ve been putting the pathfinding back in to support some new plot segments, and it’s been more than a little difficult. The problem is that I not only ripped out everyone’s brains, I also changed how the software gives commands to the different characters. Circa 2003-04, I figured out how to get from point A to point B as part of the main document class. Since then, I’ve moved the brain power into the characters themselves, leaving the document class to basically store data. While that makes sense from a compartmentalization standpoint, it didn’t work at all from the standpoint of making the AI work, since the data I needed for pathfinding was stored in the form of private members of the document class.

Unsurprisingly, that caused no end of heartache which in the end was resolved with a tremendously ugly (but functional) solution – I duplicated the data I needed as global data. At some point, I’ll rationalize things so that I don’t have duplicate data stored as globals and as private members of the document class. At this point though, I’m just happy that I got things working.

Manual Installer

May 31, 2009

I discovered something recently. What I discovered is that my original method of distributing the Forgotten Times series doesn’t work. Originally, I had thrown the Forgotten Times executables, and the data files that actually power the games into a folder creatively called “Files”, made a shortcut to the executable in the main distribution folder, then zipped everything up and put it on the web site.

The problem with the above approach is that windows shortcuts use absolute locations, and every location is different depending on the name of the user account it’s running under. As an example, if my user name is “Sid” (which, on this computer, it is), then the path name for any file located on my desktop will start with “C:\Documents and Settings\Sid\Desktop\”. Because of this, whenever the zipped distribution folder was downloaded onto a new machine, the shortcut would try and look for the data files the executable needs to run in the location they were stored on the original machine (e.g., “C:\Documents and Settings\Sid\Desktop\FT\Files”) rather than their location after the download (e.g., C:\Documents and Settings\Your Name\Desktop\FT\Files”). The result, predictably, was that the executable would enter an infinite loop, forlornly looking for the data files in a location that simply didn’t exist in its new host environment. Very sad.

Anyway, I ended up deciding to solve this problem by making a wink program demonstrating for a user how to find the executables, and make a new shortcut on their own machines. It isn’t the most elegant solution, but it should get over the fact that I have no real way of knowing what the relevant user names will be on the machines where my programs are downloaded. Someday, I may end up writing a full install script. However, for now, wink it shall be.

A Stitch in Time…

May 17, 2009

Well, I finally completed (re)work on the Stick Game, and got it into a form where it can be distributed. The delay: memory leaks. Yeah, that post where I said the game didn’t have any was tragically, horribly, wrong. It turned out, that it just didn’t have any in the main control loop. It had all sorts of them hiding away elsewhere, and catching and killing them was a huge pain. The bottom line: unless you’re certain that you’ll never use a piece of code ever again, write it well the first time (or, at least, make sure you properly deallocate your memory). That way, you won’t be stuck with a weeks long project many years hence putting it into a shape where it can be distributed without blackening your name.

Anyway, at this point the first pass at content for the site is up. Next, I want to do some work on the interface, then test everything out, and it’ll be ready to go live.

Too much power

April 25, 2009

So, I’m still prepping the stick game for uploading to the web site. When I wrote it originally, I thought it would be cool to allow arbitrarily large numbers of rows – probably something about showing off the robustness of the data structures. The problem is, when I wrote it, I gave short shrift to performance optimization. The AI enumerates all possible moves and possible games, then plays a move which leads to it winning (or taking the longest possible time to lose). That’s fine for when you have a small number of sticks, but when you have a larger number of sticks, the process of enumeration consumes all available RAM, and causes the program to become non-responsive. As a result, the fact that the AI will only play once it’s found the correct move means that it (effectively) never plays once it has to deal with a large number of sticks.

Now, if I’d been thinking about performance optimization when I originally wrote the game, I would have done something marginally clever, like have a calculation thread constantly running the background, enumerating away, rather than waiting till it’s the computer’s turn to start thinking. I could also have done something like add a timer on the computer’s thinking, and forced it to make a move after it had been enumerating for some set period (e.g., 30 seconds). That second one would actually go hand-in-hand with a third optimization that I didn’t implement: I could have written the code to save intermediate results, rather than only saving the results once the enumeration’s finished (the current approach).

So there are all sorts of optimizations I could implement to help clean up the mess from when I originally wrote it. Alternatively, I could just remove the user’s ability to start the game with an arbitrarily large number of sticks (thereby making sure the game is only played with the smaller numbers where the lack of optimization isn’t a problem). As it happens, in my relatively old age, I have found that there’s another optimization that is more important than the ones I described above: minimizing my own time expenditure. Given that, what will actually happen is that I’m going to remove the functionality that’s causing the problems, and leave the task of optimizing for another day.