Skip to content

Commit 2ffdd87

Browse files
author
Balint66
committed
Add localization support.
1 parent 76f44ce commit 2ffdd87

10 files changed

Lines changed: 288 additions & 97 deletions

GoingMedievalModLauncher.sln

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DummyPlugin", "DummyPlugin\
1414
EndProject
1515
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ExperimentalMod", "ExperimentalMod\ExperimentalMod.csproj", "{AFB3E9BE-3087-4033-8B07-E5F18D99A25F}"
1616
EndProject
17+
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ExampleMenuMod", "ExampleMenuMod\ExampleMenuMod.csproj", "{EDE0D18F-D050-437B-8A12-D6593EE7168E}"
18+
EndProject
1719
Global
1820
GlobalSection(SolutionConfigurationPlatforms) = preSolution
1921
Debug|Any CPU = Debug|Any CPU
@@ -47,5 +49,9 @@ Global
4749
{AFB3E9BE-3087-4033-8B07-E5F18D99A25F}.Debug|Any CPU.Build.0 = Debug|Any CPU
4850
{AFB3E9BE-3087-4033-8B07-E5F18D99A25F}.Release|Any CPU.ActiveCfg = Release|Any CPU
4951
{AFB3E9BE-3087-4033-8B07-E5F18D99A25F}.Release|Any CPU.Build.0 = Release|Any CPU
52+
{EDE0D18F-D050-437B-8A12-D6593EE7168E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
53+
{EDE0D18F-D050-437B-8A12-D6593EE7168E}.Debug|Any CPU.Build.0 = Debug|Any CPU
54+
{EDE0D18F-D050-437B-8A12-D6593EE7168E}.Release|Any CPU.ActiveCfg = Release|Any CPU
55+
{EDE0D18F-D050-437B-8A12-D6593EE7168E}.Release|Any CPU.Build.0 = Release|Any CPU
5056
EndGlobalSection
5157
EndGlobal

GoingMedievalModLauncher.sln.DotSettings.user

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,15 @@
1313
<s:Boolean x:Key="/Default/AddReferences/RecentPaths/=C_003A_005Cdev_005Csteam_005Csteamapps_005Ccommon_005CGoing_0020Medieval_005CGoing_0020Medieval_005FData_005CManaged_005CUnity_002ETextMeshPro_002Edll/@EntryIndexedValue">True</s:Boolean>
1414
<s:Boolean x:Key="/Default/AddReferences/RecentPaths/=D_003A_005CWorkspaces_005CGoingMedievalModLauncher_005CGoingMedievalModLauncher_005Clibs_005C0Harmony_002Edll/@EntryIndexedValue">True</s:Boolean>
1515
<s:Boolean x:Key="/Default/AddReferences/RecentPaths/=D_003A_005CWorkspaces_005CGoingMedievalModLauncher_005CGoingMedievalModLauncher_005Clibs_005C0Harmony_002Exml/@EntryIndexedValue">True</s:Boolean>
16+
<s:Boolean x:Key="/Default/AddReferences/RecentPaths/=E_003A_005CPrograms_005CGoingMedievalModLauncher_005Clibs_005CAssembly_002DCSharp_002Edll/@EntryIndexedValue">True</s:Boolean>
17+
<s:Boolean x:Key="/Default/AddReferences/RecentPaths/=E_003A_005CPrograms_005CGoingMedievalModLauncher_005Clibs_005CUnityEngine_002ECoreModule_002Edll/@EntryIndexedValue">True</s:Boolean>
18+
<s:Boolean x:Key="/Default/AddReferences/RecentPaths/=E_003A_005CPrograms_005CGoingMedievalModLauncher_005Clibs_005CUnityEngine_002Edll/@EntryIndexedValue">True</s:Boolean>
19+
<s:Boolean x:Key="/Default/AddReferences/RecentPaths/=E_003A_005CPrograms_005CGoingMedievalModLauncher_005Clibs_005CUnityEngine_002EIMGUIModule_002Edll/@EntryIndexedValue">True</s:Boolean>
20+
<s:Boolean x:Key="/Default/AddReferences/RecentPaths/=E_003A_005CPrograms_005CGoingMedievalModLauncher_005Clibs_005CUnityEngine_002EInputLegacyModule_002Edll/@EntryIndexedValue">True</s:Boolean>
21+
<s:Boolean x:Key="/Default/AddReferences/RecentPaths/=E_003A_005CPrograms_005CGoingMedievalModLauncher_005Clibs_005CUnityEngine_002EInputModule_002Edll/@EntryIndexedValue">True</s:Boolean>
22+
<s:Boolean x:Key="/Default/AddReferences/RecentPaths/=E_003A_005CPrograms_005CGoingMedievalModLauncher_005Clibs_005CUnityEngine_002EUI_002Edll/@EntryIndexedValue">True</s:Boolean>
1623
<s:String x:Key="/Default/Environment/AssemblyExplorer/XmlDocument/@EntryValue">&lt;AssemblyExplorer&gt;&#xD;
1724
&lt;Assembly Path="C:\dev\steam\steamapps\common\Going Medieval\Going Medieval_Data\Managed\Assembly-CSharp.dll" /&gt;&#xD;
25+
&lt;Assembly Path="E:\Programs\GoingMedievalModLauncher\GoingMedievalModLauncher\libs\0Harmony.dll" /&gt;&#xD;
26+
&lt;Assembly Path="E:\Programs\GoingMedievalModLauncher\libs\Assembly-CSharp.dll" /&gt;&#xD;
1827
&lt;/AssemblyExplorer&gt;</s:String></wpf:ResourceDictionary>

GoingMedievalModLauncher/src/Launcher.cs

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -44,9 +44,7 @@ public static void startup(Scene arg0, LoadSceneMode arg1)
4444

4545
var harmony = new Harmony("com.modloader.nsmeadival");
4646

47-
var orig = typeof(MainMenuView).GetMethod("Start", BindingFlags.Instance | BindingFlags.NonPublic);
48-
var post = typeof(MainMenuPatch).GetMethod("Start", BindingFlags.Static | BindingFlags.Public);
49-
harmony.Patch(orig, postfix: new HarmonyMethod(post));
47+
MainMenuPatch.ApplyPatch(harmony);
5048

5149
Singleton<PluginManager>.Instance.loadAssemblies();
5250

GoingMedievalModLauncher/src/engine/EngineLauncher.cs

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -37,8 +37,6 @@ public void Awake()
3737
t.Field("suffix").SetValue(" - mods active");
3838
t.Method("Start").GetValue();
3939

40-
Logger.Instance.info("Start was patched with prefix");
41-
4240
// dont destroy the engines object when loading another scene, etc.
4341
DontDestroyOnLoad(this);
4442

@@ -52,7 +50,7 @@ public void Awake()
5250
foreach (var loadedPlugin in loadedPlugins)
5351
{
5452
PluginComponent pluginComponent = this.gameObject.AddComponent<PluginComponent>();
55-
pluginComponent.setup(loadedPlugin);
53+
pluginComponent.setup(loadedPlugin.plugin);
5654
}
5755

5856
// Show a fancy ui to display and control every loaded mod at runtime

GoingMedievalModLauncher/src/plugins/IPlugin.cs

Lines changed: 25 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -4,35 +4,52 @@ namespace GoingMedievalModLauncher
44
{
55
public interface IPlugin
66
{
7-
// A alphanumeric string which describes the name of the plugin / mod.
7+
8+
/// <summary>
9+
/// A alphanumeric string which describes the name of the plugin / mod.
10+
/// </summary>
811
string Name { get; }
912

10-
// A alphanumeric string which describes the functionality of the plugin / mod.
13+
/// <summary>
14+
/// A alphanumeric string which describes the functionality of the plugin / mod.
15+
/// </summary>
1116
string Description { get; }
1217

13-
// A alphanumeric string which describes the version of the plugin / mod.
18+
/// <summary>
19+
/// A alphanumeric string which describes the version of the plugin / mod.
20+
/// </summary>
1421
string Version { get; }
1522

16-
// a boolean variable which indicates that this mod is active or not
23+
/// <summary>
24+
/// a boolean variable which indicates that this mod is active or not
25+
/// </summary>
1726
bool activeState { get; set; }
1827

28+
/// <summary>
29+
/// The plugins initalization phase
30+
/// </summary>
1931
void initialize();
2032

21-
/**
33+
/** <summary>
2234
* The start-method is called once after the corresponding gameObject has been enabled.
2335
* For more details, see: https://docs.unity3d.com/ScriptReference/MonoBehaviour.Start.html
24-
*/
36+
* </summary>
37+
*/
2538
void start(MonoBehaviour root);
2639

2740
/**
41+
* <summary>
2842
* The update-method is called every frame.
2943
* For more details, see:https://docs.unity3d.com/ScriptReference/MonoBehaviour.Update.html
30-
*/
44+
* </summary>
45+
*/
3146
void update(MonoBehaviour root);
3247

33-
/*
48+
/*
49+
* <summary>
3450
* The disable-method is called by the user or at the end of the game (quitting the application)
3551
* This can be used for final operations before the application gets shutdown.
52+
* </summary>
3653
*/
3754
void disable(MonoBehaviour root);
3855

Lines changed: 191 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,191 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.IO;
4+
using System.Linq;
5+
using System.Reflection;
6+
using I2.Loc;
7+
using Newtonsoft.Json;
8+
using NSMedieval.Enums;
9+
10+
11+
namespace GoingMedievalModLauncher
12+
{
13+
14+
/// <summary>
15+
/// A class that contains a plugin.
16+
/// TODO: the static tags (Name description) should be separated from code?
17+
/// </summary>
18+
public class PluginContainer
19+
{
20+
21+
/// <summary>
22+
/// the mod's folder
23+
/// </summary>
24+
private readonly DirectoryInfo _path;
25+
/// <summary>
26+
/// The contained plugin
27+
/// </summary>
28+
public readonly IPlugin plugin;
29+
30+
public string Name => plugin.Name;
31+
32+
/// <summary>
33+
/// A alphanumeric string which describes the functionality of the contained plugin / mod.
34+
/// </summary>
35+
public string Description => plugin.Description;
36+
37+
/// <summary>
38+
/// A alphanumeric string which describes the version of the contained plugin / mod.
39+
/// </summary>
40+
public string Version => plugin.Version;
41+
42+
/// <summary>
43+
/// a boolean variable which indicates that the contained mod is active or not
44+
/// </summary>
45+
public bool activeState {
46+
get => plugin.activeState;
47+
set => plugin.activeState = value;
48+
49+
}
50+
51+
protected PluginContainer(DirectoryInfo path, IPlugin plugin)
52+
{
53+
_path = path;
54+
this.plugin = plugin;
55+
}
56+
57+
public bool loadLanguageFile()
58+
{
59+
Logger.Instance.info("Loading language file for: " + Name);
60+
FileInfo langFile = null;
61+
foreach ( var file in _path.EnumerateFiles("*.json") )
62+
{
63+
if ( file.Name == "lang.json" && file.Length != 0)
64+
{
65+
langFile = file;
66+
}
67+
}
68+
69+
if ( langFile == null )
70+
{
71+
//No language file was found.
72+
Logger.Instance.info("No langugae file was found for: " + Name);
73+
return true;
74+
}
75+
76+
var data = (Dictionary<string, Dictionary<string, string>>) JsonConvert.DeserializeObject(
77+
langFile.OpenText().ReadToEnd(),
78+
typeof(Dictionary<string, Dictionary<string, string>>));
79+
80+
var source = LocalizationManager.Sources[0];
81+
82+
foreach ( var node in data )
83+
{
84+
foreach ( var translation in node.Value )
85+
{
86+
var id = source.GetLanguageIndex(translation.Key);
87+
var term = source.AddTerm(node.Key);
88+
term.SetTranslation(id, translation.Value);
89+
}
90+
}
91+
92+
source.UpdateDictionary();
93+
94+
return true;
95+
96+
}
97+
98+
public static PluginContainer Create(DirectoryInfo dir)
99+
{
100+
Logger.Instance.info("Creating plugin container for: " + dir.Name);
101+
102+
// load all the assemblies from the directory
103+
ICollection<Assembly> assemblies = new List<Assembly>();
104+
foreach (FileInfo dllFile in dir.GetFiles("*.dll"))
105+
{
106+
Logger.Instance.info("Found dll: " + dllFile + " ...");
107+
AssemblyName an = AssemblyName.GetAssemblyName(dllFile.FullName);
108+
Assembly assembly = Assembly.Load(an);
109+
assemblies.Add(assembly);
110+
}
111+
112+
// Check if the assembly is valid (we don't want to load any dll within the mods-directory
113+
Type pluginType = typeof(IPlugin);
114+
List<Type> validPlugins = new List<Type>();
115+
foreach (Assembly assembly in assemblies)
116+
{
117+
if (assembly != null)
118+
{
119+
Type[] types = assembly.GetTypes();
120+
foreach (Type type in types)
121+
{
122+
if (type.IsInterface || type.IsAbstract)
123+
{
124+
continue;
125+
}
126+
127+
if
128+
(
129+
type.GetInterface(pluginType.FullName) != null &&
130+
type.GetInterfaces().Contains(typeof(IPlugin))
131+
)
132+
{
133+
validPlugins.Add(type);
134+
}
135+
}
136+
}
137+
}
138+
139+
if ( validPlugins.Count == 0 )
140+
{
141+
Logger.Instance.info("No valid plugin were found for this directory.");
142+
143+
return InvalidPluginContainer.Instance;
144+
}
145+
146+
if ( validPlugins.Count > 2 )
147+
{
148+
Logger.Instance.info("More than two plugins were found in this directory!");
149+
150+
return InvalidPluginContainer.Instance;
151+
}
152+
153+
//TODO: more erros?
154+
155+
var plugin = (IPlugin) Activator.CreateInstance(validPlugins[0]);
156+
157+
try
158+
{
159+
plugin.initialize();
160+
// TODO: plugin.start(doorstepGameObject);
161+
}
162+
catch (Exception e)
163+
{
164+
Logger.Instance.info("An error happened initalizting a plugin!\n"+ e);
165+
166+
return InvalidPluginContainer.Instance;
167+
}
168+
169+
var container = new PluginContainer(dir, plugin);
170+
171+
container.loadLanguageFile();
172+
173+
return container;
174+
175+
}
176+
}
177+
178+
179+
/// <summary>
180+
/// An invalid plugins container;
181+
/// </summary>
182+
public class InvalidPluginContainer : PluginContainer
183+
{
184+
185+
public static readonly InvalidPluginContainer Instance = new InvalidPluginContainer();
186+
187+
private InvalidPluginContainer() : base(null, null){}
188+
189+
}
190+
191+
}

0 commit comments

Comments
 (0)