Skip to content

Commit 1dbd527

Browse files
committed
Initial commit
0 parents  commit 1dbd527

File tree

12 files changed

+903
-0
lines changed

12 files changed

+903
-0
lines changed

.gitignore

Lines changed: 512 additions & 0 deletions
Large diffs are not rendered by default.

ScriptLoader.sln

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
2+
Microsoft Visual Studio Solution File, Format Version 12.00
3+
# Visual Studio 15
4+
VisualStudioVersion = 15.0.27428.2043
5+
MinimumVisualStudioVersion = 10.0.40219.1
6+
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ScriptLoader", "ScriptLoader\ScriptLoader.csproj", "{5A46E7E5-DFC6-4443-B865-11442C7FAD17}"
7+
EndProject
8+
Global
9+
GlobalSection(SolutionConfigurationPlatforms) = preSolution
10+
Debug|Any CPU = Debug|Any CPU
11+
Release|Any CPU = Release|Any CPU
12+
EndGlobalSection
13+
GlobalSection(ProjectConfigurationPlatforms) = postSolution
14+
{5A46E7E5-DFC6-4443-B865-11442C7FAD17}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
15+
{5A46E7E5-DFC6-4443-B865-11442C7FAD17}.Debug|Any CPU.Build.0 = Debug|Any CPU
16+
{5A46E7E5-DFC6-4443-B865-11442C7FAD17}.Release|Any CPU.ActiveCfg = Release|Any CPU
17+
{5A46E7E5-DFC6-4443-B865-11442C7FAD17}.Release|Any CPU.Build.0 = Release|Any CPU
18+
EndGlobalSection
19+
GlobalSection(SolutionProperties) = preSolution
20+
HideSolutionNode = FALSE
21+
EndGlobalSection
22+
GlobalSection(ExtensibilityGlobals) = postSolution
23+
SolutionGuid = {2EBA3D8B-D830-4843-81AD-235317CC886D}
24+
EndGlobalSection
25+
EndGlobal

ScriptLoader/ILRepack.targets

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
<?xml version="1.0" encoding="utf-8" ?>
2+
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
3+
<Target Name="ILRepacker" AfterTargets="Build" Condition="'$(Configuration)' == 'Release'">
4+
5+
<ItemGroup>
6+
<InputAssemblies Include="$(SolutionDir)\lib\mcs.dll" />
7+
<InputAssemblies Include="$(OutputPath)\$(AssemblyName).dll" />
8+
</ItemGroup>
9+
10+
<ILRepack
11+
Parallel="true"
12+
Internalize="true"
13+
InputAssemblies="@(InputAssemblies)"
14+
TargetKind="Dll"
15+
LibraryPath="$(SolutionDir)\lib"
16+
OutputFile="$(OutputPath)\$(AssemblyName).dll"
17+
/>
18+
19+
</Target>
20+
</Project>

ScriptLoader/MonoCompiler.cs

Lines changed: 192 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,192 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.IO;
4+
using System.Reflection;
5+
using System.Reflection.Emit;
6+
using System.Text;
7+
using Mono.CSharp;
8+
9+
namespace ScriptLoader
10+
{
11+
public interface ICSharpSource
12+
{
13+
byte[] Bytes { get; }
14+
string Location { get; }
15+
string Name { get; }
16+
}
17+
18+
public class CSharpFile : ICSharpSource
19+
{
20+
public CSharpFile(string path)
21+
{
22+
if (!File.Exists(path))
23+
throw new FileNotFoundException("The given source file does not exist!", path);
24+
if (Path.GetExtension(path)?.ToLowerInvariant() != ".cs")
25+
throw new ArgumentException("The given path is not a .cs file!", nameof(path));
26+
Location = path;
27+
}
28+
29+
public byte[] Bytes => File.ReadAllBytes(Location);
30+
public string Location { get; }
31+
32+
public string Name => Path.GetFileName(Location);
33+
}
34+
35+
public class CSharpCode : ICSharpSource
36+
{
37+
public CSharpCode()
38+
{
39+
SourceCode = new StringBuilder();
40+
}
41+
42+
public StringBuilder SourceCode { get; }
43+
44+
public byte[] Bytes => Encoding.UTF8.GetBytes(SourceCode.ToString());
45+
public string Location => "<eval>";
46+
public string Name => "<eval>";
47+
}
48+
49+
public static class MonoCompiler
50+
{
51+
private static readonly HashSet<string> StdLib =
52+
new HashSet<string>(StringComparer.InvariantCultureIgnoreCase) {"mscorlib", "System.Core", "System", "System.Xml"};
53+
54+
//public MonoCompiler(TextWriter logger) : this(null, logger) { }
55+
//public MonoCompiler() : this(null, null) { }
56+
57+
//public MonoCompiler(CompilerContext ctx, TextWriter logger) : base(ctx ?? CreateContext(reportPrinter))
58+
//{
59+
// Reporter = logger != null ? CreatePrinter(logger) : new ConsoleReportPrinter();
60+
// Logger = logger;
61+
// ImportAppdomainAssemblies(ReferenceAssembly);
62+
// ActiveDomain.AssemblyLoad += OnAssemblyLoad;
63+
//}
64+
65+
//private AppDomain ActiveDomain => AppDomain.CurrentDomain;
66+
//private TextWriter Logger { get; }
67+
//private ReportPrinter Reporter { get; }
68+
69+
//public void Dispose()
70+
//{
71+
// ActiveDomain.AssemblyLoad -= OnAssemblyLoad;
72+
//}
73+
74+
// Mimicked from https://github.com/kkdevs/Patchwork/blob/master/Patchwork/MonoScript.cs#L124
75+
public static Assembly Compile<T>(IList<T> sources, IEnumerable<Assembly> imports = null, TextWriter logger = null) where T : ICSharpSource
76+
{
77+
ReportPrinter reporter = logger == null ? new ConsoleReportPrinter() : new StreamReportPrinter(logger);
78+
Location.Reset();
79+
80+
string dllName = $"compiled_{DateTime.Now.Ticks}";
81+
82+
CompilerContext ctx = CreateContext(reporter);
83+
ctx.Settings.SourceFiles.Clear();
84+
85+
int i = 0;
86+
87+
SeekableStreamReader GetFile(SourceFile file)
88+
{
89+
return new SeekableStreamReader(new MemoryStream(sources[file.Index].Bytes), Encoding.UTF8);
90+
}
91+
92+
foreach (ICSharpSource source in sources)
93+
{
94+
ctx.Settings.SourceFiles.Add(new SourceFile(source.Name, source.Location, i, GetFile));
95+
i++;
96+
}
97+
98+
var container = new ModuleContainer(ctx);
99+
100+
RootContext.ToplevelTypes = container;
101+
Location.Initialize(ctx.Settings.SourceFiles);
102+
103+
var session = new ParserSession {UseJayGlobalArrays = true, LocatedTokens = new LocatedToken[15000]};
104+
container.EnableRedefinition();
105+
106+
foreach (SourceFile sourceFile in ctx.Settings.SourceFiles)
107+
{
108+
SeekableStreamReader stream = sourceFile.GetInputStream(sourceFile);
109+
var source = new CompilationSourceFile(container, sourceFile);
110+
source.EnableRedefinition();
111+
container.AddTypeContainer(source);
112+
var parser = new CSharpParser(stream, source, session);
113+
parser.parse();
114+
}
115+
116+
var ass = new AssemblyDefinitionDynamic(container, dllName, $"{dllName}.dll");
117+
container.SetDeclaringAssembly(ass);
118+
119+
var importer = new ReflectionImporter(container, ctx.BuiltinTypes);
120+
ass.Importer = importer;
121+
122+
var loader = new DynamicLoader(importer, ctx);
123+
ImportAppdomainAssemblies(a => importer.ImportAssembly(a, container.GlobalRootNamespace));
124+
125+
if (imports != null)
126+
foreach (Assembly assembly in imports)
127+
importer.ImportAssembly(assembly, container.GlobalRootNamespace);
128+
129+
loader.LoadReferences(container);
130+
ass.Create(AppDomain.CurrentDomain, AssemblyBuilderAccess.RunAndSave);
131+
container.CreateContainer();
132+
loader.LoadModules(ass, container.GlobalRootNamespace);
133+
container.InitializePredefinedTypes();
134+
container.Define();
135+
136+
if (ctx.Report.Errors > 0)
137+
{
138+
logger?.WriteLine("Found errors! Aborting compilation...");
139+
return null;
140+
}
141+
142+
try
143+
{
144+
ass.Resolve();
145+
ass.Emit();
146+
container.CloseContainer();
147+
ass.EmbedResources();
148+
}
149+
catch (Exception e)
150+
{
151+
logger?.WriteLine($"Failed to compile because {e}");
152+
return null;
153+
}
154+
155+
return ass.Builder;
156+
}
157+
158+
//private void OnAssemblyLoad(object sender, AssemblyLoadEventArgs args)
159+
//{
160+
// ReferenceAssembly(args.LoadedAssembly);
161+
//}
162+
163+
private static void ImportAppdomainAssemblies(Action<Assembly> import)
164+
{
165+
foreach (Assembly assembly in AppDomain.CurrentDomain.GetAssemblies())
166+
{
167+
string name = assembly.GetName().Name;
168+
if (StdLib.Contains(name))
169+
continue;
170+
import(assembly);
171+
}
172+
}
173+
174+
private static CompilerContext CreateContext(ReportPrinter reportPrinter)
175+
{
176+
var settings = new CompilerSettings
177+
{
178+
Version = LanguageVersion.Experimental,
179+
GenerateDebugInfo = false,
180+
StdLib = true,
181+
Target = Target.Library
182+
};
183+
184+
return new CompilerContext(settings, reportPrinter);
185+
}
186+
187+
//private static ReportPrinter CreatePrinter(TextWriter tw)
188+
//{
189+
// return new StreamReportPrinter(tw);
190+
//}
191+
}
192+
}
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
using System.Reflection;
2+
using System.Runtime.CompilerServices;
3+
using System.Runtime.InteropServices;
4+
5+
// General Information about an assembly is controlled through the following
6+
// set of attributes. Change these attribute values to modify the information
7+
// associated with an assembly.
8+
[assembly: AssemblyTitle("ScriptLoader")]
9+
[assembly: AssemblyDescription("")]
10+
[assembly: AssemblyConfiguration("")]
11+
[assembly: AssemblyCompany("")]
12+
[assembly: AssemblyProduct("ScriptLoader")]
13+
[assembly: AssemblyCopyright("Copyright © 2018")]
14+
[assembly: AssemblyTrademark("")]
15+
[assembly: AssemblyCulture("")]
16+
17+
// Setting ComVisible to false makes the types in this assembly not visible
18+
// to COM components. If you need to access a type in this assembly from
19+
// COM, set the ComVisible attribute to true on that type.
20+
[assembly: ComVisible(false)]
21+
22+
// The following GUID is for the ID of the typelib if this project is exposed to COM
23+
[assembly: Guid("5a46e7e5-dfc6-4443-b865-11442c7fad17")]
24+
25+
// Version information for an assembly consists of the following four values:
26+
//
27+
// Major Version
28+
// Minor Version
29+
// Build Number
30+
// Revision
31+
//
32+
// You can specify all the values or you can default the Build and Revision Numbers
33+
// by using the '*' as shown below:
34+
// [assembly: AssemblyVersion("1.0.*")]
35+
[assembly: AssemblyVersion("1.0.0.0")]
36+
[assembly: AssemblyFileVersion("1.0.0.0")]

