I found an article that explains why STUN doesn't work when Carrier-Grade NAT is involved.
STUN assumes that the network address translation (NAT) mapping and firewalling preserves the NAT mapping and firewall opening, for return traffic to the source UDP port, regardless of the destination address. CGNAT has endpoint-dependent NAT and firewalling, so STUN doesn't work with it.
Solutions for people who have CGNAT.
1. Sign up for public IPv4 address service
or,
2. Use a VPN that has endpoint-independent NAT and firewalling.
or,
3. Use a VPN that has endpoint-dependent NAT and firewalling, but that supports port forwarding. Enable a port forward to your public address. Host the game on the port that you have forwarded.
or,
4. Same as solution 3, but instead of hosting, apply a patch to the game to control the source port for the outgoing connection. Ensure that 0ad uses the port number that you have forwarded as its source port for the outgoing connection. Connect to the friend's game as a client.