Skip to content
Permalink

Comparing changes

Choose two branches to see what’s changed or to start a new pull request. If you need to, you can also or learn more about diff comparisons.

Open a pull request

Create a new pull request by comparing changes across two branches. If you need to, you can also . Learn more about diff comparisons here.
base repository: rfg-modding/Nanoforge
Failed to load repositories. Confirm that selected base ref is valid, then try again.
Loading
base: v0.21.1
Choose a base ref
...
head repository: rfg-modding/Nanoforge
Failed to load repositories. Confirm that selected head ref is valid, then try again.
Loading
compare: master
Choose a head ref

Commits on Jul 17, 2022

  1. Basic app window with win32 & rendering with d3d11

    In this branch I'm rewriting Nanoforge in the programming language called Beef. C++ is just becoming too much of a hassle to work with. Beef has what I like about C++ & fixes the things I dislike about it. It should be a much nicer development experience, have quicker iteration time, and less bugs & memory leaks. A lot of old code needs to be rewritten anyway since it hasn't held up to changes in the projects scope & overall goals. So the rewrite isn't as much of a time cost as it might've been.
    
    I copied over some code from a closed source project of mine so there might be some comments that don't make sense in the context of Nanoforge. The existing code has a nice way of adding new per-frame systems & specifying when in the frame + which app states they should run in. Also has a state stack, resource system, event system, and built in profiling. There's lots of ways this can be improved, but this is a good start.
    
    Next up I'll get dear imgui working along with a basic dockspace & main menu bar. After that I'll have to figure out what the order of rewrite will be and which systems should be redesigned instead of just porting the old code to Beef. Some systems were written back when Nanoforge was just a simple zone viewer. Now that the scope of the project is much larger they aren't sufficient. A lot of code also needs to be updated to use the editor data model. I think the syntax improvements & features Beef has also open up some possibilities that weren't feasible in C++.
    
    Some features won't immediately come to the rewrite. This time around I'll be prioritizing the map editor first and expanding out from there with other features in mind.
    Moneyl committed Jul 17, 2022

    Verified

    This commit was created on GitHub.com and signed with GitHub’s verified signature. The key has expired.
    Copy the full SHA
    d59f12a View commit details

Commits on Jul 31, 2022

  1. Dear ImGui integration

    Ported the example win32 & d3d11 backends from the main imgui repo. I didn't bother implementing gamepads or multi-viewport yet since Nanoforge doesn't use them yet. Multi viewports also looks like a pain to convert.
    Moneyl committed Jul 31, 2022
    Copy the full SHA
    7c124e5 View commit details

Commits on Aug 21, 2022

  1. Add average frame time value

    Smoothed frametime for UI use. There's a lot of fluctuation per frame which makes the actual frame time harder to read
    Moneyl committed Aug 21, 2022
    Copy the full SHA
    74b30a4 View commit details
  2. Gui cleanup

    Remove gui layers & move imgui extension functions to that namespace. Gui layers are a concept from the project I used as a base for this. Not needed by Nanoforge at the moment
    Moneyl committed Aug 21, 2022
    Copy the full SHA
    1a3895d View commit details
  3. Code cleanup

    Moneyl committed Aug 21, 2022
    Copy the full SHA
    046d353 View commit details
  4. Add main menu bar generation from gui panel paths

    Bringing over all the gui code from the c++ version. Next up is documents & change tracking & save confirmation dialogs
    Moneyl committed Aug 21, 2022
    Copy the full SHA
    41be9e3 View commit details
  5. Add RfgTools submodule

    Moneyl committed Aug 21, 2022
    Copy the full SHA
    c215d07 View commit details
  6. Copy the full SHA
    9bae5d0 View commit details
  7. Partial PackfileVFS port

    Partially ported PackfileVFS from the C++ version. Will implement the rest of it when it's needed. Just using the existing behavior for now. Will rewrite it in the near future since it's not sufficient for Nanoforge anymore.
    Moneyl committed Aug 21, 2022
    Copy the full SHA
    7be1e3d View commit details
  8. Add gui documents

    Moneyl committed Aug 21, 2022
    Copy the full SHA
    99c2d16 View commit details
  9. Copy the full SHA
    3539709 View commit details

Commits on Sep 23, 2022

  1. Fix key press detection bug

    Moneyl committed Sep 23, 2022
    Copy the full SHA
    1472b5c View commit details

Commits on Jan 15, 2023

  1. Initial partial implementation of ProjectDB + editor data model

    Still need to finish implementing DiffUtil, transactions, undo/redo, serialization, and buffers. I think it'll be easier to write that stuff with an actual use case though so I'm gonna start implementing the map editor first.
    Moneyl committed Jan 15, 2023
    Copy the full SHA
    64df9ce View commit details
  2. GUI bugfixes

    Moneyl committed Jan 15, 2023
    Copy the full SHA
    51df188 View commit details
  3. Update gitignore

    Moneyl committed Jan 15, 2023
    Copy the full SHA
    9ba33b4 View commit details
  4. Update asset paths

    Need to de-hardcode these before v1.0.0
    Moneyl committed Jan 15, 2023
    Copy the full SHA
    a3e293b View commit details
  5. Update RfgTools submodule

    Moneyl committed Jan 15, 2023
    Copy the full SHA
    f470d88 View commit details

Commits on Jan 19, 2023

  1. Copy the full SHA
    f9f5417 View commit details
  2. Copy the full SHA
    082ef8a View commit details
  3. Log API

    Actual logging to file + console not yet implemented. The API is unlikely to change though so I can start adding log statements now and automatically have them later once this class is fully implemented.
    Moneyl committed Jan 19, 2023
    Copy the full SHA
    1fdfa0d View commit details
  4. Copy the full SHA
    28a52cf View commit details

Commits on Jan 23, 2023

  1. Edit tracking + undo redo for primitive properties

    Working on class/struct properties next. Don't want to resort to making every complex field a full EditorObject like I did in C++. That created some serious bloat in the DB text file and could add unecessary overhead if editor objects get heavier in the future.
    Moneyl committed Jan 23, 2023
    Copy the full SHA
    72ef349 View commit details

Commits on Jan 24, 2023

  1. Update dear imgui submodule

    Moneyl committed Jan 24, 2023
    Copy the full SHA
    bfd1667 View commit details
  2. Fix a lot of warnings and build output spam

    It made it difficult to find the actual issues. Should try to fix all warnings when reasonable so bugs/mistakes aren't overlooked.
    Moneyl committed Jan 24, 2023
    Copy the full SHA
    5247036 View commit details

