Wednesday, June 23, 2021

The Simple Fix That Took Way Too Long

As Unity 2018.4 LTS is reaching EOL now I was tasked to upgrade the Unity version of the game I'm working on. The upgrade was quite easy and after a couple of days everything seemed to work. When doing the final test before we were supposed to ship the game built with this version however, I found a problem when changing profile picture. It opened the gallery app fine and allowed me to pick a photo, but as soon as I did the game crashed! 

The crashlog pointed me to the open source cropping library used by the Image And Video Picker asset we are using in this project, saying something about not finding the class. Both ClassNotFoundException and NoClassDefFoundError were shown, so I assumed that for some reason the JAR file wasn't included in the build anymore. It was also searching in some really weird paths, so I thought it could have something to do with obfuscation as well. I started going through all config files, gradle scripts and android manifests that had been changed during the upgrade, but couldn't really find anything relevant. I did a few changes and built new versions only to find the problem remaining after every try. I took to google and thought surely someone else must have had this problem, but I couldn't really find anything.

I then decided to upgrade the asset from the Unity Asset Store as I found there was a new version during my googling, but still the same error remained. At this point I finally looked closer at the error and realised it said:

Failed resolution of: Leu/janmuller/android/simplecropimage/R$layout;          eu.janmuller.android.simplecropimage.CropImage.onCreate (CropImage.java:111)

I went to the open source repo and found that line, and it was when trying to load the layout it failed:

setContentView(R.layout.cropimage);

This made me realise I'd been chasing the wrong problem. Apparently the CropImage class had been loaded, which is in the JAR, but it failed when loading the layout from resources. I unzipped an old APK and a new APK and sure enough the resources from the library was present in the old one that worked, but not in the new. 

This made me remember that old warning I've seen in Unity about putting android resources in res folders being deprecated, so I started reading up on that. Apparently what you need to do is to either put your resources in an AAR or in something called an "Android Library", that is basically an exploded AAR where you have a folder with a project.properties and it's own manifest, and a special folder structure inside that contains the assets. The CropLib however seemed to be a proper "Android Library", so I couldn't understand why it didn't work. I went googling more and still couldn't find any good resources to solve this, so I decided that once I find the solution to this problem I must publish it on my blog to make sure no one else has to spend all this time on this.

I went for lunch and an appointment with the barber, and when I came back I opened up the documentation for using Android Libraries again with restored energy, and now I noticed that Google had given me the documentation for Unity 2019.4. I changed the little drop-down in the upper left corner to 2020.3 and there I finally found the solution! In the end of the article it said:

Android Library projects must have the .androidlib extension for Unity to support them. Add this extension to your library’s root folder name (for example, mylibrary.androidlib), and place the folder in the Assets folder of your Unity Project.

This is apparently new in Unity 2020, changed by the rational that you now don't have to put it in the Plugins folder, and the asset had not been updated for this in Asset Store. So, I renamed CropLib to CropLib.androidlib, built a new version, and it worked! So in the end, I spent about two full days on something that turned out to be solved by just renaming a folder! One could have thought that the Unity upgrade should have done this, or at least pointed it out, but at least I didn't see it do that.

If anyone else have this problem and finds this blog post useful, please make a comment to make me feel better about spending all that time figuring this out.

Monday, November 25, 2019

My Way: Working With Versions

Today I want to share with you my favourite way of working with versions. I've been around, so to speak, and seen different ways, and this is the way I would suggest.

First thing we do is to create a branch called "develop", off which almost all work is done.

  • Tiny fixes that are just one commit, I usually do directly on the "develop" branch.
  • Small tasks, that are usually completed within a day, and with a single contributor, I suggest branching off from "develop" into a kebab-case-name in the folder "dev/*".
  • Bigger tasks, that might go on for several days and with more than one person contributing, I suggest branching off from "develop" into the folder "feature/*". What's important is to never have more than one of these long living branches, and that you often (at least daily) merge develop into the feature branch.

When the time come for us to do a release, we create a branch in the folder "release/*" called major.minor.x based on "develop". If we're about to release version 1.39 for example, the branch would be called "release/1.39.x". The 'x' is because the number of iterations that will be needed to stabilize the release is unknown at this point. 

Once we have the branch we change the version number in the project to 1.39.0 and make an initial build. Immediately following that we update the version number to 1.39.1, so we know that and logs, tracking or bug reports saying 1.39.0 is that exact build that we just did. During the time between builds we have the odd revision number up until the point when we're doing the next build for testing. Then we update version to 1.39.2, make the build, and immediately followed by updating to 1.39.3. Get the drill?

