Skip to content

Commit

Permalink
Initial commit
Browse files Browse the repository at this point in the history
  • Loading branch information
790 committed Dec 15, 2019
0 parents commit 6ec807b
Show file tree
Hide file tree
Showing 7 changed files with 428 additions and 0 deletions.
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
.vs
obj
bin
Binary file added 0Harmony.dll
Binary file not shown.
28 changes: 28 additions & 0 deletions Example_Plugin/Example.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
using System;
using SEHarmonyWrapper;
using Harmony;

namespace Example_Plugin
{
public class Example : ModBase
{
public Logger logger;
public void Main(HarmonyInstance harmony, Logger logger)
{
this.logger = logger;
logger.Log("Hello from plubgin");
}
public void Init(object gameObject)
{

}
public void Update()
{

}
public void Dispose()
{

}
}
}
36 changes: 36 additions & 0 deletions Example_Plugin/Example_Plugin.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
<Project Sdk="Microsoft.NET.Sdk">

<Target Name="ClearReferenceCopyLocalPaths" AfterTargets="ResolveAssemblyReferences">
<ItemGroup>
<ReferenceCopyLocalPaths Remove="@(ReferenceCopyLocalPaths)" />
</ItemGroup>
</Target>
<PropertyGroup>
<TargetFramework>net461</TargetFramework>
<AppendTargetFrameworkToOutputPath>false</AppendTargetFrameworkToOutputPath>
<AppendRuntimeIdentifierToOutputPath>false</AppendRuntimeIdentifierToOutputPath>
</PropertyGroup>

<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
<PlatformTarget>x64</PlatformTarget>
</PropertyGroup>

<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'">
<PlatformTarget>x64</PlatformTarget>
<DebugType>none</DebugType>
<DebugSymbols>false</DebugSymbols>
</PropertyGroup>

<ItemGroup>
<Reference Include="0Harmony">
<HintPath>..\bin\Release\0Harmony.dll</HintPath>
</Reference>
<Reference Include="SEHarmonyWrapper">
<HintPath>..\bin\Release\SEHarmonyWrapper.dll</HintPath>
</Reference>
<Reference Include="VRage">
<HintPath>K:\games\Steam\steamapps\common\SpaceEngineers\Bin64\VRage.dll</HintPath>
</Reference>
</ItemGroup>

</Project>
16 changes: 16 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
# SEHarmonyWrapper

