SERVER PEER-TO-PEER
SERVER PEER-TO-PEER
A peer-to-peer network, commonly abbreviated as P2P, is any distributed network architecture composed of participants that make a portion of their resources available to other network participants, without the need for central coordination instances, such as servers or stable hosts. Peers are both suppliers and consumers of resources, in contrast to the traditional client/server model, in which servers supply and clients consume.
Inter-server or inter-client communication with P2P
With uLink it is possible to connect peers directly to each other. This can for example be used to connect all servers of an instanced/zoned MMO, in a server-side LAN to each other, letting players go from one server to another when they change zone. Of course, this method is useful for smaller-sized games as well, where you would like to use multiple communicating servers. It can also be used to connect all the clients of a multiplayer game directly to each other, avoiding the need for a game server. Note that while any peer can send RPCs and replicate objects to any other peer,
only peers initialized to be servers are allowed to make handovers. This is because a handover involves redirecting a client to another server.
The most common use of P2P in commercial games is to connect servers to each other, and therefore the examples in this text will focus on this subject.
Connecting peers
uLink provides two scripts that can be used for setting up a P2P network.
uLinkNetworkP2P and
uLinkNetworkP2PConnector. If you want to connect two servers to each other, you can follow the steps outlined below.
- Add the uLinkNetworkP2P script component to a game object in server A. Choose a port number for the property named Listen Port. By doing this, server A is now prepared for incoming P2P connections.
- Add the script uLinkNetworkP2PConnector to a game object in server B. Note that this will automatically add the uLinkNetworkP2P script as well. Set the hostname and port properties to the P2P listen adress for server A, defined in the previous step.
Now the P2P connection configuration is complete for the two servers and they can send messages between them in any direction. On startup, server B will try to connect to server A every 200 ms (by default) until the connection is established. It is therefore possible to start the servers in any order.
If you have more than two servers and want to connect them to each other it is just as easy as in the above example. Every server needs only one game object with the script component
uLinkNetworkP2P. Then you need a
uLinkNetworkP2PConnector for every point-to-point connection that you want between servers. For example, if you have four servers to connect, you will need
two uLinkNetworkP2PConnector components in
three of the servers. Server A connects to B and C. Server B connects to C and D. Server C connects to A and D. Server D connects to nobody. If you make a drawing of this you will see that they all have connections to each other.
Sending RPCs
RPCs are easily used in P2P connections, through the function
uLink.NetworkP2PBase.RPC(). One of the arguments can be of type
uLink.NetworkPeer, and that argument indicates the receiver of the RPC. The
uLink.NetworkPeer of the remote server is something that uLink provides by sending the
uLink.NetworkP2P.uLink_OnPeerConnected() callback when a peer connects. Implement this callback in one of your scripts on the game object with the
uLinkNetworkP2P component to get the
uLink.NetworkPeer of the remote computer.
Once the remote peer has a known identity, it is easy to send an RPC to it. Please note that the callback will be invoked every time another peer connects to this peer.
If you want to, you can send an RPC to all connected peers. To do this, use the function
uLink.NetworkP2PBase.RPC() which has an argument of type
uLink.PeerMode and set this argument to
Others. If you want to send the RPC to every connected peer including yourself, use
All.
It is not possible to send state synchronization over P2P connections.
Transfering network-aware objects between peers
One purpose of connecting servers together like this, in a P2P network, is that it enables them to transfer network-aware objects like NPCs and players' avatars from one server to another. This will not be a seamless transfer, but more like a "teleportation" from one game zone to another in a zoned MMO game. Nevertheless, it is a powerful tool that will enable the game developer to create advanced MMO games with a static zoned world.
There are two different types of object transfer, replication and handover, that depend on the
role of the object in the transferring peer.
To transfer a network aware object owned by a server to another server, call the
uLink.NetworkP2P.Replicate() method. This will transfer the object to the target server, by serializing the creator state and sending it to the new target server via an internal RPC. The new server will call
uLink.Network.Instantiate() for this object giving it a new
viewID value on that server.
To transfer a network aware object owned by a player, the basic concept is the same, but additionally, the client has to disconnect from the old server and connect to the new server. Also, since the game object is moved, it will be destroyed on the old server. This process of switching game server is called a
handover in uLink. If you try to hand over a game object not owned by a player, the handover will fail.
To transfer a player and a player-owned game object from one server to another, call the
uLink.NetworkP2P.Handover() method, from the first server. This will destroy the object on the frist server and create a new object on the second server from the serialized state sent over the P2P connection between the servers. In addition, the client will get a uLink internal RPC to make it disconnect and connect to the new game server. This event can be caught in the client, by implementing the
uLink.Network.uLink_OnRedirectingToServer() callback, if you want to perform some specific actions, like showing a visual effect when the player is transferred from one server to another.
The callback
uLink.NetworkP2P.uLink_OnHandoverNetworkView() can be used in a script observed by the
uLinkNetworkView component of the game object. The game object must have the creator role, i.e. it must be on the Unity instance that instantiated the object, for the callback to be invoked. The callback can then be used to manually program the serialization and de-serialization of the state that should be carried from one server to another during the handover. This function follows the same pattern as the common callback
uLink.Network.uLink_OnSerializeNetworkView() that is used to serialize and de-serialize normal state-sync for game objects that are sent from a game server to clients.
Note that if you use the handover mechanism to transfer players between servers running different scenes, you have to make sure that the client also switches to the correct scene. This is done conveniently and reliably by letting the server send scene information as approval data when the client connects, in the
uLink.Network.uLink_OnPlayerApproval() callback. The client can then respont to this by reading the approval data in the
uLink.Network.uLink_OnConnectedToServer() callback. This will make sure that the client always knows which scene to load on connection to the server, and this is how it is done in the Snowbox demo game.
When a client disconnects from a server as the result of a handover, it can be a good idea to reload the current level. This is because objects instantiated by the previous server is not destroyed in the client upon disconnection, but will remain until the scene is reloaded, or manually removed.