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");