Any work being done during the release process that isn't meant to stabilize the release should still be done in "develop", following to the bullet list above. It is also good practice to continuously merge the release branch back to "develop" to make sure that the issues are solved also there. Just make sure that you don't merge it back when it is an even revision number!

Finally, when we deem the product ready to meet the world, we merge the release branch to "master", making "master" always be the latest released version of the project. We also merge the release branch back to develop one final time where it is integrated with all work that might have been done there while stabilizing the release.

I hope this article can help some people to improve their release process! I'm also curious to learn if any of you have any practices you think are better or if you have any concerns with my proposed way of working. Let me know in the comments!


See also: 

Saturday, August 10, 2019

Farewell to an Amazing Job

I have an amazing job. I work as a game developer in the new games studio for one of the leading mobile games companies in the world, experimenting with different game ideas to find the next big hit. While that is awesome, the company also earns an insane amount of money, so I can pretty much get whatever I want, as long as I can motivate it. Could it be better? I don't know, it sure seems pretty good when I put it that way. But still, that apparently isn't enough, because I just resigned. Am I crazy? Yeah, probably. Anyway, as some people have asked, here's my reasoning.

First of all, I've been here for more than five years by now, and as this is my first job in the games industry I'm kind of curious to learn how other companies operating in the same area do it. The new company I'm going to work for is also making mobile games, but they do somewhat different games and they do it in a completely different way. While the game engine is the same as I've been using the last few years, the team size and composition is very different. And I'm really curious to learn what else is different.

Second, while being this successful and having all the money in the world is kind of convenient, it also makes it very hard to really make an impact. Making a new game or a feature that earns a million dollars in a company valued to more than five billion dollars is something else than doing the same in a company valued to 35 million dollars. Also in a less monetary sense, you're a much bigger part of the company when there is less than a hundred employees rather than over two thousand. I'm hoping that will also make the company more personal, with less policies and processes that can't be bent and more space for doing the smart choices.

And third, it come down to the actual games in the making. The company I am joining have a strategy to focus on social multiplayer games, something that is very dear to me. While my current company also have an ambition to do that, it just doesn't seem to really happen. To be fair though, it is more easily done with the games of the new company, where you actually play with others, compared to the primarily single player games we make here and add a social meta layer on.

So, to sum it up, I'm leaving a great place with great people, to learn more, to have a greater opportunity to make a difference, and to make games I can be even more passionate about. I really would like to thank everyone here for this amazing time, and for all the faith you've put in me. It's been a blast!

Sunday, June 16, 2019

Reboot Complete

Last I posted here was five years ago when I published My Reboot Manifesto.
Geez, what happened?

I guess it would be fair to sum it up real quick with these words;
WOW! WHAT A RIDE!

I have not regretted for a single second that I took that leap. I learned so much and met so many great people. I've made an impact on so many millions of peoples lives and helped make tons of money to both the company and to charity, and a little bit to myself too. And it totally drowned me in swag! It has sure been the ride of my life!

But it is coming to an end, and with that I thought it would be interesting to revisit the "manifesto". So let me just walk through it bullet by bullet, and please comment if you don't agree so we can talk it out. =)


Working with products for my employer instead of consulting for external clients

Round one is definitely for the better, for me I might add. I really enjoyed working for my own company's good rather than helping others, and with the added benefit of no time reporting it wasn't even a close game.


Using a MacBook Pro with OS X instead of a Dell with Windows

Another win! I generally don't really like Apple to be honest, but the MacBook Pro is an outstanding product, both the hardware and software. I really don't want to go back to Windows ever again.


Programming in Java with IntelliJ instead of C# with Visual Studio

This is a closer one. I didn't really become friends with Java, but I did like IntelliJ. However, the way it went down I only stayed with Java for about a year, after that I changed position and did C++ for a couple of years - where I used AppCode (also from JetBrains that do IntelliJ), and after that I actually came back to C#, but now using Rider (also from JetBrains) as editor. And I must admit C# is the better of these languages, but as the JetBrains products are superior to Visual Studio I'm gonna call this a tie.


Writing tests with JUnit and Mockachino instead of NUnit and FakeItEasy

This one was for the worse. While JUnit and NUnit is kind of the same, Mockachino had nothing on FakeItEasy. Also I do like the testing syntax in C# better than in Java, it's even more for the worse. Luckily that sorted itself as mentioned in the previous section.


Persisting data with Hibernate to MySQL instead of Entity Framework to MS SQL

To be honest, I can't really remember this in enough detail to judge it, as it was only during the first year I did any persistence using database. After that I've been working with mobile client as platform, where I've either persisted on the device as files or by rpc calls to a server where the actual persisting was made by our backend developers.


Having SCM in Mercurial with SourceTree instead of Subversion with TortoiseSVN

