Skip to content
Merged
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
64 changes: 64 additions & 0 deletions .github/workflows/Rocket.AutoInstaller.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
name: Rocket.AutoInstaller

on:
create:
tags:
- "*"
push:
branches: [ master ]
paths:
- '.github/workflows/Rocket.AutoInstaller.yaml'
- 'Rocket.AutoInstaller/**'
pull_request:
paths:
- '.github/workflows/Rocket.AutoInstaller.yaml'
- 'Rocket.AutoInstaller/**'

jobs:
Build:
runs-on: ubuntu-latest

steps:
- name: Checkout code
uses: actions/checkout@v4
with:
fetch-depth: 0

- uses: actions/setup-dotnet@v4
name: Setup .NET
with:
dotnet-version: 8.x

- name: Build Project
uses: ./.github/actions/project-build
id: project-build
with:
project_path: Rocket.AutoInstaller
nuget_key: ${{ secrets.NUGET_DEPLOY_KEY }}
nuget_push: false
github_token: ${{ secrets.PAT }}

- name: Install zip
run: sudo apt-get install zip

- name: Zip artifacts
run: "cd ./Rocket.AutoInstaller/bin/Release/net461/linux-x64/Rocket.AutoInstaller && zip -qq -r ./Rocket.AutoInstaller.zip *"

- name: Upload build
uses: actions/upload-artifact@v4
with:
name: Rocket.AutoInstaller.zip
path: "./Rocket.AutoInstaller/bin/Release/net461/linux-x64/Rocket.AutoInstaller/Rocket.AutoInstaller.zip"
if-no-files-found: error

- name: Create Release
if: github.event_name == 'create' && github.event.ref_type == 'tag'
uses: ncipollo/release-action@v1.14.0
with:
name: RocketModFix Release v${{ steps.project-build.outputs.version }}
tag: ${{ steps.project-build.outputs.version }}
artifacts: "./Rocket.AutoInstaller/bin/Release/net461/linux-x64/Rocket.AutoInstaller/Rocket.AutoInstaller.Module.zip"
token: ${{ secrets.PAT }}
prerelease: ${{ steps.project-build.outputs.is_prerelease }}
allowUpdates: true
draft: true
18 changes: 18 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ You can still use old plugins without any changes/recompilation/updates.
- [x] Reset changelog.
- [x] For versioning use [SemVer][semver_url].
- [x] Installation guides inside of the Rocket Unturned Module.
- [x] Rocket.AutoInstaller to automaticaly install Rocket.
- [x] Keep backward compatibility.
- [x] Test with RocketMod plugins that uses old RocketMod libraries, and make sure current changes doesn't break anything.
- [x] Test with most used Modules:
Expand Down Expand Up @@ -53,13 +54,30 @@ After plan is finished -> Add new plans, keep coding, and don't forget to approv

## Installation

Now we have 2 different ways you can install Rocket, either `Standard Way` or `Auto-Installer`, select the one you like more, but we highly recommend to use `Auto-Installer`.

### Standard Way

