Twitter is having a little trouble. I’m going to back up some good tweets here. I really did love one liners as a medium. Twitter offers a way to download your tweets, but it’s a long running asynchronous process so I have no idea if it’s going to work. And anyway, it’s neat to look back on this stuff. I suppose I should also mention that I made a mastodon profile but I can’t promise I’ll use it.
A good #codereview is like an all-you-can-eat buffet: an opportunity for personal growth
To keep things light, all future
discussions of employee salaries will use “smackeroos” instead of dollars.
29 Aug 2022
Be @arborday > Send entire packet of mail made of dead trees > I mean we’re talking about bookmarks, a calendar, personalized correspondence labels, a survey, and a certificate of completion for the survey > At least I’m thinking about it. Well played.
24 Aug 2022
#Security Thought of the day: Piccard set his auto destruct PIN to 0000
It’s no secret that I’ve been captivated by trying to make vehicle driving feel good for as long as I’ve been programming. Initially Hovertank 22 (formerly OWTD) used Kinematic movement as described here. But it wasn’t quite right. I have a strong memory of how much more fun World Of Tanks was after they introduced proper physics. I decided that if a third person perspective (as oppose to top down) was going to be used, I should probably use some sort of physics.
As you can see, though wonky, the Tilter is able to navigate the terrain. It’s using a single visible steering wheel in front and the tracks are represented by two invisible wheels in the back. This movement model is, of course, built for car driving games, so the trike configuration cuts into the stability badly. The requirements of “Eamonn’s Ideal Vehicle Game” and thus Hovertank 22, however, call for tanks. Unfortunately, this has major disagreements with the movement model.
The basic idea, I figured, for implementing tank movement has been to treat each track as a series of wheels. To turn right, you drive the wheels on the left side and brake or reverse the wheels on the right side. In a real tracked vehicle, this causes it to turn. In Godot’s model, however, wheel slip seems to be very all-or-nothing: the vehicle stays in motion nicely but as soon as you cut the wheel and one track slips, the whole vehicle starts to drift uncontrollably. While some amount of drifting was desired, this turns out to be unplayable. And, worse, messing with wheel slip values didn’t really help.
Another problem with this movement style was that when the vehicle was in motion, differential steering worked far more effectively than from a dead stop (I assume that this has something to do with how overcoming friction is coded.) In order to turn from a stop you need to overcome the friction of all of the wheels, so turning while stopped was impractically slow. I implemented a fix which increased power when you weren’t moving but it was unsatisfactory and resulted in a vehicle that jerked around suddenly and violently.
I suspect that customizing the 6dof version might be able to overcome these limitations, but I don’t have the bandwidth to try it. Similarly, it might be very possible to implement this sort of thing as a custom integrator and bunch of rays, but again, that was more of a lift than I had time for.
Faced with basically a negative result from this experiment, I’ve decided that I’m going to roll back (ha) from the VehicleBody approach. I may switch back to a top-down perspective, and also may try switching out heighmapped terrain for tile terrain.
So what did I learn from this little excursion? You really need to validate your assumptions about how something is going to behave before you stake a project on it. I’m glad I didn’t fully do that (I still have a path forward for Hovertank ’22), though pulling it out is definitely going to be a setback.
The central feature of Westfield State University’s campus is a large globe sculpture. This has been true for a while, but not always the same globe. In 2015, during the excitement around the Patriots Superbowl win, enthusiastic students cleared the benches, rejoiced on the green, and climbed inside the globe. This wasn’t the first time the campus had hosted an impromptu outdoor party of this nature – I seem to recall a similar reaction to a Redsox championship in 2013 (but it could have been a playoff game – my memory is foggy. But this time, the globe sustained major damage. The overly enthusiastic students shook the globe until it strained against its joints, and ultimately failed in several places. Tectonic plates torn asunder like something out of a disaster film.
You may notice from these pictures that the entire continent of Australia is missing. However, this was a pre-existing condition – I’m pretty sure the absent continent left a hole which allowed the revelers access to the interior. So consider this excerpt press release that the school released about the incident:
The university inherited the Globe from Stanley Home Products and it is missing the continent of Australia because it was not within Stanley’s global business reach.
This is an incredibly weird statement. As you can see from the first image in this article, the globe used to feature Australia. The loss of Australia running joke on campus. Rumors abound about where it ended up (which I won’t relate to protect the innocent… or guilty.) The story was, however, that it up and fell off. To my knowledge, no great effort was made to replace it.
So why make up this absurd lie? It’s so easily verified to be false – campus promotional material (including the desktop background of campus PCs, the file photo above!) contained images of Australia on the globe. Anyone who’d attended the school during or before 2011 (latest possible year it could have fallen off.) It’s just a silly idea: nobody commissions a globe sculpture missing a continent. It’s not a thing people do. Nobody would accept a donation of an incomplete globe like that.
I guess I’d never imagined that level of brazen dishonesty from an institution before. I suppose that was a very naive, 2015 attitude to have.
The author of the statement was contacted for further insight, but there was no response.
The damaged globe was replaced by a much nicer sculpture which does feature Australia.
EV Nova was a formative game for me. One thing I noticed about it, and wanted to document, was that there are a lot of what I’ll call name collisions: names that are almost the same but refer to different things. Did I miss any? Leave a comment, I’ll add it.
Temmin Shard / Shard (faction)
Temmin Shard is a character in a side quest. However, “Shard” is also used to describe a faction formed when you win the Fed campaign and everyone else comes after you.
Krane / Kane (incl. Port Kane, Kania, and the Kane band)
Omata Kane invented galaxy-spanning hypergates and a bunch of things are named Kane in her honor, including earth’s artificial ring, the Kane Band. There’s also a Port Kane in the Kania system. Krane is the sociopath leader of the secret fed faction that you work for in some campaigns. Very different people.
Glimmer / Gli-Tech / Gli-Tech-Nia
Both are companies that make missiles. Huh? Gli Tech also owns an entire planet, called Gli-Tech-Nia. But wait, Glimmer is also the name of a star system. Surely this piece of fluff for Gli-Tech-Nia clears it up:
Associated Guild Of Free Traders / Free Traders
Two opposing pirate factions that fly the same ships. One is hostile by default, the other isn’t. No idea which one though; and feds don’t care which one you blow up either. According to Word Of Atmos this is intentional.
Polaris / Polaron Cannon / Polaron Torpedo
Polaris are one of the game’s major factions. Polaron refers to a Federation weapon. But the weapon is pink… the color of Polaris technology. And the Polaris have a Polaron Torpedo which is pink but apparently unrelated. Maybe Polaron refers to some specific scifi gubbin, but then that’s still colliding with the Polaris.
Vella / Vell-os
Vell-os are a race of psychic transhumans and mostly extinct, Vella are a neighboring Arouran subfaction.
Wraiths / Wraith Cannon / Wrathii
Wraiths are a race of sentient space-monsters, Wrathii are pink projectiles that the Polaris fire out of a Wraith Cannon. Also the Wraiths are right next to the Polaris.
TOWCB / TCTLIDS
The Ones Who Came Before are your standard ancient race of aliens trope, TCTLIDS are The Creature That Lives In Deep Space harvested for drugs, an abandoned plot hook that somehow remained in the timeline preamble.
Hovertank is an honest attempt to bet big open 3d environments with AI agents running around working in Godot, using HTerrain and Godot Detour. I hope this is helpful for anyone who wants to make something similar in the future.
Create a new inherited scene from the “world” scene.
Add an HTerrain element to your level. Make sure it’s collision layers are 1 and 8.
Create a data directory in the levels folder
Apply a tileset to your HTerrain by dragging tileset.tres into the texture set slot, or making a new one from scratch.
Edit the terrain to your heart’s content using the generator and the tools.
You’ll need to repeat the next steps each time you edit the terrain mesh
From the “Terrain” menu up top, select “Generate mesh (heavy)” and select an LOD of 4. At least that’s what I’ve been using.
It will create a node called “HTerrain_fullmesh.” This is the node that the pathfinding library uses to actually determine where AIs can go. Hide this mesh; if you need to see the navmesh turn on “visible collision shapes” and you’ll see it (and it may lag real bad!)
Note that the flattened areas have nice clean navigation, but the more sloped areas don’t. You can use the flatten tool to cut paths across the map. Make sure you enable “pick” on it, it’s much nicer that way.
The prefabs folder has enemies with preconfigured weapons you can just drop right into the scene. You’ll want to drag them a bit closer to the ground though.
You can also place buildings this way. Careful not to accidentally parent stuff to random entities you’ve already placed – entities in Hovertank expect to be parented to the world.
You can set up objectives by adding the “ObjectiveMarker” component to an entity.
When all of the entities with objective markers are destroyed, the player has won the level. You can add the objective marker to any enemy or building.
The incursion spawner will spawn the entity you provide in “spawn” whenever there’s an incursion with the selected severity. Put one of the monsters in there by dragging its tscn file in to the spawn field (only monster at time of writing is our friend Kochab) (Incursions were removed, but you can see how it worked in older commits — Ed)
Add your level to the list
Add an entry to the big literal with all of the levels pointing to your level’s TSCN file, give it a name and a description, and it should show up in the dropdown in the main menu.
Godot’s High Level Networking tutorial (and book chapter) do a great job of explaining a peer-to-peer networking setup. However, many games want to be server-authoritative, and to set up a multi-area game, you sort of need to. Luckily, you can use the same primitives to build a server/client game. This is just sort of a brain dump of my notes that may be helpful in navigating the MPEVMVP source. Feel free to post any questions you have about it; I love to talk about this stuff.
The technical requirements that deviate from the basic tutorial they hand out are as follows:
Server-authoritative client/server game
Multiple levels that players can switch between
Players are able to join a match in progress
In case you haven’t tried it, the project is here: https://github.com/eamonnmr/mpevmvp. The gameplay is similar to Asteroids (and, well, similar to Flythrough.Space): it uses RigidBodies to simulate movement, you can shoot other players to force them to respawn, and you can press “J” to initiate a hyperjump after you’ve pressed “m” to select a star system from the map.
Godot’s Networking Golden Rule
A remote call (rpc, rset) is going to be called on the remote node with the same node path. That includes name of the node, the name of the parent, etc, all the way to the root node. You can set a child node’s name with set_name but if a similarly named child already exists, an @number will be added to your node. This is a major source of bugs for networked projects, so be careful. You cannot set a node to a name with @ in it. So in effect, you cannot use Godot’s auto-incremented names (since they append @increment) to sync between the client and the server. For this reason, I used a uuid library for generating node names.
The need for the same structure on client and server despite the fact that the server held a whole universe while the client only cared about one system is a major source of complexity in the codebase.
I implemented the Client and Server as singletons that exist on both sides. The reason for this is that it makes it really readable; when the client needs to send something to the server you write Server.rpc and when the server needs to send something to the client, you use Client.rpc. Besides this line of communication, I’ve used rpc and rset within various nodes to update and alter the client versions of themselves. This always happens inside an is_network_master block, to ensure that it’s being called on the server version of the game.
The one thing we do need to gather from clients is input, and for that each client has a PlayerInput node, network_master’d to them. They rset_id input up to the server. The server copy of the player nodes looks at this remote copy of the player_input to determine it’s behavior, then pushes its state back down to all clients.
Suppose you want to have multiple different levels/worlds/rooms in your game. The trick is that you need to mesh the following two constraints:
Node paths need to be the same between client and server (see discussion above)
The server needs to have different node paths for each level
The trick, then, is to send the client to the new level by loading the appropriate level and making sure it’s named appropriately. There’s one wrinkle though: different 2d physics worlds need to use a Viewport class, but you don’t want to add additional viewports on the client for performance reasons (tried it, it was way too slow.) My solution was to add an extra layer of nodes called “universes” in the comments which are a Node2d on the client but a Viewport in the server. They are managed on the server by ServerMultiverse and on the client by ClientUniverse. Client Universe cares about a single “universe” child whereas ServerMultiverse handles multiple children.
Sending Entities/Switching Levels
In order to join a match in progress or switch to a level that already has stuff in it (or, indeed, switch a player into a level) we need a robust way for the server to move fully-formed nodes to the client. Godot does not provide a generic “send this node” method, so we need to write one for ourselves. The approach I took was:
Assume that the ent will be instanced from the same scene that the ent on the server is
Write a “serialize” and “deserialize” method that allows it to dump a dictionary with all of its important state and recreate itself from that dictionary.
Make sure the node on the client side is assigned the same name as the one on the server side.
So when a client’s level is switched, they dump the current level and recreate the new one by loading in the scene and then, node by node, deserializing the state sent from the server. As it turns out, this means that any state can be synced freely, and it enables us to dispense with the lobby entirely and let players join and leave at will.
Sync In Multiple Levels
Each level owns the process of syncing its state to clients in that level. Scenes within the level can declare a method that serializes their state for a net frame, and the level gathers all of those up and sends them. The client receives the frames from the server, and the individual scenes use the content of those frames to interpolate their position. To save bandwidth, I only do this for ships and guided projectiles, everything else moves deterministically. This whole process is derived from the teaching of (and better described by) this video.
Because we’re using the player’s presence in a level to determine if we should update entities in that level for that player, when a player’s avatar is destroyed the world seems to stop for that player until they respawn. This might be desirable in some games, but I think most games at least want to show the player the immediate seconds after their own demise. So what we do is replace the player with a ghost.
Tooling & Workflow
GDScript is based on Python 3 and the most basic syntax resembles it. ‘func’ in place of ‘def’, no ‘self’ and no list or dict comprehensions or other advanced features. It feels like the python of basic python tutorials. However in exchange for this, you get one thing python 3 does not offer: true static typing. In Python 3 you can offer a type hint for a function argument but it is not truly enforced. GDscript enforces the types at compile time and boy does it ever catch a lot of bugs. You also get the ability to define classes with typed members, similar to what you get in Python with Pydantic. Overall I like gdscript and the things I miss aren’t dealbreakers.
The lack of a vi mode for the editor hurts. I know I could just use the godot plugin for vi, but I actually really like staying inside Godot’s editor. Despite the slightly crowded layout, it’s really very nice. The autocomplete to node paths is a killer feature that makes dealing with complicated trees less of a pain.
Building a multiplayer game in Godot is possible but by no means easy. The high level multiplayer API despite its gotchas works well, but you need to implement the sync logic yourself. This shouldn’t be too surprising as games have very different needs when it comes to net code. I hear that a similar mechanism for frame based sync and lerping is going to be more built-in in godot 4, which would be sweet. The puppet system (which I initially used; you can see a version in the tutorial) seems to be useful only for very low latency situations, so I regret that it’s a first-class feature because it may lead people down a rabbit hole of net code that will not work for most games.
It’s been at least a decade since I’ve serviced this box. The volume dial developed dead zones and the light won’t turn on. In addition to corroded contacts, I think it may have a dead bulb and/or a blown fuse. I’m going to open it up, clean up the dust, clean the contacts with Deoxit, and see if I can determine what else might need replacing. I found a service manual here (free account required.)
The two fuses are easily located. I don’t see any leaky capacitors, which is a good sign. I tested the two visible fuses in-situ and both read the same as shorting the contacts, so I assume they’re good. Next step was to clean the dust out and apply Deoxit to the pots and switches.
With some trepidation I powered it up. No light bulbs and there are still some deadzones in the volume knob, but I’m able to pick up FM stations. I’d probably call that a success but I’m not sure where I’m going to put the thing yet so I might as well go the distance and try to fix the bulbs before I screw it back together. I’ve seen enough mixed messages about the dead zones that I’m a little afraid of doing more harm than good by repeatedly cleaning the main pot.
I found replacement lamps from this ebay seller. When trying to replace the bulbs, that’s where my lack of experience really started to show. The bulbs where wired in like this:
Two of the bulbs are on fairly long wires which give you enough slack to pry the connectors off and squeeze the connectors onto new bulbs or splice the wires. The middle bulb, however, does not afford you enough slack to pry the connectors off, so I cut the leads from the old bulb and attempted unsuccessfully to splice the old leads to the new leads. As a hail mary, I crammed the bulb into the socket anyway, figuring that the narrow wells that admit the wires should force them into contact. And I was right! All three bulbs lit up.
And with that (and a desk our neighbor didn’t want anymore) the setup is complete. I have a Bluetooth connector so that I don’t need to plug in my phone to use it; it’s now fully integrated into the 2021 music ecosystem. Not bad for a forty year old piece of hardware. And it’s still plenty loud.
EVMVMVP is the awkward initialism for my latest project; a multiplayer EV-like. You can download it here, and look at the source code here. I know I said I shouldn’t do it, but I really wanted to learn Godot and having a multiplayer server with multiple levels seemed like a really interesting problem to tackle. Implementing a bunch of stuff I already know pretty well but in a new engine and language has been a fun ride. I’ve jotted some notes down about the project so far. I’ve saved a deep technical discussion of Godot for another post.
Using an engine is well worth it
After flythrough.space I decided that I wasn’t going to build too much from scratch next time, not was I going to fight against tooling. The options I considered for FTS where Panda3D and BabylonJS. However over the course of that project also tried out Unity and Godot for small side demos and ended up liking Godot (and it’s permissive license and thriving community) enough to go all-in. In retrospect this was the absolute right move and I regret nothing.
Working With Onyx’s Sprites
I’ve been staring at these sprites on and off for something like fifteen years, itching to put them into a game. The original models used to render them are long gone, but Onyx (in a tutorial) explained how to separate out the engine glows. Working with these sprites feels like stepping out of time. They’re from the 90s. There are dithering tricks here I wouldn’t even imagine. They use a tiny palette but they’re so damn weighty. Present. Metallic when they need to be, plastic when they want to be. They tend towards the Star Wars / Used Future look. They invite stories to be written about them. And the planets are just absolutely lush too. Something about that limited palette…
Testing with real people is essential
I’ve been nagging more people to play this one. On the one hand, it’s much easier to convince someone to play your game if you can play it with them. On the other hand, you really need to do that testing or you will (like I did) find out that the sync method you’re using is totally busted and you need to replace it with interpolation. Furthermore, you’ve got no idea if your game is fun to anyone but yourself unless you subject it to ruthless testing by people who will give honest feedback. It becomes very easy to get settled in and work around horrible UI issues you’ve coded because “well, I know how it works.”
The ability to test solo is essential
Wrangling people for a test is hard. You need to be able to fire up an instance of your server and client with minimal effort or you’re going to wait too long and leave a bunch of bugs that you’ll be scrambling to fix when you finally get people around to test with. And absolutely nothing is more frustrating than the stars aligning for a test only to have a showstopper rear its head at the worst possible moment.
I had a conversation with a coworker about EV games and roguelikes. They made a claim that I’m not sure is true but definitely opened my eyes-they thought that the star maps in EV games probably had been procedurally generated-once.
Actually hand-crafting a full EV map is a bit of a process. One thing I never finished doing in FTS was populating all of the systems.
Meanwhile, Mag Steel glass has developed a really cool universe generator spreadsheet. I figured I could leverage it to make a map. Traditional EV games require you to specify exactly who spawns in what star system and FTS carried on that painful tradition. For this one I had simpler rules; factions had starting locations and then grew “influence” where planets (or stations) would belong to them and their ships would spawn. If two areas of influence overlapped, they’d fight.
Document enough for people to get up and running locally
Port forwarding is a lost art. There was much grumbling when my friends and I went to go start a game of Valheim; luckily I’d learned to properly port forward as part of this project! If you need a server for people to play your game, you can’t rely on your master server; you’ve got to give people an option to join localhost and put it in your manual/readme. Having images in the readme also helps show people what your project is about; this is especially important for games.
You don’t get cross platform for free
Using cross platform technology does not absolve you from cross platform testing. All of your target platforms might have the same features but they may all have different bugs for you to work around. I’m developing on linux out of what is probably just bloody mindedness at this point, so when I did my live demos on windows I often learned exciting new things like ‘your sound exports are busted’ or ‘only one computer can be assigned to a given port by a given router’ or ‘you need to version bump the editor for correct mac exports.’ Guess which one of those lead to an incredibly embarrassing platform bug report!
Video Tutorials are worth it
Part of my commitment to overcoming my stubborn hangups was relenting and watching video tutorials. I still strongly prefer written tutorials as a medium but the content is moving to video. For example:
I post progress updates on discord channels full of fans from the old days, and regularly demo the game with friends. This has been a source of motivation and helped me stick to a more iteration focused (and less “well, I’ll go off for a month and make all of the models/textures/features”) work habit. I’m not sure I’m going to do this for every project; part of why I did it for this one was I wanted to spur participation and that didn’t really happen. Very few people who I didn’t know personally went to the trouble of getting the game running. Part of that is because it wasn’t immediately approachable (you need to host or find a server then run another client to join it) and wasn’t immediately grippy (ok, I can buy a ship, now what?) Any amount of friction is going to turn away potential players, and if you’re used to playing rough unfinished demos this can become a blind spot. But beyond even that, getting someone to try something new is just plain hard.
People respond to Video
At the end of the day, pictures and text don’t cut it for sharing a game. Games are animation. People are used to watching streamers. You need a video of your thing and it needs to show off what the thing is about, including how you interact with it and why it’s compelling. This was my attempt:
Have features and fixes gone in since that video was made? Sure. But it’s still the best tool to show off what the project is about.
Has the project met its goals so far?
Learn the Godot engine: ✔️
I’m comfortable with the technology now; expect future prototypes to be more rapid.
Use Onyx’s Sprites in a game: ✔️
I used most of the ship sprites themselves and lovingly separated out the engine glows for even the ones I didn’t use, in case someone in the future wants to use them again. Cosmic Frontier mod anyone?
Build a reusable platform for multiplayer EV-clones: ✔️
Maybe this is a partial check. If someone has an idea and that idea is based on multiplayer EV gameplay, I’m confident that if they’re willing to learn Godot they could at the very least copypasta big chunks of the MPEVMVP codebase and get something running. Or they could fork it, replace everything, add missions, remove multiplayer, and make an EV clone real fast.
Discover if EV gameplay works in multiplayer: ✔️
Learned important lessons about how much bullet hell spam a networked game can handle, and as a straight dogfighting sim it let me figure out what is and isn’t fun in PVP EV.
Build a game people want/like/play: ❌
As far as I can tell, nobody enjoyed playing this besides on demo night. The service I think people want this game to be is beyond the scope of what it can be with a team this size.
4/5 ain’t bad. Thanks a bunch to everyone who tested it, gave feedback, and flew too far to find the center of the map again.
The iMac G3 is, to my nostalgic eyes, the most beautiful computer ever designed. So when I saw one lying there at the dump I had to take it, to see if it would boot. It did. It hadn’t been used since ’06 and was slightly beat up. I took pictures while restoring it, but much of this post is from memory.
When I found the machine it was booting OSX, and absolutely dog slow. It’s crazy how much faster SSDs have made computing; we used to wait. I posted on /r/vintageapple and got tons of help picking a hard drive replacement. Of course I found out that you pretty much need a working disc drive to boot one of these things, so I picked one of those up on ebay.
In order to get the thing apart you need to first remove the plastic on the bottom, then remove the EMI shield which is a metallic mesh under the plastic. It requires some finicky movements and faith that it’s not all going to fall apart on you. Also screws will try to get lost, so be careful about that. I was able to recover all of the screws once I removed the drive cage; I even had some extra!
Once the thing was put back together with a new hard drive and disc drive, I installed OS9Lives’s version of OS9 from a burned CD. I was then able to load games on via a USB key!