Bug Hunt Safari - Tracking The Most Elusive Bugs
Added 2018-07-13 05:33:33 +0000 UTCWork on v0.7 has been going great the last few days - the new clothing system is working well and has most of it's new contented added in, a few screens have been redesigned, and new poses and clothing items are being rendered as I type. In a day or two I should be far enough along to set a solid release date.
While working on the wardrobe changes there were a pair of very interesting, very strange bugs that popped up that I thought would be interesting to write about. If you end up working with Python or Ren'py hopefully this will save you some time.
The first bug reared its head while I was tidying up the last of the new uniform code. The new code turns your business uniform into a full wardrobe, so you can have full outfits saved, just overwear, just underwear, etc. and your employees will create their daily uniforms from the options you give them. Once the code was finished I sat down and started testing - make an outfit, add it to the business, see if someone wears it. They do, so far so good. Open up the outfit manager, create a new overwear set, add it to the business. This is where things go wrong: the uniform wardrobe had two of the original items in it. When I backed out and checked my personal wardrobe it also had duplicates of the same item.
At this point I go into full bug testing mode, trying to see what situations make this strange duplication happen. It becomes clear that all of the business wardrobes and the players personal wardrobe share items of clothing, so adding and removing one from one list removes it from all the other ones. That shouldn't be possible though, because every wardrobe is separate and there doesn't seem to be any chance of referencing the wrong one in my code.
I do some more digging and googling. I eventually find my culprit, and it turns out it's a strange design quirk of Python. When you define a function (a chunk of code that manipulates an object or data in a useful way) you can pass parameters - no surprise there. Python also gives you the option to set default parameters, so if nothing is passed it has a default value. This is very useful in cases where an option is only used in a small number of cases. The wardrobe object makes use of a default list to hold all of the outfits inside of it: the player wardrobe and the business wardrobe are initialised to empty and filled during gameplay. Here's the issue - if the default argument you provide is mutable (like a list, which can have things added or removed while still being the same object) each use of the function uses the SAME default argument. So when any of the wardrobes tried to use the "add_outfit" function they would be extending the same list that was used by other wardrobes, instead of each wardrobe having a unique list. The fix was to simply use a None value as the default and check if the variable is None, then assign it a (unique) empty list.
The second bug was directly related to Ren'py and showed up while adding new outerwear and underwear to the game to fill out the clothing system. While adding items I noticed that if I saved the game, then loaded that save the outfits I had created recently were gone. Some testing revealed that it was only recently created outfits that would disappear - if the main character moved location or interacted with something they would save completely fine.
Again it took some time testing and googling to figure out the answer, which turned out to be a combined quirk of how Ren'py saves games and how I was looping the player into the outfit creator. To save games Ren'py only keeps track of variables and objects that change during the course of the game. The player wardrobe was properly initialised "in sight" of Ren'py, but it still wasn't being save.
The problem was in how the outfit manager screen was brought up. To allow the UI to have a button on the main screen I was using a Ren'py function that lets you call functions inside of a "new context". Launching it in a new context meant that Ren'py would put the rest of the game on hold, and return you to exactly the place you were when you were done. This was useful because it gave you access to designer at any point in the game. The problem was that, because all of the activity took place in a completely new context (kind of like a mini program being run on the side) Ren'py didn't notice any changes. When control was given back to the main context it looks like nothing has immediately changed - it's only when the player moves around or interacts with something that Ren'py notices the differences and will save them properly.
I've also fixed up this bug - the outfit manager is now called as a normal screen rather than inside of it's own context. A previously unnoticed option for the "call screen" function keeps all of the functionality I had before with this new method.
So that's my story of the two major bugs to be crushed over the last couple of days. I'm looking forward to getting some more work done now and getting v0.7 pushed out to patrons in the near future!
Comments
Found a strange bug in v0.6.1 - When I take a closer look at the employees the hair color that is listed doesn't match the hair color the character has. One of my employees clearly had black or brown hair and it read as blond. Also a minor grammatical error "blond" should be written as "blonde" when used to describe a woman's hair color.
TheMGA
2018-07-14 15:04:01 +0000 UTCUp until now Python has been a dream to work with, half the time if feels like writing psudocode that just works. I think this is the only time I've run into something that feels fundamentally wrong with how it does things. I know it's a thing now, so it won't trip me up again at least.
Vren
2018-07-14 06:19:12 +0000 UTCI have, and thanks for all of the useful feedback! Sorry for not responding a little sooner, the bug hunting kept my attention longer than I expected and I've been neglecting my patron-side duties.
Vren
2018-07-14 06:17:34 +0000 UTC