diff --git a/PixelVision8.sln b/PixelVision8.sln index 6673e9db..b685f6f4 100644 --- a/PixelVision8.sln +++ b/PixelVision8.sln @@ -8,6 +8,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PixelVision8.CSharpRunner", EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PixelVision8.LuaRunner", "Projects\LuaRunner\PixelVision8.LuaRunner.csproj", "{3110A80A-C768-4075-B92B-46300A60A0B9}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PixelVision8.RoslynRunner", "Projects\RoslynRunner\PixelVision8.RoslynRunner.csproj", "{F8BF76F8-6C53-435D-8CF8-C1AFDFA57226}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -30,5 +32,9 @@ Global {3110A80A-C768-4075-B92B-46300A60A0B9}.Debug|Any CPU.Build.0 = Debug|Any CPU {3110A80A-C768-4075-B92B-46300A60A0B9}.Release|Any CPU.ActiveCfg = Release|Any CPU {3110A80A-C768-4075-B92B-46300A60A0B9}.Release|Any CPU.Build.0 = Release|Any CPU + {F8BF76F8-6C53-435D-8CF8-C1AFDFA57226}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {F8BF76F8-6C53-435D-8CF8-C1AFDFA57226}.Debug|Any CPU.Build.0 = Debug|Any CPU + {F8BF76F8-6C53-435D-8CF8-C1AFDFA57226}.Release|Any CPU.ActiveCfg = Release|Any CPU + {F8BF76F8-6C53-435D-8CF8-C1AFDFA57226}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection EndGlobal diff --git a/Projects/CSharpRunner/PixelVision8.CSharpRunner.csproj b/Projects/CSharpRunner/PixelVision8.CSharpRunner.csproj index ed30ec6d..9566523b 100755 --- a/Projects/CSharpRunner/PixelVision8.CSharpRunner.csproj +++ b/Projects/CSharpRunner/PixelVision8.CSharpRunner.csproj @@ -14,14 +14,14 @@ - Pixel Vision 8 Lite + Pixel Vision 8 CSharp Runner Icon.ico - Pixel Vision 8 Lite - com.pixelvision8.lite + CSharp Runner + com.pixelvision8.csharp 0.0.0 - Pixel Vision 8 Lite + CSharp Runner Icon.png - Pixel Vision 8 Lite + Pixel Vision 8 Jesse Freeman 2020 @@ -37,10 +37,9 @@ - - code.cs + + SDK/Game/%(Filename)%(Extension) - SDK/Player/PixelVision.cs @@ -66,8 +65,8 @@ Content/%(RecursiveDir)%(Filename)%(Extension) PreserveNewest - - + + diff --git a/Projects/LuaRunner/PixelVision8.LuaRunner.csproj b/Projects/LuaRunner/PixelVision8.LuaRunner.csproj index 7523426f..a820d75b 100755 --- a/Projects/LuaRunner/PixelVision8.LuaRunner.csproj +++ b/Projects/LuaRunner/PixelVision8.LuaRunner.csproj @@ -64,7 +64,7 @@ PreserveNewest - + diff --git a/Projects/RoslynRunner/Icon.bmp b/Projects/RoslynRunner/Icon.bmp new file mode 100755 index 00000000..74703994 Binary files /dev/null and b/Projects/RoslynRunner/Icon.bmp differ diff --git a/Projects/RoslynRunner/Icon.icns b/Projects/RoslynRunner/Icon.icns new file mode 100755 index 00000000..a5c35142 Binary files /dev/null and b/Projects/RoslynRunner/Icon.icns differ diff --git a/Projects/RoslynRunner/Icon.ico b/Projects/RoslynRunner/Icon.ico new file mode 100755 index 00000000..f224ef8f Binary files /dev/null and b/Projects/RoslynRunner/Icon.ico differ diff --git a/Projects/RoslynRunner/PixelVision8.RoslynRunner.csproj b/Projects/RoslynRunner/PixelVision8.RoslynRunner.csproj new file mode 100755 index 00000000..30fdf5e2 --- /dev/null +++ b/Projects/RoslynRunner/PixelVision8.RoslynRunner.csproj @@ -0,0 +1,359 @@ + + + + WinExe + netcoreapp3.1 + true + false + true + false + false + false + true + + + + + Pixel Vision 8 Roslyn Runner + Icon.ico + Roslyn Runner + com.pixelvision8.roslyn + 0.0.0 + Pixel Vision 8 Roslyn Runner + Icon.png + Pixel Vision 8 + Jesse Freeman 2020 + + + + + + + + + + BackgroundColor + + + + + + SDK/Player/PixelVision.cs + + + SDK/Player/Utils/Utilities.Calculations.cs + + + SDK/Player/Utils/Utilities.PixelData.cs + + + SDK/Runner/Game/GameRunner.Sound.cs + + + SDK/Runner/Game/GameRunner.cs + + + SDK/Runner/Game/GameRunner.Display.cs + + + SDK/Runner/Game/GameRunner.Engine.cs + + + Content/%(RecursiveDir)%(Filename)%(Extension) + PreserveNewest + + + + + + + + + SDK/Player/Chips/Game/Buttons.cs + + + SDK/Player/Chips/Game/DrawMode.cs + + + SDK/Player/Chips/Game/InputMode.cs + + + SDK/Player/Chips/Game/SpriteSizes.cs + + + SDK/Player/Chips/Game/GameChip.cs + + + SDK/Player/Chips/Game/GameChip.Draw.cs + + + SDK/Player/Chips/Game/GameChip.Debug.cs + + + SDK/Player/Chips/Game/GameChip.Input.cs + + + SDK/Player/Chips/Game/GameChip.Math.cs + + + SDK/Player/Chips/Game/GameChip.Sound.cs + + + SDK/Player/Chips/Game/GameChip.Sprite.cs + + + SDK/Player/Chips/Game/GameChip.Text.cs + + + SDK/Player/Chips/Game/GameChip.Tilemap.cs + + + SDK/Player/Chips/Game/GameChip.TilemapCache.cs + + + SDK/Player/Chips/Game/GameChip.Utils.cs + + + + + + + SDK/Player/Chips/Game/GameChip.MetaSprite.cs + + + SDK/Player/Data/SpriteCollection.cs + + + + + + + + + Content/%(RecursiveDir)%(Filename)%(Extension) + PreserveNewest + + + + + + + SDK\Player\Chips\Audio\MusicChip.cs + + + SDK\Player\Chips\Audio\SoundChannel.MonoGame.cs + + + SDK\Player\Chips\Game\GameChip.Keyboard.cs + + + SDK\Player\Chips\Game\GameChip.Music.cs + + + SDK\Player\Chips\Input\ControllerChip.MonogGame.cs + + + SDK\Player\Chips\Input\KeyboardInputChip.MonoGame.cs + + + SDK\Player\Chips\Input\MouseInputChip.MonoGame.cs + + + SDK\Player\Data\Point.cs + + + SDK\Player\Data\Rectangle.cs + + + SDK\Player\Data\SongData.cs + + + SDK\Player\Data\TrackData.cs + + + SDK\Player\Data\TrackerData.cs + + + SDK\Runner\Data\ColorData.cs + + + SDK\Runner\Data\DisplayTarget.MonoGame.cs + + + SDK/Runner/Parsers/AbstractParser.cs + + + SDK/Runner/Parsers/ColorParser.cs + + + SDK/Runner/Parsers/FontParser.cs + + + + SDK/Runner/Parsers/IParser.cs + + + SDK/Runner/Parsers/IFileLoader.cs + + + SDK/Runner/Parsers/IImageParser.cs + + + SDK/Runner/Parsers/ImageParser.cs + + + SDK/Runner/Parsers/JsonParser.cs + + + SDK/Runner/Parsers/Loader.cs + + + + + SDK/Runner/Parsers/PNGParser.cs + + + + SDK/Runner/Parsers/SpriteImageParser.cs + + + SDK\Runner\Parsers\SystemParser.ColorChip.cs + + + SDK\Runner\Parsers\SystemParser.ControllerChip.cs + + + SDK\Runner\Parsers\SystemParser.cs + + + SDK\Runner\Parsers\SystemParser.DisplayChip.cs + + + SDK\Runner\Parsers\SystemParser.FontChip.cs + + + SDK\Runner\Parsers\SystemParser.MetaSprites.cs + + + SDK\Runner\Parsers\SystemParser.SpriteChip.cs + + + SDK\Runner\Parsers\SystemParser.TilemapChip.cs + + + + SDK/Runner/Parsers/TilemapJsonParser.cs + + + SDK/Runner/Parsers/TilemapParser.cs + + + SDK/Runner/Parsers/WavParser.cs + + + + + + + SDK\Runner\RoslynRunner.cs + + + SDK/Runner/Services/SaveFlags.cs + + + + + + + SDK/Player/Chips/IDraw.cs + + + + SDK/Player/Chips/IUpdate.cs + + + SDK/Player/Chips/Audio/SoundChip.cs + + + SDK/Player/Chips/Audio/SoundChannel.cs + + + SDK/Player/Chips/AbstractChip.cs + + + SDK/Player/Chips/Graphics/SpriteChip.cs + + + SDK/Player/Chips/Graphics/TilemapChip.cs + + + SDK/Player/Chips/Graphics/ColorChip.cs + + + SDK/Player/Chips/Graphics/DisplayChip.cs + + + SDK/Player/Chips/Graphics/FontChip.cs + + + + + + + SDK/Runner/Utils/FileLoadHelper.cs + + + SDK/Runner/Utils/MiniJSON.cs + + + + + + + SDK/Player/Data/AbstractData.cs + + + SDK/Player/Data/PixelData.cs + + + SDK/Player/Data/SoundData.cs + + + SDK/Player/Data/TileData.cs + + + SDK/Player/Data/DrawRequest.cs + + + SDK/Player/Data/ImageData.cs + + + + + + + SDK\Runner\Utils\ColorUtils.MonoGame.cs + + + + SDK/Runner/Data/DisplayTarget.cs + + + + + + + + + + diff --git a/Projects/RoslynRunner/Program.cs b/Projects/RoslynRunner/Program.cs new file mode 100755 index 00000000..236ba372 --- /dev/null +++ b/Projects/RoslynRunner/Program.cs @@ -0,0 +1,28 @@ +using System; +using System.Globalization; +using System.IO; + +namespace PixelVision8.Runner +{ + public static class Program + { + + [STAThread] + public static void Main(string[] args) + { + // Fix a bug related to parsing numbers in Europe, among other things + CultureInfo.DefaultThreadCurrentCulture = CultureInfo.InvariantCulture; + CultureInfo.DefaultThreadCurrentUICulture = CultureInfo.InvariantCulture; + + var root = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Content"); + + // TODO there is a bug where this will not go to the boot error + using (var game = new RoslynRunner(root)) + { + game.Run(); + } + + } + } + +} diff --git a/SDK/Player/Chips/Audio/MusicChip.cs b/SDK/Player/Chips/Audio/MusicChip.cs index f1051326..1f420135 100755 --- a/SDK/Player/Chips/Audio/MusicChip.cs +++ b/SDK/Player/Chips/Audio/MusicChip.cs @@ -23,13 +23,24 @@ namespace PixelVision8.Player { + + public partial class PixelVision + { + + /// + /// Access to the MusicChip. + /// + /// + public MusicChip MusicChip { get; set; } + } + // public partial interface IPlayerChips // { // public MusicChip MusicChip { get; set; } // } /// - /// The MusicChpip is a sequencer for playing back ISoundData. It + /// The MusicChip is a sequencer for playing back ISoundData. It /// keeps track of playback time and moves through TrackData playing /// each beat based on the supplied note frequency. /// Loop = one set of 32 beats in X number of tracks. Stored in SongData class. diff --git a/SDK/Player/PixelVision.Plus.cs b/SDK/Player/PixelVision.Plus.cs index 090cade8..5e1b8639 100755 --- a/SDK/Player/PixelVision.Plus.cs +++ b/SDK/Player/PixelVision.Plus.cs @@ -38,12 +38,6 @@ public partial class PixelVision : IServiceLocator {"name", "untitled"} }; - /// - /// Access to the MusicChip. - /// - /// - public MusicChip MusicChip { get; set; } - public void AddService(string id, IService service) { ServiceLocator.AddService(id, service); diff --git a/SDK/Runner/RoslynRunner.cs b/SDK/Runner/RoslynRunner.cs new file mode 100755 index 00000000..59008dbe --- /dev/null +++ b/SDK/Runner/RoslynRunner.cs @@ -0,0 +1,273 @@ +// +// Copyright (c) Jesse Freeman, Pixel Vision 8. All rights reserved. +// +// Licensed under the Microsoft Public License (MS-PL) except for a few +// portions of the code. See LICENSE file in the project root for full +// license information. Third-party libraries used by Pixel Vision 8 are +// under their own licenses. Please refer to those libraries for details +// on the license they use. +// +// Contributors +// -------------------------------------------------------- +// This is the official list of Pixel Vision 8 contributors: +// +// Jesse Freeman - @JesseFreeman +// Christina-Antoinette Neofotistou @CastPixel +// Christer Kaitila - @McFunkypants +// Pedro Medeiros - @saint11 +// Shawn Rakowski - @shwany +// + +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Reflection; +using System.Text; +using PixelVision8.Player; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CSharp; +using Microsoft.CodeAnalysis.Emit; +using Microsoft.CodeAnalysis.Text; +using Microsoft.Xna.Framework; +using Microsoft.Xna.Framework.Input; + +namespace PixelVision8.Runner +{ + + public class RoslynRunner : GameRunner + { + + private KeyboardInputChip _controllerChip; + + // Store the path to the game's files + // private readonly string gamePath; + // private readonly string gameClass; + + /// + /// This constructor saves the path to the game's files and kicks off the base constructor + /// + /// + public RoslynRunner(string gamePath) : base(gamePath) + { + // this.gamePath = gamePath; + // this.gameClass = gameClass; + } + + /// + /// This is called when the runner first starts up. + /// + protected override void Initialize() + { + + // Configure the runner + ConfigureDisplayTarget(); + + // Manually override scale on boot up + Scale(1); + + // Load the game + LoadDefaultGame(); + } + + public override void ActivateEngine(PixelVision engine) + { + base.ActivateEngine(engine); + + // Reset the game's resolution before it loads up + ResetResolution(); + + // Make sure that the first frame is cleared with the default color + ActiveEngine.GameChip.Clear(); + } + + /// + /// This mthod manually loads the game file's binary data then configures the engine and processes the files. + /// + private void LoadDefaultGame() + { + + // Create a list of valid files we want to load from the game directory + var fileExtensions = new[] + { + "cs", + "png", + "json" + }; + + // Create a new dictionary to store the file binary data + var gameFiles = new List(); + + // Get only the files we need from the directory base on their extension above. + var files = from p in Directory.EnumerateFiles(RootPath) + where fileExtensions.Any(val => p.EndsWith(val)) + select p; + + // Loop through each file in the list + foreach (string file in files) + { + // Read the binary data and save it along with the file name to the dictionary. + gameFiles.Add(file); + } + + var chips = new List + { + typeof(ColorChip).FullName, + typeof(SpriteChip).FullName, + typeof(TilemapChip).FullName, + typeof(MusicChip).FullName, + typeof(FontChip).FullName, + typeof(ControllerChip).FullName, + typeof(KeyboardInputChip).FullName, + typeof(DisplayChip).FullName, + typeof(SoundChip).FullName, + }; + + // Configure a new PV8 engine to play the game + TmpEngine = new PixelVision(chips.ToArray()); + + // Load the default class based on it's full package path + BuildRoslynGameChip(gameFiles.FindAll(f => f.EndsWith(".cs")).ToArray()); + + var fileHelper = new FileLoadHelper(); + var imageParser = new PNGParser(Graphics.GraphicsDevice); + + var loader = new Loader(fileHelper, imageParser); + + // Process the files + loader.ParseFiles(gameFiles.ToArray(), TmpEngine); + loader.LoadAll(); + + RunGame(); + + _controllerChip = ActiveEngine.KeyboardInputChip; + + } + + protected override void Update(GameTime gameTime) + { + base.Update(gameTime); + + if (_controllerChip.GetKeyDown(Keys.LeftControl) || + _controllerChip.GetKeyDown(Keys.LeftControl)) + if (_controllerChip.GetKeyUp(Keys.R) || _controllerChip.GetKeyUp(Keys.D4)) + ResetGame(); + } + + private void ResetGame() + { + LoadDefaultGame(); + } + + public void BuildRoslynGameChip(string[] files, bool buildDebugData = true) + { + var total = files.Length; + var syntaxTrees = new SyntaxTree[total]; + var embeddedTexts = new EmbeddedText[total]; + + for (var i = 0; i < total; i++) + { + var path = Path.Combine(RootPath, files[i]); + + if (File.Exists(path)) + { + var data = File.ReadAllText(path); + syntaxTrees[i] = CSharpSyntaxTree.ParseText(data); + var st = SourceText.From(text: data, encoding: Encoding.UTF8); + embeddedTexts[i] = EmbeddedText.FromSource(files[i], st); + } + + } + + //Compilation options, should line up 1:1 with Visual Studio since it's the same underlying compiler. + var options = new CSharpCompilationOptions( + OutputKind.DynamicallyLinkedLibrary, + optimizationLevel: buildDebugData ? OptimizationLevel.Debug : OptimizationLevel.Release, + moduleName: "RoslynGame"); + + //This list of references is what limits the user from breaking out of the PV8 limitations through Roslyn. + //The first few lines are mandatory references, with the later ones being common helpful core libraries. + //We specifically exclude some from the list (System.IO.File, System.Net specifically for security, and System.Threading.Tasks to avoid bugs around parallel DrawX() calls). + var references = new MetadataReference[] + { + MetadataReference.CreateFromFile(Assembly.Load("System.Runtime").Location), + MetadataReference.CreateFromFile(Assembly.Load("System.Private.CoreLib").Location), + MetadataReference.CreateFromFile(Assembly.Load("System.Console").Location), + MetadataReference.CreateFromFile(Assembly.Load("Microsoft.CSharp").Location), + MetadataReference.CreateFromFile(Assembly.Load("Roslyn Runner").Location), + MetadataReference.CreateFromFile(Assembly.Load("netstandard").Location), //Required due to a .NET Standard 2.0 dependency somewhere. + MetadataReference.CreateFromFile(Assembly.Load("System.Collections").Location), //required for Linq + MetadataReference.CreateFromFile(Assembly.Load("System.Linq").Location), + MetadataReference.CreateFromFile(Assembly.Load("System.Linq.Expressions").Location), + }; + + var compiler = CSharpCompilation.Create("LoadedGame", syntaxTrees, references, options); + + //Compile the existing file into memory, or error out. + var dllStream = new MemoryStream(); + var pdbStream = new MemoryStream(); + + //This lets us get data if we hit a runtime error. + var emitOptions = new EmitOptions( + debugInformationFormat: DebugInformationFormat.PortablePdb + ); + + var compileResults = compiler.Emit(peStream: dllStream, pdbStream: pdbStream, embeddedTexts: embeddedTexts, + options: emitOptions); + if (compileResults.Success) + { + dllStream.Seek(0, SeekOrigin.Begin); + pdbStream.Seek(0, SeekOrigin.Begin); + } + else + { + var errors = compileResults.Diagnostics.Where(d => d.Severity == DiagnosticSeverity.Error).ToList(); + var errorData = errors[0].Location.GetLineSpan(); + var lineNumber = errorData.StartLinePosition.Line + 1; + var charNumber = errorData.StartLinePosition.Character + 1; + + Console.WriteLine("Error compiling"); + // DisplayError(ErrorCode.Exception, + // new Dictionary + // { + // { + // "@{error}", + // errors.Count > 0 + // ? "Line " + lineNumber + " Pos " + charNumber + ": " + errors[0].GetMessage() + // : "There was an unknown error trying to compile a C# file." + // } + // }); + return; + } + + //Get the DLL into active memory so we can use it. Runtime errors will give the wrong line number if we're in Release mode, so don't include the pdbStream for that. + var loadedAsm = Assembly.Load(dllStream.ToArray(), buildDebugData ? pdbStream.ToArray() : null); + + // TODO change this for net5.0 + // var roslynGameChipType = loadedAsm.GetTypes().Where(t => t.IsAssignableTo(typeof(GameChip))).FirstOrDefault(); //code.cs must use this namespace and class name. + + // TODO this is only for net3.1 + var roslynGameChipType = + loadedAsm.GetTypes().Where(t => typeof(GameChip).IsAssignableFrom(t)).FirstOrDefault(); + + //Could theoretically iterate over types until one that inherits from GameChip is found, but this strictness may be a better idea. + + dllStream.Close(); + dllStream.Dispose(); + pdbStream.Close(); + pdbStream.Dispose(); + + Console.WriteLine("roslynGameChipType " + roslynGameChipType); + + if (roslynGameChipType != null) + { + TmpEngine.ActivateChip("GameChip", + (AbstractChip) Activator.CreateInstance( + roslynGameChipType)); //Inserts the DLL's GameChip descendent into the engine. + } + } + + } + + +} \ No newline at end of file