Overview
Overview
Peer Types
There are three types of network peers that take part in a uLobby session -
lobby, game servers and game clients. These connect together and communicate
with each other to perform the tasks needed to create the game's lobby system.
The
lobby is a normal Unity game instance that uses uLobby's
lobby-side API to initialize and create the actual lobby that others can then
connect to. It is similar to a server in uLink, and therefore uses a similar
API.
A
game server is a Unity game instance that uses uLobby's server API
to identify that it is a server able to host games. Note that in uLobby's
network topology, game servers are clients that connect to the lobby in the same
way as game clients do. It is only in the context of the game they host that
they are actual servers.
A
game client is a Unity game instance that uses the client API of
uLobby to identify that it is a player client interested in finding a game to
join and play. It connects to the lobby just like a game server does.
Operation
To start a uLobby session, the first thing to do is to start the lobby
instance. After this, game servers can be started and connected to the lobby to
make them available for players. After starting the lobby and connecting game
servers to it, game clients can start connecting to the lobby to find available
games. Game servers can be dynamically removed and added in runtime either
manually or using code, and uLobby will reflect this in the lobby.
After finding a game on a certain server for a player, the player's client can
proceed to connect to the server and start playing. How the connection to the
server is made is outside of uLobby's area of responsibility - it does not
need to know anything about the game being played or of its network
architecture. Having found a game, the game client can choose to either
disconnect from the lobby entirely or to keep the connection during the game
(thereby having both a connection to the lobby and the game server). uLobby
provides several features that can be used while playing the game, such as
chatting with people playing other games on other servers, adding them as
friends and seeing when they log in to an account.
Code Overview
The core functionality of uLobby is accessed through the
Lobby class
in the
uLobby namespace. This class is similar in structure to the
Network class in Unity's built-in network and uLink, and consists of a set of
static methods. It is used by each of the lobby, game servers and game clients
for performing basic tasks such as connecting to the lobby system and sending
RPCs.
The rest of uLobby's functionality is provided by a separate set of classes;
each major piece of functionality consists of a main class with static methods
for performing the primary functions, and some helper classes used by this
class. For example, the account management functionality is implemented in the
AccountManager class, which has static methods for registering new
accounts, logging in and so forth. It also contains the class
Account
that represents a single account.
Database
Some of the lobby components store and load information from a database. uLobby
currently only supports using uGameDB as a database, but this might change in
the future to allow any type of database to be used.
Authoritative Lobby
Several of the classes in uLobby offer both a
client-side and a
lobby-side API. In these cases, the lobby-side API performs the actual
functions, while the client-side API acts as a proxy to provide them to clients.
The lobby-side API in these classes is accessed from a nestled Master class
inside the main class. Some methods are available in both client-side and
lobby-side versions - these have the same name in the main class and the Master
class. Other methods are only available on one of the sides.
To take an example, the
AccountManager.LogIn() method is used by clients
to log in to a specified account. This method does little more than send a
request to the lobby, which then executes the corresponding lobby-side version
of the method,
AccountManager.Master.LogIn(). The lobby-side
method can also be called directly by code on the lobby, bypassing the need for
the client to initiate the action. Regardless of which method is used, the
outcome on the client will be identical.
The lobby-side version of a method is typically more general than the equivalent
client method and takes extra parameters that specify for whom or what the
operation is carried out. For example, the
AccountManager.LogOut()
client-side method takes no parameters and logs out the peer that executed it
from its currently logged in account.
AccountManager.Master.LogOut(), however, needs to know what peer
is going to be logged out, so this must be supplied as a parameter.
The client-side API is offered as a convenience to the developer of the lobby
system to make it quick and simple to implement lobby clients. However, the
lobby-side API is completely sufficient on its own and developers can choose to
bypass the client-side API and implement their own communication with the lobby
if this is desirable. For example, you might want to implement extra checks
before allowing a client to log in to an account. Instead of using
AccountManager.LogIn() you could then implement your own log in method
that sends an RPC to the lobby, performs the checks, and then executes
AccountManager.Master.LogIn().
uLobby also supports turning off the client-side API of specific features
entirely. This means the lobby will no longer blindly execute requests initiated
by clients, but instead the developer must implement this manually. The main
reason to do this is for security. A hacked client could otherwise be used for
sending crafted RPCs that perform lobby-side functions through uLobby's
client API. If you do not intend to use the client API as-is, it should be
turned off to make the lobby completely authoritative.
Requests
Many lobby-side API methods return objects of type
Request. This signals
that the method is asynchronous and takes a while to execute, often because it
needs to speak to the database to perform its function. The returned request
object represents the specific request that was made and can be used for keeping
track of it, waiting for it or finding out its status.
The
Request class contains a few properties that inform about the status
of the request. The
isDone property says whether the
request has completed yet. The
isSuccessful property
says whether the request is done and was successful. The
exception property will be
null as long as
the request does not encounter any problems. If an error occurs during execution
of a request, the request will be stopped,
isSuccessful
will be set to
false, and
exception will be
set to the exception that occurred.
The
WaitUntilDone() method returns a coroutine that finishes
execution when the request completes (either successfully or with an exception).
Using this method makes it simple to write code that needs to perform a sequence
of events in a linear fashion.
Some requests return a value upon finishing execution. These requests have the
generic type
Request<T>, where
T is the type
of the return value. The
Request<T>.result
property contains the value returned by a successful execution of such a
request. It can only be retrieved after the request has completed successfully.
The
Request class contains a few events that can be subscribed to:
- OnDone - Called when the request has completed
(successfully or with an exception).
- OnSuccessful - Called when the request has finished
successfully.
- OnException - Called when an exception occurred
during execution of the request.