Unity BatchMode Console

If you’ve made a game that has a dedicated server in Unity using -batchmode you probably noticed that when you use Console.Print it doesn’t print to the console. And it’s pretty hard to make it happen.

So here’s a couple of classes that we use in Rust’s dedicated server. This is Windows Only. Download them here.

2014-04-23_15-48-13

Here’s how we use it in Rust:

public class ServerConsole : MonoBehaviour
{
#if UNITY_EDITOR_WIN || UNITY_STANDALONE_WIN
 
	Windows.ConsoleWindow console = new Windows.ConsoleWindow();
	Windows.ConsoleInput input = new Windows.ConsoleInput();
 
	string strInput;
	
	//
	// Create console window, register callbacks
	//
	void Awake() 
	{
		DontDestroyOnLoadgameObject );
 
		console.Initialize();
		console.SetTitle"Rust Server" );
 
		input.OnInputText += OnInputText;
 
		Application.RegisterLogCallbackHandleLog );
 
		Debug.Log"Console Started" );
	}
 
	//
	// Text has been entered into the console
	// Run it as a console command
	//
	void OnInputTextstring obj )
	{
		ConsoleSystem.Runobjtrue );
	}
 
	//
	// Debug.Log* callback
	//
	void HandleLogstring messagestring stackTraceLogType type )
	{
		if ( type == LogType.Warning )		
			System.Console.ForegroundColor = ConsoleColor.Yellow;
		else if ( type == LogType.Error )	
			System.Console.ForegroundColor = ConsoleColor.Red;
		else								
			System.Console.ForegroundColor = ConsoleColor.White;
 
		// We're half way through typing something, so clear this line ..
		if ( Console.CursorLeft != 0 )
			input.ClearLine();
 
		System.Console.WriteLinemessage );
 
		// If we were typing something re-add it.
		input.RedrawInputLine();
	}
 
	//
	// Update the input every frame
	// This gets new key input and calls the OnInputText callback
	//
	void Update()
	{
		input.Update();
	}
 
	//
	// It's important to call console.ShutDown in OnDestroy
	// because compiling will error out in the editor if you don't
	// because we redirected output. This sets it back to normal.
	//
	void OnDestroy()
	{
		console.Shutdown();
	}
 
#endif
}

