Throne Ridge

Since 2016 Throne Ridge has been my little side project of a game – there have been many late nights turned early morning, arduous debugging sessions, arguments over variable naming, and lots of testing. This is about our fork of TTT, the friends who contributed1, and its outcome…

TTT?

Directly from the developer:

Trouble in Terrorist Town (TTT) is a multiplayer gamemode included with Garry’s Mod. Set in a parody of the Counter-Strike universe, the game is about a group of “terrorists” who have traitors among them, out to kill everyone who’s not a traitor.
When the game starts, a small number of players is selected as Traitors, who have to kill all the Innocent players (ie. the rest of the players). The Innocent players know they are in the majority, but they do not know who is a Traitor and who is not.

— Bad King Urgrain (Creator of TTT) via https://www.troubleinterroristtown.com/

So, a social whodunit game where every player is also judge, jury, and executioner.


Goals

The initial scope of Throne Ridge was simply to re-balance and polish up the existing gamemode, which had seen little development since its initial inclusion in Garry’s Mod, and realistically even before that, after the gamemode competition that lead to its inclusion2.
And while on the surface TR plays and looks very much like its progenitor, the reality is that scope-creep lead to a significant amount of rewrites in the codebase. Not only was a lot of supporting and structural code changed, but a few systems and designs completely overhauled.


How It Started

The first commit is here, on 2016-07-16.

And the shortlog of those first changes
Disable ragdoll collide
Buff revolver
Upate ttt.cfg comments
Merge
Fix the mac10 ammo. Tweak deagle, knife, m16, pistol.
Finish TTT Cfg documentation
Add comments to ttt.cfg
Fix the mac10 ammo crate, max ammo of mac10 to 150.
Raise ammo given by mac10 crate to 50. Removing the ammo max override
Increase damage a tad
Reduce shotgun damage, increase spread, and increase shots fired to improve close ranged potential, but decrease ranged capability.
Lower ROF slightly
Decrease MAC10 damage, increase ROF, and ammo.
Fix cstrike mounting content
weapon changes. Obvious mac 10 thing and I broke the rifle :)
Reduce headshot bonus
Initial

This was actually before I had learned to program, so I while I was making configuration changes, and some minor code changes for weapon values, most of the actual work was being done by friends, or the couple of people we contracted to make changes.


Contractors

I ended up using one of the GMod oriented job-boards and posted our requirements for two of the bigger changes we needed made early on in the project.

“Shootnades”

The first big one was an overhaul to the base grenades in TTT, which are, I think pretty agreeably, a minimum viable design. I wanted something closer to Counter-Strike or Halo in variety, predictability, and systemic interaction. To those ends, we hired a contractor. The end result was grenades that used a sphere for collision interactions3, played a bounce sound that depended upon the type of grenade, could break through glass windows. And could be shot or set off by other grenades.

The earliest implementation.

Bugs

We of course had lots of funny and weird bugs, here’s a selection.

Grenade spawning and colliding with player.
Thrown/Dropped grenades inconsistent damage.
No collision at all…
Non-bullet/explosive damage causing detonation.

Result

And we dealt with all that just to integrate grenades systematically; it did mean you could do some cool things…

A team favorite:

“Dropfix”

Another issue I had with TTT was how it dropped weapons. GMod has an included function to drop weapons, that just exposes a base engine utility from 20034. It wasn’t designed for a third-person view — it just spawns the weapon at the player camera with some velocity, and a random roll.

And what it looks like now, weapons drop from their correct world-space location.
This includes on player death, and takes player velocity into account.


ttt_tr_debug

As can be seen in the background of those prior videos, a lot of testing, debugging, and development was done on a ‘dev map’.
This developer map was one of my first contributions to the project, as mapping is something I had experience in, and could meaningfully contribute with.
The objective with the test level was to facilitate the moment-to-moment development of scripting our new features; this meant weapon spawners to easily get any of the arsenal for testing.

The devmap ended up being very useful, and helped greatly in debugging almost every feature added, I’m pretty proud of it.


Systems

Another large project was adding several back-end systems to the gamemode, this included version information, system metrics, and player metrics.

Version Information

This was actually relatively simple, and was implemented by Shadari.
Our solution was to use an update-hook as part of our version control system. When the server received a commit bundle it would fire off a script emitting a .lua file, which is loaded by the gamemode and contains the latest version information.

This is then consumed and networked for display during gameplay. If users face an issue we can ask for what version they were playing. It also gets pushed to our server metrics.

Server Metrics