Commits on Jan 28, 2023

  1. Fix memory leak

    Moneyl committed Jan 28, 2023
    Copy the full SHA
    9076318 View commit details
  2. Add version to main menu bar

    Moneyl committed Jan 28, 2023
    Copy the full SHA
    e68b26a View commit details
  3. Temporarily disable imgui version check

    Looks like imgui-beef is using the wrong version string in their implementation. Either need to wait for them to fix it or submit a PR.
    Moneyl committed Jan 28, 2023
    Copy the full SHA
    a6d78ed View commit details
  4. Indicate pre-release status in version

    I'd like v1.0.0 to be the first public release of the rewrite
    Moneyl committed Jan 28, 2023
    Copy the full SHA
    7c6e4ce View commit details

Commits on Jan 29, 2023

  1. Copy the full SHA
    22db67e View commit details
  2. Update README.md

    Moneyl authored Jan 29, 2023
    Copy the full SHA
    d55ee2c View commit details
  3. Update submodules

    Moneyl committed Jan 29, 2023
    Copy the full SHA
    e05b357 View commit details

Commits on Feb 2, 2023

  1. Transaction system changes

    - Renamed code generation functions from `GenerateXXX()` to `EmitXXX()` for clarity and to match style used by some corlib code generation examples.
    - Improved code generation for more complex types like strings and lists. This will be changed again in the near future since I'm still working out how to properly handle complex field types. I'm gonna finish implementing the zone file importer first to have some complex data to test the system on. The current code doesn't properly track changes to properties that are classes, structs, or containers.
    - Made name argument optional for `DiffUtil.CreateObject()`.
    Moneyl committed Feb 2, 2023
    Copy the full SHA
    571f516 View commit details
  2. Misc changes

    - Remove extension methods that were moved into the common lib
    - Update gitignore
    Moneyl committed Feb 2, 2023
    Copy the full SHA
    f28308a View commit details
  3. Initial zone file importer

    This is the basis for the zone file importer. RfgTools exposes a read-only interface for reading data from zone files. The Nanoforge ZoneImporter class converts that data to NFs internal data model.
    Next up:
    - Implement the rest of the object class readers needed for MP/WC maps. SP object classes won't be implemented until SP editing is added since there's ~30 SP object classes.
    - Get the projectDB + transactions working for more complex types. Using the data generated by the zone importer to test it on.
    - Implement the rest of the importers needed for maps (terrain, rocks, buildings, textures).
    - Implement map rendering (terrain, rocks, buildings, etc).
    Moneyl committed Feb 2, 2023
    Copy the full SHA
    8a43a5f View commit details

Commits on Feb 5, 2023

  1. Implement remaining MP types for ZoneImporter

    Still a bunch of SP only types not implemented. Leaving them for later to save time so I can get V1 of the rewritten map editor out sooner. Rewrite will only support MP and WC editing to start.
    Moneyl committed Feb 5, 2023
    Copy the full SHA
    3e298d1 View commit details

Commits on Feb 8, 2023

  1. Copy the full SHA
    33a0ba1 View commit details
  2. Copy the full SHA
    e6b6789 View commit details

Commits on Feb 10, 2023

  1. WorkspaceGenerator bugfix

    Using the right click "regenerate" button wasn't filling out namespace. Made a temporary work-around in the generator to fix this.
    Moneyl committed Feb 10, 2023
    Copy the full SHA
    f8d2ef0 View commit details
  2. Update README.md

    Moneyl authored Feb 10, 2023
    Copy the full SHA
    cf8de5b View commit details

Commits on Feb 11, 2023

  1. Change tracking improvements

    Add few more type checks to handle new types introduced by ZoneImporter. This will continue to be updated as other importers are added. I think a more general approach should be made for structs and classes so any struct/class field is supported. Structs and classes that don't inherit `EditorObject` will likely be treated as plain data. It might need a concept of ownership too. That would let the system distinguish between referenced objects and fields which must be classes to manage complex data. Though if they're complex enough to be a class maybe they should be required to inherit `EditorObject`. I'll figure it out as I work on the remaining importers. They'll have more complex data to test with.
    Moneyl committed Feb 11, 2023
    Copy the full SHA
    339855a View commit details

Commits on Feb 25, 2023

  1. Basic log to file

    Moneyl committed Feb 25, 2023
    Copy the full SHA
    0df9986 View commit details

Commits on Feb 26, 2023

  1. Basic scene rendering

    Adds bounding box rendering for map documents. Mesh rendering hasn't been added yet but shouldn't require many changes. The hard part of porting from C++ has is done.
    - Port scene, renderer resources, and render math from NF++
    - Update ReleaseCOM to also null the D3D ptrs to make it clear that they've been released
    - Make BuildConfig a static class instead of a resource. Its values are constant so it makes more sense to be static. I'm not so sure that the resource system has any value either. It was an idea inherited from another project and based on the Bevy game engine resources. It doesn't make as much sense here since my code isn't strictly functional like bevy code is.
    Moneyl committed Feb 26, 2023
    Copy the full SHA
    cf5bd2d View commit details

Commits on Feb 27, 2023

  1. Move renderer debug define to BuildConfig

    This way I don't have to define and update it in every file that uses it
    Moneyl committed Feb 27, 2023
    Copy the full SHA
    f6c34cd View commit details

Commits on Mar 5, 2023

  1. Add basic implementation for ProjectDB buffers

    Still need to port over the tiny buffer optimization from the C++ version and make them work with the undo/redo system.
    Moneyl committed Mar 5, 2023
    Copy the full SHA
    d9750d1 View commit details
  2. Add mesh rendering

    Moneyl committed Mar 5, 2023
    Copy the full SHA
    526a84d View commit details
  3. Add low lod terrain importer

    Still requires changes to be fully supported by undo/redo + need loading for the baked lighting and minimap textures. Going to add texture loading soon since it'll be needed for high lod terrain and buildings to look pretty.
    Moneyl committed Mar 5, 2023
    Copy the full SHA
    aeaa5c8 View commit details
  4. Fix zone import bug

    Moneyl committed Mar 5, 2023
    Copy the full SHA
    280c6c4 View commit details
  5. Basic map editor outliner

    Missing a lot of features from the C++ version. WIll implement fully once map import and project serialization are working.
    Moneyl committed Mar 5, 2023
    Copy the full SHA
    74fbac7 View commit details

Commits on Mar 12, 2023

  1. High lod terrain importer

    Still need to add a few things:
    - Stitch mesh loading
    - Rock mesh loading
    - Road mesh loading
    - Texture loading
    
    Adding these next along with some basic systems for asset loading and texture searching. Should make it easier to load assets from packfiles + fix performance issues when loading many files from str2_pc files. The texture search helper will be a starting point for replacing/improving the texture index from the C++ version of NF, which had issues once we started creating brand new map files or changing packfiles.
    Moneyl committed Mar 12, 2023
    Copy the full SHA
    544ab53 View commit details

Commits on Mar 13, 2023

  1. Make vector types non-generic

    See common lib commit abfc58900c18c3bbe8e1be173d53eceee9693c38 for more info. Only used VecX<f32> 99% of the time. Making them generic just made the code more complicated, more of a pain to type, and more pain to develop when having to deal with many possible field types in the vectors.
    Moneyl committed Mar 13, 2023
    Copy the full SHA
    41c09a5 View commit details
Showing 432 changed files with 16,829 additions and 20,545 deletions.
42 changes: 42 additions & 0 deletions .github/workflows/dotnet-desktop.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
name: .NET Core Desktop

on:
push:
branches: [ "master" ]
pull_request:
branches: [ "master" ]

jobs:

build:

strategy:
matrix:
configuration: [Release]

runs-on: windows-latest # For a list of available runner types, refer to
# https://help.github.com/en/actions/reference/workflow-syntax-for-github-actions#jobsjob_idruns-on

env:
Solution_Name: Nanoforge.sln
Test_Project_Path: Nanoforge.Tests\Nanoforge.Tests.csproj

steps:
- name: Checkout
uses: actions/checkout@v4
with:
fetch-depth: 0

# Install the .NET Core workload
- name: Install .NET Core
uses: actions/setup-dotnet@v4
with:
dotnet-version: 8.0.x

# Add MSBuild to the PATH: https://github.com/microsoft/setup-msbuild
- name: Setup MSBuild.exe
uses: microsoft/setup-msbuild@v2

# Execute all unit tests in the solution
- name: Execute unit tests
run: dotnet test
20 changes: 20 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -279,3 +279,23 @@ Project26CppCheck-cppcheck-build-dir/
Project26CppCheck.cppcheck

StructLayoutSettings.json

recovery/

build/

BeefSpace_User.toml
imgui.ini
OLD_TODO
TODO.old.cppVersion
Count.bat
combined.txt
GourceCombined.txt
GourceCombinedFiltered.txt
GourceNF.txt
GourceRfgTools.txt
GourceRfgToolsPlusPlus.txt
Config.nanodata


.idea/
33 changes: 0 additions & 33 deletions .gitmodules

This file was deleted.

Binary file removed Assets/fonts/fa-solid-900.ttf
Binary file not shown.
100 changes: 0 additions & 100 deletions Assets/shaders/Linelist.hlsl

This file was deleted.

71 changes: 0 additions & 71 deletions Assets/shaders/LitTriList.hlsl

This file was deleted.

66 changes: 0 additions & 66 deletions Assets/shaders/Pixlit.hlsl

This file was deleted.

86 changes: 0 additions & 86 deletions Assets/shaders/Pixlit1Uv.hlsl

This file was deleted.

89 changes: 0 additions & 89 deletions Assets/shaders/Pixlit1UvNmap.hlsl

This file was deleted.

92 changes: 0 additions & 92 deletions Assets/shaders/Pixlit1UvNmapCa.hlsl

This file was deleted.

92 changes: 0 additions & 92 deletions Assets/shaders/Pixlit2UvNmap.hlsl

This file was deleted.

94 changes: 0 additions & 94 deletions Assets/shaders/Pixlit3UvNmap.hlsl

This file was deleted.

98 changes: 0 additions & 98 deletions Assets/shaders/Pixlit3UvNmapCa.hlsl

This file was deleted.

47 changes: 0 additions & 47 deletions Assets/shaders/SolidTriList.hlsl

This file was deleted.

271 changes: 0 additions & 271 deletions Assets/shaders/Terrain.hlsl

This file was deleted.

121 changes: 0 additions & 121 deletions Assets/shaders/TerrainLowLod.hlsl

This file was deleted.

154 changes: 0 additions & 154 deletions Assets/shaders/TerrainRoad.hlsl

This file was deleted.

256 changes: 0 additions & 256 deletions Assets/shaders/TerrainStitch.hlsl

This file was deleted.

40 changes: 0 additions & 40 deletions Assets/shaders/imgui.hlsl

This file was deleted.

60 changes: 0 additions & 60 deletions CMakeLists.txt

This file was deleted.

46 changes: 0 additions & 46 deletions CMakeSettings.json

This file was deleted.

61 changes: 0 additions & 61 deletions Dependencies/CMakeLists.txt

This file was deleted.

1 change: 0 additions & 1 deletion Dependencies/DirectXTex
Submodule DirectXTex deleted from ecf5f1
1 change: 0 additions & 1 deletion Dependencies/IconFontCppHeaders
Submodule IconFontCppHeaders deleted from ed9535
1 change: 0 additions & 1 deletion Dependencies/RfgToolsPlusPlus
Submodule RfgToolsPlusPlus deleted from b0adb5
1 change: 0 additions & 1 deletion Dependencies/imgui
Submodule imgui deleted from 06d5f9
1 change: 0 additions & 1 deletion Dependencies/imgui_markdown
Submodule imgui_markdown deleted from 0301a3
1 change: 0 additions & 1 deletion Dependencies/imnodes
Submodule imnodes deleted from a2d73a
1 change: 0 additions & 1 deletion Dependencies/nativefiledialog
Submodule nativefiledialog deleted from 67345b
1 change: 0 additions & 1 deletion Dependencies/spdlog
Submodule spdlog deleted from 7a7611
1 change: 0 additions & 1 deletion Dependencies/tinyxml2
Submodule tinyxml2 deleted from 2c5a6b
1 change: 0 additions & 1 deletion Dependencies/tracy
Submodule tracy deleted from d011d6
97 changes: 97 additions & 0 deletions Nanoforge.Tests/DataHelperTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
using Nanoforge.Misc;
using Nanoforge.Rfg;

namespace Nanoforge.Tests;

