Sunday 20 March 2016

Rebase - Why merge pulls when you can commit lies and rewrite history

When I introduced a client to rebasing, they asked me: would it avoid our coders breaking wood?

Erm... not exactly. neither rebasing nor merging avoid conflicts. Conflicts arise from people changing the same file roughly at the same time. 

Both merge and rebase involve taking the bull by the horns; The more often you do this, the smaller the trouble.

The chief way to avoid "broken branches" (so to speak) is programmers don't commit files more than necessary and keep them (files) reasonably small.

Digression: one reason I like SourceTree is giving me a convenient interface to review and cancel (before committing) edits which I'd rather not push - log-fu, red herrings, blunders.

Interactive rebase is a popular way to pull changes from master while working on something. I suspect it's popular because it's also a swiss-army-knife letting you do semi-useful stuff like trash the inept commit you did 5 minutes ago, reword PG-rated commits and combine 25 puny commits into the 2001 Space Odyssey monolith.

WHAT IS it?

So, while Nono was fixing this and that on his branch Sassy has been committing wildly on master. What rebase does is, it rebuilds Nono's changes on top of master.
It is cleaner and morally satisfying in that:
- Keeps one's private changes in a sequence.
- Changes appear in the history in the order that they got committed to master.

A fun (huh) example

On Sassy's branch

14:00 Sassy: check garden
14:15 Sassy: investigate living room
14:30 Sassy: look in the bathroom
14:45 Sassy: report no intruders

Meanwhile

13:55 Nono: break into garden.
14:10 Nono: 'borrow' Sassy's Gameboy
14:29 Nono: hide in the bathroom.

What a merge would do:

13:55 Nono: break into the garden.
14:00 Sassy: check garden
14:10 Nono: 'borrow' Sassy's Gameboy
14:15 Sassy: investigate living room
14:29 Nono: hides in the bathroom.
14:30 Sassy: look in the bathroom
14:45 Sassy: report no intruders

Wacky eh. Now the rebase:

14:00 Sassy: check garden
14:15 Sassy: investigate living room
14:30 Sassy: look in the bathroom
14:45 Sassy: report no intruders
13:55 Nono: break into the garden.
14:10 Nono: 'borrow' Sassy's Gameboy
14:29 Nono: hides in the bathroom.

And you know what? it all makes sense because local branches are private, parallel universes. What was happening on Nono's branch indeed was perfectly hidden from Sassy until Nono committed to master.

I'm not expecting anybody to love it until they used it like, 4-5 times. It does have detractors after all. Still, it's massively popular.

In my humble opinion history rewriting (including rebase) is fundamentally wrong and I'll be happy to get rid of git... when something better comes along. In the meantime it offers great convenience at low cost (statistically) so it's a neat compromise.

TAKE ME TO THE DARK SIDE

Before we start.
Assuming you're not vimdictive the following git-fu needs the text editor setup I describe at the top of my cheat sheet.

1. commit

git commit -m "fix something" # commit changes locally, on your branch, first

2. pull master changes from remote

git checkout master # landed on master branch!
git pull # now master is up to date.

3. rebase

git checkout nono # get back to your branch
git rebase master -i # "i" for interactive.

Up, pops your text editor. Do read what it says in here truly fascinating - so many parlour tricks/substitutes for git-fu that no sane coder will ever remember.
Well. You can edit this file (hence interactive) to do nice touch-ups. Like fix a garbage comment slandering the web administrator, merge commits or forever delete spoof commits.
Now Save and Quit the Text Editor to continue the rebase. At which point, if conflicts are detected, the rebase will stall and...

git status # shows conflicted files among other things

4. Fix conflicts before continue

Just do it!

git add --all # when you solved ALL the conflicts

5. Rebase on and on

git rebase --continue

6. Push to your remote branch

git push -f # DANGEROUS don't ever -f(orce) except while doing this. But necessary if you already committed to your remote branch

7. If you change your mind

Too much for you to handle? Witchcraft afoot?

 # instead of rebase --continue
rebase --abort

INTERLUDE

I encourage everybody - even project owners - to work on their own branch (unless of course you are using feature branches but... ...alright, not today)

THE FINAL ACT

Once again without comments. First the non-screwy rebase:

git commit -m "fix something"
git checkout master
git pull
git checkout noha2
git rebase master -i
git push -f

Then the screwy rebase (notice the font change?)

git commit -m "fix something"
git checkout master
git pull
git checkout noha2
git rebase master -i
git status
git add --all
git rebase --continue
git push -f

