Monday, November 19, 2012

Unity Networking is easy? part -1-


Decisions, Descisions

There are quite a few solutions to Unity3d multiplayer frameworks. The bottom line is you can use the free unity3d native networking for basic-needs or 3rd-party solutions for a cost. I used the free version for prototyping purposes. more details on this towards the end of this post.
I will go over 3 of the options. If you want a more in-detail explanation,see: comparing photon, uLink and unity3d and,  This table includes a dozen options.

Unity itself 

Out of the box unity provides networking-libraries, ability to run a unity instance as a dedicated/non-dedicated server, which you need to host yourself , and a "lobby-manager" called the Master-Server which is hosted by unity and good for low-scale.
Cost : code is free, hosting is not.
Problems:  unkown scale, not-very-mature-code, see:

Photon

Non-unity specific which means server code can`t use Unity physics/colliders.
For very low-scale, provide SaaS (PhotonCloud). but it`s limited to 500 message per second per room, which means, for a fast-moving game (15 update FPS) only ~5 users.  Also the SaaS solution limits the server-logic.
For larger scale or more server-logic freedom, you need to rent and install a dedicated server.
Cost : small PaaS free . For bigger it costs per server and hosting.
Problems: non-unity server-logic, SaaS is impractical for fast/big games.

uLink

Unity based MMO Server, which built upon and improves the problems/scale of Unity libraries.
Cost : for indie 550Euro per title license. hosting not included..
Problems: the cost.





Let`s start with the free one


Native unity3d, which has the great benefit of being free, and should provide us with a baseline.
There is a badly documented Networking demo on Unity3d site, download the zip file and open the car scene.  Run two instances of the scenes, one inside the editor , the "Server" and another a standalone/web-player will be the "Client". This is the log in the server side.

The user clicks on the start-server button:
1. We call to: UnityEngine.Network:InitializeServer(Int32, Int32, Boolean)
    Running as server. Player ID is 0.
2. OnServerInitialized()  - will be called on the server as a callback auto-magically.
   GUID is 549439155633276175. Use this on clients to connect with NAT punchthrough.
    Local IP/port is 192.168.1.100/25000. Use this on clients to connect directly.
3. We call to Spawn object (non-API) OnNetworkLoadedLevel call, which will actually call the API method:  UnityEngine.Network:Instantiate()
     Sent RPC call '__RPCNetworkInstantiate' to all connected clients
      Added RPC '__RPCNetworkInstantiate' to buffer.
* Let`s go over what happened so far. We called Network:InitializeServer() to create a server inside our unity process. Unity did just that and opened a listening-port for other clients to connect.
The callback OnServerInitialized was invoked, but we didnt do anything meaningful there except printing to the log. As you can see we got a GUID from the MasterServer which will identify our "game-room". Anyone which knows it can ask the MasterServer to connect to us without knowing our IP.
Last , we invoked Network:Instantiate() to create a car instance. This also created, behind the scenes an RPC call which will be queued for any new client, so that this client will see our car too.

Now, from another unity player , use the NAT-punchthrough GUID to connect
   New connection established (192.168.1.100:57240)
   Scope index 0 is now in scope for AllocatedID: 0
   New scope index 0 is now in scope for SceneID: 1 Level Prefix: 0
   New scope index 0 is now in scope for AllocatedID: 1
   Sent initalization to player 1
   Added RPC '__RPCNetworkInstantiate' to buffer.
* A new client just connected with player id=1  (remember that the server was player id=0).  The initialization queue was automatically sent to client too.
OnPlayerConnected callback was called on the server , but we didn`t catch it.
OnConnectedToServer was called on the client and caused it to spawn it`s own car using NetworkInstantiate. That RPC got to the server which used it behind the scenes to spawn the new client-car and then this RPC was added to the server queue.
Let`s emphasize this point again, no specific server code was written for the car prefab to be created, it just did.

Now , from that other player, I clicked disconnect
   A client has disconnected from this server. Player ID 1, IP 192.168.1.100:57240
1. CarSpawnPrefab:OnPlayerDisconnected(NetworkPlayer) - called automagically
   Server destroying player
   RPC __RPCNetworkInstantiate with AllocatedID: 0, player ID 1 and group 0, removed from RPC buffer.
2.  UnityEngine.Network:RemoveRPCs(NetworkPlayer, Int32)
      1 RPC function were removed with RemoveRPC
3.  UnityEngine.Network:DestroyPlayerObjects(NetworkPlayer)
      Destroying objects belonging to player 1
      Destroying object with view ID 'AllocatedID: 150'
* OnPlayerDisconnected calledback was called on the server. Here we wrote code to remove it`s RPC from the queue (as in this game, we want it`s car to vanish- game dependent logic) and then called DestoryPlayerObject which knows which instances were created using the NetworkInstansiate in the past and calling only them.  The actual destroyed object have view-id of 150. This view-id is a unique id across all game objects.  If you connect and disconnect with another player you will see that player2 objects are destoryed and the destoyed view id will be 250.





No comments: