I’ve spent a lot of time working with UNET and thought I’d compile a list of bugs/issues that I’ve come across. Overally I think UNET will be good once it gets finished, but some issues at the moment. I hope this can help other players and also that a Unity developer can help straiten things out.
I’ll update this as I find new bugs or as things are fixed. Let me know if anything is incorrect.
Also if anybody from Unity reads this I’ll be happy to create reduced bare-bones code to replicate any of these issues for debugging.
BUGS
1: [734257] UNET [Command] Does Not Work With Inheritance or Commands With the Same Name.
2: [794443] UNET NetworkAnimator Broken With Multiple Animation Layers
When using NetworkAnimator only the base layer[0] will sync to client. Other layers (like layer [1] or [2]) will not sync to client.
https://fogbugz.unity3d.com/default.asp?794443_l24atkuvp8v1kcli
Oct 17th, 2016: Issue is marked as closed/postponed (will be fixed at a later date).
3: [TBD] UNET SyncList Throws Not Initialized Errors When Set During Awake()
4: [TBD] UNET Setting SyncVar/SyncList Repeatedly to the Same Value Still Sets Dirty Bits and Chews Up Network Bandwidth
I’d recommend a UNET check to see if value has changed before setting dirty bit. This prevents improper use of SyncVar’s/SyncList where bandwidth is used up for some obscure reason.
5: [762719] UNET Message Inheritance Broken
https://fogbugz.unity3d.com/default.asp?747334_sfs90f3bcm8nrcok
6: [TBD] UNET SyncStructs Do Not Automatically Set Dirty Bit When an Internal Field is Changed
I’d recommend either SetDirtyBit when internal struct field is changed, or throw errors when changing internal fields to make SyncStructs foolproof. As it is they can be used incorrectly with no warnings.
7: [TBD] UNET Commands Have Delay Even on Host
When you issue a command on server there is a delay even though it’s talking to itself and seems like it should be instant. For example, if you [Command] move inventory item to slot 17, then access the item in slot 17… the item doesn’t exist yet… there’s a delay before it happens. So this forces me to have 2 separate codes, one for host that does not use commands (and is therefore instant sequential code), and one for client, which is the whole thing UNET was trying to avoid.
8: [822355] UNET [SyncVar] Hook Not Called on Client in Time Around Scene Change
In the time around a scene change when a SyncVar is changed the hook will not always be called on the client… even though the value properly updates. Bug reported 2016_08_08.
https://fogbugz.unity3d.com/default.asp?822355_sv7nmvbc0bv08rlh
https://fogbugz.unity3d.com/default.asp?821633_5ps5qsis7vrrevrr
9: [TBD] UNET SyncList Callbacks Have Insufficient Information
For proper usage you need access to the OldVal and NewVal at the time of a Callback. SyncList callbacks only provide information on the newValue, not the oldValue. So when a value is changed/removed you do not know __what__ was changed or removed.
https://fogbugz.unity3d.com/default.asp?804379_hukgv5qh81vqncb0
https://feedback.unity3d.com/suggestions/unet-synclist-callback-needs-access-to-oldvalue
10: [TBD] ClientRPC’s are Dropped if Sent Around SceneChange
I sent a ClientRPC from the Host around the time of a new scene load the ClientRPC is never received by the clients (even the host client!).
https://fogbugz.unity3d.com/default.asp?831893_63262uhermqgeju4
11: [TBD] Ambiguous NetworkDestroy
NetworkServer.Destroy() destroys an object on server and all clients. It calls a NetworkBehaviour.OnNetworkDestroy() on all clients. However, OnNetworkDestroy() is also called when destroying an object other ways (closing editor, exiting scene, etc.) and there is no easy way to distinguish if the object is being destroyed due to NetworkServer.Destroy()… or due to another reason.
12: [TBD] SyncList Callback Failure
For persistent DontDestroyOnLoad network objects with a SyncList the callbacks are not received on the client during the period around a scene transition. I believe this is due to the client’s isReady flag being set to false and NetworkIdentiy.observer list momentarily being removed.
https://fogbugz.unity3d.com/default.asp?833578_76432msnc8da1g6k
13: [TBD] NetworkServer.connections Null
After a client disconnects the NetworkServer.connections associated with that client will not be removed but the collection, but instead made null. This can cause errors unless you are constantly performing null checks.
14: [TBD] SyncVars can Become Desynced Between Server and Client
After a client connects all serialized data is sent only to the new client and dirty bits are cleared. This means that any SyncVar values are not sent to existing clients. Existing clients will have an incorrect SyncVar value indefinitely unless it is changed again on the server forcing all values to be sent again to all ready observers.
15: [TBD] Networked GameObjects Sometimes are Not Destroyed when Network is Terminated
Sometimes (both on Host and Client) networked GameObjects are disabled (not destroyed) when the network is terminated. Unsure of all the various ways this can occur but I submitted reduced sample code to replicate bug to QA.
https://fogbugz.unity3d.com/default.asp?867411_621m3r66s84lt94f
16: [898387] NetworkIdentity Causes Animator.SetFloat to Fail
Calling NetworkIdentity.SetFloat(…) does not always work if the object has a NetworkIdentity component attached. The Animator.parameter value in GetFloat(…) and the value in the inspector also do not match.
https://fogbugz.unity3d.com/default.asp?898387_m1dp0u9neo5qlsa4
17: [932531] NetworkIdentity Causes GetComponentInParent to Return Null
In the build (not the editor) GetComponentInParent will fail and return null when called externally before Awake(). Bug only occurs on UNET objects with NetworkIdentity… non-networked objects work fine. Bug #16 and #17 likely related?
https://fogbugz.unity3d.com/default.asp?932531_okip5dh462jnf23q
DESIGN ISSUES
101: Initialization of Player is Tricky to Do
UNET serialization and Unity prefab/scene serialization are both incompatible with reading/writing to files. So essentially you end up with 3 forms of serialization… ugh!
102: Cannot AddComponent at Runtime or by Script During Initialization
It really makes it hard to use inheritance and manage scripts when you must drag & drop and manage EVERY prefab. Change 1 thing and you have to repair every prefab in the game manually…
103: HLAPI Does Not Allow For Split Authority
i.e. where NetworkAnimator is controlled by server, but NetworkTransform is controlled by client (this is a common strategy… server side authoritative player action control, but client side player motion control)
104: SyncVar’s & Client Control
It would be nice if SyncVars could be controlled by a client. Instead you have to send message/Command to server and then have the server change the SyncVar, and then ignore the callback on the original client sender of the SyncVar. It’s very convoluted.
105: NetworkWriter and Bools
When using the NetworkWriter/Reader bools are treated as bytes. Instead of writing a 1 or 0 it writes 1000000 or 00000000. This causes as much as 8x the bandwidth as necessary. I’d recommend the network writer/reader to allow writing single bits. If whole bytes are needed for the network package then round-up at the very end to the nearest whole byte. This would allow bools and syncVar dirty masks (see below) to be much more efficient.
106: SyncVar’s Dirty Mask Length = 32
You cannot have more than 32 syncVars. And if you have less than 32 SyncVars then it sends unnecessary data. For example if I have an on/off switch with 1 SyncVar bool it will send 16 bits of data (8 bit compressed mask and 8 bit bool)! Bools should not be part of the DirtyBit mask system (since the dirtyBitMask itself uses as much bandwidth as the bool!). Additionally I think the dirtyBitMask should be variable length instead of Uint32. Summary:
-DirtyBitMask should be variable length, not constant uint32
-SyncVar Bools should not be part of dirtBitMask
-Decouple dirtyBitMask from NetworkBehaviour.isDirty (changing a bool should still mark NetworkBehaviour as dirty)
-Allow NetworkReader/Writer to send single bits (bits shouldn’t read/write as 8bit bytes)
107: NetworkBehaviour Do Not Have DirtyBit For the Script
If you have an object with 20 NetworkBehaviour scripts attached and you change 1 SyncVar bool then all 19 other NetworkBehaviour scripts must write a 32bit zero to the NetworkWriter during OnSerialize. This means you will send 648 bits (20x32 + 8) of data just to send 1bit of bool data. Each NetworkBehaviour script should really have it’s own dirtyBit (a dirtyBitMask for all NetworkBehaviour scripts) for network bandwith reasons.
108: Client-Control of NetworkBehaviour SyncVar Fields is an Absolute Nightmare!!!
During OnDeserialize you must be careful to always read data, even if you are ignoring it. This is because OnSerialize always sends information to all clients… even if the client is the owner. So if you have a client-controlled script (i.e. NetworkTransform) you must send a serialized message from Client → Server, then replicate this data to other clients by NetworkBehaviour.OnSerialize/Deserialize. The problem is that this also sends the data back to the original sender (the client authoritative owner). The owner must read this data (otherwise NetworkReader becomes out-of-sync) but ignore this data. Also, during scene changes all the serialized data is re-sent and this data also needs to be ignored otherwise a small backwards time step is taken where a client-owner can have it’s data overridden by older server controlled data. Client-side control of synced data (like player position) is something many games will have but there is no clean UNET implementation.
109: NetworkTransform Compressed Rotation Resolution Poor
If compressed, the NetworkTransform compresses rotation to a short (16bits). The angles are basically rounded to whole degrees (i.e. 183.27deg becomes 183deg). With 16 bits the compressed angle could be 360/(2^16) or 0.005deg of resolution… but instead we get 1.000deg of resolution which makes things appear glitch even though enough bits are being transmitted to make it smooth. On a similar note if Bits (instead of Bytes) are used then a BitWriter/Reader is capable of something like this: BitWriter.WriteFloatCompressed(0f, 360f, 12) //corresponding to 0degMin, 360degMax, and 12bits of resolution. And float = BitReader.ReadFloatCompressed(0f, 360f, 12f).