Monday, April 1, 2013

Stupid Reflection Tricks

The game that I'm currently working on (based on parts of Box2D, Artemis, libGDX and a dash of RUBE for level design) employs reflection.  I've got custom properties defined for various bodies, fixtures, and joints in RUBE.  Some of these custom properties are strings that are interpreted as classes for instantiation at run-time via Java reflection.  For example, I have eggs in the game that have a custom property "PreSolverScript".  This field is defined with EggImpact.  EggImpact is a Java class that defines what happens when the related fixture is detected for the Box2D presolver.  This class checks the velocity of the egg object just before impact.  If the velocity is great enough, the egg receives damage and potentially breaks.

The script references are resolved at run-time through the use of Java reflection.  Doing reflection with simple Java constructors is fairly simple.  It gets somewhat tricky when you have to worry about parameters.  Here's an example of my code that does reflection with parameterized script strings.  If parameters for a script exist, they appear as comma-separated values following the script name.

In the example below, preSolverProperty is a string containing the custom property for the string.


            String [] parms = preSolverProperty.split(",");
            IPreSolverScript preSolverScript;
            if (parms.length > 1)
            {
               preSolverScript = (IPreSolverScript)Class.forName(ScriptConstants.SCRIPT_PACKAGE_NAME + parms[0]).getConstructor(String[].class).newInstance((Object)parms);
            }
            else
            {
               preSolverScript = (IPreSolverScript)Class.forName(ScriptConstants.SCRIPT_PACKAGE_NAME + preSolverProperty).newInstance();
            }

The sticking part for me was casting parms to (Object).  Without this I got a invalid number of parameters error at run-time.

Sunday, December 2, 2012

libgdx and iOS deployment quest - Part IV

I discovered that my installation of TortoiseHg was not 100% complete.  I had not installed kdiff3 or added any of the necessary configurations to get that tool to work.  The net result was that I was not able to do any sort of merges or "diff to local" type operations.  After a bit of hunting, there are additional configurations that you need to implement after installing kdiff3.

To wit:

1) Install kdiff3 from kdiff3.sourceforge.net.
2) Update your .hgrc similar to below:


[extensions]
hgext.bookmarks =
hggit =
mq =
hgext.extdiff =
[extdiff]
cmd.kdiff3 =
[merge-tools]
kdiff3.args = $base $local $other -o $output
kdiff3.priority=-3
kdiff3.args=--auto --L1 base --L2 parent1 --L3 parent2 $base $local $other -o $output
;kdiff3.fixeol=True
kdiff3.gui=True
kdiff3.diffargs=--L1 '$plabel1' --L2 '$clabel' $parent $child
kdiff3.diff3args=--L1 '$plabel1' --L2 '$clabel' --L3 '$plabel2' $parent1 $child $parent2
;kdiff3.diff3args=--L1 '$alabel' --L2 '$plabel1' --L3 '$plabel2' $ancestor $parent1 $parent2 -o $child
kdiff3.dirdiff=True

Sunday, November 25, 2012

libgdx and iOS deployment quest - Part III

I discovered that my version of libgdx is a bit outdated and I needed to sync things up with the latest and greatest that includes ikvm support.  Since I'm trying to do everything on the MacBook Pro, that means I need hg-git support.  This allows you to deal with a git repo (which is how libgdx is version controlled) from the Mercurial tool.

On Windows, this is fairly easy.  Instructions are here.

On a Mac, it's also pretty easy.  If you followed the instructions for installing TortoiseHg from my earlier blogpost, you can simply open up a terminal and run this: "sudo easy_install hg-git".  From there you need to edit the .hgrc file:

[extensions]
hgext.bookmarks =
hggit =

Verify things work by entering "hg help hggit.

Hat tip to this site.

In other news, I was able to get both Outbreak: Zombie Slots and Toyland Slots running on the MacBook Pro.  It was scary easy.  libgdx is awesome!!!

Obligatory screenshot below:


libgdx and iOS deployment quest - Part II


