depends what you're actually trying to achieve with it.
at the most basic, its 'merely' a case of changing the 'cl' structure into an array instead, fixing the renderer/mixer to not flush too much, and moving all the various cl_entities etc arrays into the cl structure/array.
the more globals you have, the more effort it will take.
of course, you have additional issues like svc(nq)_serverinfo clearing the hunk destroying the hunk of the other connections.
for splitscreen, fte uses a single connection. the server merges the pvs of each player on that connection so ents are properly visible for every player. stats, angle changes, centerprints all have some svc_selectsplit or something before them to ensure they update the correct player. but as there's only a single connection, there's only one set of precaches, only one serverinfo, only one set of baselines and only one hunk - only the obvious parts need to be changed.
note that this breaks viewmodelforclient.
nq would be slightly easier to implement this as it doesn't have lots of prediction code. frankly, input is the hardest area. You'd have to use something like rawinput to get specific identifiers for each device for separate players.
So yeah, avoid globals, because it makes stuff like this hard.

.