Groundwork for simple authentication

Client and server can now encrypt sensitive information when necessary.  Uses Diffie-Hellman key exchange to establish the symmetric encryption key, with a default key-size of 1024 bits.  New client-side option 'key-size' to customize this, because generating a 1024-bit key takes too long for repeated runs.

Client now sends a password after handshake.  Encryption is used to protect the password in transit.  If the password is wrong, the server kicks the client off.  Currently the password is hard-coded, so fixing that (adding player persistence) is my next task.

DH takes long enough that it's worth telling the player the reason for the delay.  We now pop up a message in the center of the screen.

Menu generation has been broken up into separate methods, making for cleaner code.

GameWindow constructor now accepts an options-hash, like Game.

Fixed some hard-coded references to the font size.
Simple password pop-up

Now the user can enter whatever password they like.  But we still accept only 'swordfish'.
Introducing player persistence

All we store so far is the password hash.  If the player isn't known, it is created and saved.  No other password management facilities yet (change password, reset password).

Password is hashed using crypt(), client-side, before encryption.  The server doesn't care.  It just compares the password hash it is given with the one it previously stored.  But this does mean that someone who gets to peek at the player storage file will have to actually break the crypt()ed passwords before being to impersonate someone.
Bug fixes, start of grab-block feature.

Fixed a bug where the player could use a build-block to walk away from solid ground.  The build-block is not to be used as solid ground for the player building it.  I forbid this.

Fixed a bug with the initial update -- server was sending an incomplete frame to the new player.

Fixed the class methods in Entity.

Fixed the registry broadcast, so it no longer sends to players who aren't yet logged in.

Fixed specs so they pass again.  Had to be adjusted after the addition of password auth.

I grew tired of the combinatorial explosion, created by telling every entity-type whether it was opaque or transparent to every other type.  Extracted that logic out into a single module, Transparency.

Building is now done by holding Shift and right-clicking.  Unmodified right-click now "grabs" an entity and moves it around, or releases it.  This isn't working properly yet.

Introduced a teleporter, and a destination for it.  This isn't working yet either, as I don't have a reasonable way to tie one to the other.  My plan is to get the "grab" feature working first, then automatically create-and-grab a teleporter-destination when a teleporter is created.
Factor out a module GameClient

Hoping to make it possible to test more of these methods, if I can separate them from the Gosu window.
Moving toward fuller integ test

Removing bits of FakeGameWindow, in favor of GameClient mixin.
More integ test improvements

We've now discarded the faked-up generate_move().  Instead we simulate keypresses in the FakeGameWindow, and that results in moves being queued up and sent, as usual.
Fixed object-moving... mostly

It now works most of the time.  Occasionally it can cause a client-side crash, with the client thinking that two objects are occupying the same space.  Obviously still a bug to find and squash.
Better block-moving (I think)

Instead of sending everything the client knows about the object, it sends only the velocity to apply.  update_from_json() implementations now meet the contract that they accept partial information, and if fields are missing, it leaves them unchanged.  The velocity the client sends is now 1/6 of the delta, intended to compensate for the fact that motions are scheduled for 6 ticks in the future.  Interestingly, this produces a behavior where the grabbed block sort of bounces around the mouse, converging on its position fairly quickly, but with some wobble that I rather like.
Teleporters work now -- fun!

Teleporter is the owner of a Destination.  When an object intersects with the teleporter's center, and the destination is free of obstructions (objects opaque to the teleportee), its location is changed.  Its velocity is left alone.

Creating teleporter destinations is pretty much as I planned: You create the teleporter, and it automatically creates a destination too.  Ended up doing this by adding a hook, :on_create, to the local (ClientEngine's) copy of the add_npcs delta.  As soon as the delta has been executed, thus creating the teleporter and assigning it an ID, the hook (a Proc) is invoked and passed the teleporter as an argument.  The hook can then create a Destination whose owner is the new Teleporter.  There is still a bug here, because occasionally the Destination doesn't get created -- perhaps because the mouse moves during that 1/10 second.  The Destination should really be created exactly where the teleporter is.  Anyway, the Destination has a hook too, causing it to be grabbed right after it's created, as if right-clicked.

Needed a convenient way to find objects owned by another.  GameSpace now tracks this for us.  It has to be the GameSpace, because we keep copying it in the ClientEngine.  Try to put ownership information somewhere else, and your list of owned items quickly fills up with duplicates.

Ditched the flag in deserialization that was trying to predict whether the registry ID was supposed to be included in the JSON, or generated when the object is added to the space.  Logic for this got unduly complicated.
Make snap-to-grid work

With the way grabbed-entities wobble around, even with snap turned on, they weren't ending up in the right place.  Tried a couple things using 'update_entities', but it wasn't working.  So I decided it would be easiest to tell the game exactly what I wanted: actually snap the entity to the grid.  Works like a charm now.

Since I haven't yet found a use for the display of player-score, I replaced it with some diagnostic information about the entities under the mouse.