I got SourceTree installed and cloning my repos, but didn't care for how it operated since I'm used to the TortoiseHg work flow.  Additionally, I noticed that it didn't set up 'hg' to be run from the command line by default.  I could do a "hg clone" from the terminal which is sometimes useful versus using a GUI to do all the heavy lifting.

I did some more searching and came across this page: https://bitbucket.org/tortoisehg/thg/wiki/developers/MacOSX. I came across this before as I was looking at my Hg options but didn't pursue it because the steps look way too involved and I wanted a pre-packaged approach to getting things running.  It turns out there are actually several different approaches at getting TortoiseHg running that are listed on this page... they just aren't presented very well to make you realize that.

At any rate, if you scroll down, you'll see instructions for installing things using HomeBrew.  They were very easy and very straightforward.  I only had a couple of hiccups where I tried to run the command and the instruction failed because it expected that I was running as root instead of a standard user.  Boiling things down, here's what I did:
  1. Install Xcode from App Store
  2. ruby -e "$(curl -fsSkL raw.github.com/mxcl/homebrew/go)"
  3. brew doctor
  4. Open Xcode → Preferences → Downloads → Install command line tools
  5. Set up .bash_profile as specified on the Wiki page (BREW setup, etc.)
  6. brew install pyqt
  7. brew install qscintilla2
  8. sudo easy_install pip
  9. sudo pip install Pygments iniparse Mercurial
  10. hg clone http://bitbucket.org/tortoisehg/thg ~/Documents/TortoiseHg
  11. cd ~/Documents/TortoiseHg
  12. ./thg
Success! Although it doesn't appear to have Finder integration, like the Windows version and its Windows Explorer integration, it does work and present me with the TortoiseHg Workbench I'm extremely familiar with.

Success!

Next up, to see if I can get my Outbreak: Zombie Slots running with Eclipse on the Mac platform.

libgdx and iOS deployment quest - Part I


After seeing the successful deployment of Noblemaster's libgdx-based games to the iTunes store, I figured it would be fun to port the libgdx games that I've created to iOS and release them to the iTunes store as well. So, today I went out to the local Best Buy and picked up a Macbook Pro 15” MD103LL/A.  It was on sale for about $120 less than the retail price that was listed.  I could have went for the more expensive model that included more system memory, but after doing some reading, saw that adding memory is pretty simple and cost effective so I'll probably go that route.

I brought the machine home and the first order of business was to get it running on my network.  Fail.

First and foremost – for whatever reason, the MacBook Pro would knock my Netgear WND3300 Wireless router out of commission. The net result (heh!) was that not only could the Apple machine NOT get on the Internet, but it also fouled up the rest of my network. Other machines that were working just fine would no longer be able to access the Internet. This is not the first time I've seen this behavior.

My brother-in-law has had a MacBook Pro for a while now. Every time he has brought his machine over, he has caused the same issues... after setting up the wireless configuration, boom – my network goes down. The only way to correct this in the past has been to reset the router.

This time I was determined to figure out a workaround if not an out and out fix. Using Google was worthless.  None of the tips I saw worked out.  After poking around with “ifconfig” and messing around with general settings, I noticed that I could get things to work once again if I manually defined the DNS values. My standard test to verify is simply “ping www.google.com” from a terminal window.

Now, normally, the DHCP server capabilities of the router kick in and assign the router as the DNS server for the DHCP client. This has worked flawlessly with Windows machine. For some odd reason, this causes issues using an Apple machine. I logged into the WND3300 and took a look at its DNS capabilities. It's cable modem side acts a DHCP client itself, which gets its configuration from the cable configurations that are assigned by the cable company. I looked at the assigned DNS server values. They looked correct. Based on a hunch, I modified the “Domain Name Server (DNS) Address” setting on the Basic Settings page from “Get Automatically From ISP” to “Use These DNS Servers” and specified what the cable modem always gives me. After doing that, the MacBook Pro no longer has any issues accessing the Internet and the other machines on the network similarly work fine. Whew! That blew about an hour or so of time.

