April's 2017 Lisp Game Jam Postmortem

That was a blast! And so Mortar Combat was born.

I probably should have stopped right here, but, apparently, there quite a few people interested in the events of those days. Their minds call them to fill up the void made by vicious curiosity that delves deeper and deeper to clear space for pure knowledge of what actually happened during the sacred ritual known as Lisp Game Jam where miraculous Mortar Combat was bor… —Nah, right, you got me there— No one cares, but I’m still gonna put some words about it here nevertheless.

Preparations

Games, Socks and Decision

Lets be honest up front: I cook socks as a game designer. Doubt never crossed my mind about this fact. Why do even bother with a game engine then, you might ask. Exactly because, would be my answer. But really I’m just working on a tool that helps me to learn magic of virtual worlds. Exploratory programming they call it.

And so that was it. I decided to make a “game”, more of a tech demo, as complex as the engine would allow me to finish in time while adding minor missing components I could incorporate and reuse later. Current goal is VR+networking so it was sort of no brainer when I decided to go with Multiplayer 3D First Person Shooter™.

Realization

It was a Friday’s afternoon. Few lazy hours left till the Jam. Suddenly, belated realization came to me as to awake me gently: how the fuck holy duck, how did I forget I actually could prepare goddamn assets for a Jam game in advance. That’s how I lost a day or two of Jam before it even started.

No need to mention that socks I cook as an artist are so huge, Big Foot dudes use them as race sacks. Hopefully, I fairly quickly found free model I felt like using as a “Dude” to portray combatants, but I failed hard to find simple animated human-like skeleton to transfer onto this model1. My only luck was an IK-driven skeleton from different model I successfuly imported into Blender. So I merged those into one and the model was ready to be animated.

Plans

I attacked the available timeline by figuring out what features I would need the engine lacks yet. Knowing out of experience from the previous Jam that shipping Common Lisp games is not a trivial task these days, I thoroughly prepared a delivery mechanism before this Jam started and tested it with help from #lispgames community beforehand. So, ultimately, distribution part was out of concern.

What was missing:

Last feature was an extra, because at this point I knew I didn’t have time to actually make a model ragdolled, because I don’t have a tool that would allow me to apply rigid bodies and joints to a skeleton in WYSIWYG-manner2 and doing that manually by hand is much pita. But I thought I would have time to at least use single rigid body for cringy ragdolled death animation. I was wrong, but meh, extra is an extra: no regrets.

DevLog

Apr 15, 2017

8a77603 Initial commit

Many of us start here. Conventional copy-pasting from other projects. One day I’ll fix that.

7d49cae Distribution skeleton

I had to make sure that distribution subsystem still functions properly, so I decided to check it up early in the process. I’ve got bare window, context and systems initialization all working alongside functional distribution. Mild success, but huge relief - there’s already something that other people can actually try and see.

f03e304 Dude

0003b9d Running dude

Someone mentioned at #lispgames channel that there’s a forum thread already to show the progression.

“Dammit! I need to demonstrate something too.”

At this point I already learned3 how to do animations in Blender and made a crappy running cycle, so the next goal was to bring the result of my Blender madskillz into game viewport asap. So I did.

Apr 16, 2017

01beb4d Strafing dude

Progression thread continued to fill up with drafts and screenshots. It was one of the driving forces behind decision making I did to schedule next goals for a while. I needed more visuals. So, obviously, strafing came next.

4262e0b Dudes and mortars

People started wondering what the hell is going to be in the hands of those dudes? Babies? So I thought it is time to show the weapon model I prepared. Found and prepared.

This day I learned: Never forget to apply goddamn global model transformations before exporting.

I spent a few hours figuring out why exported mortar model is totally messed up after loading into the engine. I thought topology might be broken somewhere, so the polygons or winding got screwed during export. And I indeed found topology problems. Now, it is probably good to know how to fix broken topology in Blender and knowledge of non-manifold geometry existence is helpful, but only after I learned all that crap I realized.. Shit! Shading! It was just incorrectly lighted. Shading is totally a culprit here. Shaders were sort of the same I used for dudes, so problem lied somewhere else.

Oh, yes, it did. First, apparently, model had negative scale so all normals and faces came out inverted. Second, holy crap! crop, scaling was set to 0.28 or smth for all axes. —Eh?!— But at least model had correct topology now… gaawddammit.

Show off time!

3c90417 Ball

Well, I needed it. I even made it myself. I still am able to click a proper button, hopefully.

Apr 17, 2017

369ad98 Room

78d757c Split into client and proxy server

Having smth to show is nice, but there was quite a lot of work to do that no one will ever see and it would be taken for granted: networking. Still, I was excited: I hardly wrote much of real-time network communication code - mostly REST stuff. Now I finally had a solid reason to implement it.

Apr 18, 2017

bbf0592 Add proxy routing stub

4b9fc4c Add peer registry

130fc99 Add identify command

c66234e Async networking with cl-flow

Server instance I had to serve as a medium for game communications was not particularly performat one: single-core 2GHz with 512Mb RAM. Not much resources to spare. Blocking/synchronous approach wasn’t much of an option, so I went straight with asynchronous code.

Asynchronous approach was especially attractive, because I could and did integrate it nicely with cl-flow library. Success!

248ae83 Arena commands

d756789 Add register-game-stream command

