100daysofcode done, Alpha launched!

Well, it took me, uh, 352 days from September 4, 2018, to August 22, 2019, to complete the 100daysofcode challenge, but I did it. 100 days where I worked on my game. Granted, nowhere near the 100 days in a row, but it was really useful to keep logging things and keep track of what I was doing and where I was going. Plus, reading about my failures and/or challenges now is just funny.

This log entry might be one of my favourites. I am so frustrated, you can practically hear the fatigue in my text.

Anyway, in my last blog post, I figured 62ish hours of work to get things done for Labour Day. Well, my alpha launched at 2am the morning after Labour Day (so close enough), and it was close to 130 hours of work, from June 30-September 2. Note to self: double all estimated hours going forward.

Throughout the last year and more, I have learned a great deal about PHP, MySQL, Amazon Web Services, Docker, Composer, sessions, CSVs, importing CSVs, SSL certificates… A lot of stuff. And now that the game is launched, the adventure is kind of only starting. I’m going to have data! Actual, real, user data!

I also desperately need a LOT more questions, so that’ll be my focus for the next couple of weeks. Why do I need a LOT more questions? Well, the game is currently, shall we say, heavily weighted towards the nerds and geeks of the world. There are 300 Star Trek questions. 100 Red Dwarf questions. Over 100 Doctor Who questions. 100 Back to the Future questions. It’s, uh, not great for a well-rounded trivia experience, so when I looked at the real-time “Correctly-answered questions percentage” at some point today, it was 38%! Like, what?!? Sure, even I don’t get 100%, but I get in the mid-70s, typically. Granted, I wrote the vast majority of the questions, so I do have an unfair advantage, but even taking that into consideration, I thought for sure we’d be looking at closer to 50%.

Well, my dad signed up and played and, because we have a small sample size and because it seems he got a bunch about the periodic table, he brought the overall correct question percentage up to 42% and change. But, the wonderful thing is that I can call up all these stats and find out which questions are hard. Or too hard. Or too easy. Like, okay, I have a question about a hockey player named Blake Geoffrion, who’s descended from not one, but two famous Montreal Canadiens players. Sure. That’s hard. I don’t expect a lot of people to get that.

Should a non-Canadian know the capital of our Nunavut territory? Well, probably not.

But you’d think most people would know Brandon Routh played Superman in Superman Returns, no? Well, still very geeky, I guess.

Anyway, I’m watching the data and will be writing many more questions that are less nerdy in scope. The goal is to get like 5-6000 questions total for the main launch. Then the Star Trek questions would only be five percent of the total questions instead of a whopping 25% or so right now.

That said, props to whoever knew Deanna Troi’s mother’s name, whoever knew who played the Seventh Doctor and whoever knew what game Queeg challenged Holly to play!

All right. Bedtime now. Go play my game!

https://www.riverofkurn.com/

NaNoWriMo done!

Well, I took a really, really long break from #100daysofcode. Like, 31 days. And it was all because of National Novel Writing Month. For the 8th time ever (and seventh time in a row), I achieved the goal of writing 50,000 words in the 30 days of November. NaNoWriMo is something I have attempted sixteen times. I started in 2002 and I skipped 2011, but I’ve at least attempted it 16 times.

Finally, this year, I’ve pulled even. 16 attempts, 8 victories, 8 losses. And, in the process, I crossed the 500,000 lifetime word mark. Solely during November, over these last 16 years, I have written 500,000 words of various novels. It’s astounding to me. That doesn’t count my other writings, it doesn’t count how I’ve continued on with some of these novels. It’s literally just 480 days of writing. That’s just over 1000 words each and every one of those days. Some days, of course, I’ve written as many as 17,000 words (for real) or as few as 0, but the average is about 1000.

So another November comes to an end and another December begins.

And now, I can go back to coding.

I had figured I could absolutely write for NaNoWriMo and code for 100 days of code at the same time.

Wrong! Just wrong. Maybe I had time, but my brain did not like the shifting back and forth from writing to coding, so I did no coding at all during November. Oh God. What is code?

I’m planning to get back to it tonight or tomorrow. In the meantime, I haven’t been posting here about coding because I’ve been posting about coding in my 100 days of code log on GitHub. I do want to spend more time writing about my challenges and problems here, though, in more long-form writing.

Highlights include my doing a Docker course by Bret Fisher, doing #Hacktoberfest, and generally getting through portions of my game. I’m still in the intro in the game, but 90% of the code I’m writing now will be used in the actual game, so… this is good.

That’s about it for now, but that’s what I’ve been up to. :)

So! Much! Progress!

I started doing the #100daysofcode challenge on Tuesday morning. It was a modest start, just did about an hour of stuff before work, since I was inexplicably up at 4:30am or something dumb. And yet, I was able to actually tackle true email uniqueness.

So I had two concerns here:

  1. Prevent people from creating multiple accounts with the same email address. Like, user @ gmail.com is the same as u.s.e.r @ gmail.com or user+game @gmail.com. So I didn’t want that to be possible.
  2. Not mess up people’s given email addresses so I can actually send them communication about the game. What if someone’s not on gmail and, I don’t know, the period between their first and last names actually matters? What if someone set up a mail filter for the game using the plus sign? I definitely wanted to send mail to the address they had provided, but I didn’t want to have that address be the unique address.