///Tests for <see cref="Nanoforge.Misc.DataHelper"/>
public class DataHelperTests
{
[Fact]
public void DeepCopyToggleableBytes()
{
Toggleable<byte[]?> obj = new([1, 2, 3]);
Toggleable<byte[]?> objCopy = DataHelper.DeepCopyToggleableBytes(obj);
Assert.False(ReferenceEquals(obj, objCopy));
Assert.False(ReferenceEquals(obj.Value, objCopy.Value));
Assert.NotNull(objCopy.Value);

Toggleable<byte[]?> objWithNullArray = new(null);
Toggleable<byte[]?> objWithNullArrayCopy = DataHelper.DeepCopyToggleableBytes(objWithNullArray);
Assert.False(ReferenceEquals(objWithNullArray, objWithNullArrayCopy));
Assert.Null(objWithNullArray.Value);
Assert.Null(objWithNullArrayCopy.Value);
}

[Fact]
public void EnumToRfgName()
{
//Test a few of the zone object enums that have RfgNameAttribute
Assert.True(DataHelper.ToRfgName(ObjectBoundingBox.BoundingBoxType.GpsTarget, out string boundBoxTypeRfgName) && boundBoxTypeRfgName == "GPS Target");
Assert.True(DataHelper.ToRfgName(ObjectDummy.DummyTypeEnum.Rally, out string dummyTypeRfgName) && dummyTypeRfgName == "Rally");
Assert.True(DataHelper.ToRfgName(TriggerRegion.RegionTypeEnum.KillHuman, out string regionTypeRfgName) && regionTypeRfgName == "kill human");
Assert.True(DataHelper.ToRfgName(ObjLight.LightTypeEnum.Spot, out string lightTypeRfgName) && lightTypeRfgName == "spot");
Assert.True(DataHelper.ToRfgName(MultiMarker.MultiMarkerType.KingOfTheHillTarget, out string markerTypeRfgName) && markerTypeRfgName == "King of the Hill target");

//This enum doesn't have RfgNameAttribute so it should return false
Assert.False(DataHelper.ToRfgName<RfgMover.MoveTypeEnum>(RfgMover.MoveTypeEnum.Normal, out string moveTypeRfgName));
}

[Fact]
public void RfgNameToEnum()
{
Assert.True(DataHelper.FromRfgName("GPS Target", out ObjectBoundingBox.BoundingBoxType boundingBoxType) && boundingBoxType == ObjectBoundingBox.BoundingBoxType.GpsTarget);
Assert.True(DataHelper.FromRfgName("None", out ObjectBoundingBox.BoundingBoxType boundingBoxType2) && boundingBoxType2 == ObjectBoundingBox.BoundingBoxType.None);
//This one is important to test since the rfg name has a typo in it. We need to preserve that typo during map export for the game to properly read the field.
// ReSharper disable once StringLiteralTypo
Assert.True(DataHelper.FromRfgName("Tech Reponse Pos", out ObjectDummy.DummyTypeEnum dummyType) && dummyType == ObjectDummy.DummyTypeEnum.TechResponsePos);
Assert.True(DataHelper.FromRfgName("kill human", out TriggerRegion.RegionTypeEnum regionType) && regionType == TriggerRegion.RegionTypeEnum.KillHuman);
Assert.True(DataHelper.FromRfgName("spot", out ObjLight.LightTypeEnum lightType) && lightType == ObjLight.LightTypeEnum.Spot);
Assert.True(DataHelper.FromRfgName("King of the Hill target", out MultiMarker.MultiMarkerType markerType) && markerType == MultiMarker.MultiMarkerType.KingOfTheHillTarget);

//Fake/undefined rfg names. Should fail
Assert.False(DataHelper.FromRfgName("Directional", out ObjLight.LightTypeEnum badLightType));
Assert.False(DataHelper.FromRfgName("Tech Response Pos", out ObjectDummy.DummyTypeEnum badDummyType)); //Another check to make sure the typo isn't fixed
Assert.False(DataHelper.FromRfgName("Fake dummy type name", out ObjectDummy.DummyTypeEnum badDummyType2)); //Another check to make sure the typo isn't fixed
Assert.False(DataHelper.FromRfgName("Marker type", out MultiMarker.MultiMarkerType badMarkerType));
}

[Fact]
public void EnumToRfgFlagsString()
{
ObjectMover.BuildingTypeFlagsEnum buildingTypeFlags = ObjectMover.BuildingTypeFlagsEnum.Dynamic | ObjectMover.BuildingTypeFlagsEnum.ForceField | ObjectMover.BuildingTypeFlagsEnum.House;
ObjectMover.BuildingTypeFlagsEnum buildingTypeFlags2 = ObjectMover.BuildingTypeFlagsEnum.None;
ObjectMover.ChunkFlagsEnum chunkFlags = ObjectMover.ChunkFlagsEnum.Building | ObjectMover.ChunkFlagsEnum.Regenerate;
ObjectMover.ChunkFlagsEnum chunkFlags2 = ObjectMover.ChunkFlagsEnum.PlumeOnDeath | ObjectMover.ChunkFlagsEnum.InheritDamagePercentage;
ObjLight.ObjLightFlags objLightFlags = ObjLight.ObjLightFlags.UseClipping | ObjLight.ObjLightFlags.NightTime | ObjLight.ObjLightFlags.DayTime;
ObjLight.ObjLightFlags objLightFlags2 = ObjLight.ObjLightFlags.UseClipping;
ObjLight.ObjLightFlags objLightFlags3 = ObjLight.ObjLightFlags.None;

Assert.True(DataHelper.ToRfgFlagsString(buildingTypeFlags, out string buildingTypeFlagsString) && buildingTypeFlagsString == "Dynamic Force_Field House");
Assert.True(DataHelper.ToRfgFlagsString(buildingTypeFlags2, out string buildingTypeFlagsString2) && buildingTypeFlagsString2 == "");
Assert.True(DataHelper.ToRfgFlagsString(chunkFlags, out string chunkFlagsString) && chunkFlagsString == "building regenerate");
Assert.True(DataHelper.ToRfgFlagsString(chunkFlags2, out string chunkFlagsString2) && chunkFlagsString2 == "plume_on_death inherit_damage_pct");
Assert.True(DataHelper.ToRfgFlagsString(objLightFlags, out string objLightFlagsString) && objLightFlagsString == "use_clipping daytime nighttime");
Assert.True(DataHelper.ToRfgFlagsString(objLightFlags2, out string objLightFlagsString2) && objLightFlagsString2 == "use_clipping");
Assert.True(DataHelper.ToRfgFlagsString(objLightFlags3, out string objLightFlagsString3) && objLightFlagsString3 == "");
}

[Fact]
public void RfgFlagsStringToEnum()
{
string buildingTypeFlagsString = "Dynamic Force_Field House";
string buildingTypeFlagsString2 = "";
string chunkFlagsString = "building regenerate";
string chunkFlagsString2 = "plume_on_death inherit_damage_pct";
string objLightFlagsString = "use_clipping daytime nighttime";
string objLightFlagsString2 = "use_clipping";
string objLightFlagsString3 = "";

Assert.True(DataHelper.FromRfgFlagsString(buildingTypeFlagsString, out ObjectMover.BuildingTypeFlagsEnum buildingTypeFlags) && buildingTypeFlags == (ObjectMover.BuildingTypeFlagsEnum.Dynamic | ObjectMover.BuildingTypeFlagsEnum.ForceField | ObjectMover.BuildingTypeFlagsEnum.House));
Assert.True(DataHelper.FromRfgFlagsString(buildingTypeFlagsString2, out ObjectMover.BuildingTypeFlagsEnum buildingTypeFlags2) && buildingTypeFlags2 == ObjectMover.BuildingTypeFlagsEnum.None);
Assert.True(DataHelper.FromRfgFlagsString(chunkFlagsString, out ObjectMover.ChunkFlagsEnum chunkFlags) && chunkFlags == (ObjectMover.ChunkFlagsEnum.Building | ObjectMover.ChunkFlagsEnum.Regenerate));
Assert.True(DataHelper.FromRfgFlagsString(chunkFlagsString2, out ObjectMover.ChunkFlagsEnum chunkFlags2) && chunkFlags2 == (ObjectMover.ChunkFlagsEnum.PlumeOnDeath | ObjectMover.ChunkFlagsEnum.InheritDamagePercentage));
Assert.True(DataHelper.FromRfgFlagsString(objLightFlagsString, out ObjLight.ObjLightFlags lightFlags) && lightFlags == (ObjLight.ObjLightFlags.UseClipping | ObjLight.ObjLightFlags.NightTime | ObjLight.ObjLightFlags.DayTime));
Assert.True(DataHelper.FromRfgFlagsString(objLightFlagsString2, out ObjLight.ObjLightFlags lightFlags2) && lightFlags2 == ObjLight.ObjLightFlags.UseClipping);
Assert.True(DataHelper.FromRfgFlagsString(objLightFlagsString3, out ObjLight.ObjLightFlags lightFlags3) && lightFlags3 == ObjLight.ObjLightFlags.None);
}
}
1 change: 1 addition & 0 deletions Nanoforge.Tests/GlobalUsings.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
global using Xunit;
283 changes: 283 additions & 0 deletions Nanoforge.Tests/NanoDBTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,283 @@
using System.Reflection;
using AutoFixture;
using AutoFixture.Kernel;
using FluentAssertions;
using Nanoforge.Editor;
using Nanoforge.Render.Resources;
using Nanoforge.Rfg;