Next on the list was to download Xcode. Xcode is the IDE used for developing iOS applications. I've used it a little bit in the past. I had to log into the Apple App Store and download it. Annoyingly, I also had to update my credit card info on file with Apple. As great as everyone claims Apple is, I find this to grate on my nerves... Especially given that Xcode is available as a free download. I guess I should also mention, I already had an Apple ID that I created back when I had an iPod Touch.  So, getting that stuff up and running was annoying but not world-ending.

With Xcode downloaded, I needed to install Eclipse. Wow. Installing Eclipse is easy. The problem comes in that there are about 10 different flavors of Eclipse. Historically, I've always gone for Eclipse Classic. However, this only provides a bare minimum. After doing some looking around, I discovered that there is a version of Eclipse for Mobile Developers. Sweet! It includes Java and C development tools. The libgdx library backend includes certain things written in C for performance reasons (the Box2D Physics Engine, for example.) I've had to make my own tweaks to that in the past so its been cool to have C tools available. At any rate, I installed that.

Then, I tried to run Eclipse. Blah. You need the Java JDK before it will work. On a Windows machine, you get that from Sun. Not so on an Apple machine. At a terminal prompt, I entered “javac -version” and it prompted me for an install. I wanted to specify an exact version of the JDK but I didn't see an option. I've heard of problems with JDK 7 and wanted to stick with JDK 6. I bit the bullet and just accepted whatever version the default installer selected. Turns out that I lucked out and am now running 1.6.0_37. Woot!

Next up I wanted to get my Mercurial repos running. On Windows, I use TortoiseHg which has really nice integration with Windows Explorer. On the Mac, there were a few options and I decided to give Atlassian's SourceTree a shot. I've got my repos hosted on Bitbucket so it seemed to make sense since BB is operated by Atlassian.  (Note: There has been an update on this -- I'm now using TortoiseHg on my Macbook Pro.)

In order to access my BB repos, I apparently need to get the ssh keys configured. I tried to follow Atlassian's directions where they imply that you'll be prompted to enter your ssh key info when you try to clone a project. This will supposedly add your key to your system's keychain. That's not the case. Trying to clone a repo resulted in an invalid URL problem and I couldn't get any further than that.

So, I copied my private BB ssh key over to the MacBook. In order to access my repos, I need to have my ssh key in the Mac's key ring. This is done via “ssh-add -K /path/to/key/here”. So I did this, got prompted for the passphrase and then.. it failed. Mwah? I generated the key using Puttygen and use it on nearly a daily basis, so I'm 100% the passphrase was being entered in correctly. Turns out that you need to use PuttyGen to export the key in an openSSH format. After doing that, I could add the key successfully.

Next up was to get my graphical development environment going. This meant getting Inkscape and GIMP up and running. Since I'm running OS X 10.8 (Mountain Lion), this is not as straightforward as simply installing the related dmgs. No. Trying to run Inkscape gives you a prompt saying “Where is X11?” Turns out that you need to first install Xquartz, which is the Open Source X11 that Apple no longer includes on its machines. Yippee.

After downloading that and rebooting, and trying to Inksape again, I was still prompted for “Where is X11”. Mwah? You need to go to your Utilities folder and start Xquartz and THEN start Inkscape. Geez. So much for the “ease” of using Apple products. On the other hand, the GIMP version was already setup with Mountain Lion compatibility so I didn't need to do much there other than copying the GIMP application to my Applications folder. Sweet.

Saturday, September 1, 2012

Stupid AdMob tricks

I use AdMob exclusively for my applications.  There's no particular reason other than it is a lot easier to set things up than having to futz with three or four different ad company SDKs, and having a single location to track my voluminous income (heh) is just super-convenient.

Today, I set things up so that all my apps will serve house ads for the duration of the (US) holiday weekend.

What's an AdMob House Ad?  An AdMob house ad allows you to serve up *free* ads in your applications.  You can do things like specify how many impressions you want to serve, which applications will display which ads, etc. The ads are restricted to applications you define, but otherwise free you from having to pay AdMob for them to appear.

At a high level, I've got two ad configurations defined for every app.  One version serves the Google Play link and the other version serves the Amazon App Store link.  The ad that gets served is based on the device that ad is appearing on.

Specifically, here's how I set things up:

1) I created a house ad campaign called "all house ads".  At this level in AdMob, you are able to define the start and end times.  I've got things setup to run from today until midnight Monday (end of the Labor Day holiday).  That's a total of three days of house ad rotation.