What I did is save the given email address, but also normalized it (strtolower). Then, I made it unique by virtue of, well, here’s the code and I’ll explain it below.

Code to normalize and sanitize emails
Code to normalize and sanitize emails

Breaking the email up into its component parts of username @ and domain by exploding on the @ sign made things easy for me to modify just the username and then reconstruct the email address as a new variable called $uniqueEmail. So I then explode on any + sign. Then, by focusing on the first part of the resulting array ($sanitizingUsername), which is to say anything before any + sign that might exist in it, I’m now dealing with just the username. Then, I replace anything that isn’t a-z, A-Z or 0-9 with nothing (“”). Then, I rebuild the email address with the sanitized username I just created, concatenated with the @ sign and the previously-split apart domain.

This allows for an email address like julie.m.a.r.t.i.n+game @domain to be viewed by my program as juliemartin @domain for the uniqueEmail() check function, but, I’m still emailing that specific julie.m.a.r.t.i.n+game @domain address for any game communications.

Bonus: julie.martin @domain works fine, as does juliemartin @domain. Exploding on a + that may or may not exist has no ill-effects if it doesn’t.

So that’s how Day 1 of #100daysofcode went.

I’m logging my daily progress in a log file on GitHub, so you can check it out here:

https://github.com/juliebugmtl/100-days-of-code/blob/master/log.md

If you read it, you’ll see that, on day 1, I had neglected to change my email variable to the all-important email1 when I made the change for JavaScript validation purposes. I snag a bunch of information from the user’s submitted form on POST, but I forgot to change email to email1 when I changed the form ID/name for that field.

Guess what else I forgot to change?

The password field. I’m validating the password too, so I made two fields, one called password1 and one called password2 but at no point in time did I change my snagging information from the POST to snag from password1, so it was still pulling from the (non-existent) password.

Do you know how much fun it is to try to log in with a password that doesn’t actually exist when you don’t know it doesn’t exist?

Answer: not a lot, let me tell you! You can read about that in Day 4’s entry.

What’s hilarious is that the issue with the password storage and hashing comes on the heels of resounding success on Day 3 where I basically coded a whole function blindly and, to my utter shock, it worked perfectly on the first try.

Day 3 was dedicated to getting SendGrid to send out emails for validation and for blacklisting and I’d set up the validation workflow on Day 2 and finally got the link mailed out on Day 3, whereupon I then coded the blacklisting function without testing it at all until the end and it worked.

But Julie, you may ask, why the hell would you use a blacklist function? Surely people who are signing up for your game are, you know, actually interested in playing your game, no?

NO.

I have a Gmail address. I’ve had it since April 29, 2004. Unfortunately, being an early adopter means that I got my first choice of email address. As such, everyone else who wants that email address has to modify it, like if it were julie @gmail (it’s not), then everyone after me had to do julie1 @gmail or juliea @gmail or julie01 etc, etc, etc.

Do you know how often people forget that they have appended something to their email username?

It’s often.

Like, multiple-times-a-day-often, sometimes. Certainly multiple times a week. In the past, I have received emails from banks, airlines, universities, family and friends of other Julies, real estate agents and, the worst of them all, REPUBLICAN CAMPAIGN NEWSLETTERS.

All because these… these… these bozos, can’t properly understand what the hell their email address is.

So many of them are important messages so I try to write back with a canned response going “hey, wrong person” basically.

Just slightly above the Republican campaign newsletter crap I get, in terms of annoyance, is the stuff other people have signed me up for. Not actually me, obviously, but like an acquaintance, friend, family member of someone else out there has signed up my email address for Blue Apron in the past. Multiple times. This woman Susan signed me up to Blue Apron in March of 2016. Three. Separate. Times.

Thankfully, they listened to me and blacklisted my address, but the point here is that I don’t ever want anyone to have to deal with that pain in the ass when it comes to my game.

Hence, a blacklist system. My blacklist function does the following:

  • looks up the user
  • snags the unique email
  • adds the unique email to a voluntary blacklist
  • deletes the user
  • deletes the token associated with that user

You can only get to the blacklist page by virtue of clicking the link in the email sent. The URL adds the token and the user ID in its parameters, so it’s like domain.com/blacklist.php?t=TOKENHERE&user=USERIDHERE. If the token doesn’t match the user ID, it won’t work. And I do refresh the tokens once someone validates, so they can’t accidentally delete themselves.

Gotta say, I’m pretty pleased with that functionality. Plus, if someone wants to register and uses a blacklisted email, they get asked to email me to remove the email from the blacklist. If the email doesn’t match, then I won’t remove it, simply.

So there’s been a lot of progress. Registration is effectively done. Login works. Time to clean up and refine All The Things and then work on:

  • identifying an admin user
  • adding menu options for logged-in users (admin and not)
  • working out what the hell logged-in users should be able to do! hahaha

Some planning to do, for sure, but this stuff is getting interesting. Keep up to date with me on Instagram, where I’m posting stories when I sit down to code and post a pic when I’m done working for that day.

I built a computer!

On Monday, August 27th, for the first time in my life, I assembled a computer from scratch. It even booted on the first attempt! I documented it on Instagram, in my stories, and here are some of the pics, including some of the comments I made on the pics.

