Skip to content

Commit

Permalink
Merge pull request #62 from nexus4880/3.9-dev
Browse files Browse the repository at this point in the history
Added the mod verification logic
  • Loading branch information
Lacyway authored May 30, 2024
2 parents 5fe4576 + 9e507c6 commit fb87eaf
Show file tree
Hide file tree
Showing 3 changed files with 90 additions and 32 deletions.
2 changes: 1 addition & 1 deletion Fika.Core/FikaPlugin.cs
Original file line number Diff line number Diff line change
Expand Up @@ -246,7 +246,7 @@ protected void Awake()
private IEnumerator RunModHandler()
{
yield return new WaitForSeconds(5);
ModHandler.Run();
ModHandler.VerifyMods();
}

private void GetClientConfig()
Expand Down
17 changes: 17 additions & 0 deletions Fika.Core/Networking/Models/ModValidationResponse.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
using System.Runtime.Serialization;

namespace Fika.Core.Networking.Http.Models
{
[DataContract]
public struct ModValidationResponse
{
[DataMember(Name = "forbidden")]
public string[] Forbidden;

[DataMember(Name = "missingRequired")]
public string[] MissingRequired;

[DataMember(Name = "hashMismatch")]
public string[] HashMismatch;
}
}
103 changes: 72 additions & 31 deletions Fika.Core/Utils/FikaModHandler.cs
Original file line number Diff line number Diff line change
@@ -1,70 +1,111 @@
using BepInEx;
using BepInEx.Bootstrap;
using BepInEx.Logging;
using Fika.Core.Networking.Models;
using Comfort.Common;
using EFT;
using EFT.UI;
using Fika.Core.Networking.Http.Models;
using LiteNetLib.Utils;
using Newtonsoft.Json;
using SPT.Common.Http;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using UnityEngine;
using Logger = BepInEx.Logging.Logger;

namespace Fika.Core.Utils
{
public class FikaModHandler
{
private string[] loadedMods;
private readonly ManualLogSource logger = Logger.CreateLogSource("FikaModHandler");

public bool QuestingBotsLoaded = false;
public bool SAINLoaded = false;

public void Run()
public void VerifyMods()
{
// Store all loaded plugins (mods) to improve compatibility
List<string> tempPluginInfos = [];
PluginInfo[] pluginInfos = [.. Chainloader.PluginInfos.Values];

// Set capacity to avoid unnecessarily resizing for people who have a lot of mods loaded
Dictionary<string, uint> loadedMods = new Dictionary<string, uint>(pluginInfos.Length);

foreach (PluginInfo pluginInfo in pluginInfos)
{
string location = pluginInfo.Location;
byte[] fileBytes = File.ReadAllBytes(location);
uint crc32 = CRC32C.Compute(fileBytes, 0, fileBytes.Length);
loadedMods.Add(pluginInfo.Metadata.GUID, crc32);
logger.LogInfo($"Loaded plugin: [{pluginInfo.Metadata.Name}] with GUID [{pluginInfo.Metadata.GUID}] and crc32 [{crc32}]");
CheckSpecialMods(pluginInfo.Metadata.GUID);
}

string modValidationRequestJson = JsonConvert.SerializeObject(loadedMods);
logger.LogDebug(modValidationRequestJson);

string validationJson = RequestHandler.PostJson("/fika/client/check/mods", modValidationRequestJson);
logger.LogDebug(validationJson);

foreach (string key in Chainloader.PluginInfos.Keys)
ModValidationResponse validationResult =
JsonConvert.DeserializeObject<ModValidationResponse>(validationJson);

// If any errors were detected we will print what has happened
bool installationError =
validationResult.Forbidden.Length > 0 ||
validationResult.MissingRequired.Length > 0 ||
validationResult.HashMismatch.Length > 0;

if (validationResult.Forbidden.Length > 0)
{
logger.LogInfo($"Adding {key}, {Chainloader.PluginInfos[key].Metadata.Name} to loaded mods.");
tempPluginInfos.Add(key);
CheckSpecialMods(key);
logger.LogError($"{validationResult.Forbidden.Length} forbidden mod(s) are loaded, have the server host allow or remove the following mods: {string.Join(", ", validationResult.Forbidden)}");
}

/*if (FikaPlugin.Instance.RequiredMods.Count > 0)
if (validationResult.MissingRequired.Length > 0)
{
VerifyMods();
}*/
logger.LogError($"{validationResult.MissingRequired.Length} missing required mod(s), verify the following mods are present: {string.Join(", ", validationResult.MissingRequired)}");
}

loadedMods = [.. tempPluginInfos];
if (validationResult.HashMismatch.Length > 0)
{
logger.LogWarning($"{validationResult.HashMismatch.Length} mismatched mod(s) are loaded, verify the following mods are up to date with the server host: {string.Join(", ", validationResult.HashMismatch)}");
}

logger.LogInfo($"Loaded {loadedMods.Length} mods!");
if (installationError)
{
StaticManager.BeginCoroutine(InformInstallationError());
}
}

private void VerifyMods()
private IEnumerator InformInstallationError()
{
PluginInfo[] pluginInfos = [.. Chainloader.PluginInfos.Values];
Dictionary<string, string> loadedMods = [];

foreach (PluginInfo pluginInfo in pluginInfos)
while (!Singleton<PreloaderUI>.Instantiated)
{
string location = pluginInfo.Location;
byte[] fileBytes = File.ReadAllBytes(location);
uint crc32 = CRC32C.Compute(fileBytes, 0, fileBytes.Length);
loadedMods.Add(pluginInfo.Metadata.GUID, crc32.ToString());
yield return null;
}

ModValidationRequest modValidationRequest = new(loadedMods);
// Send
string message = "Your client doesn't meet server requirements, check logs for more details";

// -1f time makes the message permanent
Singleton<PreloaderUI>.Instance.ShowCriticalErrorScreen("INSTALLATION ERROR", message,
ErrorScreen.EButtonType.QuitButton, -1f, Application.Quit, null);
}

private void CheckSpecialMods(string key)
{
if (key == "com.DanW.QuestingBots")
switch (key)
{
QuestingBotsLoaded = true;
}
case "com.DanW.QuestingBots":
{
QuestingBotsLoaded = true;

if (key == "me.sol.sain")
{
SAINLoaded = true;
break;
}
case "me.sol.sain":
{
SAINLoaded = true;

break;
}
}
}
}
Expand Down

0 comments on commit fb87eaf

Please sign in to comment.