This would also bring a kind of non-determinism to your code, which is bad
The core problem is that "players disconnecting" is non-deterministic. You don't have control over that.
What you can do in your code is have a clear interface to the non-deterministic parts of the program (player connection/disconnection and player commands) and funnel between non-deterministic and deterministic domains (code modules) in your program at pre-determined times (typically, once every main "tick" or simulation step.)
In many ways "player X disconnected" is no different from "player X walked forward" and is another event that your system needs to react to and do the right thing for. Typically, your network connection handling code will forward events from the connection, which includes things like "player gave a command," and thus also "player disconnected," and the rest of the code simply reacts to those commands. Your world, in turn, typically will emit events such as "bomb B exploded at position C" or "player Q picked up health pack H" or whatever. This will also include "player X disconnected" so that other players and entities who need to know this, find out about it.
Yes, this means that you have to sequence the shutdown of players, because other players will be referencing the original player entity after the network has disconnected but before everything is properly torn down. Typically, you solve this by having "network connection" be different from "player entity" in your object model. And, typically, you actually end up with an even finer granularity because different objects need different lifetimes. For example, "player controller objects" are often as thing, as are "player world effects" (spells, etc) Each of these objects have a carefully managed, well defined lifetime, and the role of the game engine is to make sure that objects live during their lifetime, emit events to those who need to see those events, and then reap the objects when they are supposed to go away.