Photo of my new case
And so it begins.
Something like 8 power cables from the power supply.
So. Many. Power. Cables.
Power supply installed.
Power supply: check!
Motherboard map.
Honestly, this was the most useful piece of paper.
The spot on the motherboard for the CPU.
The seat for the CPU. This was the most nerve-wracking portion of the whole thing.
Image of the CPU.
More than $400 right here.
CPU in its seat.
Seated!
CPU locked into place with the frame.
Locked in.
Fan installed in the front of the case.
Installed the fan for my CPU cooler.
Container of Arctic Silver thermal paste with the CPU and motherboard in the background.
Okay, this was also terrifying — applying thermal paste to the CPU in preparation to put the cooler on top.
Corsair CPU cooler in place on the motherboard.
Cooler installed!
Power cables plugged into the power supply.
Remember all those power cables? hahaha
Video card seated in motherboard.
Video card in!
Shot of the left side of the case, showing the interior of the computer.
Done!

There were a couple of small issues, though:

a) The case fans weren’t running. I had completely forgotten to hook them up to power. There was this wire coming down in the case with the old four-pin system and I’m like “what does this even connect to??” since it wasn’t terribly obvious, so I’d ignored it. But I knew to hook up the peripheral power cable to the peripheral power source in the power supply and then connect that cable to the four-pin wire. So that wasn’t a big deal. And, it being 1:30am when I finally booted the thing, it was something I could deal with Tuesday.

b) My SSD wasn’t recognized. I’d actually thought this might be an issue. My friend, Andrew, who was on the phone (well, FaceTime Audio) with me for like, 90 minutes on Monday evening) had indicated that he wasn’t sure that was the right slot for the SSD M2 and I looked at my motherboard map (YES THERE WAS A FREAKING MAP) and saw that the other slot was underneath some branded plastic casing. So I kept it in the original slot and figured that if it wasn’t recognized, I’d fix it the next day. Sure enough, wrong slot.

SSD in the wrong M2 slot on the board.
But this ended up being the wrong slot.

So on Tuesday, I connected the power for the fans and then uninstalled the video card, pulled off the casing hiding the M2 slot, moved the SSD there, then reinstalled my video card and crossed my fingers and it booted into the Windows setup (courtesy of my USB key with the Windows installation on it) and it recognized my SSD!

I installed Windows (that was a whole Thing, too, but eventually got resolved) and then started the painful process of copying stuff over. Fun fact: I’m still not done copying and installing things, but the vast majority of important stuff is done. I haven’t yet installed my writing program (Scrivener), don’t think I’ve installed Dropbox, etc, but most of my actual files are copied — creative writing stuff, my programming stuff, photos, other documents… That said, my old computer is still sitting on my dining room table, hooked up to the router via ethernet cable and the drives are shared, so I can just copy as needed.

Overall, it went a LOT better than I thought it would. I also engaged in some light cable management on Saturday, and this is the end result.

Photo of my workspace.
Clean! Sort of!

In terms of the game, I haven’t done a ton of stuff, owing to, you know, computer building and copying stuff over. That said, my dev environment with Docker used to take in excess of 8.5 minutes to boot up on the old computer.

It takes 27 seconds on the new one. Just let that sink in for a sec. Or 27.

But even without that blazing speed, I’m pleased to say that I solved the problem with the password strength script last weekend! The issue was that I was calling the script too early, so I shoved it to the bottom of the form and it worked just fine at that point. Since building the computer, I’ve made some modifications to it and to my own registration validation script, so now I have JavaScript checks for:

  • username length (must be at least 5 characters, no more than 16)
  • username allowable characters (a-zA-Z and 0-9, but can’t start with a number)
  • password1 matching password2 (you know, your typical re-enter to verify password thing)
  • password strength must be at least “good”, as per the password strength check
  • email1 matching email2 (verify your email)

Plus, I have PHP checks for:

  • username uniqueness
  • email uniqueness (this still needs work)
  • spammer/disposable domain emails
  • disallowed usernames (admin and such)
  • profane usernames
  • voluntary blacklist (to prevent people from accidentally getting signed up because how are people so stupid as to not know their own email address? Trust me, this happens to me constantly.)

I also added some styling. Just some basic CSS to make sure the form looks nice as well as making sure error messages are easily shown.

Screenshot of registration screen where the user is asked to input a stronger password.
See? Error messages!
Screenshot of the registration form, Sublime Text 3 and my Docker container log.
Here’s a better pic.

So that’s what’s up with me. I’ve got a four-day weekend this weekend, due to Labour Day on Monday and I also took the Friday off, so it’s been a pretty glorious weekend for me. My living room is a disaster, so my goal tonight is to clean that up and put away all the documentation for all my components, my spare cables, spare screws, etc. Then, while my old computer will still be on my dining room table, at least it won’t be surrounded by an insane amount of empty boxes, ripped plastic bags, electronic cables and screwdrivers and such.

More updates as I make progress in the game, certainly. My next task is to clean up the unique email check and move on to sending out validation emails. And then login stuff! Man, this stuff is really starting to come together and I am psyched, dudes. Psyched.

A new computer and game updates

Earlier this year, I made the decision that I needed a new computer. I mean, this decision has been in the making for a while. I bought my last computer from Dell in 2011 and have upgraded it a few times — added RAM, swapped video cards and such. But it takes forever to load programs. Even loading Chrome can make the computer temperamental. Multitasking? Mostly out the window.

