Unity from the Command Line in Linux

I go through a LOT of unity projects. It would be *so* cool to automate the first open, so that I don’t have to import all projects into UnityHub and wait for the build process. With some automation I could let my computer build 90 Unity projects and once it’s done, I could start taking a look.

For that, we can use the command line!

$ /home/myname/Unity/Hub/Editor/6000.2.7f2/Editor/Unity \
   -projectpath /some/path/somewhere

Except, apparently, we can’t?

This message pops up and cannot be interacted with.

How Unity is invoked by UnityHub

If we instead open a Unity project from UnityHub the intended (boring, slow, high-overhead) way, the command line looks like this:

$ /home/myname/Unity/Hub/Editor/6000.2.7f2/Editor/Unity \
  -projectpath /some/path/somewhere \
  -useHub \
  -hubIPC \
  -cloudEnvironment production \
  -licensingIpc LicenseClient-myname \
  -hubSessionId some-uuid \
  -accessToken some-token

The hubSession UUID seems to stay the same as long as UnityHub is running. The accessToken is different for some Unity projects, but also sometimes reused. This will need more investigation. It might be a short lived token that gets cycled every hour or something along those lines.

Information like this can be found in /proc/process-id/cmdline.

If we replicate this exact pattern, we can try starting this on our own including those extra bits and pieces, but it will still fail.

Taking a look at the environment

The environments of both processes (as seen in /proc/process-id/environ look vastly different. Most of that comes from the fact that UnityHub is a Flatpak program, and that likely means all Unity processes that are installed by it will themselves be started within that sandbox.

There is a one env-var that looks unity-specific though:

UNITY_DATADIR=/home/myname/.var/app/com.unity.UnityHub/data

But even including that only gets me the Licensing box above.

Running within Flatpak

So let’s try opening this in flatpak. Let’s start with a boring shell in that same container:

flatpak run --command=sh com.unity.UnityHub

Its environment looks much more similar to the one we observed from a working Unity process. Let’s start a Unity process from within this shell in the simple way that we tried all the way at the beginning:

/home/myname/Unity/Hub/Editor/6000.2.7f2/Editor/Unity \
  -projectpath /some/path/somewhere

And it immediately works. No extra stuff about licenses needed!

Finding All Projects

The original point was to run through a big number of these, so let’s find them! Furthering my problems is, that none of the paths are really predictable, and neither are the unity versions. So let’s look for something that appears to be constant, at least across Unity 2021 up to 6000: ProjectVersion.txt. This file lives in /ProjectSettings/ of a given project and is useful because it contains the expected editor version in form of: m_EditorVersion. Let’s look at them:

find -name ProjectVersion.txt -exec bash -c 'grep --with-filename m_EditorVersion: "{}"' \;

This will find all of them from wherever you are currently and then print their filename and that line with the version in it. It might, for example, look like this:

./projects/student projects/student-name-example/Conditionals/ProjectSettings/ProjectVersion.txt:m_EditorVersion: 6000.0.59f2

Most of the path varies from student to student and from assignment to assignment. But we still found it. For the next steps I will not try to do this as a shell one-liner, because what I want to do is going to be a little more complicated.

Opening and compiling MANY sequentially

so, each line now contains the info we need, but there’s another catch: Version numbers can be all over the place. I don’t want to install 15 different editors. Let’s map them to a smaller set of versions at the same time:

#!/usr/bin/env bash
version_to_use="none"
match_version()
{
  version_to_use="none"
  case $1 in
    "6000.0.43f1")
      ;&
    "6000.0.55f1")
      ;&
    "6000.0.59f2")
      ;&
    "6000.0.60f1")
      ;&
    "6000.0.61f1")
      version_to_use="6000.0.59f2"
      ;;
    "6000.2.0f1")
      ;&
    "6000.2.10f1")
      ;&
    "6000.2.12f1")
      ;&
    "6000.2.7f2")
      ;&
    "6000.2.8f1")
      ;&
    "6000.2.9f1")
      version_to_use="6000.2.7f2"
      ;;
    *)
      echo "unknown version for $1"
      ;;
  esac
}

projects=$(find -name ProjectVersion.txt -exec bash -c 'grep --with-filename m_EditorVersion: "{}"' \;)

echo "$projects" | while read -r line
do
  settings_path="$(dirname "$(echo "${line}" | cut -d':' -f 1)")"
  project="$(dirname "${settings_path}")"
  match_version "$(echo "$line" | awk -F" " '{print $NF}')"
  echo "opening unity ${version_to_use} for path: ---${project}---"
  time ~/Unity/Hub/Editor/${version_to_use}/Editor/Unity -projectpath "${project}" -quit -batchmode
done

So this now runs ~40-60 seconds per project. I can have this run for an hour and compile all my projects, after which opening a project will happen in about 10 seconds instead of 40. This does not sound like much, but it minimizes a very dreaded unusable half-minute between projects.


Leave a Reply


Author

Claudius Coenen is a tech-enthusiast. He's writing on all kinds of topics, including programming, technology, gadgets and media.

This site features his occasional articles, findings, documentations.

Categories

Connect

RSS Recent Bookmarks

Legal