ScriptLoader/ScriptLoader.cs

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
using System;
2+
using System.IO;
3+
using System.Linq;
4+
using System.Reflection;
5+
using BepInEx;
6+
using BepInEx.Logging;
7+
8+
namespace ScriptLoader
9+
{
10+
[BepInPlugin("horse.coder.tools.scriptloader", "C# Script Loader", "1.0")]
11+
public class ScriptLoader : BaseUnityPlugin
12+
{
13+
public void Awake()
14+
{
15+
DontDestroyOnLoad(this);
16+
17+
if (!Directory.Exists("scripts"))
18+
{
19+
Directory.CreateDirectory("scripts");
20+
Destroy(this);
21+
return;
22+
}
23+
24+
var files = Directory.GetFiles("scripts", "*.cs").Select(p => new CSharpFile(p)).ToList();
25+
Assembly ass = MonoCompiler.Compile(files);
26+
Logger.Log(LogLevel.Info, $"Compiling {files.Count} files");
27+
28+
if (ass == null)
29+
{
30+
Logger.Log(LogLevel.Error, "Failed to compile!");
31+
Destroy(this);
32+
return;
33+
}
34+
35+
foreach (Type type in ass.GetTypes())
36+
{
37+
MethodInfo method = type.GetMethods(BindingFlags.Static | BindingFlags.Public)
38+
.FirstOrDefault(m => m.Name == "Main" && m.GetParameters().Length == 0);
39+
40+
if (method == null)
41+
continue;
42+
43+
Logger.Log(LogLevel.Info, $"Running {type.Name}");
44+
method.Invoke(null, new object[0]);
45+
}
46+
}
47+
}
48+
}