The CPU is an Intel i7 930 quad core and, at cpu.userbenchmark.com, it ranks at 330th of 1122 CPUs ranked. I mean, not terrible, right? Still, I can’t do most of what I want to do as well as I’d like to do it. So I decided, with the help of a friend of mine, that I’d not only buy a new computer, but I’d assemble it myself.

I bought all the parts on Tuesday night and they’re starting to arrive today (Thursday).

The new CPU I’m getting is ranked 8th/1122. Just a bit of an upgrade, eh?

I went all-out on this. We’re talking 16GB RAM (expandable to 64), 500GB SSD and 1TB HDD, GeForce GTX 1080Ti graphics card, SLI-compatible motherboard — and I suspect the 1080Ti will drop (eventually) so it won’t be quite so expensive to buy a second one later on, since the 2080 and 2080Ti have been announced. No peripherals, since I’m just going to use the ones I already have. And I also want a new monitor to replace one of my two (and I’d give one of them to my brother). So the new rig will be able to handle video card upgrades, RAM upgrades and such.

My only worry is nuking my CPU by accident, hahaha oh god. Anyway, I should be assembling everything late next week and I’m super excited to be able to open programs like Photoshop while Spotify is open, or open Discord while playing a game with my brother and not have my ENTIRE COMPUTER FREAK OUT. Even opening my writing program, Scrivener, can make everything chug and then I’ll realize that oops, I left Docker open from the last time I was coding and maybe THAT’S the problem.

So, coding.

When last I wrote, I was dealing with registration validation of things like unique usernames and such. I’ve moved from the PHP validation (mostly — still something left there) to JavaScript/form validation. I’ve used regular expressions (oh, regex, I did not miss you) to ensure that usernames are only 5-16 characters and don’t begin with a number. I’ve made sure that if your email1 and email2 fields don’t match that it alerts you to that. I’m currently working on an implementation of password strength based on the zxcvbn library by Dropbox. I’m having trouble with it and, of course, if something is wrong with the JavaScript, the user gets registered anyway because the validation won’t occur and, since I’m only doing uniqueness checks in PHP, it won’t stop “test” from getting registered unless I’ve already registered “test”. (Yes, I’ll have to ensure that JavaScript is running on the user’s browser in order to play the game, good times.)

It’s slow going with JavaScript. It’s definitely the language I’m least familiar with after my web programming diploma. Like my current error is, I’m sure, a variable scope problem, but I’m not really sure how to fix it. Guess I’ll figure it out going forward.

So once the JavaScript validation is done, I want to go back to the PHP checks and look at making sure I have a unique email, which will mean stripping all the dots and plus signs (and everything after a plus sign) from addresses, to ensure that Gmail users, for example, can’t sign up for multiple accounts. In truth, it’s not the end of the world if there are multiple user accounts, but I’m hoping for some measure of cooperation in the game and that would make things too easy for people.

So I figure I need to hold on to two email addresses — the original one and then a unique check one, and run the unique checks on … both? I guess? I still need to figure out the logic there, which I’ve been putting off, if I’m honest, because I haven’t quite thought it all the way through yet.

Also to do:

  • add validation email with validation link
  • ensure validation email has opt-out/blacklist link

And then, I think I could move on to login. Which is also terrifying to me, but I’ve ostensibly done it before, in PHP II, so we’ll see where that goes.

From there, I’ll certainly want an account page so people can:

  • change their email address on file (but not their username)
  • reset their password
  • invite a friend…? (This may only be a v2 thing, but we’ll see)

THEN, I might be ready to actually create a flow for people to actually play the game. Part of the issue here is that I’m still working on story. So far, I can pull trivia questions from a database and keep score. This is awesome. It was hard work to get to that point! But there’s this whole story I vaguely have in mind, which is going to mean keeping track of different variables and that will affect how the gameplay works. I also want to build in a maintenance function (one that runs automatically but also one I can run manually if need be) and I want to add in all kinds of admin functions like being able to add, delete and modify users through an admin frontend rather than mess with the database directly. Same with adding, deleting and modifying questions. I also want to code something to back up my databases on command and reset the game.

Still, once I get the login going and allow for a flow for people to get to the game, maybe I’ll send out a few invitations to some friends to try it out, just to see if they can break the (very basic) functionality I have. If they can break it, I’ll have a lot of work to do before I can think about bringing in the actual gameplay I have in mind.

Whew. So that’s my update.

And while I’ve been writing this, I got notification from Intelcom that my delivery consisting of my new:

  • CPU
  • SSD
  • HD
  • power supply
  • motherboard

is arriving in the next ~3 hours. If everything that should be delivered today does arrive, I’ll have had deliveries from UPS, Purolator, Canada Post and Intelcom. In a single day. And I feel for Purolator, because they’re deliverying three packages to me, apparently, including my CPU cooler and my RAM. UPS is bringing me my video card, while Canada Post is bringing me the last part of my nephew’s birthday gift, which is a Spider-Man t-shirt.

Anyway, I should get my day started, despite the fact I just want to go back to sleep. I’ll have pictures of stuff next time, no doubt!

The Continuing Stooooory of a Nerd who Can’t Stop Coding

(That title, just so you know, should absolutely be read in the voice of the voiceover dude from The Muppet Show‘s Veterinarian’s Hospital.)

In the week and a half or so since my last entry, I’ve been working on my registration branch, trying to do All The Things ™. Primarily, I wanted to check the username for uniqueness (well, a close approximation thereof) and I wanted to check the email address against a list of domains known to be used by spammers. The username check went pretty smoothly, all things told. I’m using this:

function checkUniqueUsername($normalizedUsername) {
echo "
Unique Username Check
";
// Get the database instance
$pdo = database::getInstance()->getConnection();
//Construct the SQL statement and prepare it.
$sql = "SELECT COUNT(username) AS num FROM users WHERE username = :username";
$stmt = $pdo->prepare($sql);

//Bind the provided username to our prepared statement.
$stmt->bindValue(':username', $normalizedUsername);

//Execute.
$stmt->execute();

//Fetch the row.
$row = $stmt->fetch(PDO::FETCH_ASSOC);
if($row['num'] > 0) {
return false;
} else
echo 'Username is unique.';
return true;
}

Basically, this is a function that is called from the registration page that grabs our database connection info and then asks the database to perform a count of usernames on the users table where the username is the bindValue of :username (meaning $normalizedUsername).

In MySQL, this will return a single-column table, where the column is named “num“. And it will say 0 if there are no entries with that queried username and it will say 1 (… or more?) if 1 (… or more?) exists. Then I’ll return false if there are any entries and return true if there aren’t.

Perfectly logical, perfectly reasonable, works just fine.

So imagine my utter shock and consternation when I tried almost identical code to check the domain of the submitted email address and nothing was flipping showing up in my results. Like, absolutely nothing. Dumping out $row with a var_dump($row) returned a boolean false. What on earth? From my Instagram:

Domain on email is good. But why?
But whyyyyy is the domain gooooood?

You can see in the image that I tested out the query on the MySQL command line and I got 1 row. So why was I getting a boolean false and why wasn’t I getting my 1 count in the row?!

Obviously, the MySQL query, when I typed it out manually on the command line, was fine. And I had logs running, so I could see that the bindValue was working and was fetching the domain in question. So what was the issue?

I took the weekend off because I went to my friend’s wedding in Ontario — I was a bridesmaid — and, rather than sleep like the dead on Sunday night, I coded. I was getting ticked off. Something wasn’t right.

I tried all kinds of things — different bindValue. No bindValue. Different files. Taking the checks out of the functions. Putting them back in. And why on earth was the username check doing just fine but the domain blacklist wasn’t?!?

I finally figured it out on Tuesday evening. I was googling stuff, for the billionth time, and found this issue on GitHub. In it, down a bit, it says:

Running a GRANT command to allow SELECT for this user solved the problem

I froze, the realization crashing over me like a wave. But of course the user has permission, my queries worked… just… fi… I was logged in as root. OH MY GOD, I WAS LOGGED IN AS ROOT.

Fun fact: the user didn’t have permission on the email blacklist table. Just the user table. That’s why everything was empty! That’s why I was getting a boolean false! And I was consistently logging in to the MySQL database as root, which is why those queries worked! I quickly made an edit to my creation SQL file and then manually added permission for my user — voilà. It worked perfectly.

It worked so well that I added the following checks:

  • email blacklist (the original one I’d been having trouble with)
  • disallowed usernames (like admin, etc)
  • voluntary email blacklist (for people who want to opt-out and don’t want to receive invitations to the game)

I still need to compile a list of profane words to prevent people from using them as a username, then add that check.

I also want to add in a unique email check, which will require:

  • storing the email address entered and use this for any communications
  • transforming it all to lowercase for uniqueness, so JULIE @ is the same as julie @
  • stripping periods from the username portion of the email address (julie [at] gmail.com is the same as j.ulie [at] gmail.com is the same as j.u.l.i.e [at] gmail.com) to check for uniqueness
  • stripping any plus signs and anything that follows up through to the @ sign (julie+test [at] gmail.com, for example, is the same as julie+test2 [at] gmail.com) to check for uniqueness

I also want to implement some JavaScript checks, which, shockingly, I actually know how to do:

  • Username between 5 and 16 alphanumeric characters in length
  • Password strength
  • Email address (1) matches email address (2)

Once all the checks are implemented, I want to send a verification email to the email on file, which ideally also holds a “delete me” link that, when clicked, will trigger insertion of that email address into the voluntary blacklist. Of course, if they click the verification link, this will activate the actual account.

Plus, I probably want to ask permission for whether or not I can add them to a mailing list. That seems useful.

So, I’m feeling pretty awesome for realizing it was a permissions issue. I’m feeling slightly less awesome for having made the mistake of testing with root versus testing with the actual user in question, or having forgotten to give the user the right privileges in the first place, but I still fixed it.

And that feels pretty great.

Game update!

I have made a great amount of progress on my game so far, at least as I see it. But before we get to that — and we will! — I wanted to show off my new living room workspace. I’m fortunate to live in a 4 1/2 (two bedroom) apartment by myself, so I have a spare room. The spare room has been the office for just about a year, now, and that’s where I primarily do, you know, work. My previous remote-working experiences have shown me that I’m way too likely to work too hard and too long if the work area is in my living area, so I keep work and life as separate as possible. (Although this past week, I’ve worked exclusively from my living room because I have an air conditioner in here but not in the office. As a result, I’m pretty sure I worked about 42 or 43 hours this week. Oops?)