We wanted to have insight into the state of our servers, and I decided to use InfluxDB to this end, a time-series database that allows pushing values associated with keys that are indexed by their insert timedate.
InfluxDB takes JSON input, but we didn’t have a particularly good way of getting our Lua gamestate into that, GMod has a built-in JSON serializer, but it’s doesn’t cooperate very well with the structures we needed to submit.

So to that end, Shadari ended up writing the first pass implementation. A database “driver”, compliant serializer, and the associated scaffolding.

The driver maintains a connection (monitors status and so on) to Influx, handles error logs, and so on.

The full diagnostics is output here, but you can see the basic flow, in a less verbose mode it just outputs the major state changes.

Metrics Dashboard
Version #

Implementation Details

The default GMod HTTP library is callback based, you construct a request, and plumb in your anonymous callback functions. In the initial implementation this how it was structured entirely. But as anyone who has written callback heavy code knows, it becomes deeply nested very quickly.

I came up with a solution using Lua Coroutines, where a wrapper function takes the request, and a reference to the coroutine, the closures (anonymous functions) are then constructed to reenter the coroutine with the HTTP return data. You just make the request, and yield to get the results.

Example usage.

A proper library could definitely be written around this feature (could even call it promises 🙃), but it’s only used a couple times. Mostly it just makes writing asynchronous requests much easier, you just handle everything in a normal linear program flow.


Gathering Metrics

I wrote a library to gather the metrics we care about in the engine.

Functions exist for all the data you’d want, server performance, playercount, etc.


Player Stats

We wanted to track individual performance, playtime, etc. And that is coordinated through the ‘Player Stats’ module I wrote.

The backend uses HTTP to submit stats changes to a server that Shadari also wrote, we don’t have any visualizations hooked up beyond the fun fact stuff we display on the Loadingurl…

Loadingurl

One of the earlier efforts was our loadingurl, since I wasn’t coding at that point I concepted the page.

2017 Image Concept

Shadari turned that concept into a real webpage, including the necessary dynamic programming (originally in PHP), client-side JS, and CSS.

You can see what the live version looks like right now


Weighted Roles

In the default gamemode the picking of Traitors and Detectives is truly random, the issue with true random is that if you play several rounds in a row, it’s entirely possible to passed up for playing T, and one player might get T several times. In an effort to make the random T role “feel” better, I implemented a simple weighing system.

Debug Output

The behavior is pretty simple, we randomly iterate over all the players, then roll a dice, if they’re equal or above the face value then they’re promoted T, otherwise they’re ignored and another random player is rolled, until the number of desired roles is filled. Every time a player isn’t picked for a round, their weight is incremented. Higher weight, more likely to be picked by the dice.

We actually kept this pretty quiet, in totality it’s a small change, but didn’t want players catching on to the statistical nudging we did. It’s your secret now too 😉.


Death Cards

When players die, they can be searched, and players are presented with a death card.

Example of the death card.

By default this was a very hardcoded and naively serialized structure. One important thing it lacked was information about player suicides, this was something I wanted included.

So I rewrote much of the system, it’s now serialized into JSON and compressed before being sent to the client, and can even be chunked if a player death card contains some huge amount of information (such as the list of players the deceased killed).

No more faking — if a player killbinds, you’ll know.

Crosshairs

By default the gamemode has a fairly minimal crosshair, it draws single pixel thick lines at the vertical and horizontal center of the screen.

There are several issues with this, the biggest was when upgrading to a 4K screen, 1 pixel is tiny.
The other is that when the player is viewpunched (such as weapon recoil) the crosshair no longer actually represents the true aiming location.

I ended up doing a system rewrite, you can now pick whatever width you want for the crosshair, by default it scales as a multiple of 1080P so a 4K user already gets a 2px wide crossahair. And the crosshair is now correctly positioned when viewpunched.

Demonstration of the original TTT crosshair behavior. The grey spinning indicator is the true aiming location.

I also took the time to make some fancy UI stuff for the DNA scanner.

I implemented third person crosshairs, so spectators have an idea of what the player they’re following is gonna’ do.


Holstered Weapons

Another huge important feature I wanted was displaying held weapons physically on players, this is present is games like Counter-Strike: Source, and Halo 3.

This was super important for me. From a design standpoint, showing other players what everyone has equipped is extremely valuable, especially in a social deduction game; players can’t hide what their primary weapon is anymore after slaying another player, they have to find somewhere to stash it.
It also adds value to the smaller weapons, since those don’t get displayed on players, so it might be difficult to kill another player with a pistol, but also more difficult to deduce the murderer.

Players with their weapons holstered on their back.

