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.