My brain is half fried so I'll take a look again after I get some sleep, but you basically have three options, listed from worst-to-best:
Switch block -- literally horrible, please don't do this. It's infinitely better than an if-block or something super stupid but it's still going to be literal spaghetti hell.
Reflection -- really only needs to be done at start-up and it automatically subscribes, but you'll need extra code to pre-define or smart-sort opcodes so they don't change from build-to-build. Also since I'm pretty sure you're using C++, I'm not really sure this is even possible.
Subscriber pattern -- Sure it's annoying because you have a subscription block, but it's still better than a switch-block (which is either all of the handlers shoved into a block, or an expanded and not at all faster version of the subscriber block), it's more reliable with less code than reflection, and it works in every language I know of.
I can't really think of any other patterns, and quite frankly I'm not sure they exist. I think when it boils down to it you have a spectrum of options between "automated" and "efficient" packet registration and handling, and reflection is the (reasonable) limit of automation and a variant of the subscriber method is the most efficient if you're looking at the protocol layer or below. There's just a limit to how little code you can write for this stuff.