I’ve learned much since my last post working with Unity’s new networking layer. I’ve stumbled, I’ve fallen, I’ve picked myself back up only to have UNet push me back down again with its comically over-sized shoe.
The fundamental problem I am facing is that Unity’s high level networking api (HLAPI for short) is designed to make a certain breed of game simpler. Those are any sort of real-time game where players are expected to see the same actions on-screen at the same time. This covers a not insignificant number of game types, but does not cover Altair.
Altair is a turn-based game where each player sees their own unique view of the game state and is free to manipulate it as they see fit to plan out their next move. This could mean they test out several orders and revert them before settling on a decision and submitting their turn for processing or they may issue orders to units that they do not control in an attempt to predict the outcome of a particular tactical maneuver.
Players are also given the opportunity to replay the results of the last round as they see fit. They can watch the action unfold from any angle over and over again to try and determine their opponent’s strategy before planning their next move.
Neither of these two requirements work terribly well with a networking model based around automatically updating the state of shared game objects across multiple players.
So my first hope was to free myself of the majority of networking complexities by leveraging the default NetworkManager where possible and dropping down to a lower level messaging API without having to worry about synchronizing game objects. This basically ends up utilizing Unity’s built in PlayerObject concept which makes things a little bit irritating but theoretically workable.
There are two problems that I ran in to with the baked in PlayerObject. The first is actually caused by my choice of underlying framework. I settled on strangeioc a while back as it promised a beautiful nirvana of event-driven and disconnected game code not unlike some enterprise applications I’ve worked with. The short version here is that strangeioc encourages you to structure your application in a certain way and it just doesn’t fit together nicely with the HLAPI in UNet. There is very little information to be found on this topic at the time of this writing but just know that you’re going to have a heck of a time trying to bring these two things together.
Assuming I could live with the hacks I had to do to get UNet and strange working together, the biggest problem is actually with the PlayerObject itself as it enforces a server authoritative model which comes with a bunch of intentional limitations to funnel communications to the server exclusively through the PlayerObject as Command and ClientRpc calls. I have been playing with this idea for over a week and I thought I was getting somewhere until I tried to actually send data to the server.
As I mentioned in my previous post, Unity only lets you serialize a relatively small subset of what I actually need to be able to serialize. The biggest gotcha is the inability to serialize complex classes of any kind. This was a problem and so I turned to using the built in .NET serializers to instead generate a byte array or some other primitive representation to fire across the wire. That’s when I ran in to the next problem:
SerializationException: Type UnityEngine.Vector3 is not marked as Serializable.
So much for that then. Without having those basic Unity types marked as Serializable, I would need to either create some sort of surrogate data types for serialization or write custom serialization methods all over the place. Not terribly appealing.
For my next and final attempt at this, I decided to go to the Unity community and find a serializer that would actually do what I needed it to do. I toyed around with a couple of options including protobuf but ultimately settled on fullserializer which happens to be supported on every target platform that Unity can publish to; how convenient!
I would love to have a happy ending to be able to wrap this post up with but the reality is that I’m still chipping away at turning this in to a working solution.
My current build does have designated GameServer and GameClient objects that both play nicely with strangeioc and I am able to serialize objects into messages and have handlers process them appropriately on both the server and the client.
There is however still a whole lot of housekeeping that needs to be done like linking a network connection to a player representation and plugging it in to the actual game model.
I suppose I needed something to write about next week anyway..