2) I created several ad groups under the "all house ads" campaign.  At this level in AdMob, you are able to define which apps the ad group will appear in and your impression goal. 

I defined separate ad groups for: DB42 Lite, DB42 Full Version, Doodle Bug, Drawccupations, MatchCard, Pyromancer's Laboratory, and Sky Ninja.

Now, I could have defined a single group and placed all of these ads in that group.  I did not go this route because I wanted fine tuned control over which apps my ads appear in. For example, I don't want the Sky Ninja ad to appear in the Sky Ninja app... The user is already playing Sky Ninja -- therefore I don't need to drive him or her to download that exact same app.


For each ad group in this section, I selected the impression goal of "Fully Allocate".  This means, I want to completely turn my ad mediation over to my house ads.  In other words -- don't serve up any paid ads!  I'm trying to drum up support in my apps for my other apps, so that's why I'm bailing on the paid ads for this duration.

I wasn't sure if the "Fully Allocate" configuration would limit the ad group to a single group or not.  Through experimentation, I see that the ads are rotating through all the ad groups I have defined for the campaign.  This is EXACTLY what I want.

It is also at this level that you can define target demographics and devices.  I initially set things up to serve all demographics and devices EXCEPT the Amazon Kindle Fire.  Ads served to the Kindle Fire are served somewhat differently because I want to reference the Amazon App Store link for those.  Keep reading...

3) For each ad in the group, I've got the URL defined for the app and the image to display with that URL.  I initially created URLs that pointed to the Google Play Market URL.  For example, the DB42 Lite URL points to:

market://details?id=com.gushikustudios.db42lite

4) After I set all of that up for the Google Play links, I copied every single Ad group, and added "Amazon" to the name (e.g. "Sky Ninja Amazon").  Then, I went into each Amazon ad group and edited the URL to point to the Amazon App Store's URL.  For example, the DB42Lite URL changed from what I list above to:

amzn://apps/android?asin=B006KU55UO

The 'asin' value is listed on the Amazon App Store's page for the app.

When you click on this app from the Kindle Fire, you open up the App Store view of your app instead of a the Amazon Retail Web Site.  This makes it easier for the user to install your app on a Kindle Fire for obvious reasons.

I'll post the results of doing all this in a few days.  Hope this helps someone else out there!

Sunday, August 26, 2012

Ant, Javadoc, Libgdx, CreateProcess error=87, and YOU!

Gratuitously copied from the Western Skies blog since I *just* got bit by this -- and apparently got bit by it before 'cuz Google showed that I had visited this link sometime in the past:

I've been developing an Ant build file for a large-ish base of existing code. I've been using my Mac as the primary devleopment platform, but the ultimate target (for the other developers) is Windows. My task to build the javadocs runs fine on OS X, but the first time I rant it on Windows, I got the following:

c:\demo\build.xml:180: Javadoc failed: java.io.IOException: Cannot run program "c:\Program Files\Java\jdk1.6.0\bin\javadoc.exe": CreateProcess error=87, The parameter is incorrect

After about 10 minutes of Googling, I found a/the simple solution: add the useexternalfile attribute to the javadoc task in Ant. Voila , back in business, on the PC at least, and it didn't seem to break the build on the Mac.

 I got bit by the most recent version libgdx when trying to run "ant" in order to build the "dist", which includes the API documentation that I was really aiming for.