namespace Nanoforge.Tests;

public class NanoDBTests
{
private static List<Type> _editorObjectTypes => AppDomain.CurrentDomain.GetAssemblies()
.SelectMany(assembly => assembly.GetTypes())
.Where(type => typeof(EditorObject).IsAssignableFrom(type)).ToList();

public static IEnumerable<object[]> TestEditorObjects;

static NanoDBTests()
{
var typeList = _editorObjectTypes;
Fixture fixture = new();
//Make it so the fixture doesn't complain about circular references on objects. All ZoneObjects can have this issue due to the Parent and Children fields
fixture.Behaviors.OfType<ThrowingRecursionBehavior>().ToList().ForEach(b => fixture.Behaviors.Remove(b));
fixture.Behaviors.Add(new OmitOnRecursionBehavior());
//Don't set RenderObject fields. AutoFixture throws exceptions on some unsafe types. For the moment its fine to exclude that type from the tests.
fixture.Customizations.Add(new PropertyTypeOmitter(typeof(RenderObjectBase)));

TestEditorObjects = _editorObjectTypes.Select(type => new object[] { new SpecimenContext(fixture).Resolve(type) });
var list = TestEditorObjects.ToList();
}

//Runs once per class that is derived from EditorObject. Tests serialization and deserialization of a single object.
[Theory, MemberData(nameof(TestEditorObjects))]
public void AllEditorObjectSaveLoad(EditorObject originalObject)
{
string testObjectName = "TestObject";
originalObject.Name = testObjectName;

//TODO: Change to write to temporary directory in project dir that gets git ignored
string projectDirectory = "./AllEditorObjectSaveLoadTest/";
string projectFilePath = "";
NanoDB.NewProject(projectDirectory, "AllEditorObjectSaveLoadTest", "UnitTestRunner", "", false);
projectFilePath = NanoDB.CurrentProject.FilePath;

NanoDB.AddObject(originalObject);
NanoDB.Save();
NanoDB.Load(projectFilePath);

EditorObject? deserializedObject = NanoDB.Find(testObjectName);
Assert.NotNull(deserializedObject);
deserializedObject.Should().BeEquivalentTo(originalObject);
}

//Tests serialization & deserialization on several ZoneObjects with parent-child relationships set.
[Fact]
public void ObjectSaveLoadWithHierarchy()
{
string projectDirectory = "./ObjectSaveLoadWithHierarchyTest/";
string projectFilePath = "";
NanoDB.NewProject(projectDirectory, "ObjectSaveLoadWithHierarchyTest", "UnitTestRunner", "", false);
projectFilePath = NanoDB.CurrentProject.FilePath;

Fixture fixture = new();
fixture.Behaviors.OfType<ThrowingRecursionBehavior>().ToList().ForEach(b => fixture.Behaviors.Remove(b));
fixture.Behaviors.Add(new OmitOnRecursionBehavior());
fixture.Customizations.Add(new PropertyTypeOmitter(typeof(RenderObjectBase)));

//Note: The object types were picked at random to have some variety. This is not representative of a typical RFG map.
var objectA = fixture.Create<RfgMover>();
objectA.Name = "objectA";
objectA.Parent = null;
objectA.Children.Clear();
var objectB = fixture.Create<PlayerStart>();
objectB.Name = "objectB";
objectB.Parent = null;
objectB.Children.Clear();
var objectC = fixture.Create<PlayerStart>();
objectC.Name = "objectC";
objectC.Parent = null;
objectC.Children.Clear();
var objectD = fixture.Create<Weapon>();
objectD.Name = "objectD";
objectD.Parent = null;
objectD.Children.Clear();

objectA.AddChild(objectB);
objectA.AddChild(objectC);
objectC.AddChild(objectD);

NanoDB.AddObject(objectA);
NanoDB.AddObject(objectB);
NanoDB.AddObject(objectC);
NanoDB.AddObject(objectD);

NanoDB.Save();
NanoDB.Load(projectFilePath);

ZoneObject? objectA2 = (ZoneObject?)NanoDB.Find(objectA.Name);
ZoneObject? objectB2 = (ZoneObject?)NanoDB.Find(objectB.Name);
ZoneObject? objectC2 = (ZoneObject?)NanoDB.Find(objectC.Name);
ZoneObject? objectD2 = (ZoneObject?)NanoDB.Find(objectD.Name);

Assert.NotNull(objectA2);
Assert.NotNull(objectB2);
Assert.NotNull(objectC2);
Assert.NotNull(objectD2);

objectA2.Should().BeEquivalentTo(objectA, options => options.IgnoringCyclicReferences());
objectB2.Should().BeEquivalentTo(objectB, options => options.IgnoringCyclicReferences());
objectC2.Should().BeEquivalentTo(objectC, options => options.IgnoringCyclicReferences());
objectD2.Should().BeEquivalentTo(objectD, options => options.IgnoringCyclicReferences());

//TODO: Figure out a better way of handling this
//Manually check parent/child fields since I couldn't figure out how to do it with FluentAssertions
Assert.True(ReferenceEquals(objectA2, objectB2.Parent));
Assert.True(ReferenceEquals(objectA2, objectC2.Parent));
Assert.True(objectA2.Children.Count == 2);
Assert.Contains(objectB2, objectA2.Children);
Assert.Contains(objectC2, objectA2.Children);

Assert.True(objectB2.Children.Count == 0);

Assert.True(ReferenceEquals(objectC2, objectD2.Parent));
Assert.True(objectC2.Children.Count == 1);
Assert.Contains(objectD2, objectC2.Children);

Assert.True(objectD2.Children.Count == 0);
}

//Tests save/load for global objects. These are just like normal editor objects, except they aren't tied to a project. Used for persistent data like editor settings.
[Fact]
public void GlobalObjectSaveLoad()
{
Fixture fixture = new();
fixture.Customizations.Add(new PropertyTypeOmitter(typeof(RenderObjectBase)));
GeneralSettings objA = fixture.Create<GeneralSettings>();
objA.Name = "objA-Global";

NanoDB.AddGlobalObject(objA);

NanoDB.SaveGlobals();
NanoDB.LoadGlobals();

GeneralSettings? objA2 = (GeneralSettings?)NanoDB.Find(objA.Name);

Assert.NotNull(objA2);
objA.Should().BeEquivalentTo(objA2, options => options.IgnoringCyclicReferences());

//Cleanup globals so there's a clean state for any tests that follow
NanoDB.ClearGlobals();
NanoDB.SaveGlobals();
}

//Test basic buffer save/load
[Fact]
public void BufferSaveLoad()
{
string projectDirectory = "./BufferSaveLoadTest/";
NanoDB.NewProject(projectDirectory, "BufferSaveLoadTest", "UnitTestRunner", "", false);

Fixture fixture = new();
byte[] bufferBytes = fixture.CreateMany<byte>(1024).ToArray();
Assert.NotNull(bufferBytes);

ProjectBuffer? buffer = NanoDB.CreateBuffer(bufferBytes, "TestProjectBuffer");
Assert.NotNull(buffer);

byte[] bufferBytesLoaded = buffer.Load();
Assert.NotNull(bufferBytesLoaded);
Assert.Equal(bufferBytes, bufferBytesLoaded);
}

//Test that buffer metadata is preserved across project save/load
[Fact]
public void BufferProjectSaveLoad()
{
string projectDirectory = "./BufferProjectSaveLoadTest/";
string projectFilePath = "";
NanoDB.NewProject(projectDirectory, "BufferProjectSaveLoadTest", "UnitTestRunner", "", false);
projectFilePath = NanoDB.CurrentProject.FilePath;

Fixture fixture = new();
byte[] bufferBytes = fixture.CreateMany<byte>(1024).ToArray();
Assert.NotNull(bufferBytes);

ProjectBuffer? buffer = NanoDB.CreateBuffer(bufferBytes, "TestProjectBuffer");
Assert.NotNull(buffer);

NanoDB.Save();
NanoDB.Load(projectFilePath);

ProjectBuffer? bufferLoaded = NanoDB.Find<ProjectBuffer>(buffer.Name);
Assert.NotNull(bufferLoaded);

//Load the buffer from the drive again and make sure it still works
byte[] bufferBytesLoaded = bufferLoaded.Load();
Assert.NotNull(bufferBytesLoaded);
Assert.Equal(bufferBytes, bufferBytesLoaded);
}

//Test writing a smaller byte array to a buffer than it originally had
[Fact]
public void BufferShrink()
{
string projectDirectory = "./BufferShrinkTest/";
NanoDB.NewProject(projectDirectory, "BufferShrinkTest", "UnitTestRunner", "", false);

Fixture fixture = new();
byte[] bufferBytes = fixture.CreateMany<byte>(1024).ToArray();
Assert.NotNull(bufferBytes);

ProjectBuffer? buffer = NanoDB.CreateBuffer(bufferBytes, "TestProjectBuffer");
Assert.NotNull(buffer);

byte[] bufferBytes2 = fixture.CreateMany<byte>(10).ToArray();
Assert.NotNull(bufferBytes2);
Assert.True(buffer.Save(bufferBytes2));

byte[] bufferBytes2Loaded = buffer.Load();
Assert.NotNull(bufferBytes2Loaded);
Assert.Equal(bufferBytes2, bufferBytes2Loaded);
Assert.NotEqual(bufferBytes, bufferBytes2Loaded);
}

//Test writing a larger byte array to a buffer than it originally had
[Fact]
public void BufferGrow()
{
string projectDirectory = "./BufferGrowTest/";
NanoDB.NewProject(projectDirectory, "BufferGrowTest", "UnitTestRunner", "", false);

Fixture fixture = new();
byte[] bufferBytes = fixture.CreateMany<byte>(1024).ToArray();
Assert.NotNull(bufferBytes);

ProjectBuffer? buffer = NanoDB.CreateBuffer(bufferBytes, "TestProjectBuffer");
Assert.NotNull(buffer);

byte[] bufferBytes2 = fixture.CreateMany<byte>(2048).ToArray();
Assert.NotNull(bufferBytes2);
Assert.True(buffer.Save(bufferBytes2));

byte[] bufferBytes2Loaded = buffer.Load();
Assert.NotNull(bufferBytes2Loaded);
Assert.Equal(bufferBytes2, bufferBytes2Loaded);
Assert.NotEqual(bufferBytes, bufferBytes2Loaded);
}

[Fact]
public void ReferencedBufferSaveLoad()
{
string projectDirectory = "./ReferencedBufferSaveLoad/";
string projectFilePath = "";
NanoDB.NewProject(projectDirectory, "ReferencedBufferSaveLoad", "UnitTestRunner", "", false);
projectFilePath = NanoDB.CurrentProject.FilePath;

Fixture fixture = new();
byte[] bufferBytes = fixture.CreateMany<byte>(1024).ToArray();
ProjectBuffer? buffer = NanoDB.CreateBuffer(bufferBytes, "ReferencedProjectBuffer");
Assert.NotNull(buffer);

ProjectTexture referencingObject = new(buffer);
referencingObject.Name = "ObjectThatReferencesBuffer";
NanoDB.AddObject(referencingObject);

NanoDB.Save();
NanoDB.Load(projectFilePath);

ProjectTexture? referencingObjectLoaded = NanoDB.Find<ProjectTexture>(referencingObject.Name);
Assert.NotNull(referencingObjectLoaded);

ProjectBuffer? bufferLoadedFromReference = referencingObjectLoaded.Data;
Assert.NotNull(bufferLoadedFromReference);

ProjectBuffer? bufferLoadedFromSearch = NanoDB.Find<ProjectBuffer>(buffer.Name);
Assert.NotNull(bufferLoadedFromSearch);
//The object should be referencing the same ProjectBuffer instance that NanoDB tracks
Assert.True(ReferenceEquals(bufferLoadedFromReference, bufferLoadedFromSearch));
//The original buffer and the instance created while loading the project should have the same name and size
Assert.Equivalent(buffer, bufferLoadedFromSearch);
}
}
32 changes: 32 additions & 0 deletions Nanoforge.Tests/Nanoforge.Tests.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<WarningsAsErrors>Nullable</WarningsAsErrors>
<IsPackable>false</IsPackable>
<IsTestProject>true</IsTestProject>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="AutoFixture" Version="4.18.1" />
<PackageReference Include="AutoFixture.Xunit2" Version="4.18.1" />
<PackageReference Include="FluentAssertions" Version="6.12.2" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.6.0"/>
<PackageReference Include="xunit" Version="2.4.2"/>
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.5">
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
<PrivateAssets>all</PrivateAssets>
</PackageReference>
<PackageReference Include="coverlet.collector" Version="6.0.0">
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
<PrivateAssets>all</PrivateAssets>
</PackageReference>
</ItemGroup>