Ultimately I created a scripted entity to represent the holstered weapon, this allowed pushing more of the rendering and visibility logic to the base engine, instead of handling it in Lua. I arrived at this implementation after looking at workshop addons that achieved the same result, however they all did Lua clientside rendering and perform poorly compared to my solution.
The downside is that mine require a bit more managing, the server needs to pin the actual weapons and their holster entity together and make sure they don’t desync (such as if a player drops their primary, or dies). In the end I’m very happy with my implementation.


Contrails

Following a theme, another piece of visual information players need to receive is where they’re being shot from, in default Source engine games this is accomplished by bullet “tracers” that the engine natively supports. These are generally fine for most mid-range fights, you don’t want a ton of visual obstruction, and they serve their purpose along with muzzleflash to communicate who is shooting where.

However, for the longer range weapons, it’s insufficient.

So, for those, I implemented contrails. They’re basically a particle system “rope” that grows from the firing position to the destination, with a few extra flourishes.

Particle editor view (old version of the effect)

Again, very happy with how this mechanic turned out, it provided a much needed balance to the power snipers have in TTT.


Documentation

When writing a number of the libraries for the gamemode, I also wanted to write documentation to guide the usage of those libraries. I ended up picking WikiJS, and manually writing the document pages. I would’ve liked to auto-generate docs from the source code, but that would’ve been a considerable effort (a whole documentation system), and require marking up huge amounts of base Lua.

Module View
Function documentation.

Outcome

As you can see, Throne Ridge evolved far from being a simple balancing patch.
I didn’t cover everything we changed, there is so much added, removed, and modified — but I did cover the most impactful, or interesting topics.

Programming

As has been alluded to through this article, I actually didn’t know know programming when TR started in late 2016, but wanting to contribute more deeply than direction and mapping, so I dove in and learned Lua programing. Eventually I became the largest contributor of code (aside from the base gamemode), and it honestly changed the direction of my life. Turns out I really enjoy programming.

Throne Ridge Today

This has all been very much in retrospect, TR officially went public June 17th of 2020.
You can play it here:

Postmortem

The launch went pretty good, however the long-term playercount has been virtually zero.
While I’m really proud of what we made, and genuinely think it stands pretty far above almost all TTT experiences, the truth is that…

  • Nobody cares.
    • Making a highly refined and balanced sandbox is something for commercial games, not GMod.
  • This kind of project is no longer relevant.
  • I don’t want to run a community.

Playing Games Online Has Changed

Maybe it’s simply my perspective, after all I grew up playing Quake 3, Battlefield 2142, Halo: Custom Edition, and of course Garry’s Mod — but that all happened on dedicated servers.
And that’s not how it’s done anymore, really it hasn’t been for a long time.

With the release of Xbox Live and especially Halo, and Call of Duty, games have rapidly moved to a lobby and matchmaking system; even worse with the current competitive landscape where every game feels laser focused on ranked competitive matchmaking. Another contributing factor here has been Discord. Groups of players can sequester themselves away entirely and disengage from public play spaces.

The culture change has been overwhelming, and I’ve largely missed it. I still prefer dedicated servers, and the communities that formed around them. But if you try to play on the still extant dedi-server games like GMod, it’s eerie, servers will suddenly get a mass join of like 4–8 people who refuse to engage with the other players over text or voice; they’re in their own little bubble on a Discord chat. The servers have gone quiet, and asocial.

So, while I can lament and complain about it, this is the reality, and I launched a TTT sever in 2020 under these conditions, if you’re doing the same, maybe temper your expectations. Unless you’ve got gambling, P2W and skins, I don’t think you’ll see much traction.

Releasing the Source

I’m very tempted to release our fork to be publicly hosted, however the biggest issue is all the tooling and telemetry we packed in to it; some of that actually goes pretty deep and errors out if values aren’t provided. So it would take a non-zero amount of effort to fix it up for public consumption, and as elucidated above, I don’t think there’s any demand that — so we haven’t.


If you read the whole article, wow, I appreciate it.

I’m glad to finally be talking about some of the neat solutions we came up with for Throne Ridge; especially the back-end stuff.

Also want to thank the friends that also worked on this, you put up with a lot of nitpicking to get the game where I wanted it, it wouldn’t have worked without you.


Footnotes

  1. Shadari, and Zarth. ♥ ↩︎
  2. https://www.troubleinterroristtown.com/about/history/ ↩︎
  3. TTT uses a mesh collision model. So grenades frequently bounce in unpredictable directions without this change. ↩︎
  4. https://youtu.be/C0TIYxzK7SQ?t=125 ↩︎

Bookmark the permalink.

Comments are closed.