diff --git a/bindings/Cocos2d.bro b/bindings/Cocos2d.bro
index 1801d8ff..95650cd3 100644
--- a/bindings/Cocos2d.bro
+++ b/bindings/Cocos2d.bro
@@ -264,9 +264,8 @@ class cocos2d::CCLabelBMFont {
 	static cocos2d::CCLabelBMFont* create(char const*, char const*) = mac 0x347660;
 	auto limitLabelWidth(float, float, float) = mac 0x34a6e0, ios 0x21b740;
 
-	virtual ~CCLabelBMFont() = mac 0x347e80, ios 0x219afc;
-
 	virtual auto init() = mac 0x347b10, ios 0x2198e0;
+	bool initWithString(const char* str, const char* fnt, float width, cocos2d::CCTextAlignment align, cocos2d::CCPoint offset);
 	virtual auto setScaleX(float) = mac 0x34a5b0, ios 0x21b6e8;
 	virtual auto setScaleY(float) = mac 0x34a5d0, ios 0x21b714;
 	virtual auto setScale(float) = mac 0x34a590, ios 0x21b6bc;
diff --git a/bindings/GeometryDash.bro b/bindings/GeometryDash.bro
index 82e6f1e1..241b5eb9 100644
--- a/bindings/GeometryDash.bro
+++ b/bindings/GeometryDash.bro
@@ -5258,7 +5258,7 @@ class TeleportPortalObject : GameObject {
     bool m_teleportEase;
 }
 
-class TextAlertPopup {
+class TextAlertPopup : cocos2d::CCNode {
     static TextAlertPopup* create(gd::string const& text, float time, float scale) = win 0x1450b0;
 }
 
diff --git a/loader/include/Geode/loader/Setting.hpp b/loader/include/Geode/loader/Setting.hpp
index 3912dd26..9cb1da57 100644
--- a/loader/include/Geode/loader/Setting.hpp
+++ b/loader/include/Geode/loader/Setting.hpp
@@ -468,11 +468,19 @@ namespace geode {
         }                                                                    \
     }
 
+    // clang-format off
+
     template <class T>
     T getBuiltInSettingValue(const std::shared_ptr<Setting> setting) {
         GEODE_INT_BUILTIN_SETTING_IF(Bool, getValue(), std::is_same_v<T, bool>)
-        else GEODE_INT_BUILTIN_SETTING_IF(Float, getValue(), std::is_floating_point_v<T>) else GEODE_INT_BUILTIN_SETTING_IF(Int, getValue(), std::is_integral_v<T>) else GEODE_INT_BUILTIN_SETTING_IF(String, getValue(), std::is_same_v<T, std::string>) else {
-            static_assert(!std::is_same_v<T, T>, "todo: implement");
+        else GEODE_INT_BUILTIN_SETTING_IF(Float, getValue(), std::is_floating_point_v<T>)
+        else GEODE_INT_BUILTIN_SETTING_IF(Int, getValue(), std::is_integral_v<T>)
+        else GEODE_INT_BUILTIN_SETTING_IF(String, getValue(), std::is_same_v<T, std::string>)
+        else GEODE_INT_BUILTIN_SETTING_IF(File, getValue(), std::is_same_v<T, ghc::filesystem::path>)
+        else GEODE_INT_BUILTIN_SETTING_IF(Color, getValue(), std::is_same_v<T, cocos2d::ccColor3B>)
+        else GEODE_INT_BUILTIN_SETTING_IF(ColorAlpha, getValue(), std::is_same_v<T, cocos2d::ccColor4B>)
+        else {
+            static_assert(!std::is_same_v<T, T>, "Unsupported type for getting setting value!");
         }
         return T();
     }
@@ -480,10 +488,17 @@ namespace geode {
     template <class T>
     void setBuiltInSettingValue(const std::shared_ptr<Setting> setting, T const& value) {
         GEODE_INT_BUILTIN_SETTING_IF(Bool, setValue(value), std::is_same_v<T, bool>)
-        else GEODE_INT_BUILTIN_SETTING_IF(Float, setValue(value), std::is_floating_point_v<T>) else GEODE_INT_BUILTIN_SETTING_IF(Int, setValue(value), std::is_integral_v<T>) else GEODE_INT_BUILTIN_SETTING_IF(String, setValue(value), std::is_same_v<T, std::string>) else {
-            static_assert(!std::is_same_v<T, T>, "todo: implement");
+        else GEODE_INT_BUILTIN_SETTING_IF(Float, setValue(value), std::is_floating_point_v<T>)
+        else GEODE_INT_BUILTIN_SETTING_IF(Int, setValue(value), std::is_integral_v<T>)
+        else GEODE_INT_BUILTIN_SETTING_IF(String, setValue(value), std::is_same_v<T, std::string>)
+        else GEODE_INT_BUILTIN_SETTING_IF(File, setValue(value), std::is_same_v<T, ghc::filesystem::path>)
+        else GEODE_INT_BUILTIN_SETTING_IF(Color, setValue(value), std::is_same_v<T, cocos2d::ccColor3B>)
+        else GEODE_INT_BUILTIN_SETTING_IF(ColorAlpha, setValue(value), std::is_same_v<T, cocos2d::ccColor4B>)
+        else {
+            static_assert(!std::is_same_v<T, T>, "Unsupported type for getting setting value!");
         }
     }
+    // clang-format on
 }
 
 #pragma warning(pop)
diff --git a/loader/include/Geode/utils/Ref.hpp b/loader/include/Geode/utils/Ref.hpp
index d26ab030..f8d01cf7 100644
--- a/loader/include/Geode/utils/Ref.hpp
+++ b/loader/include/Geode/utils/Ref.hpp
@@ -101,9 +101,32 @@ namespace geode {
         bool operator==(T* other) const {
             return m_obj == other;
         }
-
         bool operator==(Ref<T> const& other) const {
             return m_obj == other.m_obj;
         }
+
+        bool operator!=(T* other) const {
+            return m_obj != other;
+        }
+        bool operator!=(Ref<T> const& other) const {
+            return m_obj != other.m_obj;
+        }
+
+        // for containers
+        bool operator<(Ref<T> const& other) const {
+            return m_obj < other.m_obj;
+        }
+        bool operator>(Ref<T> const& other) const {
+            return m_obj > other.m_obj;
+        }
+    };
+}
+
+namespace std {
+    template<class T>
+    struct hash<geode::Ref<T>> {
+        size_t operator()(geode::Ref<T> const& ref) const {
+            return std::hash<T*>()(ref.data());
+        }
     };
 }
diff --git a/loader/include/Geode/utils/operators.hpp b/loader/include/Geode/utils/operators.hpp
index 5354fbf6..ea9c43ff 100644
--- a/loader/include/Geode/utils/operators.hpp
+++ b/loader/include/Geode/utils/operators.hpp
@@ -28,6 +28,15 @@ namespace geode {
         return rect;
     }
 
+    static cocos2d::CCRect operator*(cocos2d::CCRect const& rect, float mul) {
+        return {
+            rect.origin.x * mul,
+            rect.origin.y * mul,
+            rect.size.width * mul,
+            rect.size.height * mul,
+        };
+    }
+
     static cocos2d::CCPoint operator/=(cocos2d::CCPoint& pos, float div) {
         pos.x /= div;
         pos.y /= div;
diff --git a/loader/include/Geode/utils/ranges.hpp b/loader/include/Geode/utils/ranges.hpp
index 1c55e148..96f65298 100644
--- a/loader/include/Geode/utils/ranges.hpp
+++ b/loader/include/Geode/utils/ranges.hpp
@@ -4,6 +4,9 @@
 #include <algorithm>
 #include <string>
 
+#undef min
+#undef max
+
 namespace geode::utils::ranges {
     template <class C>
     concept ValidConstContainer = requires(C const& c) {
@@ -138,4 +141,57 @@ namespace geode::utils::ranges {
         std::transform(from.begin(), from.end(), res.end(), mapper);
         return res;
     }
+
+    template <ValidConstContainer C>
+    typename C::value_type min(C const& container) {
+        auto it = std::min_element(container.begin(), container.end());
+        if (it == container.end()) {
+            return C::value_type();
+        }
+        return *it;
+    }
+
+    template <class T, ValidConstContainer C, ValidIntoConverter<typename C::value_type, T> Member>
+        requires requires(T a, T b) {
+            a < b;
+        }
+    T min(C const& container, Member member) {
+        auto it = std::min_element(
+            container.begin(), container.end(),
+            [member](auto const& a, auto const& b) -> bool {
+                return member(a) < member(b);
+            }
+        );
+        if (it == container.end()) {
+            return T();
+        }
+        return member(*it);
+    }
+
+    template <ValidConstContainer C>
+    typename C::value_type max(C const& container) {
+        auto it = std::max_element(container.begin(), container.end());
+        if (it == container.end()) {
+            return C::value_type();
+        }
+        return *it;
+    }
+
+    template <class T, ValidConstContainer C, ValidIntoConverter<typename C::value_type, T> Member>
+        requires requires(T a, T b) {
+            a < b;
+            T();
+        }
+    T max(C const& container, Member member) {
+        auto it = std::max_element(
+            container.begin(), container.end(),
+            [member](auto const& a, auto const& b) -> bool {
+                return member(a) < member(b);
+            }
+        );
+        if (it == container.end()) {
+            return T();
+        }
+        return member(*it);
+    }
 }
diff --git a/loader/src/index/Index.cpp b/loader/src/index/Index.cpp
index 9f379960..ced6e9a7 100644
--- a/loader/src/index/Index.cpp
+++ b/loader/src/index/Index.cpp
@@ -218,16 +218,34 @@ void Index::addIndexItemFromFolder(ghc::filesystem::path const& dir) {
             return;
         }
 
-        auto info = ModInfo::createFromFile(dir / "mod.json");
-        if (!info) {
-            log::warn("{}: {}, skipping", dir, info.error());
+        auto infoRes = ModInfo::createFromFile(dir / "mod.json");
+        if (!infoRes) {
+            log::warn("{}: {}, skipping", dir, infoRes.error());
             return;
         }
+        auto info = infoRes.value();
+
+        // make sure only latest version is present in index
+        auto old = std::find_if(m_items.begin(), m_items.end(), [info](IndexItem const& item) {
+            return item.m_info.m_id == info.m_id;
+        });
+        if (old != m_items.end()) {
+            // this one is newer
+            if (old->m_info.m_version < info.m_version) {
+                m_items.erase(old);
+            } else {
+                log::warn(
+                    "Found older version of ({} < {}) of {}, skipping",
+                    info.m_version, old->m_info.m_version, info.m_id
+                );
+                return;
+            }
+        }
 
         IndexItem item;
 
         item.m_path = dir;
-        item.m_info = info.value();
+        item.m_info = info;
 
         if (!json.contains("download") || !json["download"].is_object()) {
             log::warn("[index.json].download is not an object, skipping");