<ItemGroup>
<ProjectReference Include="..\Nanoforge\Nanoforge.csproj" />
</ItemGroup>

</Project>
28 changes: 28 additions & 0 deletions Nanoforge.Tests/PropertyTypeOmitter.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
using System.Reflection;
using AutoFixture.Kernel;

namespace Nanoforge.Tests;

public class PropertyTypeOmitter : ISpecimenBuilder
{
Type[] _typesToOmit;

public PropertyTypeOmitter(Type typeToOmit)
{
_typesToOmit = [typeToOmit];
}

public PropertyTypeOmitter(Type[] typesToOmit)
{
_typesToOmit = typesToOmit;
}

public object Create(object request, ISpecimenContext context)
{
var propInfo = request as PropertyInfo;
if (propInfo != null && _typesToOmit.Contains(propInfo.PropertyType))
return new OmitSpecimen();

return new NoSpecimen();
}
}
22 changes: 22 additions & 0 deletions Nanoforge.sln
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@

Microsoft Visual Studio Solution File, Format Version 12.00
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nanoforge", "Nanoforge\Nanoforge.csproj", "{0BCBDFFF-431D-49B1-9A15-1C238BB280F9}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nanoforge.Tests", "Nanoforge.Tests\Nanoforge.Tests.csproj", "{D544DFAF-EF6F-4CA1-887C-736D3DD6840C}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{0BCBDFFF-431D-49B1-9A15-1C238BB280F9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{0BCBDFFF-431D-49B1-9A15-1C238BB280F9}.Debug|Any CPU.Build.0 = Debug|Any CPU
{0BCBDFFF-431D-49B1-9A15-1C238BB280F9}.Release|Any CPU.ActiveCfg = Release|Any CPU
{0BCBDFFF-431D-49B1-9A15-1C238BB280F9}.Release|Any CPU.Build.0 = Release|Any CPU
{D544DFAF-EF6F-4CA1-887C-736D3DD6840C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{D544DFAF-EF6F-4CA1-887C-736D3DD6840C}.Debug|Any CPU.Build.0 = Debug|Any CPU
{D544DFAF-EF6F-4CA1-887C-736D3DD6840C}.Release|Any CPU.ActiveCfg = Release|Any CPU
{D544DFAF-EF6F-4CA1-887C-736D3DD6840C}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
EndGlobal
54 changes: 54 additions & 0 deletions Nanoforge/App.axaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
<Application xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
x:Class="Nanoforge.App"
xmlns:local="using:Nanoforge"
xmlns:core="clr-namespace:Dock.Model.Core;assembly=Dock.Model"
RequestedThemeVariant="Default">
<!-- "Default" ThemeVariant follows system theme variant. "Dark" or "Light" are other available options. -->

<Application.DataTemplates>
<local:ViewLocator/>
</Application.DataTemplates>

<Application.Resources>
<ControlRecycling x:Key="ControlRecyclingKey" />
</Application.Resources>

<Application.Styles>
<FluentTheme />
<StyleInclude Source="avares://Avalonia.Controls.TreeDataGrid/Themes/Fluent.axaml"/>

<Style Selector="DockControl">
<Setter Property="(ControlRecyclingDataTemplate.ControlRecycling)" Value="{StaticResource ControlRecyclingKey}" />
</Style>

<Style Selector="DocumentControl">
<Setter Property="HeaderTemplate">
<DataTemplate DataType="core:IDockable">
<StackPanel Orientation="Horizontal">
<PathIcon
Data="M5 1C3.89543 1 3 1.89543 3 3V13C3 14.1046 3.89543 15 5 15H11C12.1046 15 13 14.1046 13 13V5.41421C13 5.01639 12.842 4.63486 12.5607 4.35355L9.64645 1.43934C9.36514 1.15804 8.98361 1 8.58579 1H5ZM4 3C4 2.44772 4.44772 2 5 2H8V4.5C8 5.32843 8.67157 6 9.5 6H12V13C12 13.5523 11.5523 14 11 14H5C4.44772 14 4 13.5523 4 13V3ZM11.7929 5H9.5C9.22386 5 9 4.77614 9 4.5V2.20711L11.7929 5Z"
Width="16"
Height="16"
Margin="0" />
<TextBlock Text="{Binding Title}"
VerticalAlignment="Center"
Padding="4,0,0,0" />
</StackPanel>
</DataTemplate>
</Setter>
</Style>

<Style Selector="ToolControl">
<Setter Property="HeaderTemplate">
<DataTemplate DataType="core:IDockable">
<TextBlock Text="{Binding Title}" Padding="2" />
</DataTemplate>
</Setter>
</Style>

<Style Selector="ToolChromeControl">
<Setter Property="Background" Value="Transparent" />
</Style>
</Application.Styles>
</Application>
115 changes: 115 additions & 0 deletions Nanoforge/App.axaml.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using Avalonia;
using Avalonia.Controls;
using Avalonia.Controls.ApplicationLifetimes;
using Avalonia.Data.Core.Plugins;
using Avalonia.Markup.Xaml;
using Microsoft.Extensions.DependencyInjection;
using Nanoforge.Gui.Themes;
using Nanoforge.Gui.Views;
using Nanoforge.Render;
using Nanoforge.Services;
using MainWindowViewModel = Nanoforge.Gui.ViewModels.MainWindowViewModel;

namespace Nanoforge;

public partial class App : Application
{
public readonly Renderer? Renderer;

public static IThemeManager? ThemeManager;

public new static App Current => (App)Application.Current!;

public ServiceProvider Services { get; }

private static IEnumerable<Window> Windows => (Application.Current?.ApplicationLifetime as IClassicDesktopStyleApplicationLifetime)?.Windows ?? Array.Empty<Window>();

public App()
{
//Don't try starting the renderer if we're in the designer. Breaks the preview.
if (!Design.IsDesignMode)
{
Renderer = new Renderer(1920, 1080);
}
Services = ConfigureServices();
}

public override void Initialize()
{
ThemeManager = new FluentThemeManager();
ThemeManager.Initialize(this);

AvaloniaXamlLoader.Load(this);

//TODO: Make this user configurable
//Default to dark theme for now
ThemeManager.Switch(1);
}

public override void OnFrameworkInitializationCompleted()
{
// DockManager.s_enableSplitToWindow = true;

var mainWindowViewModel = new MainWindowViewModel();

switch (ApplicationLifetime)
{
case IClassicDesktopStyleApplicationLifetime desktopLifetime:
{
// Line below is needed to remove Avalonia data validation.
// Without this line you will get duplicate validations from both Avalonia and CT
BindingPlugins.DataValidators.RemoveAt(0);
var mainWindow = new MainWindow
{
DataContext = mainWindowViewModel
};

mainWindow.Closing += (_, _) =>
{
mainWindowViewModel.CloseLayout();
};

desktopLifetime.MainWindow = mainWindow;

desktopLifetime.Exit += (_, _) =>
{
mainWindowViewModel.CloseLayout();
};

break;
}
case ISingleViewApplicationLifetime singleViewLifetime:
{
var mainView = new MainView()
{
DataContext = mainWindowViewModel
};

singleViewLifetime.MainView = mainView;

break;
}
}

base.OnFrameworkInitializationCompleted();
#if DEBUG
this.AttachDevTools();
#endif
}

private ServiceProvider ConfigureServices()
{
var services = new ServiceCollection();
services.AddSingleton<IFileDialogService, FileDialogService>();
return services.BuildServiceProvider();
}

public Window? FindWindowByViewModel(INotifyPropertyChanged viewModel)
{
return Windows.FirstOrDefault(x => ReferenceEquals(viewModel, x.DataContext));
}
}
29 changes: 29 additions & 0 deletions Nanoforge/BuildConfig.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@

using System;
using System.IO;

namespace Nanoforge;

public static class BuildConfig
{
public static readonly string ProjectName;
public static readonly string AssetsDirectory;
public static readonly string ShadersDirectory;
public static readonly string Version;
public static readonly bool EnableGraphicsDebugFeatures = false;

static BuildConfig()
{
ProjectName = "Nanoforge";
#if DEBUG
string workingDirectory = Environment.CurrentDirectory;
string? projectDir = Directory.GetParent(workingDirectory)?.Parent?.Parent?.FullName;

AssetsDirectory = $"{projectDir}/assets/";
#else
AssetsDirectory = "./assets/";
#endif
ShadersDirectory = $@"{AssetsDirectory}shaders/";
Version = "v2.0.0";
}
}
13 changes: 0 additions & 13 deletions Nanoforge/BuildConfig.h

This file was deleted.

74 changes: 0 additions & 74 deletions Nanoforge/CMakeLists.txt

This file was deleted.

80 changes: 0 additions & 80 deletions Nanoforge/CrashHandler.h

This file was deleted.

39 changes: 39 additions & 0 deletions Nanoforge/Editor/Config.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@

namespace Nanoforge.Editor;

/// <summary>
/// CVars are an easy way to define global EditorObjects without needing to manually add & retrieve them from NanoDB.
/// Just define a field/property of the type CVar<T> where T is an EditorObject you want to persist across NanoDB sessions and projects.
/// When the instance is it'll handle loading its value from NanoDB and creating a global EditorObject for the data if it doesn't already exist.
/// </summary>
public class CVar<T> where T : EditorObject, new()
{
public T Value { get; private set; }
public readonly string Name;

public CVar(string name)
{
Name = name;
if (!NanoDB.LoadedGlobalObjects)
{
NanoDB.LoadGlobals();
}

T? value = NanoDB.Find<T>(Name);
if (value == null)
{
Value = NanoDB.CreateGlobalObject<T>(Name);
NanoDB.SaveGlobals();
}
else
{
Value = value;
}
}

//Save the variable to Settings.nanodata. Note: Really saves all the CVars since ProjectDB doesn't support selective saves. So don't spam this.
public void Save()
{
NanoDB.SaveGlobals();
}
}
41 changes: 41 additions & 0 deletions Nanoforge/Editor/EditorObject.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
using System.Text.Json.Serialization;

namespace Nanoforge.Editor;

public class EditorObject
{
// ReSharper disable once InconsistentNaming
public const ulong NullUID = ulong.MaxValue;

[JsonInclude]
private ulong _uid = NullUID;

// ReSharper disable once InconsistentNaming
[JsonIgnore]
public ulong UID
{
get => _uid;
private set
{
_uid = value;
}

}

public string Name = "";

public virtual EditorObject Clone()
{
EditorObject clone = (EditorObject)MemberwiseClone();
clone.UID = NullUID; //Default to NullUID so it's clear in the debugger that the object isn't valid yet. Gets a valid UID when finally added to NanoDB.
clone.Name = new string(Name);
return clone;
}

//Do NOT call this unless you know what you're doing. This is really only for NanoDB to use when creating objects.
//I made it into a function instead of using the property so it's hard to accidentally change the UID.
public void SetUID(ulong uid)
{
UID = uid;
}
}
13 changes: 13 additions & 0 deletions Nanoforge/Editor/GeneralSettings.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
using System.Collections.Generic;
using System.Collections.ObjectModel;

namespace Nanoforge.Editor;

public class GeneralSettings : EditorObject
{
public string DataPath = "";
public ObservableCollection<string> RecentProjects { get; set; } = new();
public string NewProjectDirectory = ""; //So you don't have to keep picking the folder every time you make a project

public static CVar<GeneralSettings> CVar { get; } = new("GeneralSettings");
}
Loading