Q:

Realtime Database Rules with Pushed Objects

I have a Realtime Database that pushes objects of the following structure into the DB.
Teams
- 0
-- Players
--- 0
---- ID
--- 1
---- ID
- 1
--- 0
---- ID
--- 1
---- ID

Basically, there are two teams, each team has up to two players, and each player has an ID. Once a game is saved, the game object is pushed to the DB. I am able to get the game data from /games/[unique-ID-generated-by-pushed]
Currently, all my (authenticated) users are able to read all the data under /games. I would like to restrict this: users are to only see games that they were involved in.
I managed to get this far with my rules.
{
  "rules":
  {
    "games":
    {
      "$match_id":
      {
    ".read": "data.child('$match_id').child('teams').child('0').child('players').child('0').child('ID').val() == auth.uid ||
        data.child('$match_id').child('teams').child('0').child('players').child('1').child('ID').val() == auth.uid ||
        data.child('$match_id').child('teams').child('1').child('players').child('0').child('ID').val() == auth.uid ||
        data.child('$match_id').child('teams').child('1').child('players').child('1').child('ID').val() == auth.uid"
      },
    }
  }
}

It isn't elegant, but works when I am querying /games/[unique-ID]. I don't seem to be getting any DataChanged event from /games reference, however, which is what I need.
Any idea how I can proceed from here? Or if it is even possible?

A:

If you want to only allow read access to a game to players that were in that game, something like this should be enough in your current data structure:
{
  "rules": {
    "games": {
      "$match_id": {
      ".read": "data.child('teams/0/players/0/ID').val() == auth.uid ||
                data.child('teams/0/players/1/ID').val() == auth.uid ||
                data.child('teams/1/players/0/ID').val() == auth.uid ||
                data.child('teams/1/players/1/ID').val() == auth.uid"
      },
    }
  }
}

Changes from your rules:

The child($match_id) seems like a mistake, so I removed it.
You can pass an entire path to child, which makes the rules a lot shorter.

If you'd like simpler rules, consider keeping an (additional) simpler list of all players for the game:
games
  gameid1
    players
      player1id: true
      player2id: true
      player3id: true
      player4id: true

Then the read rule can be a single clause:
{
  "rules": {
    "games": {
      "$match_id": {
      ".read": "data.child('players').child(auth.uid).exists()"
      },
    }
  }
}

Essentially you're trading simpler code/rules for more complex data in this case.

