Hello! I’m a little confused about how to go about properly setting up a dedicated server for my game. My end goal is a server like minecraft or rust where you can execute console commands and clients can connect to.
How do I setup a headless dedicated server? My current ‘dedicated’ server is also running a client and rendering the map. Causing the server list, displayed on the client, to show 1/30 players when obviously, no one is connected.
Here is my current method of starting the server. Just a blank gameobject running this script.
public class ServerManager : MonoBehaviour {
private NetworkManager netManager;
public void Start () {
netManager = GetComponent<NetworkManager>();
Debug.Log("Creating Server on port '" + netManager.networkPort + "'...");
netManager.StartMatchMaker();
Debug.Log("Attempting MM Connection...");
netManager.matchMaker.CreateMatch("US 1", 30, true, "", "", "", 0, 0, OnMatchCreated);
}
public void OnMatchCreated(bool par1, string par2, MatchInfo par3) {
if(par1) {
print("Starting Server!");
netManager.StartServer(par3);
return;
}
print("Failed.");
}
}
Note, when I export it, there is no headless option. Some threads said to use that.
Why are you running a client instance on the server? And on your code above you are not doing that. Anyways, just checked the Minecraft server. They have a custom Java GUI. So I guess what you could do is to make a WinForms application and integrate the Network Library in there. However, as far as I know you cannot use Unity Mulitplayer Services outside Unity (Matchmaker, Relay). So you would have to replace those with your own or just make players directly connect to the server like the Minecraft game.
As for your last statement. Headless mode is for Linux only and great for running automated servers with no input. As in Once excecuted. it minds its own bussiness.
You can still run headless on windows using the command line options -batchmode -nographics which will run it as a service. I do this and it runs just fine. Certainly the server does not want to be running as a client as well. It’s going to spend the majority of it’s time on the simulation and network traffic.
You could have the dedicated server read from a database once in a while to check for commands, or even have such commands sendable from clients (or a specific client).
@TwoTen That’s my problem, its running a client but I never started a client. I just start the MM and Server but when I retrieve the data for a server list, it shows 1/30 players when I never connected to it.
@Whippets How do I go about adding command line arguments? Do I have to make a .bat and run the .exe?
No. For a UI you will have to make something like a forms application to replicate what Minecraft has. What @Whippets did state tho is a fairly good idea. Make a forms app that has connection (localy somhow) to your server (and even controls states of the server). So you only ever start the Forms app. Then it will launch the actual server when you press “start”. And then have a textbox where you can somehow interact with the server, (Possibly poll a text file for easiest way to test the idea).
For a quick and dirty test, polling a text file is exactly what I’d do.
Ideally, you want a control panel in your client, that is only accessible by the room/world opener. So that they can set any specific controls for that world.
Obviously, you’re going to need to think about instances and how many world instances a server can cope with (and players per instance). From then on it’s how many servers you need, and what sort of load balancing system will railroad connected players to specific servers/instances.
You still could have console) I’ve made it for my Master Server, but in order to make it work you need to use system libs. So basically you should start your server on Start(); but your console on Awake(); I’ve also added my .cfg for tweak the server port. And you could run your server via .bat:
@echo off
masterserver.exe -batchmode
There is also a chat in game menu, message sends to the Server - Server checks it for bad words and then simply sending the message to the all clients via NetworkServer.SendToAll(msg);
Thank you guys for all the help and direction. I actually found an asset on the store that was basically a pre-made console. Hooked that bad boy up to my server and seems to work perfectly fine with the -batchmode and -nographics. Just have to run some tests with the Match Making and then fix that issue with the server also have a client some reason…
Okay I still have an issue with the server showing 1/30 players when no one is actually playing on it. Maybe it has something to do with the Match Maker?
I use the match maker to register the server to a server list basically. The client can just connect to the MM service and retrieve a server list.
@ I think its a little trickier than that. I messed around with that and even debugged some values such as the numPlayers and client connects. Both 0. But when I retrieve the match data on the client and display it on the server list, it shows 1/30
This is the client reading the info in from the matchlist request.
[HideInInspector]
public MatchInfoSnapshot serverInfo;
private bool mouseOver = false;
public void UpdateInformation() {
Text name = transform.GetChild(1).GetComponent<Text>();
Text players = transform.GetChild(2).GetComponent<Text>();
Text ping = transform.GetChild(3).GetComponent<Text>();
name.text = serverInfo.name;
players.text = serverInfo.currentSize + "/" + serverInfo.maxSize;
ping.text = "???";
}
Im not sure which value is sent from server to the MM Service to ‘count’ players or currentSize either.
Hm, Im using the latest version of Unity. NetworkMasterClient doesn’t exist? And I have my server manager set up that way too but Im not using a custom variable for player count. Ill just attach the class.
Please note, this is rough code and not very clean.
ServerManager
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Networking;
using UnityEngine.Networking.Match;
using UnityEngine.Networking.Types;
using EConsole;
using System;
using System.IO;
using System.Reflection;
using SimpleJSON;
public class ServerManager : NetworkManager {
private static EConsole.Console theConsole;
/**
* -1 = quit
* 0 = connect to console
* 1 = server start
* 2 = started
*/
private static int status = 0;
/** List of registered commands */
private static List<MethodInfo> commands = new List<MethodInfo>();
private NetworkMatch netMatch;
private Queue<string> commandsReceived = new Queue<string>();
private object commandLock = new object();
public void Awake() {
netMatch = gameObject.AddComponent<NetworkMatch>();
}
public void Start() {
Setup();
}
public void Setup() {
//Setup Paths here
status = 0;
theConsole = new EConsole.Console(Path.Combine(Application.streamingAssetsPath, "Console.exe"), "CD Server Console");
theConsole.OnRead += OnTextReceived;
theConsole.OnConnect += OnConsoleConnected;
}
public void InitServer() {
status = 2;
LogWarning("Loading Match Data...");
MasterServer.dedicatedServer = true;
netMatch.CreateMatch(this.matchName, this.matchSize, true, "", "", "", 0, 0, OnMatchCreated);
}
public void OnDestroy() {
theConsole.SetColor(ConsoleColorLayer.Forground, ConsoleColor.Yellow);
theConsole.CloseConsole();
}
private void CloseGame() {
LogError("Closing Application");
#if UNITY_EDITOR
UnityEditor.EditorApplication.isPlaying = false;
#endif
Application.Quit();
}
public void Update() {
if (status == 1) {
InitServer();
}
if ((theConsole.isWaitingForConnection == false && theConsole.isConnected == false) || theConsole.isConsoleOpen == false || status == -1) {
CloseGame();
}
if(commandsReceived.Count > 0) {
string command = commandsReceived.Dequeue();
string[] args = command.Split(new char[] { ' ' });
if (args.Length > 0) {
foreach (MethodInfo mi in commands) {
if (mi.Name.ToLower() == args[0].ToLower()) {
mi.Invoke(null, new object[] { args });
break;
}
}
}
}
}
private void OnTextReceived(string par1) {
Log(par1);
string[] args = par1.Split(new char[] { ' ' });
if (args.Length > 0) {
foreach (MethodInfo mi in commands) {
if (mi.Name.ToLower() == args[0].ToLower()) {
lock(commandLock) {
commandsReceived.Enqueue(par1);
}
return;
}
}
}
LogError("'" + par1 + "' Is not a command.");
}
private void OnConsoleConnected() {
//Load Server Config
LogFinest("Ello! Initilizing Server.");
LogWarning("Loading Configs...");
RegisterCommands();
status = 1;
}
private void RegisterCommands() {
LogWarning("Registering console commands...");
commands = new List<MethodInfo>();
Type[] types = Assembly.GetExecutingAssembly().GetTypes();
foreach (Type CMDType in types) {
MethodInfo[] methods = CMDType.GetMethods();
for (int i = 0; i < methods.Length; i++) {
MethodInfo method = methods[i];
if (method.IsStatic && method.GetCustomAttributes(typeof(ConsoleCommandAttribute), false).Length > 0) {
commands.Add(method);
}
}
}
}
public void OnMatchCreated(bool par1, string par2, MatchInfo par3) {
if (par1) {
LogWarning("Starting Network Listener...");
NetworkServer.Listen(par3, networkPort);
StartServer(par3);
status = 2;
LogFinest("Server Address: " + par3.address + ":" + networkPort);
LogFinest("Loading Complete!");
return;
}
}
public override void OnServerConnect(NetworkConnection conn)
{
if(conn.connectionId != 0)
{
//Missing
currentPlayers += 1;
masterClient.UpdateHost(currentPlayers);
}
}
public static void Log(string par1) {
ConsoleUtility.ColorWriteLine(theConsole, par1);
}
public static void LogWarning(string par1) {
ConsoleUtility.ColorWriteLine(theConsole, "#f=Yellow#" + par1);
}
public static void LogError(string par1) {
ConsoleUtility.ColorWriteLine(theConsole, "#f=Red#" + par1);
}
public static void LogFinest(string par1) {
ConsoleUtility.ColorWriteLine(theConsole, "#f=Green#" + par1);
}
public static void QuitServer() {
status = -1;
}
}