ScriptLoader/ScriptLoader.csproj

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
<?xml version="1.0" encoding="utf-8"?>
2+
<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
3+
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
4+
<PropertyGroup>
5+
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
6+
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
7+
<ProjectGuid>{5A46E7E5-DFC6-4443-B865-11442C7FAD17}</ProjectGuid>
8+
<OutputType>Library</OutputType>
9+
<AppDesignerFolder>Properties</AppDesignerFolder>
10+
<RootNamespace>ScriptLoader</RootNamespace>
11+
<AssemblyName>ScriptLoader</AssemblyName>
12+
<TargetFrameworkVersion>v3.5</TargetFrameworkVersion>
13+
<FileAlignment>512</FileAlignment>
14+
<NuGetPackageImportStamp>
15+
</NuGetPackageImportStamp>
16+
</PropertyGroup>
17+
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
18+
<DebugSymbols>true</DebugSymbols>
19+
<DebugType>full</DebugType>
20+
<Optimize>false</Optimize>
21+
<OutputPath>bin\Debug\</OutputPath>
22+
<DefineConstants>DEBUG;TRACE</DefineConstants>
23+
<ErrorReport>prompt</ErrorReport>
24+
<WarningLevel>4</WarningLevel>
25+
</PropertyGroup>
26+
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
27+
<DebugType>pdbonly</DebugType>
28+
<Optimize>true</Optimize>
29+
<OutputPath>bin\Release\</OutputPath>
30+
<DefineConstants>TRACE</DefineConstants>
31+
<ErrorReport>prompt</ErrorReport>
32+
<WarningLevel>4</WarningLevel>
33+
</PropertyGroup>
34+
<ItemGroup>
35+
<Reference Include="BepInEx">
36+
<HintPath>..\lib\BepInEx.dll</HintPath>
37+
<Private>False</Private>
38+
</Reference>
39+
<Reference Include="mcs">
40+
<HintPath>..\lib\mcs.dll</HintPath>
41+
<Private>False</Private>
42+
</Reference>
43+
<Reference Include="System.Core" />
44+
<Reference Include="UnityEngine">
45+
<HintPath>..\lib\UnityEngine.dll</HintPath>
46+
<Private>False</Private>
47+
</Reference>
48+
</ItemGroup>
49+
<ItemGroup>
50+
<Compile Include="MonoCompiler.cs" />
51+
<Compile Include="ScriptLoader.cs" />
52+
<Compile Include="Properties\AssemblyInfo.cs" />
53+
</ItemGroup>
54+
<ItemGroup>
55+
<None Include="ILRepack.targets" />
56+
<None Include="packages.config" />
57+
</ItemGroup>
58+
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
59+
<Import Project="..\packages\ILRepack.Lib.MSBuild.Task.2.0.18\build\ILRepack.Lib.MSBuild.Task.targets" Condition="Exists('..\packages\ILRepack.Lib.MSBuild.Task.2.0.18\build\ILRepack.Lib.MSBuild.Task.targets')" />
60+
<Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
61+
<PropertyGroup>
62+
<ErrorText>This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.</ErrorText>
63+
</PropertyGroup>
64+
<Error Condition="!Exists('..\packages\ILRepack.Lib.MSBuild.Task.2.0.18\build\ILRepack.Lib.MSBuild.Task.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\ILRepack.Lib.MSBuild.Task.2.0.18\build\ILRepack.Lib.MSBuild.Task.targets'))" />
65+
</Target>
66+
</Project>

0 commit comments

Comments
 (0)