gameplay: to hard-code or to not hard-code, that is the question

Talk about anything related to Unvanquished.
User avatar
Ishq
Project Head
Posts: 1145
Joined: Tue Mar 06, 2012 8:32 pm UTC

Re: gameplay: to hard-code or to not hard-code, that is the question

Post by Ishq »

I think we should make the game as flexible as possible, which means allowing as many changes as possible without recompiling the nexes.

However, I don't think this comes down to as a matter of "let's hardcode the game or not". Without a proper framework for extending the game without recompiling nexes (and config files and cvars are NOT a proper framework), the maintenance burden of supporting configuration hurts velocity when prototyping new features (that not just changing values around..)

User avatar
killing time
Programmer
Posts: 162
Joined: Wed Jul 04, 2012 7:55 am UTC

Re: gameplay: to hard-code or to not hard-code, that is the question

Post by killing time »

I rate the different possibilities out of 5 for four use cases: (1) hosting a mod with modified gamelogic; (2) hosting a server with a vanilla gamelogic binary; (3) maintaining the codebase; (4) doing quick experiments with different values of a cvar.

Code: Select all

            Modhost   Vanillahost   Maintainer   Quickexperiment
Hardcode    3         0             5            1
Cvar        5         5             3            5
Configfile  2         3             1            2

For a mod host, hardcoding is not so bad since you're maintaining a set of gamelogic patches anyway. A cvar is best since it gives you the options to change it either via patching the code (might be preferred if you're concerned about version control or spelling errors in the cvar), or setting it via configs. Config files are worse since it requires shipping additional files and it's especially bad if the file lives in a different repo - then you lose version control.

For a vanilla gamelogic host, of course hardcoding is worst: you can't do anything. Otherwise same considerations as a mod host.

From a code maintainer perspective, I really hate config files. When I encounter a variable that comes from a config file it's a big speed bump because I have to figure out which pak the config file is, what the config file name is, open the zip file in a file browser and open the file just to see the value. While from this perspective hard-coded is the best because it's the simplest. Cvars are not too bad either. Maybe there would be some sort of scaling problem from having thousands of cvars, but I don't especially think so. But we'd have to come up for some framework for programmatically generating cvars if we want to replace all config file stuff and this might be somewhat complex.

Finally, we consider rapidly testing different values of a cvar to find the optimum. The example I have in mind here is testing something like x y z offsets for a model to make it appear in the right place. Config files are almost as bad as hard-coding here since they require a gamelogic restart to take effect. When I'm doing something like this I usually hack in a cvar to be able to rapidly test the values.

So in conclusion config files score so poorly across the different axes that they should never be used. Cvars are the nicest, but it's more work to set them up so I'm fine with hard-coding too.

SeanCJ
Posts: 12
Joined: Tue Mar 15, 2022 9:31 pm UTC

Re: gameplay: to hard-code or to not hard-code, that is the question

Post by SeanCJ »

I'll just go for a short reply here for now. (I'll try to give more detail later).