Remote server is just a medium to help players find each other and to establish communication channel between them. So I decided to go with two connections per peer: one would serve as a channel between communication server and a peer, and the other connection is for data stream to be routed between peers without any parsing done by communication server to preserve resources. So, basically, server<->peer and peer<->peer connections.

Apr 19, 2017

d64ff70 Extract common code between client and server

232f276 Add ping command

That was a moment of truth. First pings sent across peers and a server! Too bad there was no entertaining way to post such a progress into screenshot thread :/

1ad1d04 Force output pouring

e8a41e5 Switch from usocket to cl-async

Unfortunately, usocket4 didn’t allow me to write fully asynchronous code (efficient reading was a problem: read-sequence is still a blocking operation), so I rewrote communication server using cl-async networking capabilities. That didn’t give me any particular troubles. Moreso, I was happy with it.

f9948c7 Add LGJ mention

cea602a Rest pose

750002a Add animation blending

Adding animation blending was the next huge milestone. Interpolation part was already there, so all I had to do was a bit of refactoring and adjusting a few interfaces to introduce blending into the world.

New visuals were ready!

Apr 20, 2017

fc34a5b Add keymap

5a1adeb Player camera and movement

f23d6d9 Ball body

By this time I slowly got to the point where I should be able to move around to test upcoming features.

Apr 21, 2017

c0207d3 Add shooting

3a8cd03 Dude body

723fe65 Add room mesh wall geom

Physics simulation was finally ready to be demonstrated. Basically, at this point, it was possible to turn it into some proper game with distinct goals and visuals, but without any networking. Maybe some sort of aim’n’shoot stuff like shooting gallery or whatevs. But it still would ended up crappy, because I was in charge, but I never actually considered pivoting it into something else anyway :)

d0cfc8f Add basic communication

29a4b8f Game server and client

Apr 22, 2017

b5fa9e1 Clean up peer data on disconnect

687a95e Add :exit-arena message

5311d42 Add Makefile and Dockerfile for building the server

It was the time communication server should have been stabilized to the point when I could just deploy it and focus on game server and game client code. I already used Docker to deploy several backends including lisp ones, so going that route was an obvious choice. No obstacles met. Except, maybe, that Alpine Linux actualy uses musl-libc instead of glibc, meaning I couldn’t deploy systems built on my Arch box for it as a base image. Meh, but no worries: we have officially supported bloated Ubuntu base image. Yays for 150Mb of bloat.

3f0762c Add naive position smoothing

Well, as expected, roundtrip time through remote communication server was quite crappy: something about 0.125s. I needed smoothing so movement of the players would look acceptable. I tried to implement naive prediction, but that didn’t go well, so I just went with “last packed interpolation”. Basically, players were one packet behind their actual positions reported to game server. But the end result was actually very nice: only perceivable lag was actually network one. Arguably, smoothing didn’t contribute any visible lag.

Apr 23, 2017

af8617a Add arena object

b681885 Send game updates every couple of frames

91065e3 Add movement state

c05401c Relay movement state

d980404 Add movement animation decorators

I felt too lazy to make 8 distinct animations to introduce backward running, other way strafing and everything in between. So after some tinkering I actually ended up with animation player and animation decoration mechanism. Now I could combine several animation sequences into one by decorating them with “modifiers”: looping, backward playing, blending. So now I could do 8-way movements out of running and strafing animations with just a couple lines of code. Wows. Result was crappy and even creepy, but wows.

e395466 Add client/server shot handling

4d626a1 Add login window

bbf5d6f Add base menus

7cdabeb Add arena creation and joining to GUI

7925fdb Toggle game menu

9179e6b Add score table

GUI was known to work, but I met very interesting bug while implementing a couple of menus.

If you look into the sources, one of the button titles is "Refresh ". This whitespace in the end is not a typo. Apparently, if I name it "Refresh" any attempt to close an in-game window will hang a whole GUI. Yes. After a couple of “no really, wtf” moments I decided not to waste time investigating. I would have plenty of time to do so post-Jam.

11eab86 Register when server player is hit

Apr 24, 2017

f5dda29 Cap physics step

c279bc8 Disable camera before start

055b8d6 Add arena leaving

Mostly minor stuff left so I devoted time to plug most obvious holes. Unfortunately, at this point it was clear I wouldn’t have time to add any sounds into the game or any, even crappy, ragdolls or death animations. I barely had time to introduce at least some goal into the game (Score table). And even then, it was bugged: it counted how many times player was hit, instead of how many times player has hit anyone :/

36ad117 Add dude and mortar model sources

Obligatory asset sources. I probably should have added them to the repository from the start, but didn’t do so for some reason I can’t remember myself.

Conclusion

A blast indeed! Native, shippable across platforms, complex 3D tech demo with smooth multiplayer in 10 days - Achievement unlocked!

Yes, exhausted by the end, but I reached all the planned goals, except for optional ragdolling. New features are ready to be incorporated into the engine and I learned several ways in which it could be improved.

Big thanks to all participants and especially to Michael axion Fiano for organizing such an event. It was fun!

P.S. Woohoo, Mortar Combat didn’t even get last place, but 4th. Wows!


  1. Skeleton of which I couldn’t get to import into Blender correctly. Sigh. 

  2. Blender actually can, but I don’t have an exporter/importer that would allow to bring that information into the game engine. 

  3. By means of whatever lesson popped up first in search results 

  4. Relevant issue