Thursday 17 March 2016

GameSparks micro-tutorial: leaderboards and analytics for Unity games

GameSparks is an attractive BaaS (back-end-as-a-service) option. Their docs are a bit stuffy though, so I created this quick-start document and a convenient wrapper so you can get started with 4-5 lines of code.
GameSparks provide a variety of back end services including cloud storage, cloud code... The basic integration exposed here illustrates authentication, leaderboards and analytics.

The integration and sample code are designed to work with Unity 3D.

Setup

I take you through the steps for a simple setup using anonymous authentication.
"Anonymous" means that the device ID will be sent to the back-end service instead of a user name and password).
  1. Register and create a game on their portal; fetch the API key and secret under configurator > overview
  2. Download the GameSparks Unity SDK (it's a package, like in the Asset Store)
  3. Import the package (warning: moving the GameSparks folder could break something)
  4. Also import JSONObject from the Unity Asset Store, this is needed to parse some of their responses.
  5. Notice the added GameSparks menu. Now, enter your API key and secret via GameSparks > Edit Settings.
  6. Create an empty game object, you might call it GameSparksManager.
  7. Add the GameSparksUnity script to the manager. This is part of the downloaded SDK
  8. Add my quick integration script
  9. Press play!
This is how the inspector for GameSparksManager will look like:



If you've done everything correctly you should see console messages indicating a successful login. You are now ready to do the following:
  • Fetch and submit scores
  • Display leaderboards
  • Submit analytics requests.
Leaderboards and scores

For this I will defer to their lengthy introduction here but you can pretty much skip anything related to Unity and use my sample integration to get you started.

If you use HIGH_SCORE as leaderboard id (GameSparks refer to ids as "short codes") and SCORE as attribute and SUBMIT_SCORE as log event short code, you can leave the above fields (in GameSparksManager inspector, see picture) as is.

The utility I wrote, GameSparksApi, provides the following functions:

  • SubmitScore(int) to send a score to the server.
  • SubmitDisplayName(string) to submit the user's nickname displayed in leaderboards.
  • FetchPlayerRankAndScore() to fetch the rank and score; this will be assigned to playerRank and playerScore so you can have a text component display this (it will take a while to update after you call the request).
  • FetchLeaderboard()
    to likewise get an updated leaderboard with a formatted result available (after a while) in formattedLeaderboard

Customise the source if you're not happy with leaderboards format or want to handle callbacks yourself.

Analytics

So called analytics requests allow you to log user actions. A typical use of this is finding how far players got into your game: Did they complete the tutorial? Did the game even load correctly? Can they go past level 1... ? And so forth.

This doesn't require back-end setup, just call the Analytics(string) function on GameSparksApi. The string indicates what user action you're logging.

Results are displayed (in your admin screens on the GameSparks website) under Configurator > Analytics > Custom

Caveat: Analytics update appear to take one hour or more to go live in admin screens.

Wednesday 16 March 2016

The Pit: sharing indies online

Sharing indie games is much harder than you might think. Case in point... my technical posts about how to make games at times look more popular than the games I make.

I'll take for granted that few are in it for the money - but do you want players for your games at all? I do! 

Finding players for free games online may be no easier than selling games on mobile!

I recently released two games on indie game sites gamejolt and itch.io.
The first game is a multiplayer, toon styled shooting game one month in dev.
The second game is a shortie visual novelette made in a day.

I shared stories using Facebook, Tumblr, DeviantArt and IndieDB. I also release short news on GameJolt (probably a complete waste of time). I considered using social software a la hoot-suite but for now I might stick to posting myself.

I collated traffic sources for itch.io:



Taking this at face value, one may draw a couple of conclusions or at least, ask questions.

Is the shooter worth the effort?

30 days of dev. yields at best the same number of plays as 8 hours!
For now I’ll put this conclusion aside. The shooter got on itch.io on the late, so it would be reasonable to throw gamejolt data in the mix but I can’t think of a clean way to do that.

Also... I have data from the Photon server showing a DAU in the 5-10 range.

Contemplating a synch-ed launch on a bigger site; not expecting conclusive results.

In social mode, users engage with casual games.

I have convergent data (game jolt via google analytics) to back this up - suggesting that social accounts for no more than 12% of plays on the shooting game.

Of course, with this level engagement you don't get any explicit feedback or ratings.

Conclusion?

I don't have one really! Instead, I would ask: how do you share your indie games?

For reference, you can find the games analysed in this article here, on itch.io.