RecordingHelpers and Thirdperson Part 1

A couple of weeks ago I released version 0.3 of Recording Helpers, a client-side plugin for L4D2. After spending countless of my own hours working on making videos for L4D2 I finally decided to make a single plugin to help out with the process.

Before Recording Helpers, my process for recording involved many cfgs, addon vpks, and even SourceMod plugins. In order to make simple cvar changes I had to run server-side admin tools. It wasn’t very fun. So, I wanted to try to make a client-plugin to make the process easier.

Recording Helpers at first simply removed FCVAR_DEVELOPMENTONLY from all CVars. At some point in development of the source engine, this CVar flag was added and made default for all new CVars. Unless a CVar had FCVAR_RELEASE set, it would be hidden from users. Many of the CVars hidden like this were still accessible, either through launch parameters, gamemodes.txt “hacks”, or addons like SourceMod, so the DEVONLY flag has been nothing but an annoyance to legitimate users. Removing these was a no brainer.

But let’s talk about something more interesting: Third person camera support. Thirdpersonshoulder mode was available by default (not cheatflagged, public) for L4D2′s release and many months following. However, about a year after release, Valve released an update disabling thirdperson mode (IIRC through FCVAR_CHEAT), meaning that it could only be used with cheats enabled. This was fine for most people. Video makers could still use thirdperson in their recordings, they just couldn’t play using thirdperson.

After some time, however, people began to complain about no longer being able to use thirdperson in coop games. On October 15, 2010, Valve released an update making thirdperson available in cooperative game modes. Unfortunately, this killed thirdperson for versus modes pretty hard. The new anti-thirdperson code looked something like this:

void thirdpersonshoulder_cmd(CCommand & command)
{
	if(!GameRules::HasPlayerControlledZombies())
	{
		input->ToThirdPersonShoulder(); // global CInput * input
	}
}

Although I didn’t realize it at the time of the change, similar checks were embedded in the CInput camera code, in order to halt other attempts at using thirdperson. I tried hard patching this code out, but I had no luck. Not wanting to risk VAC bans and other things, I looked for a workaround.

As it turns out, GameRules::HasPlayerControlledZombies() is simply a lookup in a global KeyValues file called “gamemodes.txt”, a file packed away in the update/ VPKs. This file has a single section for each gamemode in the game (coop, versus, scavenge, etc.) containing various settings and properties for each. One of these settings is, in fact, the “playercontrolledzombies” boolean. By simply modifying gamemodes.txt, we could tell the game that versus does *not* have player controlled zombies, and start using thirdpersonshoulder again.

Unfortunately, however, this workaround had reprecussions. For starters, releasing a modification like this would mean that players could “cheat” by using thirdpersonshoulder in live matches. For this reason, I never overtly released or distributed the hack. I only ever directly distributed it to video makers I knew and trsuted.

There were other drawbacks of the workaround, though. As you can imagine, the thirdperson command was not the only piece of code to query “HasPlayerControlledZombies”. Turning the setting off also triggered the ingame HUD to improperly render various versus mode elements. The game also produced some sounds, actions, and props differently. This was a fairly nice deterrent to those who would like to use it as a cheat, but it was also unfortunate for video makers. If you wanted to use thirdperson, you’d also have to give up on Damage Pounce amount announcements, and scoreboard displays.

But, the workaround worked well enough. I was satisfied with it for a long time. However, once I started working on RecordingHelpers, making a *real* fix for thirdpersonshoulder became intriguing again.

To be continued….

Matchmode and Config changes

There are a couple issues with the current matchmode system that I’d like to address, and some features that I’d like to add. Currently, the whole config system sits as more of a hack on top of matchmaking, and I think it should really be fleshed out. Currently:

  1. Configs control what plugins load entirely, so wanting to add a plugin to run under confogl on your server means editing all configs separately. They also must know where the plugin exists.
  2. Confogl has to reload itself in order to load/unload plugins properly.
  3. The config files (confogl/plugins/off.cfg) are used to help run matchmode, rather than just to configure the game
  4. Configs execute a lot of options on every map that should only be run once
  5. There is no way to set different cvars on different maps.
  6. If one option needs changing (in mapinfo, or a cfg file), you must redefine the whole file in your config.