1. **Stop the Server**: If your server is running, stop it.
2. **Remove Old Rocket**: Delete the entire `Rocket.Unturned` folder located in `Modules` (if it exists).
3. **Download the Latest RocketModFix**: Go to the [RocketModFix releases page](https://github.com/RocketModFix/RocketModFix/releases).
4. **Access the Assets**: Open the "Assets" section if it's not already expanded.
5. **Download the Module**: Click `Rocket.Unturned.Module.zip` to download the latest module.
6. **Final**: Extract the downloaded archive, open the `Rocket.Unturned.Module` folder, and copy the `Rocket.Unturned` folder to `Modules` (copy the folder, not it's content, and if its asks to Replace the existing files then press to replace them).

### Auto-Installer (new way)

It's same as installing Rocket manually (standard way), however if we make an update you will receive it automatically, so you don't need to remove old Rocket, replace/delete files, etc.

See more info [here](https://github.com/RocketModFix/RocketModFix/blob/master/Rocket.AutoInstaller/README.md) about it if you're interested how it work and what we're planning to do next with it.

1. **Stop the Server**: If your server is running, stop it.
2. **Remove Rocket** (if you still have it): Delete the entire `Rocket.Unturned` folder located in `Modules` (if it exists).
3. **Download the Latest Rocket.AutoInstaller**: Go to the [RocketModFix releases page](https://github.com/RocketModFix/RocketModFix/releases).
4. **Access the Assets**: Open the "Assets" section if it's not already expanded.
5. **Download the Rocket.AutoInstaller**: Click `Rocket.AutoInstaller.zip` to download the latest module.
6. **Final**: Extract the downloaded archive, open the `Rocket.AutoInstaller` folder, and copy the `Rocket.AutoInstaller` folder to `Modules` (copy the folder, not it's content, and if its asks to Replace the existing files then press to replace them).

Contact in our discord if you have any problems. Just in case you can also read `Readme_EN.txt` or `Readme_RU.txt` inside of the installed Module.

## Discord
Expand Down
21 changes: 21 additions & 0 deletions Rocket.AutoInstaller/Installation/GitHubAsset.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
using Newtonsoft.Json;

namespace Rocket.AutoInstaller.Installation;

public class GitHubAsset
{
[JsonProperty("url")]
public string Url { get; set; }

[JsonProperty("name")]
public string Name { get; set; }

[JsonProperty("content_type")]
public string ContentType { get; set; }

[JsonProperty("size")]
public int Size { get; set; }

[JsonProperty("browser_download_url")]
public string BrowserDownloadUrl { get; set; }
}
31 changes: 31 additions & 0 deletions Rocket.AutoInstaller/Installation/GitHubRelease.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
using System;
using Newtonsoft.Json;

namespace Rocket.AutoInstaller.Installation;

public class GitHubRelease
{
[JsonProperty("url")]
public string Url { get; set; }

[JsonProperty("tag_name")]
public string TagName { get; set; }

[JsonProperty("name")]
public string Name { get; set; }

[JsonProperty("body")]
public string Body { get; set; }

[JsonProperty("draft")]
public bool Draft { get; set; }

[JsonProperty("prerelease")]
public bool Prerelease { get; set; }

[JsonProperty("published_at")]
public DateTime PublishedAt { get; set; }

[JsonProperty("assets")]
public GitHubAsset[] Assets { get; set; }
}
190 changes: 190 additions & 0 deletions Rocket.AutoInstaller/Installation/Installer.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,190 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.IO.Compression;
using System.Linq;
using System.Reflection;
using Newtonsoft.Json;
using SDG.Framework.Modules;
using SDG.Unturned;
using UnityEngine.Networking;

namespace Rocket.AutoInstaller.Installation;

public class Installer
{
public static IEnumerator Install()
{
var request = UnityWebRequest.Get(
"https://api.github.com/repos/RocketModFix/RocketModFix/releases");
request.SetRequestHeader("User-Agent", "RocketModFix");
request.redirectLimit = 5;

yield return request.SendWebRequest();

if (request.result != UnityWebRequest.Result.Success)
{
CommandWindow.LogError($"Installation Failed: An error occured while installing RocketModFix, response code: {request.responseCode}, error: {request.error}");
yield break;
}

var responseContent = request.downloadHandler.text;
var releases = JsonConvert.DeserializeObject<List<GitHubRelease>>(responseContent);
var latestRelease = releases!.FirstOrDefault();
if (latestRelease == null)
{
CommandWindow.LogError("Installation Failed: No Release Found.");
yield break;
}
if (string.IsNullOrWhiteSpace(latestRelease.TagName))
{
CommandWindow.LogError("Installation Failed: Release Tag Name doesn't seems to be valid.");
yield break;
}
var moduleAsset = latestRelease.Assets.FirstOrDefault(IsRocketModFixModule);
if (moduleAsset == null)
{
CommandWindow.LogError("Installation Failed: Module not found.");
yield break;
}

Console.WriteLine("aa: " + moduleAsset.BrowserDownloadUrl);

CommandWindow.LogWarning($"Preparing to install: " +
$"RocketModFix {latestRelease.TagName} Released: {latestRelease.PublishedAt}");

request = UnityWebRequest.Get(moduleAsset.BrowserDownloadUrl);
request.SetRequestHeader("User-Agent", "RocketModFix");
request.redirectLimit = 5;
yield return request.SendWebRequest();

if (request.result != UnityWebRequest.Result.Success)
{
CommandWindow.LogError($"Installation Failed: An error occured while getting raw data of module {moduleAsset.Name}, response code: {request.responseCode}, error: {request.error}");
yield break;
}

var rawData = request.downloadHandler.data;
File.WriteAllBytes(@"C:\Users\user\Downloads\Rocket.Unturned.Module\a.zip", rawData);
var releaseEntries = GetReleaseEntries(rawData);
byte[]? rocketModuleData = null;
List<byte[]> rocketLibraries = [];
const string rocketEntryPointFileName = "Rocket.Unturned.dll";
foreach (var releaseEntry in releaseEntries)
{
if (releaseEntry.Name == rocketEntryPointFileName)
{
rocketModuleData = releaseEntry.Content;
continue;
}
if (releaseEntry.Name != rocketEntryPointFileName && releaseEntry.FileExtension == ".dll")
{
rocketLibraries.Add(releaseEntry.Content);
continue;
}
}

if (rocketModuleData == null)
{
CommandWindow.LogError($"Installation Failed: {rocketEntryPointFileName} Rocket Entry Point was not found.");
yield break;
}

foreach (var rocketLibrary in rocketLibraries)
{
Assembly.Load(rocketLibrary);
}

var rocketModule = Assembly.Load(rocketModuleData);

var types = GetLoadableTypes(rocketModule);
var moduleType = types.FirstOrDefault(
x => x.IsAbstract == false && typeof(IModuleNexus).IsAssignableFrom(x));
if (moduleType == null)
{
CommandWindow.LogError("Installation Failed: Rocket Module Type cannot be found!");
yield break;
}

try
{
if (Activator.CreateInstance(moduleType) is not IModuleNexus plugin)
{
CommandWindow.LogError("Unable to create UnturnedGuard Module!");
yield break;
}

plugin.initialize();
}
catch (Exception ex)
{
CommandWindow.LogError("An error occured while creating UnturnedGuard Module! \n" + ex);
yield break;
}

CommandWindow.LogWarning(
$"Successfully installed: RocketModFix v{latestRelease.TagName}");
}

private static bool IsRocketModFixModule(GitHubAsset asset)
{
return asset.Name.Equals("Rocket.Unturned.Module.zip", StringComparison.Ordinal);
}
private static List<ReleaseEntry> GetReleaseEntries(byte[] assetData)
{
var entries = new List<ReleaseEntry>();
using var memoryStream = new MemoryStream(assetData);
using var zipArchive = new ZipArchive(memoryStream);
foreach (var entry in zipArchive.Entries)
{
try
{
var fileNameWithoutExtension = Path.GetFileNameWithoutExtension(entry.FullName);
var fileExtension = Path.GetExtension(entry.FullName);
var entryDirectoryName = Path.GetDirectoryName(entry.FullName)!;

using var stream = entry.Open();
var entryData = stream.CopyToArray();

entries.Add(new ReleaseEntry(entry.Name, entry.FullName, fileNameWithoutExtension, fileExtension,
entryDirectoryName, entryData));
}
catch (Exception ex)
{
CommandWindow.LogError($"Error while collecting info about release entry: {entry.FullName} \n{ex}");
}
}
return entries;
}
/// <summary>
/// Safely returns the set of loadable types from an assembly.
/// Algorithm from StackOverflow answer here:
/// https://stackoverflow.com/questions/7889228/how-to-prevent-reflectiontypeloadexception-when-calling-assembly-gettypes
/// </summary>
/// <param name="assembly">The <see cref="Assembly"/> from which to load types.</param>
/// <returns>
/// The set of types from the <paramref name="assembly" />, or the subset
/// of types that could be loaded if there was any error.
/// </returns>
/// <exception cref="ArgumentNullException">
/// Thrown if <paramref name="assembly" /> is <see langword="null" />.
/// </exception>
private static IEnumerable<Type> GetLoadableTypes(Assembly assembly)
{
if (assembly == null)
{
throw new ArgumentNullException(nameof(assembly));
}

try
{
return assembly.DefinedTypes.Select(x => x.AsType());
}
catch (ReflectionTypeLoadException ex)
{
CommandWindow.LogError($"An error occured while getting types for assembly: {assembly} \n" + ex);
return ex.Types.Where(x => x is not null);
}
}
}
22 changes: 22 additions & 0 deletions Rocket.AutoInstaller/Installation/ReleaseEntry.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
namespace Rocket.AutoInstaller.Installation;

public class ReleaseEntry
{
public ReleaseEntry(string name, string fullName, string fileNameWithoutExtension, string fileExtension,
string directoryName, byte[] content)
{
Name = name;
FullName = fullName;
FileNameWithoutExtension = fileNameWithoutExtension;
FileExtension = fileExtension;
DirectoryName = directoryName;
Content = content;
}

public string Name { get; }
public string FullName { get; }
public string FileExtension { get; }
public string FileNameWithoutExtension { get; }
public string DirectoryName { get; set; }
public byte[] Content { get; }
}
13 changes: 13 additions & 0 deletions Rocket.AutoInstaller/Installation/StreamExtensions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
using System.IO;

namespace Rocket.AutoInstaller.Installation;

public static class StreamExtensions
{
public static byte[] CopyToArray(this Stream source)
{
var memoryStream = new MemoryStream();
source.CopyTo(memoryStream);
return memoryStream.ToArray();
}
}
Loading