As for my gaming, my writing, my coding, all of these things have always happened in my living room where my desktop computer is located. Recently, I got rid of the desk I’d had for like almost 20 years, in favour of an identical desk to the one I have in the office. Then, I also got a VIVO dual monitor, desk-mounted stand for my screens in the living room, as well as a boom arm for my Yeti Blue microphone. Oh, and a great little under-the-desk headphone holder, which I’m loving.

Here’s what it all looks like.

Desk, dual monitor stand, boom microphone stand, headphone holder
New living room workspace setup!

And I’m adoring it. Sure, the wires are absolutely revolting, but I’m not going to cable manage anything just yet, because I’m on the cusp of getting a new computer. I’m going to be attempting to assemble it myself from the various component parts. I’m not much of a hardware person (although I’ve installed RAM, a secondary HD, a primary HD and a video card), so my friend, Andrew, has sworn to me that he’ll FaceTime me and walk me through the assembly if I need a hand. So with a new computer imminent, cable-managing right now is dumb. Once that’s all set up, though, you can bet your bippy I’ll be doing my best to hide all those unsightly cables.

So the new workspace is pretty great for the time being and having the monitors mounted to the stand is AMAZING. I’ve reclaimed so much desk space I hadn’t even realized I was missing. I genuinely like sitting here and my HUE lights just make me happy. :)

Okay, so that’s the update on the workspace in my living room. Now, on to the game stuff!

Once upon a time, I used to chat on IRC (Internet Relay Chat) pretty consistently. Like, I’d go to bed for the night, but just leave my computer and IRC client running, and I would always have my IRC channels in the background while I was working. I mean, I was working as an independent contractor while I was the Chatting Online Guide at About.com, sooooo I’d have to say that was pretty reasonable. Anyway, I made friends online, as one does, and several of them were in Australia. Australia has a pretty significant time difference between the East Coast of North America and, well, virtually anywhere in Australia, so getting to chat with the Aussies usually meant someone was up late or up early.

One day, I woke up and stumbled to my desk and blearily tabbed over to mIRC and, in one of my channels, one of the Aussies had been freaking out several hours prior and had eventually gone all-caps in her exclamations and said “WAKE UP I NEED TO ENTHUSE!!!!!!!”

I have had this deep need to enthuse about my game for a couple of days now, and I don’t really have a lot of people in my life who, a) care about this little project of mine, and b) know anything about programming. So that’s why I’m enthusing here, I guess. ;)

Updating my personal site last week is what got me itching to code this week, because I’d played around with self-submitting forms, which is currently how I’m testing out question/answer functionality. So I sat down to try to finish getting PDO to work for me on my test page.

I’d also made a change to my docker environment in that I created three database users that are limited in their permissions — one can edit the users table, one can edit the tokens table and one can read the questions table. So that involved making sure I was using the limited questions user rather than the root user, too.

For some reason, my MySQL PDO command wasn’t asking one question in isolation, which pointed to an issue with my select statement, which, in turn, pointed to an issue with how I was “randomly” selecting a question row. Once that was sorted, my question was being asked! Hooray! Being able to pull in that question from the array felt pretty great. Of course, in order to get to the point where I was able to replicate what I’d done when I was messing around back in January, I was going to need to be able to check the answer and inform the user if they were right or not.

So my first major issue was “how to carry over the user’s answer to the page upon submission?”, but it ended up not being that hard because I remembered I could just grab the $_POST[‘playeranswer’] data and stash that in a variable. I also strtolower() it, because all the database answers are in lowercase, so for a match, the cases need to match, too.

That worked, but if I wanted to also display the last question, along with its correct answer, and the user’s own answer (plus the correct/incorrect validation), I was going to need to pass along the row from the last question. So I pass that along, currently, in a hidden form field so I can snag that from the $_POST[‘lastquestionrow’] field.

When you load the page on a GET, you don’t have any of that, but when you load it on a POST, you now get last question row and player answer stored into their proper variables. Then, the database is called and we start getting into the fun stuff:

  • hey, here was the row for the last question, please display for me the question and the answer
  • also here’s what the player said, so show that
  • if they match, say so and grant the player a point, announce new score
  • if they don’t match, say so, don’t grant a point, repeat score
  • if they don’t match but player has no score, say so, don’t grant a point and let the player know they currently have no score

Now, for the most part, this worked just fine. However, I realized, after having done this, why my teachers in my certificate always recommended I go for a does not equal comparison rather than a strict equality comparison. It’s because a strict equality comparison is messed up, yo. I noticed, through my testing, that I was occasionally getting false positives while going through answers that had more than one possible answer and if one of the possible answers has more than one word. So like, what’s the capital of Canada was fine, because that’s Ottawa. But for asking about the capital of the province of Quebec, that’s where issues cropped up. Your choices are “quebec” or “quebec city”. And yet, somehow, with my earlier logic, “city” was acceptable.

In another example, “flash” was an acceptable answer instead of “barry allen” or “the flash”.

And, in the weirdest example, the name “david” was accepted when neither answer was even remotely close to “david”.

So there was something wrong with my comparison logic. At first, I thought it was just that I was using == instead of ===, so I thought okay, stick in a third equals sign there and I’m good! That’s what makes it a strict comparison, right? Right. Should be sorted!

Alas, no, the issues continued, but I went to bed.

