Skip to content
This repository has been archived by the owner on Apr 11, 2020. It is now read-only.

Added support for Unity 2017/2018 and Linux based systems #1

Open
wants to merge 6 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 28 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,27 +1,49 @@
# Unity Automatic Licensor

Unity doesn't support automatically licensing installations with Personal licenses. The only way to activate a Personal license is to interactively login and click through the licensing wizard.
Licensing Unity Personal via commandline is currently not supported. The only way to activate a Personal license is to interactively login and click through the licensing wizard.

This causes a problem for Windows build agents that are automated with Packer, or are otherwise dynamically spun up on public cloud infrastructure.
Using build agents this is not always a feasible option, since you are not always able(or inclined) to do this manually. Especially for volatile systems like GitHub Actions this is a problem.

This tool allows you to license Unity with a Personal license from the command line.

## Usage

Download a release from the Releases page, and extract it somewhere on your Windows system.
On a Windows System you can simply download, build and run the licensor natively.

Then run it like so:
```
.\UnityAutomaticLicensor.exe --username <your username> --password <your password> --unity-path <path-to-unity>\Editor\Unity.exe"
```

Using the dotnet SDK, this application can be run on linux systems as well. Start the application via:
```
dotnet run --username <your username> --password <your password> --unity-path "/<path-to-unity>/Editor/Unity"
```

If you are on a headless system and using Unity 2017 or newer you should run the licensor via ``xvfb-run``. The license agreement will not be confirmed properly if otherwise.

### Optional arguments

By default the license will be saved in the "C:\ProgramData\Unity" directory. You can specify the folder manually(e.g. for linux systems) via:
```
--unity-license-path <path-to-license-directory>
```

By default this application will try to license Unity 5.x versions. To activate newer versions which do not require to answer the activation survey, specify the correct version via
```
--unity-version <version> --unity-changeset <changeset>
```

Usually Unity will be started after obtaining a license to verify if it is valid. You can disable this behaviour via
```
.\UnityAutomaticLicensor.exe --username <your username> --password <your password> --unity-path "C:\Program Files\Unity\Editor\Unity.exe"
--nocheck
```

## Building from Source

You can build your own copy of the application with:

```
dotnet publish -c Release -r win10-x64
dotnet publish -c Release
```

## License
Expand Down
17 changes: 15 additions & 2 deletions UnityAutomaticLicensor/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,18 @@ public static Task<int> Main(string[] args)
[Option("--unity-path <path-to-Unity.exe>", Description = "Path to Unity executable")]
public string UnityPath { get; set; }

[Option("--unity-version <version>", Description = "'v5.x' for 5.x series, 'lic' for 2017.0 and later")]
public string UnityVersion { get; set; } = "v5.x";
[Option("--unity-license-path <path-to-directory-with-Unity.ulf>", Description =
"Path to directory containing Unity license file")]
public string UnityLicensePath { get; set; } = "C:/ProgramData/Unity";

[Option("--unity-version <version>", Description = "Unity version number (e.g. '2018.3.4f1')")]
public string UnityVersion { get; set; } = "5.4.1f1";

[Option("--unity-changeset <changeset>", Description = "Unity version changeset")]
public string UnityChangeset { get; set; } = "649f48bbbf0f";

[Option("--nocheck", Description = "Indicates that unity should not be started again to verify the obtained license.")]
public bool CheckSuccess { get; set; } = true;

private async Task OnExecute()
{
Expand All @@ -33,6 +43,9 @@ private async Task OnExecute()
Password = this.Password,
UnityExecutablePath = this.UnityPath,
UnityVersion = this.UnityVersion,
UnityChangeset = this.UnityChangeset,
UnityLicensePath = this.UnityLicensePath,
CheckSuccess = this.CheckSuccess
});
await licensor.Run();
}
Expand Down
3 changes: 0 additions & 3 deletions UnityAutomaticLicensor/UnityAutomaticLicensor.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,8 @@

<PropertyGroup>
<TargetFramework>netcoreapp2.1</TargetFramework>
<ApplicationIcon />
<OutputType>Exe</OutputType>
<StartupObject />
<LangVersion>7.1</LangVersion>
<RuntimeIdentifiers>win10-x64</RuntimeIdentifiers>
</PropertyGroup>

<ItemGroup>
Expand Down
34 changes: 22 additions & 12 deletions UnityAutomaticLicensor/UnityExecutor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,8 @@ public async Task<UnityExecutorResponse> ExecuteAsync(UnityExecutorRequest reque
}
processStartInfo.ArgumentList.Add("-logFile");
processStartInfo.ArgumentList.Add(logPath);
processStartInfo.ArgumentList.Add("-createProject");
processStartInfo.ArgumentList.Add(temporaryDirectory);
//processStartInfo.ArgumentList.Add("-createProject");
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

From memory, this was required on Unity 5.

//processStartInfo.ArgumentList.Add(temporaryDirectory);
var process = Process.Start(processStartInfo);