First, I appreciate that this is being discussed :smile: !
Second, just to state my preference I would very much be happy to see something akin to "config" files (the way you are referring to it Ishq, I'm have the feeling maybe I imagine something different than you people here mean though?).

Last (for now) - I like the way you break it down Kai, and yeah, think its a question of goals. I do disagree with your overall assesment though :tongue: . Coming at this as a programming newbie (and newbie"ish" to Trem/Unv), then the idea of having to root around for CVars (or even compile the damn thing - god forbid :P), is really unattractive compared to being able to just mess with pre-existing files in a text editor.

I wonder - might there be elegant solutions that combine the advantages of the different approaches ? (well - as a compromise, it would probably come with its own disadvantages :confused: ).

User avatar
killing time
Programmer
Posts: 162
Joined: Wed Jul 04, 2012 7:55 am UTC

Re: gameplay: to hard-code or to not hard-code, that is the question

Post by killing time »

SeanCJ, you are probably overestimating how much control the config files give you. It's basically a bunch of numbers that set some gameplay parameters and determine how things are displayed. You can only do trivial stuff like changing the rifle damage from 5 to 6 or making the Dretch appear upside down. You can't really create new content with them. If you have your own vision for the game, you're going to have to dive into the code to achieve it. "Mods" created by merely changing the config files are something I would frankly rather discourage; the motivation for these can only be "pushing buttons because they're there and easy to push" rather than having a unique vision for the gameplay.

P.S. I'm not Kai

User avatar
killing time
Programmer
Posts: 162
Joined: Wed Jul 04, 2012 7:55 am UTC

Re: gameplay: to hard-code or to not hard-code, that is the question

Post by killing time »

I strongly oppose the configurable game mechanics idea, where you would have a config file with 100 game mechanic options, most of which are only used by a single thing. bool weapon.UsesShotgunSpreadPattern, bool class.HasMantisPounce, et cetera. This is a nightmare for maintainability, much more so than current config files. How do we handle the exponentially large space of possible game mechanic combinations? Either (a) most combinations which are not actually used by Unvanquished are broken, in which the supposed "configurability" is a sham; or (b) we spend a huge amount of time testing option combinations which are not used by our game. I don't want to do (b) because I want to make a game, not a (Unity-style) game engine.

User avatar
Viech
Project Head
Posts: 2139
Joined: Fri Aug 03, 2012 11:50 pm UTC
Location: Berlin

Re: gameplay: to hard-code or to not hard-code, that is the question

Post by Viech »

Here are my two cents.

Config files

I agree with what was said before: with the current implementation they are a development hurdle. It is very inconvenient to add new config file settings to the game; they are kind of a workflow killer. However, config files do form an interesting middle ground: They allow server operators to adjust the game without being able to compile it, yet users are at least minimally warned that the server they play on is modified (although users have no reasonable way to know how).

I find that the first advantage of config files, allowing modifications without compilation, is only a minor advantage. We are talking here about servers that do not add any interesting gameplay elements (no new weapons, classes, structures) nor propose an interesting redesign of something that exists. Any such nontrivial modification would immediately require code changes (which are easier if config files don't exist). killing time summed up what I also think:

killing time wrote:

"Mods" created by merely changing the config files are something I would frankly rather discourage

On the other hand, the second advantage of config files, that they communicate at least that a server is modded, I find rather important. If servers could be modified without players being aware of this, this would be a large problem for anyone except for the server operator: both players and developers suffer from this, as detailed below. Config files at least require that a mod-*.dpk is downloaded by each player, giving a minimal warning that changes were made.

Developers would suffer from hidden mods

This is because they would not receive correct feedback about the state of the game. If a problem in the vanilla game is resolved by invisible modifications on the populated servers, then developers are less likely to become aware of this issue, leading to a stale vanilla game. A hidden change of one aspect of the game could even prompt users to ask for a change of some other aspect, leading to the vanilla state deteriorating. Only if players and developers are aware that, and also how, a server is modified, the base game can improve as a result of good changes.

Players would suffer from hidden mods

This does not apply to all modifications equally. It applies very heavily, in particular, to modifications to combat balance. There are at least three important combat skills: aim, movement, and situational awareness. Aim is what most players already bring, movement and awareness are what separates experienced players. With awareness I refer to the fact that experienced players will know instinctively when they have to switch between aggression and fleeing. They are thus more likely than newcomers to win a fight or retreat from a fight with very low health. This is in essence a consequence of health pools, damage values and attack frequencies being turned into "muscle memory". Improving at the game is fun and involves building up this kind of muscle memory.

The mentioned values can be changed by config files and every such change will make people adapt in the long run (regardless of whether players know if they play on a modded server or not, though knowing about changes gives them more agency). At the end of the day, players profit if combat balance values are equal on all servers they play: they can improve gradually, enjoy multiple servers, and do not suffer from badly informed decision making. So even though one could think that modability is the highest goal, here central decision making is in the best interest of the player.

The role of cvars

Cvars have some immediate advantages over config files: They are much easier to set up, you can more easily tune them when looking for a good default value, and one can later configure them on a per-map basis.

But, at this point cvars are only sent to the client if they are needed for predictions to stay in sync with the server, and even in these cases, non-default values are not clearly communicated to the player. So it is safe to say that cvars are hidden modifications. Unlike config files, they even open the door to abuse as server operators could change them mid-game without anyone noticing.

Why are some gameplay elements still configurable via cvars? This is only because for some aspects, which are not related to combat, we do want variance in the server landscape, and likely the developer couldn't be bothered to realize it via config files. For instance, it's nice to have servers with small maps and quick unlock progress alongside servers with large maps and, say, a high number of build points to allow for longer games. This tailors to different types of players and their needs, without causing bad muscle memory: these changes are either clearly communicated during the match (e.g. number of build points available) or they do not matter so much for a player's decision making (e.g. you want to gain momentum quickly regardless of how much you need for the next stage).

At this point in time, I'm not aware of a single combat related configuration that can be set via cvars, and for now I would prefer that it stays like this.

Looking forward

I think one could make pretty much anyone in this thread happy if there was a setup (possibly extending current cvars) that

  1. was as easy to develop with as static cvars,
  2. is possibly more structured than them (as freem pointed out), and
  3. enforces a clear communication of any modification to the player.

For (3), I wouldn't accept anything lesser than a popup or similar UI element shown on connect and map load. For any non-default, gameplay-relevant setting, it should list the current value alongside the setting's default. Changes to these settings during the live match should either require /devmap or show a warning to all clients. Players need this level of insight.

Until we have such a system, hardcoding will often be not just the laziest but also the safest option.

Responsible for: Arch Linux package & torrent distribution, Parpax (map), Chameleon (map texture editor), Sloth (material file generator), gameplay design & programming, artistic direction

User avatar
Gireen
Graphic Designer
Posts: 295
Joined: Wed Mar 07, 2012 1:26 pm UTC
Clan: [DoH]
Location: Germany
Contact:

Re: gameplay: to hard-code or to not hard-code, that is the question

Post by Gireen »

My opinion on this is Cvars to the max :grin:
Let players and server owners experiment as much as they want.
It outsources testing and experimenting and if there come up interesting changes they could be adapted. It also offers a gateway for players to become interested in modding and could bring more contributes. :smile:

Servers should communicate all gameplay cvars and display its differences from vanilla.
Mid game changes could be announced like its already with g_gravity to prevent cheating.
Abusive admins will always be there but players have the choice to not join these servers, this should not prevent needed features.

From a support standpoint Unv devs should only support and test the default values. How other combinations work or break things is up for the server owners to figure out. :tongue:

From a technical standpoint i would like to have cvars be accessed the same way for server and client. That shared cvars are temporary overwritten for the client instead of having to add new variables to cgs.

fear ma engrish :granger:

User avatar
illwieckz
Project Head
Posts: 718
Joined: Sat Aug 11, 2012 7:22 pm UTC
Location: France
Contact:

Re: gameplay: to hard-code or to not hard-code, that is the question

Post by illwieckz »

killing time wrote: Wed Apr 19, 2023 10:43 pm UTC

From a code maintainer perspective, I really hate config files. When I encounter a variable that comes from a config file it's a big speed bump because I have to figure out which pak the config file is, what the config file name is, open the zip file in a file browser and open the file just to see the value. While from this perspective hard-coded is the best because it's the simplest.

To me this is just stating that the things are currently poorly designed, not that there is no way and will never been any way to have config files that are easy to work with. I agree the current config file mechanism is bad, but on the other hand I want more config files. Those two statements don't contradict themselves, they just both mean we need a better config file system.

Let's give some design example we may implement:

  • All config files get a structured format, if possible an existing one like json, toml or yaml I don't really care, the best ones would be the ones able to determine the type of the data, and we need structures like dictionaries.
  • We need a hierarchical tree of option, a bit like new gen cvars, options like buildTime in pkg/unvanquished_src.dpkdir/configs/buildables/hive.attr.cfg would be config.buildables.hive.attributes.buildTime.
  • We need a special object (like new-gen cvars) for those config values, we would have no code to read this or that config file specifically, expecting this or that value, we would just load all the config files available in VFS (thanks to DPK DEPS, we already only load what's needed and no more), and fill the tree of values.
  • Would never do int buildTime = config.buildables.hive.attributes.buildTime.Get(); x = buildTime + something; but config buildTime = config.buildables.hive.attributes.buildTime; x = buildTime.Get() + something;, meaning we would always pass the object and retrieve the value at the very last moment. We may add special properties to the object like the default value (like new gen cvar does) and even the original file path and original dpk it was read from. This way when debugging, we can just explore the object properties to know more. With the bonus if all objects are also a cvar, developer would even not have to edit the file in the dpk to test other values.
  • Bonus: we could automatically create a cvar for all those config variables, with default value being the one in the file. It also means the previous example would reuse cvar code when doing computations like the previous example, it means the config object would just be a cvar object with more properties.

So my opinion is also Cvar to the max, because in fact Cvars are already the half-completed config mechanism we need. Half of the work is done, now we have to automatically populate them from config files, and add some attributes to make debugging easier.

This comment is licensed under cc ​​by 4 and antecedent.

User avatar
illwieckz
Project Head
Posts: 718
Joined: Sat Aug 11, 2012 7:22 pm UTC
Location: France
Contact:

Re: gameplay: to hard-code or to not hard-code, that is the question

Post by illwieckz »

There would be only one default value, the one from config file; there would be no define and no declaration in code.

So, let's give a better example than my previous one:

Code: Select all

void main() {
  // iterates all config files in all dpks
  loadEveryConfig();
  func1()
}

void func1() {
  // makes a loudy error if that config key is missing.
  config buildTime = getConfig("config.buildables.hive.attributes.buildTime");
  func2(buildTime);
}

void func2(config c) {
  x = c.Get() + something;
}

This comment is licensed under cc ​​by 4 and antecedent.

User avatar
Gireen
Graphic Designer
Posts: 295
Joined: Wed Mar 07, 2012 1:26 pm UTC
Clan: [DoH]
Location: Germany
Contact:

Re: gameplay: to hard-code or to not hard-code, that is the question

Post by Gireen »

I think that would become more complicated to use. currently this would be only

Code: Select all

int buildTime = BG_Buildable( buildable )->buildTime;

I see now a bit the problem with cvars usage in code.
How would it be to use cvars only as an interface for players and storage for values.
instead of reading config files the game iterates over the config cvars and populates eg buildableAttributes_t

in case of the hive we would have instead of configs/buildables/hive.attr.cfg any .cfg that is executed containing:

Code: Select all

g_config.building.hive.unlockThreshold   150
g_config.building.hive.buildPoints       12
g_config.building.hive.buildTime         20000
g_config.building.hive.buildWeapon       alien

on the client side the config file could be parsed instead of being executed. that works like the current parser but with informations moved from filepath into variable name.

fear ma engrish :granger:

Post Reply