A plugin for the game Space Engineers that provides an instance of the [Harmony library](https://github.com/pardeike/Harmony) to other plugins.

This tool modifies the game in unsupported ways and should be used with caution. Expect bugs, crashes, broken updates.

## Install

Extract `0Harmony.dll` and `SEHarmonyWrapper.dll` to your `steam\steamapps\common\SpaceEngineers\Bin64` directory.
In Steam, go to properties of the game, Set Launch Options and add `-plugin SEHarmonyWrapper.dll`

## Installing Plugins

Copy a plugin .dlls to a directory inside `SpaceEngineers\Bin64\seharmonywrapper` to enable them.

If a new SEHarmonyWrapper using workshop mod is detected the game will prompt on launch whether to enable it. Enabled workshop plugins can be edited from `SpaceEngineers\Bin64\seharmonywrapper\modlist.txt`
250 changes: 250 additions & 0 deletions SEHarmonyWrapper.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,250 @@
using System;
using Harmony;
using System.Reflection;
using VRage.Plugins;
using System.Collections.Generic;
using System.Linq;
using System.IO;
using System.Windows.Forms;
using System.IO.Compression;

namespace SEHarmonyWrapper
{
public interface ModBase : IPlugin, IDisposable
{
void Main(HarmonyInstance harmony, Logger log);
}
public class Logger
{
string prefix;
public Logger(string s)
{
prefix = s;
}

public void Log(string text)
{
FileLog.Log(DateTime.Now + " [" + prefix + "] " + text);
}
}

class Main : IPlugin, IDisposable
{
private List<object> loadedPlugins = new List<object>();
private Dictionary<string, bool> modList = new Dictionary<string, bool>();
private Dictionary<string, string> config = new Dictionary<string, string>();
private Logger logger = new Logger("SEHarmonyWrapper");
public Main()
{
var harmony = HarmonyInstance.Create("com.github.790.seharmonywrapper");
//MessageBox.Show("hello", "you", MessageBoxButtons.YesNo);
if (!Directory.Exists("seharmonywrapper"))
{
Directory.CreateDirectory("seharmonywrapper");
}
FileLog.logPath = "seharmonywrapper/SEHarmonyWrapper.log";
File.Delete(FileLog.logPath);
logger.Log("Hello!");
var modListFilename = Path.Combine("seharmonywrapper", "modlist.txt");
var configFilename = Path.Combine("seharmonywrapper", "config.txt");
if (!File.Exists(modListFilename))
{
File.WriteAllText(modListFilename, "");
}
if (!File.Exists(configFilename))
{
File.WriteAllText(configFilename, "# Uncomment line below to set a custom workshop path\r\n#workshop_path=c:\\Steam\\steamapps\\workshop\\content\\244850");
}
using (var fr = File.OpenText(modListFilename)) {
string line = "";
while ((line = fr.ReadLine()) != null) {
if (line.StartsWith("#"))
{
continue;
}
string[] parts = line.Split('=');
modList[parts[0]] = (parts[1] == "1");
}
fr.Close();
}

using (var fr = File.OpenText(configFilename)) {
string line = "";
while ((line = fr.ReadLine()) != null)
{
if (line.StartsWith("#"))
{
continue;
}
string[] parts = line.Split('=');
config[parts[0].Trim()] = parts[1].Trim();
}
fr.Close();
}

logger.Log("Scanning local mods from: " + Path.GetFullPath("seharmonywrapper"));
foreach (var dir in Directory.GetDirectories("seharmonywrapper"))
{
if(dir.ToLower().EndsWith(".disabled"))
{
continue;
}
foreach (var dll in Directory.GetFiles(dir, "*.dll"))
{
LoadPlugin(dll, harmony);
}
}

var modPath = (config.ContainsKey("workshop_path") && config["workshop_path"].Length>0) ? config["workshop_path"] : Path.GetFullPath("..\\..\\..\\workshop\\content\\244850\\");

if (!Directory.Exists(modPath))
{
logger.Log("Steam workshop directory can't be found. Edit config.txt to set its location");
return;
}
logger.Log("Scanning workshop mods from: " + modPath);
foreach (var dir in Directory.GetDirectories(modPath))
{
var workshopId = Path.GetFileName(dir);
var pluginZipPath = Path.Combine(dir, "Data", "sehw-plugin.zip");
var pluginPath = Path.Combine(dir, "Data", "sehw-plugin");
if (File.Exists(pluginZipPath))
{
logger.Log("Found SEHarmonyWrapper plugin zip: " + pluginZipPath);

try
{
using (ZipArchive zip = ZipFile.OpenRead(pluginZipPath))
{
var nameFile = zip.Entries.First(e => e.FullName == "name.txt");
if(nameFile == null || nameFile.Length > 100)
{
continue;
}
string name = new StreamReader(nameFile.Open()).ReadToEnd();
if(name.Length == 0)
{
continue;
}
if (!modList.ContainsKey(Path.GetFileName(dir)))
{
var pluginUrl = "https://steamcommunity.com/workshop/filedetails/?id=" + workshopId;
var result = MessageBox.Show($"New SEHarmonyWrapper plugin found:\n\n{name}\n{pluginUrl}\n\nDo you want to enable this plugin?\n\nIf you don't know why you're getting this message, click No.\nYou can edit modlist.txt later to enable/disable plugins", "SEHarmonyWrapper", MessageBoxButtons.YesNo);
if (result == DialogResult.Yes)
{
modList.Add(workshopId, true);
logger.Log("Whitelisted " + workshopId);
}
else
{
modList.Add(workshopId, false);
logger.Log("Blacklisted " + workshopId);
}
}
if (modList.ContainsKey(workshopId) && !modList[workshopId])
{
logger.Log("Skipping blacklisted " + workshopId);
continue;
}
foreach (ZipArchiveEntry entry in zip.Entries)
{
string extractPath = Path.GetFullPath(Path.Combine(pluginPath, entry.FullName));
if (extractPath.StartsWith(pluginPath, StringComparison.Ordinal))
{
entry.ExtractToFile(extractPath, true);
}
}
}
foreach (var dll in Directory.GetFiles(pluginPath, "*.dll"))
{
LoadPlugin(dll, harmony);
}
} catch(Exception e)
{
logger.Log("Could not extract workshop SEHW plugin: " + pluginZipPath);
logger.Log(e.ToString());
}
}
}
File.WriteAllText(modListFilename, String.Join("\n", modList.Select(m => m.Key.ToString() + "=" + (m.Value ? "1" : "0"))));
}
private bool LoadPlugin(string dll, HarmonyInstance harmony)
{
logger.Log("Loading plugin: " + dll);
Assembly plugin;
try
{
plugin = Assembly.LoadFile(Path.GetFullPath(dll));
}
catch (Exception e)
{
logger.Log("Exception LoadFile: " + e);
return false;
}
if(plugin == null)
{
return false;
}
Type baseType = typeof(ModBase);
var types = new List<Type>();
try
{

types = plugin.GetTypes().Where(p => baseType.IsAssignableFrom(p)).ToList();
if (types.Count() == 0)
{
return false;
}
} catch(Exception e)
{
logger.Log("Exception gettypes: " + e);
return false;
}
logger.Log("Got assembly: " + plugin);
MethodInfo mainMethod = baseType.GetMethod("Main");
foreach (var t in types)
{
logger.Log("Instantiating plugin: " + t);
try
{
object obj = Activator.CreateInstance(t);
// Execute the method.
mainMethod.Invoke(obj, new object[] { harmony, new Logger(t.ToString()) });
if (obj != null)
{
loadedPlugins.Add(obj);
}
}
catch (Exception e)
{
logger.Log("Exception create/invoke: " + e);
}
}
return true;
}
private MethodInfo initMethod = typeof(IPlugin).GetMethod("Init");
private MethodInfo updateMethod = typeof(IPlugin).GetMethod("Update");
private MethodInfo disposeMethod = typeof(IDisposable).GetMethod("Dispose");
public void Init(object gameObject)
{
foreach (var lm in loadedPlugins)
{
initMethod.Invoke(lm, new object[] { gameObject });
}
}
public void Update()
{
foreach(var lm in loadedPlugins)
{
updateMethod.Invoke(lm, null);
}
}
public void Dispose()
{
foreach (var lm in loadedPlugins)
{
disposeMethod.Invoke(lm, null);
}
}
}
}
Loading

0 comments on commit 6ec807b

Please sign in to comment.