Console.WriteLine("Unity process has been launched...");
Expand Down Expand Up @@ -119,7 +119,8 @@ public async Task<UnityExecutorResponse> ExecuteAsync(UnityExecutorRequest reque
Result = UnityExecutorResponseResult.Retry
};
}
if (newContent.Contains("Canceling DisplayDialog: Updating license failed Failed to update license within 60 seconds"))
if (newContent.Contains("Canceling DisplayDialog: Updating license failed Failed to update license within 60 seconds")
|| newContent.Contains("Cancelling DisplayDialog: Failed to activate/update license. Timeout occured while trying to update license"))
{
Console.WriteLine("Licensing timeout - Unity has stalled!");
await KillProcess(process.Id);
Expand Down Expand Up @@ -201,18 +202,27 @@ private async Task<UnityExecutorResponse> HandleMonoIsStalled(DateTimeOffset? cl

private async Task KillProcess(int processId)
{
while (!(Process.GetProcessById(processId)?.HasExited ?? true))
try
{
try
{
Console.WriteLine("Sending kill signal to Unity and waiting for it to exit...");
Process.GetProcessById(processId).Kill();
}
catch
while (!(Process.GetProcessById(processId)?.HasExited ?? true))
{
try
{
Console.WriteLine("Sending kill signal to Unity and waiting for it to exit...");
Process.GetProcessById(processId).Kill();
}
catch
{
}

await Task.Delay(1000);
}

await Task.Delay(1000);
}
catch (ArgumentException)
{
// on linux systems the process will be killed immediatly and removed from record.
// in this case simply return, as an argument exception indicates the process was properly killed.
return;
}
}
}
Expand Down
55 changes: 43 additions & 12 deletions UnityAutomaticLicensor/UnityLicensor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,8 @@ public async Task Run()
{
while (true)
{
var licensePath = $@"C:\ProgramData\Unity\Unity_{_request.UnityVersion}.ulf";
var licenseFileName = _request.UnityVersion[1] == '.' ? _request.UnityVersion : "lic";
var licensePath = $@"{_request.UnityLicensePath}/Unity_{licenseFileName}.ulf";

var licenseKeyCheck = await RunUnityAndCaptureMachineKeys();
if (licenseKeyCheck.IsActivated)
Expand All @@ -40,8 +41,8 @@ public async Task Run()
Console.WriteLine("Logging into Unity Cloud...");
var coreClient = new RestClient("https://core.cloud.unity3d.com");
var loginRequest = new RestRequest("api/login", Method.POST);
loginRequest.AddCookie("unity_version", "5.4.1f1");
loginRequest.AddCookie("unity_version_full", "5.4.1f1 (649f48bbbf0f)");
loginRequest.AddCookie("unity_version", $@"{_request.UnityVersion}");
loginRequest.AddCookie("unity_version_full", $@"{_request.UnityVersion} ({_request.UnityChangeset})");
loginRequest.AddJsonBody(new
{
grant_type = "password",
Expand All @@ -56,8 +57,8 @@ public async Task Run()

Console.WriteLine("Discovering user info for licensing...");
var meRequest = new RestRequest("api/users/me", Method.GET);
meRequest.AddCookie("unity_version", "5.4.1f1");
meRequest.AddCookie("unity_version_full", "5.4.1f1 (649f48bbbf0f)");
loginRequest.AddCookie("unity_version", $@"{_request.UnityVersion}");
loginRequest.AddCookie("unity_version_full", $@"{_request.UnityVersion} ({_request.UnityChangeset})");
meRequest.AddHeader("Authorization", "Bearer " + loginResponse.AccessToken);
response = await coreClient.ExecuteTaskAsync(meRequest);
var userResponse = JsonConvert.DeserializeObject<UnityCloudUserResponse>(response.Content);
Expand Down Expand Up @@ -86,16 +87,37 @@ public async Task Run()
var licenseRequest = new RestRequest("api/transactions/{txId}", Method.PUT);
licenseRequest.AddUrlSegment("txId", txId);
licenseRequest.AddHeader("Authorization", "Bearer " + loginResponse.AccessToken);
licenseRequest.AddJsonBody(new
// newer versions can just skip the survey
if (licenseFileName == "lic")
{
transaction = new
licenseRequest.AddJsonBody(new
{
serial = new
transaction = new
{
type = "personal"
serial = new
{
type = "personal"
},
survey_answer = new
{
skipped = true
}
}
}
});
});
}
else
{
licenseRequest.AddJsonBody(new
{
transaction = new
{
serial = new
{
type = "personal"
}
}
});
}
response = await licenseClient.ExecuteTaskAsync(licenseRequest);
licenseResponse = JsonConvert.DeserializeObject<UnityLicenseTransactionResponse>(response.Content);

Expand Down Expand Up @@ -239,6 +261,12 @@ public async Task Run()
Console.WriteLine("Successfully obtained a Unity license!");

Console.WriteLine("Finalising license by running Unity...");
if(!_request.CheckSuccess)
{
Console.WriteLine("Successfully finalised Unity license!");
return;
}

var finaliseResponse = await RunUnityToFinaliseLicense();
if (finaliseResponse.IsActivated)
{
Expand Down Expand Up @@ -274,6 +302,7 @@ private async Task<UnityLicenseStatusCheck> RunUnityAndCaptureMachineKeys()
{
"-quit",
"-batchmode",
"-nographics",
"-username",
_request.Username,
"-password",
Expand Down Expand Up @@ -322,6 +351,7 @@ private async Task<UnityLicenseStatusCheck> RunUnityAndCaptureMachineKeys()
}
else if (response.Result == UnityExecutorResponseResult.Error)
{
Console.WriteLine("Error encountered!");
throw new InvalidOperationException(response.Output);
}
}
Expand All @@ -341,14 +371,15 @@ private async Task<UnityLicenseStatusCheck> RunUnityToFinaliseLicense()
{
"-quit",
"-batchmode",
"-nographics",
"-username",
_request.Username,
"-password",
_request.Password,
"-force-free"
}
});

if (response.Result == UnityExecutorResponseResult.Success)
{
return new UnityLicenseStatusCheck
Expand Down
8 changes: 7 additions & 1 deletion UnityAutomaticLicensor/UnityLicensorRequest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,14 @@ public class UnityLicensorRequest

public string Password { get; set; }

public string UnityVersion { get; set; }

public string UnityChangeset { get; set; }

public string UnityLicensePath { get; set; }

public string UnityExecutablePath { get; set; }

public string UnityVersion { get; set; }
public bool CheckSuccess { get; set; }
}
}