Here's another tough one. Mercurial was quite confusing to me at first, but I eventually got to know it pretty good - just in time before we decided to change to Git. After that Git was quite confusing to me, but now I've gotten to know it pretty good too... but it sure took some time. Subversion was so much easier, and the time spent on helping less technical team mates was a lot less. And in our case I'm not really sure if the benefits of the distributed SCM was actually something we benefit from. Now when I'm comfortable with Git I kind of like it, especially I like the partial commits and the rebasing, but it does come to a cost. Think I'll just call this a tie, as it depends on the situation and skill levels.


Doing CI with Jenkins and Ant instead of CruiseControl and Make

I'm afraid I have to give this to CruiceControl, mainly due to all the problems we have had with Jenkins here. I really like Jenkins when it works, especially the Jenkins Pipeline, but CruiseControl just worked, and that's actually the top most feature I want in a build server.


Handling sprints and issues with Jira instead of Redmine

What to say about this one... I guess I don't really like either of them. Well, actually I guess I don't really like bug-tracking-systems at all. I think that Jira has the upper hand on Redmine though, as it's so much more widely used, and it's so customisable and have so many plugins, so it is hard to be a competitor.

Monday, March 17, 2014

My Reboot Manifesto

So, I wanted to learn something new.
An opportunity appeared and I changed employer.
And with that, everything changed!

Working with products for my employer instead of consulting for external clients
Using a MacBook Pro with OS X instead of a Dell with Windows
Programming in Java with IntelliJ instead of C# with Visual Studio
Writing tests with JUnit and Mockachino instead of NUnit and FakeItEasy
Persisting data with Hibernate to MySQL instead of Entity Framework to MS SQL
Having SCM in Mercurial with SourceTree instead of Subversion with TortoiseSVN
Doing CI with Jenkins and Ant instead of CruiseControl and Make
Handling sprints and issues with Jira instead of Redmine

That is, while there is great value in the items
on the right, the items on the left are new to me.

I'm in for a real deep dive, and I love it!

Now I learn a lot every day!

Monday, February 11, 2013

My concern about Story Points proved right!

This months PragPub Magazine contains a great article by Ron Jeffries called "Estimation is Evil". It contains a couple of great quotes I'd like to share, and also proves my concern about Story Points right.

In my previous post "Story Points vs Hours" about a year ago I wrote:
"The more unreasonable reason is that as estimates are hard to do, you put a metric on them that is hard to understand to get away with them easier. If you say that something will take three days it's easy to see if you were right, while estimating it will take three Story Points will keep you safe. Who are to say how long a Story Point is? Hardcore agilists laughs at such a silly question."
In Ron's article he writes:
"There are a number of ideas about how to estimate using something other than time. Points, Gummi Bears, Fibonacci numbers, T-shirt sizes. These were originally invented to obscure the time aspect, so that management wouldn’t be tempted to misuse the estimates. (I know: I was there when they were invented. I may actually have invented Points. If I did, I’m sorry now.)"
Wow! Reading that just forced me to write this post!

However, since I wrote my previous post I've had the chance to use Story Points and was pretty happy doing it. We started with the assumption of a story point being half a day. That way, when estimating, we could easily transform our estimates, and thinking about "half days" instead of hours made the estimations simpler. "Would you finish this is half a day? One day? Two days?" is more tangible and easier to answer than doing the same with hours.

The story point estimates keeps being translated to hours on another level though, and my most frequent question is what formula to use for that transformation now. That's alright with my though, I can stick with estimating in half days and calling it Story Points. I'm just not sure about the formula, is half a day 3 hours, 3.5 hours or 4 hours? We started with the assumption of it being 4 hours, but as we usually are faster than we estimate with that formula I'm about to lower it...

Well, back to the article... Here are some other great quotes from it:
"Most of us were taught to write down all our requirements at the very beginning of the project. There are only three things wrong with this: “requirements,” “the very beginning,” and “all.” At the very beginning, we know less about our project than we’ll ever know again."
"Anyone who has ever looked at a list of “requirements” has seen some items that were very important, and some that were—well—not so important. Not so important like 1/100th as important as the most important things. Not so important like downright bad ideas. There is a very strong “80-20” rule at work in requirements lists. The bulk of the value comes from a very small subset of the so-called requirements. So these other things aren’t “requirements” at all. They’re ideas, and some of them are not very good."
"It seems that “they” often want to know how long something is going to take, and how much it will cost. My view is that “they” don’t even know what they want, so we bloody well can’t possibly know how long it will take. However, “they” are often powerful and have the money we need, so we need to answer their question, even though we cannot."
It's a really long article, but well worth reading. You will find the full article here:
Estimation is Evil (PragPub)