Master server
This script and scene is created to using multiple MultipleServer
. The config is a json file in its root folder (with the exe).
{
"Port": 5881,
"WPort": -1,
"Password": "***",
"FPS": 60,
"RefreshInterval": 120,
"MaxInstanceNb": 50,
"WaitingTimeout": 10,
"ServersPsw": "MY STRONG PASSWORD",
"ServersPort": 5882,
"Servers": [
"localhost"
]
}
- Port: Master server port (client connection)
- WPort: Master webserver port (client connection); -1 to disabled
- Password: Master server password (client connection)
- RefreshInterval: Interval of the hot refresh of the configuration file
- MaxInstanceNb: Limit the number of instances on the
MultipleServer
. - WaitingTimeout: When a client is in the waiting list, if it does not call against in this interval, this party will be removed from the waiting list
- ServersPsw: Password of
MultipleServer
s. - ServersPort:
MultipleServer
s port. - Servers:
MultipleServer
address list.
Create a new game
Using SimpleNetMasterCmds.NewGame
without arguments, the master server returns to a new unique ID (string). Save it on the client machine or on the client account.
Join a game
Using SimpleNetMasterCmds.JoinGame
with the game ID as argument, the master server will try to create a new room on a MultipleServer
and returns [true, address, port, password].
The game ID length must be > 10
If all servers are full, the game will enter in a waiting loop. Master returns [false, position (int)]. Call this in a loop (with an interval of x seconds, this must be < WaitingTimeout
) to create a wait routine.
If the game is already online, the master server returns [true, address, port, password].
Client example
using System.Threading.Tasks;
using EODE.Wonderland;
using EODE.Wonderland.Networking;
using EODE.Wonderland.SimpleNet;
using UnityEngine;
namespace ShadowPiercer {
public class NetMaster {
/// <summary>
/// Connection status
/// </summary>
public enum EStatus {
/// <summary>Init not called ?</summary>
Unknown,
/// <summary>[Running] Wait for master server connection</summary>
MasterWaitConnection,
/// <summary>OK</summary>
Connected,
/// <summary>Master server is offline</summary>
MasterOffline,
/// <summary>Unexpected error with master server</summary>
MasterError,
/// <summary>Waiting for a new Game</summary>
MasterNewGame,
/// <summary>Waiting for the Master response</summary>
MasterWaitGame,
/// <summary>Master server cannot create the game</summary>
MasterNewGameError,
/// <summary>Unexpected master response</summary>
MasterResponseError,
/// <summary>Cannot join the game</summary>
MasterCannotJoinGame,
/// <summary>On WaitingList (not enough game server). Use WaitingList to get the number</summary>
MasterWaitingList,
/// <summary>Connecting to Game server</summary>
GameConnection,
/// <summary>Cannot connect to Game server</summary>
GameError
}
/// <summary>
/// Connection status
/// </summary>
public EStatus Status { get; protected set; }
/// <summary>
/// Get waiting list id
/// </summary>
public int WaitingList { get; protected set; }
Cloud _cloud;
Peer _master;
string _gameId;
public async Task Init(string address, int port, string password, string gameId) {
Status = EStatus.MasterWaitConnection;
_gameId = gameId;
_cloud = new Cloud();
_master = await _cloud.CreatePeer(address, port, password);
if (_master.IsConnected) {
if (string.IsNullOrEmpty(_gameId)) {
Status = EStatus.MasterNewGame;
var response = await _master.Send(SimpleNetMasterCmds.NewGame, _cloud.DefaultNetwork);
_gameId = response.Read<string>();
if (!string.IsNullOrEmpty(_gameId)) {
Status = EStatus.MasterWaitGame;
await JoinGame();
}
else {
Status = EStatus.MasterNewGameError;
_master.Disconnect();
}
}
else {
Status = EStatus.MasterWaitGame;
await JoinGame();
}
}
else {
if (_master.ConnectionException != null) {
Status = EStatus.MasterOffline;
}
else {
Status = EStatus.MasterError;
}
_master.Disconnect();
}
}
public void Close() {
if (_master != null) {
_master.Disconnect();
}
if (NetManager.Instantiated) {
NetManager.Disconnect();
}
}
public void Update() {
if (_cloud != null) _cloud.Update();
}
public async void OnDestroy() {
if (_cloud != null) await _cloud.Close();
}
public async Task JoinGame(string gameId) {
_gameId = gameId;
await JoinGame();
}
async Task JoinGame() {
var response = await _master.Send(SimpleNetMasterCmds.JoinGame, _gameId, _cloud.DefaultNetwork);
bool success = response.Read<bool>();
if (success) {
WaitingList = 0;
var address = response.Read<string>();
var port = response.Read<int>();
var password = response.Read<string>();
if (string.IsNullOrEmpty(address) || port <= 0 || string.IsNullOrEmpty(password)) {
Status = EStatus.MasterResponseError;
}
else {
Status = EStatus.GameConnection;
if (!NetManager.Instantiated) {
var go = new GameObject("NetManager");
var nm = go.AddComponent<NetManager>();
nm.Address = address;
#if UNITY_WEBGL && !UNITY_EDITOR
nm.Port = port // + [???] defined by the conf WPortValueAdd of ServerMultiple;
#else
nm.Port = port;
#endif
nm.Password = password;
nm.AutoConnect = true;
}
var timeout = Time.time + 5f;
await new WaitWhile(() => !NetManager.IsReady && timeout > Time.time);
if (!NetManager.IsReady) {
Status = EStatus.GameError;
}
else {
// Example: a simple manual scene init
Status = EStatus.Connected;
var netscene = GameObject.FindObjectOfType<NetScene>();
var sceneInstance = await netscene.Join();
if (sceneInstance.IsValid()) {
// instance joined
}
else {
// error
}
}
}
_master.Disconnect();
}
else {
WaitingList = response.Read<int>();
if (WaitingList < 0) {
_master.Disconnect();
Status = EStatus.MasterWaitGame;
}
// join immediatly
else if (WaitingList == 0) {
await new WaitForSecondsRealtime(1f);
Status = EStatus.MasterWaitingList;
JoinGame().WrapErrors();
}
// loop
else {
await new WaitForSecondsRealtime(5f);
Status = EStatus.MasterWaitingList;
JoinGame().WrapErrors();
}
}
}
}
}