Skip to main content
Phrasing improvements
Source Link
DMGregory
  • 140.8k
  • 23
  • 257
  • 401

But a bad actor could release a community mod for your game that does something popular superficially, but withwhile smuggling spyware into the executable code hiding inof the achievement conditions that does something sneaky, like acting as spywarecondition. You You want to avoid exposing your players to a threat vector like this, in case your game gets popular and develops a thriving modding community that can attract hackers who preypreying on less tech-savvy playersfolks.

In that example, my threeI only needed two types were:

  • Count - triggers when you've created enough blobs of a certain type (parameters: type, threshold count, and a flag to choose between simultaneous or lifetime total)
  • Connection - triggers when you have enough blobs of a certain type surrounding another type (parameters: center type, connected type, threshold count)

In code, this looks like a switch statement in your loading routine, checking for the key strings (or using enums if your environment makes that convenient), and populating your new Achievement object with a new Criterion object of the correct subtype, or list thereof.

But a bad actor could release a community mod for your game that does something popular, but with executable code hiding in the achievement conditions that does something sneaky, like acting as spyware. You want to avoid exposing your players to a threat vector like this, in case your game gets popular and develops a thriving modding community that can attract hackers who prey on less tech-savvy players.

In that example, my three types were:

  • Count (parameters: type, threshold count, simultaneous or lifetime total)
  • Connection (parameters: center type, connected type, threshold count)

In code, this looks like a switch statement in your loading routine, checking for the key strings (or using enums if your environment makes that convenient), and populating your new Achievement object with a new Criterion object of the correct subtype.

But a bad actor could release a community mod for your game that does something popular superficially, while smuggling spyware into the executable code of the achievement condition. You want to avoid exposing your players to a threat vector like this, in case your game gets popular and develops a thriving modding community that can attract hackers preying on less tech-savvy folks.

In that example, I only needed two types:

  • Count - triggers when you've created enough blobs of a certain type (parameters: type, threshold count, and a flag to choose between simultaneous or lifetime total)
  • Connection - triggers when you have enough blobs of a certain type surrounding another type (parameters: center type, connected type, threshold count)

In code, this looks like a switch statement in your loading routine, checking for the key strings (or using enums if your environment makes that convenient), and populating your new Achievement object with a new Criterion object of the correct subtype, or list thereof.

Explaining implementation, using criteria rather than condition (which can also mean "state")
Source Link
DMGregory
  • 140.8k
  • 23
  • 257
  • 401
{
    "defeatGruntsWithFire": {
        "name": "Playing With Fire",
        "description": "You defeated 5 grunts using fire magic",
        "repeats": 5,
        "conditions""criteria": [
            {
                "rule": "gameEventLabel",
                "match": "defeatEnemy"
            },
            {
                "rule": "enemyType",
                "match": "grunt"
            },
            {
                "rule": "damageElement",
                "match": "fire"
            }
        ]
    }
}

Each achievement then gets an array of conditionscriteria to check, and only when they all match do you increment the achievement counter (for multi-step achievements) or award the achievement. Each conditioncriterion is an object with a parameter it's looking to match, and a rule for what it's matching against. As your vocabulary of conditionscriteria grows, you'll soon be able to make new achievements by composing existing conditionscriteria rather than writing new conditioncriterion types into the system.

In code, this looks like a switch statement in your loading routine, checking for the key strings (or using enums if your environment makes that convenient), and populating your new Achievement object with a new Criterion object of the correct subtype.

{
    "defeatGruntsWithFire": {
        "name": "Playing With Fire",
        "description": "You defeated 5 grunts using fire magic",
        "repeats": 5,
        "conditions": [
            {
                "rule": "gameEventLabel",
                "match": "defeatEnemy"
            },
            {
                "rule": "enemyType",
                "match": "grunt"
            },
            {
                "rule": "damageElement",
                "match": "fire"
            }
        ]
    }
}

Each achievement then gets an array of conditions to check, and only when they all match do you increment the achievement counter (for multi-step achievements) or award the achievement. Each condition is an object with a parameter it's looking to match, and a rule for what it's matching against. As your vocabulary of conditions grows, you'll soon be able to make new achievements by composing existing conditions rather than writing new condition types into the system.

{
    "defeatGruntsWithFire": {
        "name": "Playing With Fire",
        "description": "You defeated 5 grunts using fire magic",
        "repeats": 5,
        "criteria": [
            {
                "rule": "gameEventLabel",
                "match": "defeatEnemy"
            },
            {
                "rule": "enemyType",
                "match": "grunt"
            },
            {
                "rule": "damageElement",
                "match": "fire"
            }
        ]
    }
}

Each achievement then gets an array of criteria to check, and only when they all match do you increment the achievement counter (for multi-step achievements) or award the achievement. Each criterion is an object with a parameter it's looking to match, and a rule for what it's matching against. As your vocabulary of criteria grows, you'll soon be able to make new achievements by composing existing criteria rather than writing new criterion types into the system.

In code, this looks like a switch statement in your loading routine, checking for the key strings (or using enums if your environment makes that convenient), and populating your new Achievement object with a new Criterion object of the correct subtype.

Adding caution about eval
Source Link
DMGregory
  • 140.8k
  • 23
  • 257
  • 401

The usual caution with JSON is that it's easy for end users to edit. That can be a plus for allowing flexible modding of your game, but it means you should treat the data as untrusted.

Never call eval() on untrusted data.

If this is just a player hacking their (local) game to unlock all the achievements just for walking 2 steps, or give themselves unlimited money, no biggie - as long as it's not impacting fairness in online multiplayer or leaderboards.

But a bad actor could release a community mod for your game that does something popular, but with executable code hiding in the achievement conditions that does something sneaky, like acting as spyware. You want to avoid exposing your players to a threat vector like this, in case your game gets popular and develops a thriving modding community that can attract hackers who prey on less tech-savvy players.


What I did when implementing something similar in a recent jam game was to establish a vocabulary of "types" of achievements that needed distinct behaviours, which I could then specialize into specific achievements by setting parameters.

What I did when implementing something similar in a recent jam game was to establish a vocabulary of "types" of achievements that needed distinct behaviours, which I could then specialize into specific achievements by setting parameters.

The usual caution with JSON is that it's easy for end users to edit. That can be a plus for allowing flexible modding of your game, but it means you should treat the data as untrusted.

Never call eval() on untrusted data.

If this is just a player hacking their (local) game to unlock all the achievements just for walking 2 steps, or give themselves unlimited money, no biggie - as long as it's not impacting fairness in online multiplayer or leaderboards.

But a bad actor could release a community mod for your game that does something popular, but with executable code hiding in the achievement conditions that does something sneaky, like acting as spyware. You want to avoid exposing your players to a threat vector like this, in case your game gets popular and develops a thriving modding community that can attract hackers who prey on less tech-savvy players.


What I did when implementing something similar in a recent jam game was to establish a vocabulary of "types" of achievements that needed distinct behaviours, which I could then specialize into specific achievements by setting parameters.

Source Link
DMGregory
  • 140.8k
  • 23
  • 257
  • 401
Loading