My goal for today was to get my mob coding app working well enough to test it out again at tonight’s meetup, and that’s exactly what I did! I spent the entire day hunting bugs and creating new bugs. If there’s any truth to this quote I found the other day, then today was a very honorable day for me. +1 honor!
8:41am: I got here nice and early today, but wow am I sleepy! I’m at two-tea-bag status. I’ll take another siesta today, this time earlier. (The last two times I took my siesta after 4pm, I had trouble falling asleep at night and felt super tired the next day. Maybe 1pm or 2pm is a better time.) Anyway, I’m diving right back into refactoring! I’ll fix issue #3 to remove that unneccesary Boolean and simplify the condition for forking/editing Gists and then update my flowchart accordingly.
8:57am: Worked like a charm! I even closed the issue directly from my commit. But I totally forgot that I’d lose any unstaged changes when I merged my issue3
branch into master
, oops! I really need to remember to use git stash
more, or stage my changes at the very least! Oh well. I’ll redo my edits to README.md
and then fix up and publish the new version of my flowchart.
9:17am: All fixed up! Much better:
Now I’ll just post this in the mob coding README to make it more official. (I guess I’ll post the latest version of the flowcharts and stuff in there, and I’ll keep the historical record of all the work-in-progress stuff in my blog posts like this one.)
9:27am: Done updating the README and fiddling with git stash
to put aside some changes that I’ll want to commit later. Note to self: don’t forget to retrieve those stashed changes later today! Next up, I can either work on more refactoring, work on that state machine diagram, or start hunting down that main bug. I think I’ll focus on the latter, since I do want to get the app working to test it out again at tonight’s meetup! First, time for a quick break.
9:43am: Back again. First step: try the debugger in Chrome! Oh yeah, and I’ll create a new branch.
9:52am: Yup, it is indeed a timing issue! The server emits createNewGist
which triggers the client to create a new Gist, but while that process runs asynchronously, the client also updates the Gist view in the UI upon receiving the updateState
event, even though there’s no Gist yet! Quick, temporary fix: add a conditional statement to only update the UI if a Gist exists. Better solution for later: refactor every event and function related to initializing the game!
10:10am: I fixed that bug, but I discovered (or maybe created) a couple more: the first user’s edits don’t get saved at the end of the first round! And the text editor allows all clients to type at first, until the first turn change event occurs. And the cursor and selection events don’t seem to be broadcasting anymore. I commited my changes on the issue8
branch, and now I’ll try testing the app again from the master
branch to see if those other bugs were already there before. Yup, looks like the bugs were already there. I’ll add a new issue for one and add fixes to my other branches for the others. Refactoring fail!
10:27am: I fixed the cursor/selection event bug (it was indeed due to a bad search-and-replace job!), pushed the change to my issue1
branch, and . Yup, I got issue 3 (simplifying the fork/edit logic) all wrong! If the game creates the Gist right away, then at the end of the first round, assuming the first player is still in the game, the app will always need to edit the Gist, not fork it. Hmm. I’m not sure what to do. I don’t want an extra variable in my code just for this one case. Maybe I’ll change the game logic to only create the Gist at the end of the first round? I’m going to take a quick break while I think about it.
…
11:03am: I got a couple free sweet treats and said hi to my friends downstairs and borrowed a sweatshirt. (They keep it so cold upstairs!) I’m leaning towards changing the game logic to only create a new Gist at the end of the first turn, since it’ll make more sense with my single-player mode idea. But I’m going to make a quick sketch on paper first to check on a couple ideas. Soon I need to send a few emails and I think I need a nap soon. My thoughts aren’t clear at all right now.
11:20am: Now I’m just confusing myself even further… OK, I’ve confirmed that starting the game (and creating the Gist) only when a second player joins the game will not fix this issue, because the Gist would still be created at the start of the turn and would still need to be edited (or forked) at the end of the first turn. So I could create the Gist only at the end of the first turn, but I’d rather not! I like having the link available right at the beginning, and I like being able to save a record of the Gist’s starting point before any edits were made. There are a couple scenarios that I hadn’t thought about much…
Turn change and Gist editing scenarios:
-
First player needs to edit Gist when turn ends if first player is still connected at end of turn, regardless of any other players joining and/or disconnecting! (Previous player: none, current player: 1, next player: 1)
-
If first player disconnects before end of first turn, they still need to edit the Gist because the server will end the turn early, and it makes sense to save the first player’s progress instead of misattributing their work to the next player.
-
Game ends if first player disconnects before end of first turn and no other players are present when the turn ends.
11:36am: So here’s the problem: most of the game logic depends on the state of the current and next player (for updating the UI and deciding which client should get control on the next turn change event), but the logic for forking/editing the Gist depends on the state of the previous and current player! When the game starts, there isn’t any previous player. But I can change that! Maybe I should just always track the previous, current, and next player. Maybe that’s what powers my state machines, and maybe I have two separate state machines for the server: one for the turn changes and one for the Gists! That makes so much more sense! If only I drew some state diagrams before I wrote any of this messy code!
So I think I have my solution: instead of creating some temporary variable just for the first round of the game (which felt very hacked together and unintuitive), I should just initialize the game with variables for the previous and current player both pointing to the first player. Then on every turn change, if the previous player and current player are the same, then that current player should edit the Gist just before the turn ends. Otherwise, the current player should fork the Gist just before the turn ends. (The real piece of state here is this: who owns the current version of the Gist? If the current player owns the current Gist, they should edit it; if not, they should fork it.)
11:47am: Phew! That’s a relief. Now I just need to create an issue for this and fix it. But I’m falling asleep, so I think I’ll send a couple emails and go take my nap break now.
…
1:48pm: That nap was barely successful. Lesson learned: 12pm is just too early for my nap, especially if I had caffeine in the morning! The perfect time for a nap is probably right about now. I had some lunch, walked with friends while they got food, sat and chatted for a while, had more tea, and now I’m back. Another quick note: I’m noticing that I usually do experience a brief spike in anxiety and negative thoughts immediately after a nap, I guess because not all systems are back online in my brain just yet. Then I seem to have a brief spike of elevated mood and motivation and it’s as if the anxiety was never there in the first place. That’s what I feel right now. I hope it lasts!
I made a GitHub issue for this bug, outlining my plan to fix it. During my nap, I realized on more problem with my current solution: the client can’t fork or edit the Gist if they’re disconnected! So the server would have to do it! For right now, I’ll ignore that case (but I just created a GitHub issue for it here) since it won’t break the app. It’s OK if one player’s work gets misattributed to the next player once in a while. Not the end of the world right now.
2:09pm: Time to dive back into the code and fix it!
2:36pm: I had to make some changes to the data model on the server to now track and include the previousPlayerIndex
and emit the previous player’s data along with the other turnChange
event data. The only change in the client-side code is to make previousPlayerId
another global variable (for now!) and update it inside handleTurnChange
and handleUpdateState
just like the other player state variables. This looks like it should work! Time to test it out. Hmm, for some reason that still didn’t fix it! The client still tried to fork the Gist on behalf of the first player instead of only editing it. Looking into it…
2:49pm: Oh OK, the client gets the updated code after the turn change happened. The comparison needs to happen before the turn actually changes! Bleh, this is really confusing. I need to get this straight in my head: the turn changes on the server before it changes for the clients, and the Gist forking/editing happens before the turn changes for the clients.
3:03pm: It works! Sort of. But now the server crashed because of some undefined variable. Oops, I think it’s just because I forgot to declare the new previousPlayerIndex
as a global variable so it wasn’t available to other functions. Let’s try this again!
3:20pm: I fixed something but broke something else. The app works correctly at the end of the first round, but on the second round, the wrong client attempts to fork/edit the Gist! Then all subsequent turns seem to be working correctly. I have no idea what’s wrong. How frustrating!
Also, another quick note: I got things mixed up for a moment because I forgot that the turnChange
event is only triggered when the server’s turn timer goes off. If the current player is disconnected, the server does change the turn and emits updateState
to all the other clients, but turnChange
is never emitted. That’s confusing! It sort of makes sense on the client-side, since a disconnected client wouldn’t be able to respond to the turnChange
event, but it’s unintuitive for me and difficult to remember that the turn can effectively be changed without the turnChange
event being fired at all! I’ll create a GitHub issue for this refactoring task before I forget. OK, time for a little break.
3:38pm: Back again. I added some console.log
statements because I realized that I can’t use the Chrome debugger to pause the client-side code while the server is still sending events in real time! It messes everything up! I wonder if there’s a way to debug and pause both at the same and sync them up. Anyway, for now, I’ll debug the simple way. Let’s see what’s happening here… For some reason, the game state isn’t actually updating when it should.
4:08pm: Took a break because I had a visitor asking about Danishes again, so I talked about this bug a little bit. I did realize that my console.log
statements were out of order. But I still don’t understand this bug… And I can’t seem to think straight or keep any idea in my working memory long enough to remember what I was going to try next. I’m stuck again, out of ideas. Not sure if this is a good time to try to take another nap or eat some food or go do some pushups or something. OK, I’ll take one more look at it and try sketching a couple more thoughts on paper, and then I’ll take another break if I’m not getting anywhere.
4:16pm: Ah, I think I found it! The “User’s turn is about to end” message is appearing when it shouldn’t, indicating that my outer condition is wrong, and that’s happening because now, once again, turn changes are very unintuitive the way my code is written now. My outer condition now needs to check if the current user’s turn is about to end, not the previous user, because I changed the order in which the state is updated. (It’s hard to explain, haha.) But this should fix it. It’s also a little bit more intuitive now than it was before. (The phrase “If the current player is about to change” is very misleading here, so that’s something to look at again later.)
4:26pm: Yay, bug fixed! Onto the next one, which I discovered just now: if the current user disconnects, the server crashes because the previous player’s ID is now apparently undefined. Hopefully this is a quick fix…
4:40pm: I might have fixed it (not sure), but I definitely found a related bug. Let’s tease this apart some more…
5:08pm: I discovered a problem, which may or may not be the original problem; that previous bug seems to have fixed itself in a way. Weird, lol. Anyway, here’s the new issue: if the current player disconnects before the end of their turn, depending on the number of players and which player disconnected, this can sometimes trigger the next player to try to fork their own Gist, resulting in an error! (This bug isn’t a huge deal, because the situation resolves on the next turn.) I sketched some stuff out on paper, so here’s my attempt at documenting it a bit more clearly:
Scenario for current player disconection bug:
-
Player 0 connects, creates Gist, and Player 1 connects. (Server initializes
previousPlayerIndex
andcurrentPlayerIndex
to 0.) -
First turn ends, Player 0 edits their Gist, and the second starts and passes control to Player 1. (Server’s
previousPlayerIndex
is now 0, andcurrentPlayerIndex
is now 1.) -
Player 1 disconnects on their turn, before their turn is over. (This begins the problematic part, if I continue to do client-side API calls!)
-
Server removes Player 1 from
playerList
, restarts turn timer, and changes turn to pass control back to Player 0. (SopreviousPlayerId
is now undefined becausepreviousPlayerIndex
is 1, butplayerList
now only contains information at index 0!) -
Server emits
updateStateChange
to clients, which receive that undefined value and thus set theirpreviousPlayerId
variable to undefined too! -
At the end of Player 0’s turn, their client tries to fork the Gist because the condition for forking is
currentPlayerId !== previousPlayerId
, comparing the currentPlayer toundefined
, which causes an 422 error reponse from GitHub, because Player 0 can’t fork their own Gist!
This problem doesn’t occur at all if Player 0 disconnects on their turn, because the server can always access index 0 of playerList
array, and the rest works out nicely – one big happy accident! But here’s what I’m wondering about: what if there are more than two players in the game? What other edge cases are there? One definitive fix would be to check just before the end of every turn if the current player is the owner of the Gist or not. That would always work! But for some reason I feel stubborn about this, hopeful that there’s another way to accomplish this without adding one more condition to my code. (My gut tells me I’m just overthinking this because I’m sleep-deprived and my judgement is impaired, and I should probably just add that extra condition anyway.) Well, I’ll throw this into a new GitHub issue as a handy reference, and I’ll push my fix for issue #20 (tracking previous/current players for Gist logic).
5:40pm: I think I finally did all the wrapping up neccessary to set this aside, so hopefully when I return to this, I’ll be able to quickly remember all of the context! Documentation takes a lot of work, especially when my brain isn’t functioning very well, haha. I need some more food soon, and then I’ll need to walk over to the other coworking space where I host my weekly web dev study group. I’m looking forward to catching up with some friends and hopefully doing some pair programming and learning some new stuff together! It’s going to be hard to put this aside for the next couple hours, though. But it’s probably for the best.
…
7:21pm: I’m at the meetup right now, and I successfully deployed the mob coding app to Cloud9 and we’re doing a live user test right now! We have 8 users connected and the server hasn’t crashed yet. I did get one error from the GitHub API, but I already lost the context. No other weird bugs yet. No other edge cases have come up yet.
7:49pm: I’m spending some time pair programming with my friend to learn more about some ES6 features again, and I wanted to make a note to remind myself: return to this tutorial on generators sometime, and find some practical examples that really showcase what makes them useful! I remember they were good for dealing with asynchronous code, but I don’t remember exactly how or why. We also looked at symbols, sets, and some other stuff that I was too tired to remember. But it was fun!
Wrapping up: I had a nice evening tonight, despite being super sleepy. I’m amazed that my mob coding app worked and never crashed! And I have lots of tangents to follow up with later when I feel like exploring some more new JavaScript features.