Skip to content

Instantly share code, notes, and snippets.

@ZaneDubya
Created February 19, 2019 23:20
Show Gist options
  • Save ZaneDubya/76bd8faadbb4ffbe09fc964424f9240d to your computer and use it in GitHub Desktop.
Save ZaneDubya/76bd8faadbb4ffbe09fc964424f9240d to your computer and use it in GitHub Desktop.
using System;
using System.Diagnostics;
using System.IO;
using System.Threading;
using System.Threading.Tasks;
namespace MacTranslocator {
class TranslocateHandler {
/// <summary>
/// This is the name of the game, the name of the folder in local storage where the game will be run from,
/// and the name of the .app folder that contains the game.
/// It is not the name of the executable file, use Platform_FilenameExecutable for that.
/// </summary>
public const string APPLICATION_NAME = "MacTranslocator";
private readonly Action<string> _ShowMessage;
private readonly Action _OnFail;
private readonly Action _OnSuccess;
/// <summary>
/// This mode will show an update screen while it checks for patches and then downloads any updates.
/// Normally this will only run on compiled versions, not in the debugger.
/// Pass parameter forceUpdate==true to force an update regardless of what exe is running.
/// </summary>
public TranslocateHandler(Action<string> writeDebug, Action onSuccess, Action onFail) {
_ShowMessage = writeDebug;
_OnSuccess = onSuccess;
_OnFail = onFail;
}
public void Initialize() {
ShowMessage($"ModeLoading initializing.");
ShowMessage($" Platform is: {PlatformType}");
ShowMessage($" Running in: {Platform_PathCurrentRunningDirectory}");
ShowMessage($" PathExeDir: {Platform_PathExeDirectory}");
ShowMessage($" FilenameExe: {Platform_FilenameExecutable}");
Task.Factory.StartNew(() => {
if (!RunUpdateFunctions()) {
_OnFail?.Invoke();
}
else {
_OnSuccess?.Invoke();
}
});
}
private void ShowMessage(string msg, int waitMS = 0) {
_ShowMessage?.Invoke(msg);
if (waitMS > 0) {
Thread.Sleep(waitMS);
}
}
// === Patch / Update routines live here =====================================================================
// ============================================================================================================
private bool RunUpdateFunctions() {
if (!VerifyRunningInClientAppFolder()) {
// don't continue with update, allow called method to end program.
return false;
}
return true;
}
private bool VerifyRunningInClientAppFolder() {
string currentDirectory = Platform_PathCurrentRunningDirectory;
if (!currentDirectory.Contains(Platform_PathClientApp)) {
ShowMessage("Application must be restarted in correct location.", 2000);
if (!VerifyDirectoryContents(currentDirectory, Platform_PathClientApp)) {
// delete client.app folder, copy in all files in this folder
ShowMessage($"Copying application to {Platform_PathClientApp}");
CopyAndReplace(currentDirectory, Platform_PathClientApp);
}
if (Platform_IsRunningInDebugger) {
ShowMessage("Debugger: will not restart.");
}
else {
string pathToExe = Path.Combine(Platform_PathExeDirectory, Platform_FilenameExecutable);
Process.Start(pathToExe);
return false;
}
}
return true;
}
private bool VerifyDirectoryContents(string pathSource, string pathDestination, string specificFilenameToCheck = null) {
string filePattern = specificFilenameToCheck ?? "*.exe";
ShowMessage($" Verifying files with pattern '{filePattern}':");
ShowMessage($" {pathSource} vs");
ShowMessage($" {pathDestination}");
foreach (string filePath in Directory.GetFiles(pathSource, filePattern, SearchOption.AllDirectories)) {
string fileLocalPath = filePath.Remove(0, pathSource.Length);
if (fileLocalPath.Length > 0 && fileLocalPath[0] == Path.DirectorySeparatorChar) {
fileLocalPath = fileLocalPath.Remove(0, 1);
}
if (IgnoreLogsAndPbds(fileLocalPath)) {
continue;
}
string fileWeWant = Path.Combine(pathDestination, fileLocalPath);
if (File.Exists(fileWeWant)) {
ShowMessage($" Path exists: {Platform_RemoveUserPath(fileWeWant)}");
// We should replace files that are older than the current version...
// but I haven't written that code yet.
}
else {
ShowMessage($" Path missing: {fileLocalPath}");
return false;
}
}
return true;
}
private bool IgnoreLogsAndPbds(string filename) {
if (Path.GetFileName(filename).StartsWith("log") && Path.GetFileName(filename).EndsWith(".txt"))
return true;
if (Path.GetExtension(filename) == ".pdb") {
return true;
}
return false;
}
private void CopyAndReplace(string pathSource, string pathDestination) {
if (Directory.Exists(pathDestination)) {
Directory.Delete(pathDestination, true);
}
if (!pathDestination.EndsWith(Path.DirectorySeparatorChar.ToString())) {
pathDestination += Path.DirectorySeparatorChar;
}
//Now Create all of the directories
foreach (string dirPath in Directory.GetDirectories(pathSource, "*", SearchOption.AllDirectories)) {
Directory.CreateDirectory(dirPath.Replace(pathSource, pathDestination));
}
//Copy all the files & Replaces any files with the same name
foreach (string newPath in Directory.GetFiles(pathSource, "*.*", SearchOption.AllDirectories)) {
if (IgnoreLogsAndPbds(newPath)) {
continue;
}
File.Copy(newPath, newPath.Replace(pathSource, pathDestination), true);
}
}
// === Platform code lives here ===============================================================================
// ============================================================================================================
private const int NOT_SET = 0;
public const int WINDOWS = 1;
public const int MACOS = 2;
public const int LINUX = 3;
private int _PlatformType = NOT_SET;
/// <summary>
/// Returns an integer corresponding to the platform type we are running on.
/// Possible values are Platform.WINDOWS, Platform.MACOS, and Platform.LINUX.
/// Will throw a critical error if it cannot determine the platform.
/// </summary>
public int PlatformType {
get {
if (_PlatformType == NOT_SET) {
#if SDL
string platform = SDL.SDL_GetPlatform();
if (platform.Equals("Windows")) {
_PlatformType = WINDOWS;
}
else if (platform.Equals("Mac OS X")) {
_PlatformType = MACOS;
}
else if (platform.Equals("Linux")) {
_PlatformType = LINUX;
}
else {
Tracer.Critical($"Could not determine platform type (unknown type '{platform}').");
}
#else
_PlatformType = WINDOWS;
#endif
}
return _PlatformType;
}
}
/// <summary>
/// Path to the client application folder in the local storage.
/// This is not necessarily the path to the folder containing the exectuable file and resources.
/// Use PathExeDirectory for that.
/// </summary>
public string Platform_PathClientApp => Platform_GetPlatformSubDirectory($"{APPLICATION_NAME}.app");
/// <summary>
/// This will be true if we are running in the Visual Studio debugger.
/// </summary>
public bool Platform_IsRunningInDebugger => Debugger.IsAttached;
/// <summary>
/// This is the filename that is used to start the game.
/// On Windows, it will be the name of the client executable.
/// On mac, it will be the kickstart binary.
/// </summary>
public string Platform_FilenameExecutable {
get {
switch (PlatformType) {
case WINDOWS:
return AppDomain.CurrentDomain.FriendlyName;
case MACOS:
return "CommunityClient"; // this is the kickstart script
default:
return "NoExeFile";
}
}
}
/// <summary>
/// This is the directory the application is currently running in. It is not necessarily local storage.
/// If it is not local storage, then the application should be copied to local storage and run from there.
/// </summary>
public string Platform_PathCurrentRunningDirectory {
get {
switch (PlatformType) {
case WINDOWS:
return AppDomain.CurrentDomain.BaseDirectory;
case MACOS:
DirectoryInfo basePath = Directory.GetParent(AppDomain.CurrentDomain.BaseDirectory).Parent.Parent;
return basePath.ToString();
default:
return "NoPathBaseDirectory";
}
}
}
/// <summary>
/// Path to the folder in local storage containing the executable file.
/// This is where all the resource/dll/etc. files will be found.
/// </summary>
public string Platform_PathExeDirectory {
get {
#if SERVER
return PathCurrentRunningDirectory;
#else
switch (PlatformType) {
case WINDOWS:
return Platform_PathClientApp;
case MACOS:
return $"{Platform_PathClientApp}/Contents/MacOS";
default:
return "NoPathExeDirectory";
}
#endif
}
}
private string Platform_GetPlatformSubDirectory(string subDirectory) {
return Path.Combine(Platform_GetPlatformDirectory(), subDirectory);
}
private string Platform_GetPlatformDirectory() {
switch (PlatformType) {
case WINDOWS:
return Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments), "Games", APPLICATION_NAME);
case MACOS: {
string osConfigDir = Environment.GetEnvironmentVariable("HOME");
if (string.IsNullOrEmpty(osConfigDir)) {
return "."; // Oh well.
}
return Path.Combine(osConfigDir, "Library", "Application Support", APPLICATION_NAME);
}
case LINUX: {
string osConfigDir = Environment.GetEnvironmentVariable("XDG_DATA_HOME");
if (string.IsNullOrEmpty(osConfigDir)) {
osConfigDir = Environment.GetEnvironmentVariable("HOME");
if (string.IsNullOrEmpty(osConfigDir)) {
return "."; // Oh well.
}
osConfigDir += "/.local/share";
}
return Path.Combine(osConfigDir, APPLICATION_NAME);
}
default:
return "NoPlatformDirectory";
}
}
/// <summary>
/// This is the user path.
/// </summary>
private readonly string Platform_PathUser = Environment.GetFolderPath(Environment.SpecialFolder.UserProfile);
private string Platform_RemoveUserPath(string path) => path.Replace(Platform_PathUser, ".");
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment