diff --git a/.github/workflows/Rocket.AutoInstaller.yaml b/.github/workflows/Rocket.AutoInstaller.yaml new file mode 100644 index 0000000..824ebb7 --- /dev/null +++ b/.github/workflows/Rocket.AutoInstaller.yaml @@ -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.zip" + token: ${{ secrets.PAT }} + prerelease: ${{ steps.project-build.outputs.is_prerelease }} + allowUpdates: true + draft: true \ No newline at end of file diff --git a/README.md b/README.md index c6e8e56..2a02286 100644 --- a/README.md +++ b/README.md @@ -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: @@ -53,6 +54,10 @@ 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). @@ -60,6 +65,19 @@ After plan is finished -> Add new plans, keep coding, and don't forget to approv 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 diff --git a/Rocket.AutoInstaller/Installation/GitHubAsset.cs b/Rocket.AutoInstaller/Installation/GitHubAsset.cs new file mode 100644 index 0000000..75b7229 --- /dev/null +++ b/Rocket.AutoInstaller/Installation/GitHubAsset.cs @@ -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; } +} \ No newline at end of file diff --git a/Rocket.AutoInstaller/Installation/GitHubRelease.cs b/Rocket.AutoInstaller/Installation/GitHubRelease.cs new file mode 100644 index 0000000..7d38571 --- /dev/null +++ b/Rocket.AutoInstaller/Installation/GitHubRelease.cs @@ -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; } +} \ No newline at end of file diff --git a/Rocket.AutoInstaller/Installation/Installer.cs b/Rocket.AutoInstaller/Installation/Installer.cs new file mode 100644 index 0000000..c42225f --- /dev/null +++ b/Rocket.AutoInstaller/Installation/Installer.cs @@ -0,0 +1,187 @@ +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>(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; + } + + 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; + var releaseEntries = GetReleaseEntries(rawData); + byte[]? rocketModuleData = null; + List 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 GetReleaseEntries(byte[] assetData) + { + var entries = new List(); + 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; + } + /// + /// 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 + /// + /// The from which to load types. + /// + /// The set of types from the , or the subset + /// of types that could be loaded if there was any error. + /// + /// + /// Thrown if is . + /// + private static IEnumerable 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); + } + } +} \ No newline at end of file diff --git a/Rocket.AutoInstaller/Installation/ReleaseEntry.cs b/Rocket.AutoInstaller/Installation/ReleaseEntry.cs new file mode 100644 index 0000000..d8a1aa0 --- /dev/null +++ b/Rocket.AutoInstaller/Installation/ReleaseEntry.cs @@ -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; } +} \ No newline at end of file diff --git a/Rocket.AutoInstaller/Installation/StreamExtensions.cs b/Rocket.AutoInstaller/Installation/StreamExtensions.cs new file mode 100644 index 0000000..bb771f6 --- /dev/null +++ b/Rocket.AutoInstaller/Installation/StreamExtensions.cs @@ -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(); + } +} \ No newline at end of file diff --git a/Rocket.AutoInstaller/Module.cs b/Rocket.AutoInstaller/Module.cs new file mode 100644 index 0000000..23ee300 --- /dev/null +++ b/Rocket.AutoInstaller/Module.cs @@ -0,0 +1,36 @@ +using JetBrains.Annotations; +using Rocket.AutoInstaller.Installation; +using SDG.Framework.Modules; +using SDG.Unturned; +using UnityEngine; +using UnityObject = UnityEngine.Object; + +namespace Rocket.AutoInstaller; + +/// +/// This is needed to run Coroutines. +/// +public class CoroutineRunner : MonoBehaviour; + +[UsedImplicitly] +public class Module : IModuleNexus +{ + private const string DiscordSupportURL = "https://discord.gg/z6VM7taWeG"; + + public void initialize() + { + var assembly = typeof(Module).Assembly; + var assemblyName = assembly.GetName(); + + CommandWindow.Log($"Loading Rocket.AutoInstaller... {assemblyName.Name} {assemblyName.Version} this could take for a while!"); + CommandWindow.Log($"Discord Support: {DiscordSupportURL}"); + + var instance = new GameObject(); + UnityObject.DontDestroyOnLoad(instance); + var coroutineRunner = instance.AddComponent(); + coroutineRunner.StartCoroutine(Installer.Install()); + } + public void shutdown() + { + } +} \ No newline at end of file diff --git a/Rocket.AutoInstaller/Module/InstallationGuides/Readme_EN.txt b/Rocket.AutoInstaller/Module/InstallationGuides/Readme_EN.txt new file mode 100644 index 0000000..5087564 --- /dev/null +++ b/Rocket.AutoInstaller/Module/InstallationGuides/Readme_EN.txt @@ -0,0 +1,17 @@ +Rocket.AutoInstaller For Unturned +--------------------------------- + +GitHub: https://github.com/RocketModFix/RocketModFix/Rocket.AutoInstaller + +For latest downloads, visit: https://github.com/RocketModFix/RocketModFix/releases. + +Join our Discord: https://discord.gg/z6VM7taWeG + +Installation +------------ + +Delete "Rocket.Unturned" folder from /Unturned/Modules (if you still have Rocket.Unturned installed) + +Copy the "Rocket.AutoInstaller" folder to /Unturned/Modules (copy the folder, not it's content, and if its asks to Replace the existing files then press to replace them). + +For more, visit: https://github.com/RocketModFix/RocketModFix?tab=readme-ov-file#auto-installer-new-way \ No newline at end of file diff --git a/Rocket.AutoInstaller/Module/InstallationGuides/Readme_RU.txt b/Rocket.AutoInstaller/Module/InstallationGuides/Readme_RU.txt new file mode 100644 index 0000000..058ddb6 --- /dev/null +++ b/Rocket.AutoInstaller/Module/InstallationGuides/Readme_RU.txt @@ -0,0 +1,17 @@ +Rocket.AutoInstaller для Unturned +------------------------- + +GitHub: https://github.com/RocketModFix/RocketModFix/Rocket.AutoInstaller + +Самую новейшую версию можно найти здесь: https://github.com/RocketModFix/RocketModFix/releases. + +Заходите в наш Discord: https://discord.gg/z6VM7taWeG + +Установка +--------- + +Удалите "Rocket.Unturned" папку из /Unturned/Modules (если у вас досихпор установлен Rocket.Unturned) + +Скопируйте "Rocket.AutoInstaller" папку в /Unturned/Modules (скопируйте всю папку а не то что в находится внутри папки и если оно просит вставить с заменой то жмите вставить с заменой). + +Дополнительная информация по установке: https://github.com/RocketModFix/RocketModFix?tab=readme-ov-file#auto-installer-new-way \ No newline at end of file diff --git a/Rocket.AutoInstaller/Module/Rocket.AutoInstaller/English.dat b/Rocket.AutoInstaller/Module/Rocket.AutoInstaller/English.dat new file mode 100644 index 0000000..261ac87 --- /dev/null +++ b/Rocket.AutoInstaller/Module/Rocket.AutoInstaller/English.dat @@ -0,0 +1,2 @@ +Name Rocket.AutoInstaller +Description Auto Installer for RocketModFix. \ No newline at end of file diff --git a/Rocket.AutoInstaller/Module/Rocket.AutoInstaller/Rocket.AutoInstaller.module b/Rocket.AutoInstaller/Module/Rocket.AutoInstaller/Rocket.AutoInstaller.module new file mode 100644 index 0000000..82f1cf5 --- /dev/null +++ b/Rocket.AutoInstaller/Module/Rocket.AutoInstaller/Rocket.AutoInstaller.module @@ -0,0 +1,11 @@ +{ + "Name": "Rocket.AutoInstaller", + "Version": "0.0.0.0", + "Assemblies": + [ + { + "Path": "/Rocket.AutoInstaller.dll", + "Role": "Server" + } + ] +} \ No newline at end of file diff --git a/Rocket.AutoInstaller/Module/RuntimeLibs/README.md b/Rocket.AutoInstaller/Module/RuntimeLibs/README.md new file mode 100644 index 0000000..5bb85d8 --- /dev/null +++ b/Rocket.AutoInstaller/Module/RuntimeLibs/README.md @@ -0,0 +1,16 @@ +## Runtime Libraries + +Here we just store the runtime libraries that are used by the Rocket or other plugins. These libraries are not included in the plugin itself, but are required to be present in the Module directory in order for Rocket itself and the other plugins to work correctly. + +## Add/Update Runtime Library + +To add/update a Runtime Library, we provide these options (they're different ways): + +# Option #1 +- Have installed Mono/Unity Editor with the same version as in Unturned. +- To find the necessary library: `Program Files\Mono\lib\mono\gac` or also you can try this place: `Program Files\Mono\lib\mono\4.5\Facades`. + +# Option #2 +- Copy the necessary libraries from [OpenMod](https://github.com/openmod/openmod) runtime libs or release of the module, this way we keep as close as possible same libraries. + +You can also check this [post](https://sunnamed434.github.io/posts/assemblyresolve-and-mono/) to understand things better about libraries, specifically in Unity. \ No newline at end of file diff --git a/Rocket.AutoInstaller/Module/RuntimeLibs/System.IO.Compression.FileSystem.dll b/Rocket.AutoInstaller/Module/RuntimeLibs/System.IO.Compression.FileSystem.dll new file mode 100644 index 0000000..59e95a2 Binary files /dev/null and b/Rocket.AutoInstaller/Module/RuntimeLibs/System.IO.Compression.FileSystem.dll differ diff --git a/Rocket.AutoInstaller/Module/RuntimeLibs/System.IO.Compression.dll b/Rocket.AutoInstaller/Module/RuntimeLibs/System.IO.Compression.dll new file mode 100644 index 0000000..751ef2a Binary files /dev/null and b/Rocket.AutoInstaller/Module/RuntimeLibs/System.IO.Compression.dll differ diff --git a/Rocket.AutoInstaller/README.md b/Rocket.AutoInstaller/README.md new file mode 100644 index 0000000..e41beed --- /dev/null +++ b/Rocket.AutoInstaller/README.md @@ -0,0 +1,17 @@ +# Rocket.AutoInstaller + +It's a Module auto-installer (loader/bootstrapper), which installs RocketModFix automaticaly from GitHub, you can still use a manual way of installation, however, this one is more modern, simple and faster. + +## Plans + +If you want to implement one of the plans or have better ideas or ideas to change, feel free to know us about that! + +- [x] Auto-Install from GitHub Releases. +- [ ] Auto-Install Local Build (so, no need to manually install RocketModFix everytime you test/update it, 1 click to build, restart server, and you're testing!). + - [ ] Json (or other type of config) Config with Options: + - [ ] `EnableCustomInstallPath`: false/true (default is false). + - [ ] `CustomInstallPath`: path to a build of RocketModFix. + - [ ] (Not 100% yet) We can also an option in config (`AutoInstallRocketFromExtras`) to make auto installation of Rocket (LDM) from Extras. +- [ ] Caching. + - [ ] Check if GitHub release is newer than current cached version and ONLY then install new version. + - [ ] For safe usage without Internet Connection. \ No newline at end of file diff --git a/Rocket.AutoInstaller/Rocket.AutoInstaller.csproj b/Rocket.AutoInstaller/Rocket.AutoInstaller.csproj new file mode 100644 index 0000000..8da3644 --- /dev/null +++ b/Rocket.AutoInstaller/Rocket.AutoInstaller.csproj @@ -0,0 +1,52 @@ + + + + net461 + $([System.IO.Path]::GetFullPath($(MSBuildProjectDirectory)/../../)) + + + + + + false + + + + + All + + + All + + + + + + + Module\RuntimeLibs\System.IO.Compression.dll + + + + + + + + + + + + + + $(OutDir)Rocket.AutoInstaller/Rocket.AutoInstaller + + + + + + + + + + + + \ No newline at end of file diff --git a/Rocket.Unturned/Module/InstallationGuides/Readme_DE.txt b/Rocket.Unturned/Module/InstallationGuides/Readme_DE.txt index b5da944..be329ff 100644 --- a/Rocket.Unturned/Module/InstallationGuides/Readme_DE.txt +++ b/Rocket.Unturned/Module/InstallationGuides/Readme_DE.txt @@ -12,4 +12,4 @@ Installation Copiere den "Rocket.Unturned" Ordner nach /Unturned/Modules (kopiere den Ordner, nicht den Inhalt, und wenn es fragt, ob die vorhandenen Dateien ersetzt werden sollen, dann drücke auf Ersetzen). -Für mehr, besuche: https://github.com/RocketModFix/RocketModFix?tab=readme-ov-file#installation \ No newline at end of file +Für mehr, besuche: https://github.com/RocketModFix/RocketModFix?tab=readme-ov-file#standard-way \ No newline at end of file diff --git a/Rocket.Unturned/Module/InstallationGuides/Readme_EN.txt b/Rocket.Unturned/Module/InstallationGuides/Readme_EN.txt index b2c8625..7ef9b7e 100644 --- a/Rocket.Unturned/Module/InstallationGuides/Readme_EN.txt +++ b/Rocket.Unturned/Module/InstallationGuides/Readme_EN.txt @@ -12,4 +12,4 @@ Installation Copy the "Rocket.Unturned" folder to /Unturned/Modules (copy the folder, not it's content, and if its asks to Replace the existing files then press to replace them). -For more, visit: https://github.com/RocketModFix/RocketModFix?tab=readme-ov-file#installation \ No newline at end of file +For more, visit: https://github.com/RocketModFix/RocketModFix?tab=readme-ov-file#standard-way \ No newline at end of file diff --git a/Rocket.Unturned/Module/InstallationGuides/Readme_RU.txt b/Rocket.Unturned/Module/InstallationGuides/Readme_RU.txt index 8569525..ce06586 100644 --- a/Rocket.Unturned/Module/InstallationGuides/Readme_RU.txt +++ b/Rocket.Unturned/Module/InstallationGuides/Readme_RU.txt @@ -12,4 +12,4 @@ GitHub: https://github.com/RocketModFix/RocketModFix Скопируйте "Rocket.Unturned" папку в /Unturned/Modules (скопируйте всю папку а не то что в находится внутри папки и если оно просит вставить с заменой то жмите вставить с заменой). -Дополнительная информация по установке: https://github.com/RocketModFix/RocketModFix?tab=readme-ov-file#installation \ No newline at end of file +Дополнительная информация по установке: https://github.com/RocketModFix/RocketModFix?tab=readme-ov-file#standard-way \ No newline at end of file