The next day, the strange bugginess was in the back of my head all day long. When I sat down to code a bit after work, I really could have rewritten the logic to test for a failed match rather than a positive match, but I was curious as to what in tarnation was going on. How is “david” considered equivalent to one of two completely unrelated names? (Adam and Carter, if you’re curious.)

I sat down and did more robust testing, writing down all the weird results. So I pulled out my Programming PHP O’Reilly book and looked up comparisons, because everything I’d googled was telling me that === should do the trick.

And this is where I learned about the string compare function: strcmp()

The way I understand it is that strcmp() will compare two strings and will return a 0 if they’re equal. So, this then meant I needed to tweak my logic again. A simple “if $string1 === ($string2 || $altString2)” type of statement wasn’t going to work. I needed to funnel the player’s last answer into a use of strcmp() and compare it with the last answer as well as the last alt answer, if the alt answer existed. So here’s what I ended up with.

$mainVerificationCheck = strcmp($lastPlayerAnswer, $lastAnswer);
if ($altAnswerExists == true) {
$altVerificationCheck = strcmp($lastPlayerAnswer, $lastAltAnswer);
}
if(($mainVerificationCheck === 0) || ($altVerificationCheck === 0))

Basically, that says, here’s the mainVerificationCheck variable, which is comprised of a value returned by using the string compare function. By default, this compares the variable containing the last player answer as well as the last correct answer from the last question. Additionally, if the last question had an alternate answer (like in our Quebec City/Quebec question), create a variable called altVerificationCheck and use the string compare to test the last player answer against the last alternate answer.

Then, if the main check returns a 0 (which means a match) or if the alt check returns a 0, then do stuff. (In this case, it’s notify the user they’re right and their points have increased.)

I’ve been playing around with Instagram stories and captured my bug hunting and fix. Here are the pics from that.

Funky bugs in my game.
Bug hunting!

 

=== or strcmp()?
strcmp()? What is this?

 

Fixed my comparison issue
strcmp() for the win!

 

So yeah, I got a lot done over the last couple of days. I worked on stuff on Thursday night and then Friday night well into Saturday morning. And I was going to start in on registration and user creation tonight — my Docker containers are up and running, I have a fresh branch of master that I’ve checked out to a new branch called registration, but I’ve had a headache pretty much all day Saturday and it’s like, four in the morning on Sunday, so I should probably go to bed. I’d have gone to bed way earlier but my headaches are largely sinus-based, so lying down is actually super painful when my sinus cavities are full. Gravity helps the head to drain, I guess!

So my headache meds seem to be kicking in and I probably won’t be able to get back to coding until at least the week of the 6th, because a friend of mine’s getting married on the 4th, and I’m a bridesmaid, so I have a lot of stuff to do this week, then I go to Ontario for the wedding on the weekend. Looking forward to finalizing my computer build either this week or next. I think the new computer will encourage me to do even more coding, and possibly not at ridiculously late hours of the night!

When I started talking about this project, back in January, I was thinking an alpha version might be available in last September.

… yeah, not sure that’s going to happen, hahaha. The base functionality of questions and answers? Sure. But not the game itself.

Anyway, the next task is going to be user registration and login, so I can start saving stuff to the user table and saving stuff to sessions. I want to not have repeat questions, so I’m going to save a certain number of questions in the session (I think?) and check that when doing the random question picker. I think.

Maybe. We’ll see how this goes. I’ll definitely keep you, whoever you are, dear reader, posted on my progress.

 

The Great SSL Weekend Extravaganza

This past weekend, I embarked upon a task of positively Herculean proportions. With Chrome 68 coming out and the continued emphasis by Google to ensure HTTPS is the default on sites, rather than HTTP, I decided to go ahead and add SSL certificates from Let’s Encrypt to, oh, 13 domains that I host, both for myself and for friends and family. (I had a lot of fun posting to my Instagram story about it, too!)

I’d done a test run with one of my domains earlier in the year and, though it wasn’t without a challenge or two, it seemed to go just fine.

The trouble, of course, came when dealing with various WordPress sites (including this one!) because how do you change ALL THE LINKS in ALL THE POSTS and ALL THE PAGES to HTTPS from HTTP? Well, thankfully, there are database queries and free plugins.

While I’ve generated and installed the certificates for all thirteen domains, one of them basically has no traffic, so I haven’t updated that WordPress database yet. Then, there’s another one that is just too daunting for me to bother with right now — two WordPress installations, plus my own hand-coding elsewhere, plus redirects galore… So I said to hell with it for now. I haven’t forced SSL on for either, but both are accessible via HTTPS. When I have more time, I’ll go through them both and force SSL on, after dealing with updating the references in the databases.

In addition to that, I redid my personal homepage at juliemartin.org. Not only does it also have SSL enabled (and forced), but it’s also mobile-responsive! It’s not as pretty as I’d like it, but it’s not bad for a quick redo. I also used PHP Mailer to create a contact form. Additionally, I put in a hidden field that humans can’t see (owing to CSS) as a honeypot for spammers and if that field is filled out, then the form fails to send.

It was in the creation of this form that I realized how much the form’s enctype matters, because if you choose the wrong one, it’s entirely possible that your $_POST array will be completely empty. It worked fine on my own local environment, but I suspect it was getting wiped out by my host’s mod_security in Apache, because I did get a couple of 406 errors, though they weren’t consistent. I spent a good amount of time on Saturday evening wondering why the hell my $_POST array was empty, checking everything, from making sure the form was set to POST to double, triple, quadruple checking the field names.