22 thoughts on “Unity BatchMode Console

  1. The way I did it was create a wrapper console application in .NET that opens up my game and listens for log output (by disabling log file output and adding a log handler). My game outputs logs specially formatted with the type as a prefix ([LOG], [WARNING], or [ERROR]), and these messages are re-printed to the console (and to a log file since I disabled log file output). This is an interesting way to do it though, and my solution is already windows-only (it uses Kernel32 in order to capture an event when the console window is closed, and shuts down the child server process) so this looks easier.

  2. This has nothing to do with the Batch code but people supposedly reported me of “Hacking”, so if I get VAC banned for no reason I will have a dilemma. I got RUST 4-6 days ago and it is really fun. But the people playing it Falsely accuse people and report them. I have their names too. Please contact me. I do not want to be PERMA-BANNED from RUST I’m not a Hacker. Thanks

    1. You don’t get VAC banned from people reporting you. VAC is an automatic system that checks if the memory of the game is being modified.

  3. If you still view comments on this blog, thanks for helping me with this Garry, I was wondering like “How in the hell does Garry run console commands in batch mode, I wish I could have that in my game xD” I have just successfully implemented it in my game. Anyone else trying to implement this in their unity project, you have to change the .net version of unity in the player settings in Unity 5 from .net 2.0 subset to .net 2.0 or it will give about 19 console errors. Once you do that you will be left with one error in ServerConsole.cs on line 38, you can either comment it out until you create your in game console and then send the string to that or you can directly send the string if you already have a console system. Also I will gladly suck your cock garry xD

  4. Pingback: FFIA 16
  5. This is really great! I am however having some difficulty with entering text into the console window to perform commands. I understand that the ConsoleSystem section can then be created to perform actions, but until I figure out how to get the console to accept input, I think I am stumped.

    1. Nvm this has been resolved. I think there were some issues with how I had my scenes set up. I am now stuck on performing server actions when implementing my own ConsoleSystem. Is there a reason you chose to make the function static? I’ve tried making the ConsoleSystem into a separate script extending MonoBehavior so that I can access gameobject’s from it. But this does not appear to work in batchmode as the GameObject.Find() method keeps returning null references. The odd thing is that it does work in the editor. Is there any chance you could share how it was that the ConsoleSystem was implemented in this example?
      Thanks in advance.

      1. Again I think I may have spoke to soon. I appear to have been able to get it working with my current setup. It’s likely not the best approach, but for the sake of anyone that gets caught up on a similar issue as mine, here is the ConsoleSystem I implemented:

        using UnityEngine;
        using System.Collections;
        using System;

        [RequireComponent(typeof(ServerConsole))]
        public class ConsoleSystem : MonoBehaviour {

        public Server_DataPersistence dataPersister;

        void OnEnable()
        {
        Debug.Log(“ConsoleSystem was enabled”);
        //EnsureDataPersisterLoaded();
        }

        void OnLevelWasLoaded(int level)
        {
        Debug.Log(“Scene was changed to Scene: ” + level);
        EnsureDataPersisterLoaded();
        }

        public void Run(string text)
        {
        EnsureDataPersisterLoaded();

        char[] whitespace = new char[] { ‘ ‘ };
        string[] cmds = text.Split(whitespace);

        if (cmds.Length < 1)
        return;

        switch (cmds[0].ToLowerInvariant())
        {
        case "help":
        PrintHelp();
        break;
        case "saves":
        PrintSaves();
        break;
        case "save":
        SaveServerState();
        break;
        case "loadscene":
        LoadScene(cmds);
        break;
        case "shutdown":
        //SaveServerState();
        Application.Quit();
        break;
        case "lastsavetime":
        PrintLastSaveTime();
        Application.Quit();
        break;
        default:
        PrintHelp();
        break;
        }
        }

        void PrintHelp()
        {
        Debug.Log("The following commmands are available to you:");
        Debug.Log("\tHelp – Displays available commands");
        Debug.Log("\tSaves – Displays number of saves made on current data file");
        Debug.Log("\tSave – Saves the current state of the server to a local file");
        Debug.Log("\tLoadscene [sceneNumber] – Will load the scene specified");
        Debug.Log("\tShutdown – Stops the server process");
        Debug.Log("\tLastSaveTime – Displays the date and time of the last save.");
        }

        bool EnsureDataPersisterLoaded()
        {
        if(dataPersister != null)
        return true;
        GameObject dataPersisterObj = GameObject.Find("DataPersister");
        if (dataPersisterObj != null)
        {
        dataPersister = dataPersisterObj.GetComponent();
        if (dataPersister != null)
        return true;
        }
        else
        {
        Debug.Log(“Could not find DataPersister gameObject – ConsoleSystem:EnsurePersisterLoaded()”);
        }
        Debug.LogWarning(“Unable to load DataPersister. Some commands may not function properly.”);
        return false;
        }

        void PrintSaves()
        {
        Debug.Log(“There have been ” + dataPersister.GetSaves() + ” saves made to the current world.”);
        }

        void LoadScene(string[] cmds)
        {
        int level;
        if(Int32.TryParse(cmds[1], out level)){
        if(level != Scenes.LANDING)
        {
        Application.LoadLevel(level);
        }
        else
        {
        Debug.Log(“Cannot load landing scene. Will cause issues with the network manager and server console.”);
        }
        }
        }

        void SaveServerState()
        {
        dataPersister.Save();
        Debug.Log(“State of the Server has been saved to ” + dataPersister.GetSaveFilePath());
        }

        void PrintLastSaveTime()
        {
        Debug.Log(“Last successful save on: ” + dataPersister.GetLastSaveDateTime());
        }
        }

        I would like to note that the “EnsureDataPersisterLoaded” function has been included for the sake of completion, however it does not appear that the system will not properly execute GameObject.Find(“”) to be able to retrieve references to gameobjects in your scene. I therefore just assign the dataPersister data member at the time that this object is instantiated by the networkmanager.

        I realize that this explanation is not entirely complete but I hope that it helps anyone that was stuck at the same place that I was. Please feel free to drop me a line for further clarification if you think it would be of assistance.

        Thank you.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s