mirror of
https://github.com/ficed/Braver.git
synced 2024-11-23 13:19:43 +00:00
Start adding plugin infrastructure
This commit is contained in:
parent
a41149c50b
commit
8f7210b85f
@ -4,6 +4,7 @@
|
||||
//
|
||||
// SPDX-License-Identifier: EPL-2.0
|
||||
|
||||
using Ficedula.FF7;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
@ -18,74 +19,80 @@ namespace Braver {
|
||||
public abstract IEnumerable<string> Scan();
|
||||
}
|
||||
|
||||
|
||||
public class PackDataSource : DataSource {
|
||||
private Pack _pack;
|
||||
private string _path;
|
||||
|
||||
private HashSet<string> _files;
|
||||
|
||||
public PackDataSource(Pack pack, string path) {
|
||||
_pack = pack;
|
||||
_path = path;
|
||||
|
||||
_files = new HashSet<string>(
|
||||
pack.Filenames
|
||||
.Where(s => s.StartsWith(path + "\\", StringComparison.InvariantCultureIgnoreCase)),
|
||||
StringComparer.InvariantCultureIgnoreCase
|
||||
);
|
||||
}
|
||||
|
||||
public override IEnumerable<string> Scan() {
|
||||
return _files.Select(s => Path.GetFileName(s));
|
||||
}
|
||||
|
||||
public override Stream TryOpen(string file) {
|
||||
string fn = _path + "\\" + file;
|
||||
if (_files.Contains(fn)) {
|
||||
return _pack.Read(fn);
|
||||
} else
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public class LGPDataSource : DataSource {
|
||||
private Ficedula.FF7.LGPFile _lgp;
|
||||
|
||||
public LGPDataSource(Ficedula.FF7.LGPFile lgp) {
|
||||
_lgp = lgp;
|
||||
}
|
||||
|
||||
public override IEnumerable<string> Scan() => _lgp.Filenames;
|
||||
public override Stream TryOpen(string file) => _lgp.TryOpen(file);
|
||||
}
|
||||
|
||||
public class FileDataSource : DataSource {
|
||||
private string _root;
|
||||
|
||||
public FileDataSource(string root) {
|
||||
_root = root;
|
||||
}
|
||||
|
||||
public override IEnumerable<string> Scan() {
|
||||
//TODO subdirectories
|
||||
return Directory.GetFiles(_root).Select(s => Path.GetFileName(s));
|
||||
}
|
||||
|
||||
public override Stream TryOpen(string file) {
|
||||
string fn = Path.Combine(_root, file);
|
||||
if (File.Exists(fn))
|
||||
return new FileStream(fn, FileMode.Open, FileAccess.Read);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public abstract class BGame {
|
||||
|
||||
protected class PackDataSource : DataSource {
|
||||
private Pack _pack;
|
||||
private string _path;
|
||||
|
||||
private HashSet<string> _files;
|
||||
|
||||
public PackDataSource(Pack pack, string path) {
|
||||
_pack = pack;
|
||||
_path = path;
|
||||
|
||||
_files = new HashSet<string>(
|
||||
pack.Filenames
|
||||
.Where(s => s.StartsWith(path + "\\", StringComparison.InvariantCultureIgnoreCase)),
|
||||
StringComparer.InvariantCultureIgnoreCase
|
||||
);
|
||||
}
|
||||
|
||||
public override IEnumerable<string> Scan() {
|
||||
return _files.Select(s => Path.GetFileName(s));
|
||||
}
|
||||
|
||||
public override Stream TryOpen(string file) {
|
||||
string fn = _path + "\\" + file;
|
||||
if (_files.Contains(fn)) {
|
||||
return _pack.Read(fn);
|
||||
} else
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
protected class LGPDataSource : DataSource {
|
||||
private Ficedula.FF7.LGPFile _lgp;
|
||||
|
||||
public LGPDataSource(Ficedula.FF7.LGPFile lgp) {
|
||||
_lgp = lgp;
|
||||
}
|
||||
|
||||
public override IEnumerable<string> Scan() => _lgp.Filenames;
|
||||
public override Stream TryOpen(string file) => _lgp.TryOpen(file);
|
||||
}
|
||||
|
||||
protected class FileDataSource : DataSource {
|
||||
private string _root;
|
||||
|
||||
public FileDataSource(string root) {
|
||||
_root = root;
|
||||
}
|
||||
|
||||
public override IEnumerable<string> Scan() {
|
||||
//TODO subdirectories
|
||||
return Directory.GetFiles(_root).Select(s => Path.GetFileName(s));
|
||||
}
|
||||
|
||||
public override Stream TryOpen(string file) {
|
||||
string fn = Path.Combine(_root, file);
|
||||
if (File.Exists(fn))
|
||||
return new FileStream(fn, FileMode.Open, FileAccess.Read);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
public VMM Memory { get; } = new();
|
||||
public SaveMap SaveMap { get; }
|
||||
|
||||
public IAudio Audio { get; protected set; }
|
||||
public SaveData SaveData { get; protected set; }
|
||||
|
||||
protected Dictionary<string, List<DataSource>> _data = new Dictionary<string, List<DataSource>>(StringComparer.InvariantCultureIgnoreCase);
|
||||
private Dictionary<Type, object> _singletons = new();
|
||||
protected Dictionary<string, string> _paths = new(StringComparer.InvariantCultureIgnoreCase);
|
||||
|
||||
public BGame() {
|
||||
SaveMap = new SaveMap(Memory);
|
||||
@ -114,6 +121,13 @@ namespace Braver {
|
||||
}
|
||||
}
|
||||
|
||||
public void AddDataSource(string folder, DataSource source) {
|
||||
if (!_data.TryGetValue(folder, out var list))
|
||||
list = _data[folder] = new List<DataSource>();
|
||||
list.Add(source);
|
||||
}
|
||||
public string GetPath(string name) => _paths[name];
|
||||
|
||||
protected void FrameIncrement() {
|
||||
if (++SaveMap.GameTimeFrames >= 30) {
|
||||
SaveMap.GameTimeFrames = 0;
|
||||
@ -195,13 +209,13 @@ namespace Braver {
|
||||
|
||||
public IEnumerable<string> ScanData(string category) {
|
||||
if (_data.TryGetValue(category, out var sources))
|
||||
return sources.SelectMany(s => s.Scan());
|
||||
return sources.SelectMany(s => s.Scan()).Distinct();
|
||||
else
|
||||
return Enumerable.Empty<string>();
|
||||
}
|
||||
|
||||
public Stream TryOpen(string category, string file) {
|
||||
foreach (var source in _data[category]) {
|
||||
foreach (var source in _data[category].Reverse<DataSource>()) {
|
||||
var s = source.TryOpen(file);
|
||||
if (s != null)
|
||||
return s;
|
||||
|
13
Braver.Plugins/Braver.Plugins.csproj
Normal file
13
Braver.Plugins/Braver.Plugins.csproj
Normal file
@ -0,0 +1,13 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net6.0</TargetFramework>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\Braver.Core\Braver.Core.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
19
Braver.Plugins/Field/Dialog.cs
Normal file
19
Braver.Plugins/Field/Dialog.cs
Normal file
@ -0,0 +1,19 @@
|
||||
// This program and the accompanying materials are made available under the terms of the
|
||||
// Eclipse Public License v2.0 which accompanies this distribution, and is available at
|
||||
// https://www.eclipse.org/legal/epl-v20.html
|
||||
//
|
||||
// SPDX-License-Identifier: EPL-2.0
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Braver.Plugins.Field {
|
||||
public interface IDialog : IPluginInstance {
|
||||
void Showing(int window, int tag, IEnumerable<string> text);
|
||||
void Asking(int window, int tag, IEnumerable<string> text, IEnumerable<int> choiceLines);
|
||||
void ChoiceMade(int window, int choice);
|
||||
}
|
||||
}
|
67
Braver.Plugins/Plugin.cs
Normal file
67
Braver.Plugins/Plugin.cs
Normal file
@ -0,0 +1,67 @@
|
||||
// This program and the accompanying materials are made available under the terms of the
|
||||
// Eclipse Public License v2.0 which accompanies this distribution, and is available at
|
||||
// https://www.eclipse.org/legal/epl-v20.html
|
||||
//
|
||||
// SPDX-License-Identifier: EPL-2.0
|
||||
|
||||
namespace Braver.Plugins {
|
||||
public abstract class Plugin {
|
||||
public abstract string Name { get; }
|
||||
public abstract Version Version { get; }
|
||||
public abstract IEnumerable<Type> GetPluginInstances();
|
||||
public abstract IPluginInstance Get(string context, Type t);
|
||||
|
||||
public abstract void Init(BGame game);
|
||||
}
|
||||
|
||||
public interface IPluginInstance {
|
||||
}
|
||||
|
||||
|
||||
public class PluginManager {
|
||||
|
||||
private Dictionary<Type, List<Plugin>> _types = new();
|
||||
|
||||
public PluginManager() {
|
||||
var pluginTypes = System.Reflection.Assembly.GetExecutingAssembly()
|
||||
.GetTypes()
|
||||
.Where(t => t.IsAssignableTo(typeof(IPluginInstance)));
|
||||
foreach (var type in pluginTypes)
|
||||
_types[type] = new List<Plugin>();
|
||||
}
|
||||
|
||||
public void Init(BGame game, IEnumerable<Plugin> plugins) {
|
||||
foreach(var plugin in plugins) {
|
||||
System.Diagnostics.Trace.WriteLine($"Loading plugin {plugin.Name} v{plugin.Version}");
|
||||
plugin.Init(game);
|
||||
foreach(var type in plugin.GetPluginInstances()) {
|
||||
_types[type].Add(plugin);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public PluginInstances GetInstances(string context, params Type[] types) {
|
||||
var instances = types
|
||||
.SelectMany(t => _types[t].Select(plugin => plugin.Get(context, t)));
|
||||
return new PluginInstances(instances);
|
||||
}
|
||||
}
|
||||
|
||||
public class PluginInstances : IDisposable {
|
||||
|
||||
private List<IPluginInstance> _instances;
|
||||
internal PluginInstances(IEnumerable<IPluginInstance> instances) {
|
||||
_instances = instances.ToList();
|
||||
}
|
||||
|
||||
public void Call<T>(Action<T> action) where T : IPluginInstance {
|
||||
foreach (var instance in _instances.OfType<T>())
|
||||
action(instance);
|
||||
}
|
||||
|
||||
public void Dispose() {
|
||||
foreach (var instance in _instances.OfType<IDisposable>())
|
||||
instance.Dispose();
|
||||
}
|
||||
}
|
||||
}
|
@ -24,6 +24,8 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution
|
||||
.editorconfig = .editorconfig
|
||||
EndProjectSection
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Braver.Plugins", "Braver.Plugins\Braver.Plugins.csproj", "{4C8A556C-F596-4F35-8E09-C79E5BEAFCEB}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Any CPU = Debug|Any CPU
|
||||
@ -62,6 +64,10 @@ Global
|
||||
{BC7A0739-2DEE-4396-BF05-9242D4833A5F}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{BC7A0739-2DEE-4396-BF05-9242D4833A5F}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{BC7A0739-2DEE-4396-BF05-9242D4833A5F}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{4C8A556C-F596-4F35-8E09-C79E5BEAFCEB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{4C8A556C-F596-4F35-8E09-C79E5BEAFCEB}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{4C8A556C-F596-4F35-8E09-C79E5BEAFCEB}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{4C8A556C-F596-4F35-8E09-C79E5BEAFCEB}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
|
@ -8,12 +8,15 @@ using Microsoft.Xna.Framework.Audio;
|
||||
using NAudio.Wave;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Threading.Channels;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Braver {
|
||||
public class Audio {
|
||||
|
||||
|
||||
public class Audio : IAudio {
|
||||
|
||||
private string _musicFolder;
|
||||
private Channel<MusicCommand> _channel;
|
||||
@ -336,16 +339,22 @@ namespace Braver {
|
||||
public void Quit() {
|
||||
_channel.Writer.TryWrite(null);
|
||||
}
|
||||
|
||||
|
||||
private NAudio.Wave.WaveOut _streamOut;
|
||||
|
||||
public void PlaySfxStream(Stream s, float volume, float pan) {
|
||||
//TODO very basic implementation
|
||||
//TODO pan
|
||||
//TODO non-ogg!
|
||||
_streamOut?.Stop();
|
||||
_streamOut?.Dispose();
|
||||
_streamOut = new NAudio.Wave.WaveOut();
|
||||
var vorbis = new NAudio.Vorbis.VorbisWaveReader(s, true);
|
||||
_streamOut.Init(vorbis);
|
||||
_streamOut.Volume = volume;
|
||||
_streamOut.Play();
|
||||
}
|
||||
}
|
||||
|
||||
public enum Sfx {
|
||||
Cursor = 0,
|
||||
SaveReady = 1,
|
||||
Invalid = 2,
|
||||
Cancel = 3,
|
||||
EnemyDeath = 21,
|
||||
BattleSwirl = 42,
|
||||
BuyItem = 261,
|
||||
DeEquip = 446,
|
||||
}
|
||||
}
|
||||
|
@ -39,6 +39,7 @@
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\Braver.Core\Braver.Core.csproj" />
|
||||
<ProjectReference Include="..\Braver.Plugins\Braver.Plugins.csproj" />
|
||||
<ProjectReference Include="..\Ficedula.FF7\Ficedula.FF7.csproj" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
|
@ -4,6 +4,7 @@
|
||||
//
|
||||
// SPDX-License-Identifier: EPL-2.0
|
||||
|
||||
using Braver.Plugins;
|
||||
using Microsoft.Xna.Framework;
|
||||
using Microsoft.Xna.Framework.Graphics;
|
||||
using System;
|
||||
@ -43,11 +44,10 @@ namespace Braver {
|
||||
|
||||
public Net.Net Net { get; set; }
|
||||
|
||||
public Audio Audio { get; }
|
||||
public Screen Screen => _screens.Peek();
|
||||
public DebugOptions DebugOptions { get; }
|
||||
public PluginManager PluginManager { get; }
|
||||
|
||||
private Dictionary<string, string> _paths = new(StringComparer.InvariantCultureIgnoreCase);
|
||||
|
||||
public FGame(GraphicsDevice graphics) {
|
||||
_graphics = graphics;
|
||||
@ -139,6 +139,16 @@ namespace Braver {
|
||||
Audio.Precache(Sfx.Cancel, true);
|
||||
Audio.Precache(Sfx.Invalid, true);
|
||||
|
||||
PluginManager = new PluginManager();
|
||||
if (_paths.ContainsKey("PLUGINS")) {
|
||||
var plugins = Directory.GetFiles(_paths["PLUGINS"], "*.dll")
|
||||
.Select(fn => System.Reflection.Assembly.LoadFrom(fn))
|
||||
.SelectMany(asm => asm.GetTypes())
|
||||
.Where(t => t.IsAssignableTo(typeof(Plugin)))
|
||||
.Select(t => Activator.CreateInstance(t))
|
||||
.OfType<Plugin>();
|
||||
PluginManager.Init(this, plugins);
|
||||
}
|
||||
}
|
||||
|
||||
private class TraceFile : TraceListener {
|
||||
@ -171,8 +181,6 @@ namespace Braver {
|
||||
}
|
||||
}
|
||||
|
||||
public string GetPath(string name) => _paths[name];
|
||||
|
||||
public void AutoSave() {
|
||||
string path = GetPath("save");
|
||||
foreach (string file1 in Directory.GetFiles(path, "auto1.*"))
|
||||
|
@ -4,6 +4,7 @@
|
||||
//
|
||||
// SPDX-License-Identifier: EPL-2.0
|
||||
|
||||
using Braver.Plugins;
|
||||
using Microsoft.Xna.Framework;
|
||||
using Microsoft.Xna.Framework.Graphics;
|
||||
using System;
|
||||
@ -59,12 +60,14 @@ namespace Braver.Field {
|
||||
|
||||
private List<Window> _windows = Enumerable.Range(0, 10).Select(_ => new Window()).ToList();
|
||||
private FGame _game;
|
||||
private PluginInstances _plugins;
|
||||
private UI.UIBatch _ui;
|
||||
|
||||
public bool IsActive => _windows.Any(w => (w.State != WindowState.Hidden) && !w.Options.HasFlag(DialogOptions.IsPermanent));
|
||||
|
||||
public Dialog(FGame g, GraphicsDevice graphics) {
|
||||
public Dialog(FGame g, PluginInstances plugins, GraphicsDevice graphics) {
|
||||
_game = g;
|
||||
_plugins = plugins;
|
||||
_ui = new UI.UIBatch(graphics, g);
|
||||
}
|
||||
|
||||
@ -127,18 +130,20 @@ namespace Braver.Field {
|
||||
}
|
||||
}
|
||||
|
||||
public void Show(int window, string text, Action onClosed) {
|
||||
public void Show(int window, int tag, string text, Action onClosed) {
|
||||
PrepareWindow(window, text);
|
||||
_windows[window].OnClosed = onClosed;
|
||||
_windows[window].OnChoice = null;
|
||||
_windows[window].ChoiceLines = null;
|
||||
_plugins.Call<Plugins.Field.IDialog>(dlg => dlg.Showing(window, tag, _windows[window].Text));
|
||||
}
|
||||
public void Ask(int window, string text, IEnumerable<int> choices, Action<int?> onChoice) {
|
||||
public void Ask(int window, int tag, string text, IEnumerable<int> choices, Action<int?> onChoice) {
|
||||
PrepareWindow(window, text);
|
||||
_windows[window].ChoiceLines = choices.ToArray();
|
||||
_windows[window].OnClosed = null;
|
||||
_windows[window].OnChoice = onChoice;
|
||||
_windows[window].Choice = 0;
|
||||
_plugins.Call<Plugins.Field.IDialog>(dlg => dlg.Asking(window, tag, _windows[window].Text, _windows[window].ChoiceLines));
|
||||
}
|
||||
|
||||
public void ProcessInput(InputState input) {
|
||||
|
@ -181,6 +181,7 @@ namespace Braver.Field {
|
||||
field = new FieldFile(s);
|
||||
}
|
||||
|
||||
_plugins = g.PluginManager.GetInstances(_file, typeof(Plugins.Field.IDialog));
|
||||
Background = new Background(g, graphics, field.GetBackground());
|
||||
Movie = new Movie(g, graphics);
|
||||
FieldDialog = field.GetDialogEvent();
|
||||
@ -344,7 +345,7 @@ namespace Braver.Field {
|
||||
_bgZTo = Background.AutoDetectZTo;
|
||||
}
|
||||
|
||||
Dialog = new Dialog(g, graphics);
|
||||
Dialog = new Dialog(g, _plugins, graphics);
|
||||
FieldUI = new FieldUI(g, graphics);
|
||||
|
||||
g.Memory.ResetScratch();
|
||||
|
@ -1574,7 +1574,7 @@ if (y + h + MIN_WINDOW_DISTANCE > GAME_HEIGHT) { y = GAME_HEIGHT - h - MIN_WINDO
|
||||
addr = f.ReadU8();
|
||||
|
||||
f.Pause($"Waiting for ASK on window {win}");
|
||||
s.Dialog.Ask(win, s.FieldDialog.Dialogs[msg], Enumerable.Range(firstChoice, lastChoice - firstChoice + 1), ch => {
|
||||
s.Dialog.Ask(win, msg, s.FieldDialog.Dialogs[msg], Enumerable.Range(firstChoice, lastChoice - firstChoice + 1), ch => {
|
||||
if (ch != null)
|
||||
s.Game.Memory.Write(bank, addr, (ushort)ch);
|
||||
f.Resume();
|
||||
@ -1586,7 +1586,7 @@ if (y + h + MIN_WINDOW_DISTANCE > GAME_HEIGHT) { y = GAME_HEIGHT - h - MIN_WINDO
|
||||
public static OpResult MESSAGE(Fiber f, Entity e, FieldScreen s) {
|
||||
byte id = f.ReadU8(), dlg = f.ReadU8();
|
||||
f.Pause($"Waiting for MESSAGE in window {id}");
|
||||
s.Dialog.Show(id, s.FieldDialog.Dialogs[dlg], () => f.Resume());
|
||||
s.Dialog.Show(id, dlg, s.FieldDialog.Dialogs[dlg], () => f.Resume());
|
||||
|
||||
return OpResult.Continue;
|
||||
}
|
||||
|
18
Braver/Properties/PublishProfiles/FolderProfile.pubxml
Normal file
18
Braver/Properties/PublishProfiles/FolderProfile.pubxml
Normal file
@ -0,0 +1,18 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
https://go.microsoft.com/fwlink/?LinkID=208121.
|
||||
-->
|
||||
<Project>
|
||||
<PropertyGroup>
|
||||
<Configuration>Release</Configuration>
|
||||
<Platform>Any CPU</Platform>
|
||||
<PublishDir>bin\Release\net6.0-windows\publish\win-x86\</PublishDir>
|
||||
<PublishProtocol>FileSystem</PublishProtocol>
|
||||
<_TargetId>Folder</_TargetId>
|
||||
<TargetFramework>net6.0-windows</TargetFramework>
|
||||
<RuntimeIdentifier>win-x86</RuntimeIdentifier>
|
||||
<SelfContained>true</SelfContained>
|
||||
<PublishSingleFile>true</PublishSingleFile>
|
||||
<PublishReadyToRun>false</PublishReadyToRun>
|
||||
</PropertyGroup>
|
||||
</Project>
|
@ -77,6 +77,7 @@ namespace Braver {
|
||||
private Action _transitionAction;
|
||||
|
||||
protected bool _readyToRender;
|
||||
protected Plugins.PluginInstances _plugins;
|
||||
|
||||
public virtual void Init(FGame g, GraphicsDevice graphics) {
|
||||
Game = g;
|
||||
|
@ -38,4 +38,6 @@ PATH MOVIES %movies%
|
||||
PATH DEBUG %braver%
|
||||
PATH SAVE %save%
|
||||
|
||||
PATH PLUGINS %plugins%
|
||||
|
||||
LOG %braver%\log.txt
|
Loading…
Reference in New Issue
Block a user