In the end, the enctype was the issue and everything ran beautifully once I fixed that up. I used Postman to test out a variety of differences between my browser’s POST request and finally found it.

Then I was able to easily code in the hidden field and the failure if there’s anything in it. (Fun fact: I’m pretty certain that the placeholder text in a form does, indeed, count when your conditional is if (!empty ($_POST[’emailaddress’]) though it would most certainly require more testing.)

Overall, I did a crapton of stuff this weekend, I learned a ton of stuff and my brain hurts sufficiently.

In other news, making moves towards ordering parts for a new computer. I’m getting pretty excited about it, not gonna lie.

Next post, pics of my living room setup, I promise. I just got a desk-mounted dual monitor stand from Amazon, which I am in love with and really makes my workspace look awesome.

Until next time!

PDO, why must you hate me so?

My glorious four-day weekend is just about over and I did a bunch of coding and no writing. I did, however, sit happily in air-conditioned bliss. When the humidity makes it feel like 46C (114F), you kind of don’t want to move, much less leave an air-conditioned room.

I made a lot of progress on switching things over to PDO from mysqli, but it’s still not done. I’m still flailing around, trying to figure out wtf the problems are that I’m experiencing. Here are some of the issues I’ve had so far this weekend, all of which are resolved:

  • I had an errant space in <?php. And that borked everything that came after it. Why I didn’t see the space between the question mark and the p is unknown.
  • I couldn’t figure out how to copy a file to the directory above (and thus, outside) the web directory in my PHP Docker container.
  • I further couldn’t figure out how to actually edit a file in that directory once I was able to copy it, but then inserted a nano install in my Dockerfile. (My original linux/unix experiences were heavy on pine, tin and pico.)
  • I decided to actually create and use limited-access users for the database calls (as one should) and then had a hell of a time ensuring they could connect to the MySQL Docker container.
  • Even with limited-access users being valid users, I couldn’t run a simple select statement.

All that’s been taken care of. Now it’s a matter of actually writing the code to get a question pulled from the database, displayed to the user and then matching their typed answer with the answer in the database. I gave it a couple of half-hearted attempts tonight, once I finally got one of my database users to properly make a select statement, but figured I’d accomplished enough.

A lot of it this weekend was making sure things were being done right. It’s hilarious how I’m still not to the point where I was when I was writing things with mysqli. Just need more time. This is not going to be simple and it’s not going to be solved quickly.

Speaking of “quickly”, the process of starting up my Docker containers is not so much with the speed. I obviously desperately need to update my computer, because there’s not a lot going on in that startup process, but it takes — and I timed it — upwards of eight minutes to get both the PHP and MySQL containers running. Now, this didn’t pose a huge problem in PHP II, because the containers were configured by our teacher, so we literally did docker-compose build and docker-compose up to start up and then docker-compose down when we were done. So it’s one thing to spend eight minutes or thereabouts waiting for your containers to come up when you do it once, but a whole other thing to do it numerous times in a day. Come to think of it, there’s probably a way to refresh the configuration/etc without killing the containers and starting anew, but that bears more investigation.

And speaking of refreshing configurations and such, I spent a couple of hours updating my IRC server, services and proxy monitor on Sunday. I’m a terrible person because I don’t update often and, when I do, it’s usually because a new major release has come out. As such, I generally have to start from scratch because various updates are incompatible with my existing  configuration file. But not this time! So I backed everything up (like, twice) and then proceeded to install over my IRC server with the new version and then restarted the server and that worked fine. Same with services. Same with the proxy monitor.

I’d set aside three hours for it and it barely took two — and that included all the backing up and reading I was doing to ensure I knew what the hell I was doing. It went so smoothly! I fully expect the earth to open up and swallow it, now.

And on that note, I should go to bed. Back to work tomorrow and I’ll be working from my air-conditioned living room instead of my non-air-conditioned office.

I’ll post some pics of my living room desk with and without my work laptop next time! I’m really loving how it’s turned out.

Back to it?

Tonight, I spent a couple of hours playing with code for my game.

Not that I got particularly much done, mind you. Most of the time was spent flailing wildly on the command line, trying to remember the magical combination of words and characters to push something to GitHub, or remember how to connect to my Docker containers.

I’m trying to switch from using MySQLi to PDO for the database requests, as I’d mentioned back here. It’s the most robust method, especially when you use prepared statements. And, for my game, the majority of database calls will be to a question/answer database, so prepared statements make total sense because they’ll be reused a ton.

Programming is really a skill you need to practice to retain, just like any other language. It’s been about three months since I’ve done any kind of programming at all and more than that since I’ve touched PHP. So I am definitely out of practice and need to spend time working on that some more. So the PDO thing was what I wanted to tackle tonight. It didn’t happen. But I feel better for having tried out a few different options and learning from those attempts.

This weekend is a four-day weekend for me, thanks to a company-wide holiday on Friday and observing Canada Day on Monday. My goals include a lot of sleeping, some writing, some coding and a lot of sitting with my air conditioner on high. It’s supposed to be more than 40C (104F) with the humidity, so that’s disgusting. I love my city, but living in a place where it gets down to -40C and gets up to +40C is kind of stupid.

Anyway, it’s late, so I should get to bed. More updates as events warrant!