Skip to content

Commit

Permalink
Merge pull request #2271 from peppy/portable-installation-support
Browse files Browse the repository at this point in the history
Add support for portable installations
  • Loading branch information
peppy authored Mar 27, 2019
2 parents 2875177 + a468860 commit d361fc4
Show file tree
Hide file tree
Showing 25 changed files with 428 additions and 189 deletions.
5 changes: 5 additions & 0 deletions osu.Framework.Android/AndroidGameHost.cs
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,11 @@ public class AndroidGameHost : GameHost
public AndroidGameHost(AndroidGameView gameView)
{
this.gameView = gameView;
}

protected override void SetupForRun()
{
base.SetupForRun();
AndroidGameWindow.View = gameView;
Window = new AndroidGameWindow();
}
Expand Down
2 changes: 1 addition & 1 deletion osu.Framework.Android/AndroidStorage.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@

namespace osu.Framework.Android
{
public class AndroidStorage : DesktopStorage
public class AndroidStorage : NativeStorage
{
public AndroidStorage(string baseName, GameHost host)
: base(baseName, host)
Expand Down
16 changes: 10 additions & 6 deletions osu.Framework.Tests/Exceptions/TestLoadExceptions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -200,15 +200,19 @@ private void runGameWithLogic(Action<Game> logic, Func<Game, bool> exitCondition
{
using (var host = new HeadlessGameHost($"{GetType().Name}-{Guid.NewGuid()}", realtime: false))
{
storage = host.Storage;
using (var game = new TestGame())
{
game.Schedule(() => logic(game));
host.UpdateThread.Scheduler.AddDelayed(() =>
game.Schedule(() =>
{
if (exitCondition?.Invoke(game) == true)
host.Exit();
}, 0, true);
storage = host.Storage;
host.UpdateThread.Scheduler.AddDelayed(() =>
{
if (exitCondition?.Invoke(game) == true)
host.Exit();
}, 0, true);
logic(game);
});

host.Run(game);
}
Expand Down
54 changes: 54 additions & 0 deletions osu.Framework.Tests/IO/BackgroundGameHeadlessGameHost.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.

using System.Threading;
using System.Threading.Tasks;
using osu.Framework.Platform;

namespace osu.Framework.Tests.IO
{
/// <summary>
/// Ad headless host for testing purposes. Contains an arbitrary game that is running after construction.
/// </summary>
public class BackgroundGameHeadlessGameHost : HeadlessGameHost
{
private TestGame testGame;

public BackgroundGameHeadlessGameHost(string gameName = @"", bool bindIPC = false, bool realtime = true, bool portableInstallation = false)
: base(gameName, bindIPC, realtime, portableInstallation)
{
Task.Run(() =>
{
try
{
Run(testGame = new TestGame());
}
catch
{
// may throw an unobserved exception if we don't handle here.
}
});

while (testGame?.HasProcessed != true)
Thread.Sleep(10);
}

private class TestGame : Game
{
public bool HasProcessed;

protected override void Update()
{
base.Update();
HasProcessed = true;
}
}

protected override void Dispose(bool isDisposing)
{
if (ExecutionState != ExecutionState.Stopped)
Exit();
base.Dispose(isDisposing);
}
}
}
2 changes: 1 addition & 1 deletion osu.Framework.Tests/IO/TestDesktopStorage.cs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ public class TestDesktopStorage
public void TestRelativePaths()
{
var guid = new Guid().ToString();
var storage = new DesktopStorage(guid, null);
var storage = new NativeStorage(guid);

var basePath = storage.GetFullPath(string.Empty);

Expand Down
2 changes: 1 addition & 1 deletion osu.Framework.Tests/IO/TestLogging.cs
Original file line number Diff line number Diff line change
Expand Up @@ -136,7 +136,7 @@ void logTest(LogEntry entry)

Logger.NewEntry += logTest;

using (new HeadlessGameHost())
using (new BackgroundGameHeadlessGameHost())
{
// see https://tpodolak.com/blog/2015/08/10/tpl-exception-handling-and-unobservedtaskexception-issue/
// needs to be in a separate method so the Task gets GC'd.
Expand Down
15 changes: 8 additions & 7 deletions osu.Framework.Tests/Platform/HeadlessGameHostTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,22 +6,18 @@
using System.Threading.Tasks;
using NUnit.Framework;
using osu.Framework.Platform;
using osu.Framework.Tests.IO;

namespace osu.Framework.Tests.Platform
{
[TestFixture]
public class HeadlessGameHostTest
{
private class Foobar
{
public string Bar;
}

[Test]
public void TestIpc()
{
using (var server = new HeadlessGameHost(@"server", true))
using (var client = new HeadlessGameHost(@"client", true))
using (var server = new BackgroundGameHeadlessGameHost(@"server", true))
using (var client = new BackgroundGameHeadlessGameHost(@"client", true))
{
Assert.IsTrue(server.IsPrimaryInstance, @"Server wasn't able to bind");
Assert.IsFalse(client.IsPrimaryInstance, @"Client was able to bind when it shouldn't have been able to");
Expand All @@ -47,5 +43,10 @@ public void TestIpc()
Assert.IsTrue(Task.Run(waitAction).Wait(10000), @"Message was not received in a timely fashion");
}
}

private class Foobar
{
public string Bar;
}
}
}
68 changes: 68 additions & 0 deletions osu.Framework.Tests/Platform/PortableInstallationTest.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.

using System.IO;
using NUnit.Framework;
using osu.Framework.Configuration;
using osu.Framework.Platform;

namespace osu.Framework.Tests.Platform
{
[TestFixture]
public class PortableInstallationTest
{
[Test]
public void TestPortableInstall()
{
Assert.IsFalse(File.Exists(FrameworkConfigManager.FILENAME));

using (var portable = new HeadlessGameHost(@"portable", portableInstallation: true))
{
portable.Run(new TestGame());
Assert.AreEqual(Path.GetFullPath(FrameworkConfigManager.FILENAME), portable.Storage.GetFullPath(FrameworkConfigManager.FILENAME));
}

// portable mode should write the configuration
Assert.IsTrue(File.Exists(FrameworkConfigManager.FILENAME));

// subsequent startups should detect the portable config and continue running in portable mode, even though it is not explicitly specified
using (var portable = new HeadlessGameHost(@"portable"))
{
portable.Run(new TestGame());
Assert.AreEqual(Path.GetFullPath(FrameworkConfigManager.FILENAME), portable.Storage.GetFullPath(FrameworkConfigManager.FILENAME));
}

Assert.IsTrue(File.Exists(FrameworkConfigManager.FILENAME));
}

[Test]
public void TestNonPortableInstall()
{
Assert.IsFalse(File.Exists(FrameworkConfigManager.FILENAME));

using (var portable = new HeadlessGameHost(@"non-portable"))
{
portable.Run(new TestGame());
Assert.AreEqual(Path.GetFullPath(Path.Combine(@"headless-non-portable", FrameworkConfigManager.FILENAME)), portable.Storage.GetFullPath(FrameworkConfigManager.FILENAME));
}

Assert.IsFalse(File.Exists(FrameworkConfigManager.FILENAME));
}

[TearDown]
[SetUp]
public void TearDown()
{
File.Delete(FrameworkConfigManager.FILENAME);
}

private class TestGame : Game
{
protected override void Update()
{
base.Update();
Exit();
}
}
}
}
6 changes: 4 additions & 2 deletions osu.Framework.Tests/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
// See the LICENCE file in the repository root for full licence text.

using System;
using System.Linq;
using osu.Framework.Platform;

namespace osu.Framework.Tests
Expand All @@ -11,9 +12,10 @@ public static class Program
[STAThread]
public static void Main(string[] args)
{
bool benchmark = args.Length > 0 && args[0] == @"-benchmark";
bool benchmark = args.Contains(@"--benchmark");
bool portable = args.Contains(@"--portable");

using (GameHost host = Host.GetSuitableHost(@"visual-tests"))
using (GameHost host = Host.GetSuitableHost(@"visual-tests", portableInstallation: portable))
{
if (benchmark)
host.Run(new AutomatedVisualTestGame());
Expand Down
11 changes: 8 additions & 3 deletions osu.Framework.iOS/IOSGameHost.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@
using osu.Framework.iOS.Graphics.Textures;
using osu.Framework.iOS.Input;
using osu.Framework.Platform;
using osu.Framework.Platform.MacOS;

namespace osu.Framework.iOS
{
Expand All @@ -20,7 +19,13 @@ public class IOSGameHost : GameHost

public IOSGameHost(IOSGameView gameView)
{
IOSGameWindow.GameView = this.gameView = gameView;
this.gameView = gameView;
}

protected override void SetupForRun()
{
base.SetupForRun();
IOSGameWindow.GameView = gameView;
Window = new IOSGameWindow();
}

Expand All @@ -38,7 +43,7 @@ protected override void PerformExit(bool immediately)
protected override IEnumerable<InputHandler> CreateAvailableInputHandlers() =>
new InputHandler[] { new IOSTouchHandler(gameView), new IOSKeyboardHandler(gameView), new IOSRawKeyboardHandler() };

protected override Storage GetStorage(string baseName) => new MacOSStorage(baseName, this);
protected override Storage GetStorage(string baseName) => new IOSStorage(baseName, this);

public override void OpenFileExternally(string filename) => throw new NotImplementedException();

Expand Down
18 changes: 18 additions & 0 deletions osu.Framework.iOS/IOSStorage.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.

using System;
using osu.Framework.Platform;

namespace osu.Framework.iOS
{
public class IOSStorage : NativeStorage
{
protected override string LocateBasePath() => Environment.GetFolderPath(Environment.SpecialFolder.Personal);

public IOSStorage(string baseName, IOSGameHost host = null)
: base(baseName, host)
{
}
}
}
4 changes: 3 additions & 1 deletion osu.Framework/Configuration/FrameworkConfigManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,9 @@ namespace osu.Framework.Configuration
{
public class FrameworkConfigManager : IniConfigManager<FrameworkSetting>
{
protected override string Filename => @"framework.ini";
internal const string FILENAME = @"framework.ini";

protected override string Filename => FILENAME;

protected override void InitialiseDefaults()
{
Expand Down
4 changes: 4 additions & 0 deletions osu.Framework/Game.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.

using System;
using System.Linq;
using osuTK;
using osu.Framework.Allocation;
Expand Down Expand Up @@ -208,6 +209,9 @@ public bool OnPressed(FrameworkAction action)

public void Exit()
{
if (Host == null)
throw new InvalidOperationException("Attempted to exit a game which has not yet been run");

Host.Exit();
}

Expand Down
8 changes: 4 additions & 4 deletions osu.Framework/Host.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ namespace osu.Framework
{
public static class Host
{
public static DesktopGameHost GetSuitableHost(string gameName, bool bindIPC = false)
public static DesktopGameHost GetSuitableHost(string gameName, bool bindIPC = false, bool portableInstallation = false)
{
var toolkitOptions = new ToolkitOptions
{
Expand All @@ -23,11 +23,11 @@ public static DesktopGameHost GetSuitableHost(string gameName, bool bindIPC = fa
switch (RuntimeInfo.OS)
{
case RuntimeInfo.Platform.MacOsx:
return new MacOSGameHost(gameName, bindIPC, toolkitOptions);
return new MacOSGameHost(gameName, bindIPC, toolkitOptions, portableInstallation);
case RuntimeInfo.Platform.Linux:
return new LinuxGameHost(gameName, bindIPC, toolkitOptions);
return new LinuxGameHost(gameName, bindIPC, toolkitOptions, portableInstallation);
case RuntimeInfo.Platform.Windows:
return new WindowsGameHost(gameName, bindIPC, toolkitOptions);
return new WindowsGameHost(gameName, bindIPC, toolkitOptions, portableInstallation);
default:
throw new InvalidOperationException($"Could not find a suitable host for the selected operating system ({Enum.GetName(typeof(RuntimeInfo.Platform), RuntimeInfo.OS)}).");
}
Expand Down
Loading

0 comments on commit d361fc4

Please sign in to comment.