From 4505b0da69c4234d03dc457d832a0282186e7429 Mon Sep 17 00:00:00 2001
From: altalk23 <45172705+altalk23@users.noreply.github.com>
Date: Mon, 22 Apr 2024 00:09:49 +0300
Subject: [PATCH 1/3] i hate concepts (alternate fields impl)

---
 loader/include/Geode/modify/Field.hpp | 66 +++++++++++++++++++++++++--
 loader/test/main/main.cpp             | 23 ++++++++++
 2 files changed, 84 insertions(+), 5 deletions(-)

diff --git a/loader/include/Geode/modify/Field.hpp b/loader/include/Geode/modify/Field.hpp
index d67d5d52..30cbe28e 100644
--- a/loader/include/Geode/modify/Field.hpp
+++ b/loader/include/Geode/modify/Field.hpp
@@ -51,6 +51,11 @@ namespace geode::modifier {
 
     GEODE_DLL size_t getFieldIndexForClass(char const* name);
 
+    template <class Parent>
+    concept HasFields = requires {
+        typename Parent::Fields;
+    };
+
     template <class Parent, class Base>
     class FieldIntermediate {
         using Intermediate = Modify<Parent, Base>;
@@ -61,7 +66,7 @@ namespace geode::modifier {
     public:
         // the constructor that constructs the fields.
         // we construct the Parent first,
-        static void fieldConstructor(void* offsetField) {
+        static void fieldConstructor(void* offsetField) requires (!HasFields<Parent>) {
             std::array<std::byte, sizeof(Parent)> parentContainer;
 
             auto parent = new (parentContainer.data()) Parent();
@@ -75,7 +80,7 @@ namespace geode::modifier {
             );
         }
 
-        static void fieldDestructor(void* offsetField) {
+        static void fieldDestructor(void* offsetField) requires (!HasFields<Parent>) {
             std::array<std::byte, sizeof(Parent)> parentContainer;
 
             auto parent = new (parentContainer.data()) Intermediate();
@@ -89,7 +94,7 @@ namespace geode::modifier {
             static_cast<Parent*>(parent)->Parent::~Parent();
         }
 
-        operator Parent*() {
+        operator Parent*() requires (!HasFields<Parent>) {
             // get the this pointer of the base
             // field intermediate is the first member of Modify
             // meaning we canget the base from ourself
@@ -119,11 +124,11 @@ namespace geode::modifier {
             );
         }
 
-        Parent* self() {
+        Parent* self() requires (!HasFields<Parent>) {
             return this->operator Parent*();
         }
 
-        Parent* operator->() {
+        Parent* operator->() requires (!HasFields<Parent>) {
             // workaround for "static assertion is not an integral constant expression" in CLion
             // while the solution in https://github.com/microsoft/STL/issues/3311 works, you can't provide
             // cli args to clang-tidy in clion, so we use this workaround instead
@@ -135,6 +140,57 @@ namespace geode::modifier {
             return reinterpret_cast<Parent*>(69420);
 #else
             return this->operator Parent*();
+#endif
+        }
+
+        static void fieldConstructor(void* offsetField) requires (HasFields<Parent>) {
+            (void) new (offsetField) Parent::Fields();
+        }
+
+        static void fieldDestructor(void* offsetField) requires (HasFields<Parent>) {
+            static_cast<Parent::Fields*>(offsetField)->~Fields();
+        }
+
+        auto self() requires (HasFields<Parent>) {
+            // get the this pointer of the base
+            // field intermediate is the first member of Modify
+            // meaning we canget the base from ourself
+            auto node = reinterpret_cast<Parent*>(reinterpret_cast<std::byte*>(this) - sizeof(Base));
+            static_assert(sizeof(Base) == offsetof(Parent, m_fields), "offsetof not correct");
+
+            // generating the container if it doesn't exist
+            auto container = FieldContainer::from(node, typeid(Base).name());
+
+            // the index is global across all mods, so the
+            // function is defined in the loader source
+            static size_t index = getFieldIndexForClass(typeid(Base).name());
+
+            // the fields are actually offset from their original
+            // offset, this is done to save on allocation and space
+            auto offsetField = container->getField(index);
+            if (!offsetField) {
+                offsetField = container->setField(
+                    index, sizeof(typename Parent::Fields), &FieldIntermediate::fieldDestructor
+                );
+
+                FieldIntermediate::fieldConstructor(offsetField);
+            }
+
+            return reinterpret_cast<typename Parent::Fields*>(offsetField);
+        }
+
+        auto operator->() requires (HasFields<Parent>) {
+            // workaround for "static assertion is not an integral constant expression" in CLion
+            // while the solution in https://github.com/microsoft/STL/issues/3311 works, you can't provide
+            // cli args to clang-tidy in clion, so we use this workaround instead
+            // https://youtrack.jetbrains.com/issue/CPP-27446/spurious-offsetof-in-staticassert-error-from-clangd#focus=Comments-27-8172811.0-0
+            // update: that workaround didn't work,
+            // undefining and re-defining offsetof caused another error further down
+            // so we're doing this now
+#ifdef __CLION_IDE__
+            return reinterpret_cast<Parent::Fields*>(69420);
+#else
+            return this->self();
 #endif
         }
     };
diff --git a/loader/test/main/main.cpp b/loader/test/main/main.cpp
index 1262dc3b..64dba2ef 100644
--- a/loader/test/main/main.cpp
+++ b/loader/test/main/main.cpp
@@ -114,3 +114,26 @@ struct GJGarageLayerTest : Modify<GJGarageLayerTest, GJGarageLayer> {
         return true;
     }
 };
+
+
+#include <Geode/modify/GJGarageLayer.hpp>
+
+struct GJGarageLayerTest2 : Modify<GJGarageLayerTest2, GJGarageLayer> {
+    struct Fields {
+        int myOtherValue = 80085;
+    };
+    
+    bool init() {
+        if (!GJGarageLayer::init()) return false;
+
+        if (m_fields->myOtherValue == 80085) {
+            auto label = CCLabelBMFont::create("Alternate Fields works!", "bigFont.fnt");
+            label->setPosition(100, 60);
+            label->setScale(.4f);
+            label->setZOrder(99999);
+            this->addChild(label);
+        }
+
+        return true;
+    }
+};

From 8db0e3965193f3939140b3a26d9b995b46463cf8 Mon Sep 17 00:00:00 2001
From: altalk23 <45172705+altalk23@users.noreply.github.com>
Date: Mon, 22 Apr 2024 00:20:54 +0300
Subject: [PATCH 2/3] use Fields for geode fields

---
 loader/src/hooks/LoadingLayer.cpp | 12 +++++++-----
 loader/src/hooks/MenuLayer.cpp    |  6 ++++--
 2 files changed, 11 insertions(+), 7 deletions(-)

diff --git a/loader/src/hooks/LoadingLayer.cpp b/loader/src/hooks/LoadingLayer.cpp
index 8822c4da..c633f098 100644
--- a/loader/src/hooks/LoadingLayer.cpp
+++ b/loader/src/hooks/LoadingLayer.cpp
@@ -11,11 +11,13 @@
 using namespace geode::prelude;
 
 struct CustomLoadingLayer : Modify<CustomLoadingLayer, LoadingLayer> {
-    bool m_menuDisabled;
-    CCLabelBMFont* m_smallLabel = nullptr;
-    CCLabelBMFont* m_smallLabel2 = nullptr;
-    int m_geodeLoadStep = 0;
-    int m_totalMods = 0;
+    struct Fields {
+        bool m_menuDisabled = false;
+        CCLabelBMFont* m_smallLabel = nullptr;
+        CCLabelBMFont* m_smallLabel2 = nullptr;
+        int m_geodeLoadStep = 0;
+        int m_totalMods = 0;
+    };
 
     static void onModify(auto& self) {
         if (!self.setHookPriority("LoadingLayer::init", geode::node_ids::GEODE_ID_PRIORITY)) {
diff --git a/loader/src/hooks/MenuLayer.cpp b/loader/src/hooks/MenuLayer.cpp
index 2ff81663..b3af97a6 100644
--- a/loader/src/hooks/MenuLayer.cpp
+++ b/loader/src/hooks/MenuLayer.cpp
@@ -52,8 +52,10 @@ struct CustomMenuLayer : Modify<CustomMenuLayer, MenuLayer> {
         GEODE_FORWARD_COMPAT_DISABLE_HOOKS_INNER("MenuLayer stuff disabled")
     }
 
-    bool m_menuDisabled;
-    CCSprite* m_geodeButton;
+    struct Fields {
+        bool m_menuDisabled = false;
+        CCSprite* m_geodeButton = nullptr;
+    };
 
     bool init() {
         if (!MenuLayer::init()) return false;

From 0a3efd77049e88cc01ae70fb4363cc5297eeb763 Mon Sep 17 00:00:00 2001
From: altalk23 <45172705+altalk23@users.noreply.github.com>
Date: Mon, 22 Apr 2024 00:22:40 +0300
Subject: [PATCH 3/3] more typename

---
 loader/include/Geode/modify/Field.hpp | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/loader/include/Geode/modify/Field.hpp b/loader/include/Geode/modify/Field.hpp
index 30cbe28e..ea90f4a0 100644
--- a/loader/include/Geode/modify/Field.hpp
+++ b/loader/include/Geode/modify/Field.hpp
@@ -144,11 +144,11 @@ namespace geode::modifier {
         }
 
         static void fieldConstructor(void* offsetField) requires (HasFields<Parent>) {
-            (void) new (offsetField) Parent::Fields();
+            (void) new (offsetField) typename Parent::Fields();
         }
 
         static void fieldDestructor(void* offsetField) requires (HasFields<Parent>) {
-            static_cast<Parent::Fields*>(offsetField)->~Fields();
+            static_cast<typename Parent::Fields*>(offsetField)->~Fields();
         }
 
         auto self() requires (HasFields<Parent>) {
@@ -188,7 +188,7 @@ namespace geode::modifier {
             // undefining and re-defining offsetof caused another error further down
             // so we're doing this now
 #ifdef __CLION_IDE__
-            return reinterpret_cast<Parent::Fields*>(69420);
+            return reinterpret_cast<typename Parent::Fields*>(69420);
 #else
             return this->self();
 #endif