To address these issues, we’re basically looking at producing a new config layout. In general, we need a system which will give us hierarchical option reading. That is, we want to be able to set global options, but let configs override them. For any sort of KeyValues file (like mapinfo.txt), this is very simple. For .cfg files, it’s not immediately clear how this will work out.

For #1, we essentially need Confogl to manage plugins from within the plugin itself. The best plan I can come up with is to add a KeyValues file to configs, which specifies Required Plugins (plugins to load) and perhaps Disallowed Plugins (plugins to unload). There would also be a corresponding file in addons/sourcemod/configs/ to provide global options. This would allow configs to specify that they need extra plugins, and let global options be used for things like adding Stats plugins, AdminCheats, or whatever else people think they will need on all configs on their server. By adding a Disallowed Plugins section, we can do things like specifically deny readyup from loading on all configs, and stop funcommands.smx, playercommands.smx, etc. from loading. Of course, when the global config says “Disallow plugin X”, and the local config says “Require plugin X”, or vice versa, there could be issues. We will need to determine a satisfactory solution for this.

Here’s an example of what a plugins-specification KeyValues file might look like:

"Confogl Options"
{
    "RequiredPlugins"
    {
        "l4dready"
        "l4d2_pillhots"
        "l4d2_bossspawningfix"
    }
    "DisallowedPlugins"
    {
        "kigenanticheat"
    }
}

Note that Confogl will try to automatically determine the location of the plugin–whether it is in plugins/ or plugins/optional. I’m still not 100% sure on this format, so I’d like some feedback. This file would sit in addons/sourcemod/configs/ and cfg/cfgogl/CONFIGNAME/, as confogl_options.txt or something similar.

As for the .cfg files, we’d basically be looking at the following:

  • confogl_once.cfg: Runs once when the config is loaded (before map restart, after plugins load) to run single-use commands and set up cvars.
  • confogl.cfg: Runs at map start, after server.cfg, every time the map changes and matchmode is active. Used for commands which need to be run every map. This file could end up pretty empty if confogl_addcvar works properly.
  • <mapname>.cfg (e.g. c1m1_hotel.cfg): This file will be run only when the named map is started, after confogl.cfg runs–to override existing cvar values. This would be useful for defining per-map boss spawning limits (maybe disable coaster tanks on c2m3, or stop extra bosses from spawning on c7m1, c13m2).
  • confogl_off.cfg: This would run when matchmode unloads, probably before plugins are unloaded. Right now hardly anyone customizes confogl_off.cfg, so this may become completely unnecessary if matchmode handles plugin unloading.

These are fairly easy features to implement, and they’ll allow quite a bit of customization. The only question I have is global vs. local hierarchy. For example: server.cfg already runs on every mapstart, so having a “global” confogl.cfg doesn’t seem necessary. However, confogl_once.cfg and the map-specific cfgs doesn’t really have a global counterpart, so they may still be useful. Of course, I have a hard time coming up with a lot of usage examples for any of these as global options–but perhaps they will be useful.

The mapinfo issues should be pretty straightforward–we simply try to read from the config’s mapinfo.txt first, and if the value doesn’t exist, we refer to the global mapinfo.txt. This should be a good solution, but there is still potential for conflicts.

All in all, it looks like we have a pretty good system planned out–some of these unknowns are just stressing me. We’d love to get feedback from config makers about how they feel about these proposed changes, and what they would like to see happen to the config system. The best option is probably just to get something released, and then tweak it slowly. I think it’s almost time to rm ReqMatch.sp :)

CDeveloperBlog::CDeveloperBlog()

This is something I should have done a long time ago. There’s so many issues and happenings in the Left 4 Dead scene, the eSports scene, and with our development of Confogl and related pieces of software.

It’s time to start telling people about all the things we’re doing. :)