Merge branch 'main' into layout

This commit is contained in:
HJfod 2022-11-12 12:03:15 +02:00
commit 61c0f1b274
41 changed files with 714 additions and 588 deletions

View file

@ -79,11 +79,12 @@ IncludeCategories:
IndentAccessModifiers: false IndentAccessModifiers: false
AccessModifierOffset: -4 AccessModifierOffset: -4
IndentCaseBlocks: true IndentCaseBlocks: false
IndentCaseLabels: true IndentCaseLabels: true
IndentExternBlock: Indent IndentExternBlock: Indent
IndentGotoLabels: true IndentGotoLabels: true
IndentPPDirectives: BeforeHash IndentPPDirectives: BeforeHash
IndentRequiresClause: false
IndentWrappedFunctionNames: false IndentWrappedFunctionNames: false
# InsertBraces: true # InsertBraces: true
InsertTrailingCommas: None InsertTrailingCommas: None
@ -107,6 +108,7 @@ PenaltyIndentedWhitespace: 0
QualifierAlignment: Right QualifierAlignment: Right
RequiresClausePosition: OwnLine
ReflowComments: true ReflowComments: true
SeparateDefinitionBlocks: Always SeparateDefinitionBlocks: Always

View file

@ -39,7 +39,6 @@ target_sources(${PROJECT_NAME} INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}/entry.cpp)
add_subdirectory(codegen) add_subdirectory(codegen)
message(STATUS ${GEODE_CODEGEN_PATH}/Geode/GeneratedSource.cpp)
add_custom_command( add_custom_command(
DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/bindings/GeometryDash.bro DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/bindings/GeometryDash.bro
${CMAKE_CURRENT_SOURCE_DIR}/bindings/Cocos2d.bro ${CMAKE_CURRENT_SOURCE_DIR}/bindings/Cocos2d.bro
@ -106,7 +105,7 @@ elseif(EXISTS ${GEODE_PLATFORM_BIN_PATH})
) )
else() else()
message(FATAL_ERROR message(FATAL_ERROR
"No valid loader binary to link to! Install prebuilts with `geode sdk install-prebuilts` " "No valid loader binary to link to! Install prebuilts with `geode sdk install-prebuilts`, "
"or build Geode from source." "or build Geode from source and add `set(GEODE_LINK_NIGHTLY On)` to your CMakeLists.txt."
) )
endif() endif()

View file

@ -140,6 +140,8 @@ class cocos2d::CCDrawNode {
static cocos2d::CCDrawNode* create() = mac 0x378d00; static cocos2d::CCDrawNode* create() = mac 0x378d00;
auto drawPolygon(cocos2d::CCPoint*, unsigned int, cocos2d::_ccColor4F const&, float, cocos2d::_ccColor4F const&) = mac 0x3797f0; auto drawPolygon(cocos2d::CCPoint*, unsigned int, cocos2d::_ccColor4F const&, float, cocos2d::_ccColor4F const&) = mac 0x3797f0;
auto drawSegment(cocos2d::CCPoint const&, cocos2d::CCPoint const&, float, cocos2d::_ccColor4F const&) = mac 0x3792d0; auto drawSegment(cocos2d::CCPoint const&, cocos2d::CCPoint const&, float, cocos2d::_ccColor4F const&) = mac 0x3792d0;
auto drawDot(cocos2d::CCPoint const&, float, cocos2d::_ccColor4F const&) = mac 0x379100;
auto getBlendFunc() const = mac 0x379ea0; auto getBlendFunc() const = mac 0x379ea0;
auto init() = mac 0x378e00; auto init() = mac 0x378e00;
auto setBlendFunc(cocos2d::_ccBlendFunc const&) = mac 0x379eb0; auto setBlendFunc(cocos2d::_ccBlendFunc const&) = mac 0x379eb0;
@ -262,9 +264,8 @@ class cocos2d::CCLabelBMFont {
static cocos2d::CCLabelBMFont* create(char const*, char const*) = mac 0x347660; static cocos2d::CCLabelBMFont* create(char const*, char const*) = mac 0x347660;
auto limitLabelWidth(float, float, float) = mac 0x34a6e0, ios 0x21b740; auto limitLabelWidth(float, float, float) = mac 0x34a6e0, ios 0x21b740;
virtual ~CCLabelBMFont() = mac 0x347e80, ios 0x219afc;
virtual auto init() = mac 0x347b10, ios 0x2198e0; 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 setScaleX(float) = mac 0x34a5b0, ios 0x21b6e8;
virtual auto setScaleY(float) = mac 0x34a5d0, ios 0x21b714; virtual auto setScaleY(float) = mac 0x34a5d0, ios 0x21b714;
virtual auto setScale(float) = mac 0x34a590, ios 0x21b6bc; virtual auto setScale(float) = mac 0x34a590, ios 0x21b6bc;
@ -292,6 +293,7 @@ class cocos2d::CCLabelBMFont {
virtual auto isCascadeColorEnabled() = mac 0x3493c0, ios 0x21aa3c; virtual auto isCascadeColorEnabled() = mac 0x3493c0, ios 0x21aa3c;
virtual auto setCascadeColorEnabled(bool) = mac 0x3493e0, ios 0x21aa4c; virtual auto setCascadeColorEnabled(bool) = mac 0x3493e0, ios 0x21aa4c;
virtual auto setString(unsigned short*, bool) = mac 0x348a60, ios 0x21a4b4; virtual auto setString(unsigned short*, bool) = mac 0x348a60, ios 0x21a4b4;
virtual ~CCLabelBMFont() = mac 0x347e80;
} }
class cocos2d::CCLabelTTF { class cocos2d::CCLabelTTF {
@ -657,6 +659,7 @@ class cocos2d::CCRenderTexture {
auto end() = mac 0x35d2c0; auto end() = mac 0x35d2c0;
static cocos2d::CCRenderTexture* create(int, int, cocos2d::CCTexture2DPixelFormat) = mac 0x35c720; static cocos2d::CCRenderTexture* create(int, int, cocos2d::CCTexture2DPixelFormat) = mac 0x35c720;
auto newCCImage(bool) = mac 0x35d7d0; auto newCCImage(bool) = mac 0x35d7d0;
auto saveToFile(char const*) = mac 0x35dab0;
} }
class cocos2d::CCRepeat { class cocos2d::CCRepeat {

View file

@ -1,4 +1,5 @@
// geode additions to make stl containers easier // geode additions to make stl containers easier
// clang-format off
class GDString { class GDString {
void winDtor() = win 0xf6e0; void winDtor() = win 0xf6e0;
char const* winCStr() = win 0xf710; char const* winCStr() = win 0xf710;
@ -16,7 +17,6 @@ class GDString {
void macDestroy() = mac 0x489f78; void macDestroy() = mac 0x489f78;
} }
class AchievementBar : cocos2d::CCNodeRGBA { class AchievementBar : cocos2d::CCNodeRGBA {
static AchievementBar* create(const char* title, const char* desc, const char* icon, bool quest) = mac 0x379f80, win 0x3b120, ios 0x1a4784; static AchievementBar* create(const char* title, const char* desc, const char* icon, bool quest) = mac 0x379f80, win 0x3b120, ios 0x1a4784;
@ -1104,6 +1104,17 @@ class EditorOptionsLayer {
} }
class EditorPauseLayer : CCBlockLayer, FLAlertLayerProtocol { class EditorPauseLayer : CCBlockLayer, FLAlertLayerProtocol {
static EditorPauseLayer* get() {
if (!EditorUI::get()) return nullptr;
auto editor = LevelEditorLayer::get();
for (auto i = 0; i < editor->getChildrenCount(); ++i) {
if (auto layer = cast::safe_cast<EditorPauseLayer*>(editor->getChildren()->objectAtIndex(i))) {
return layer;
}
}
return nullptr;
}
static EditorPauseLayer* create(LevelEditorLayer* editor) { static EditorPauseLayer* create(LevelEditorLayer* editor) {
auto pRet = new EditorPauseLayer(); auto pRet = new EditorPauseLayer();
if (pRet && pRet->init(editor)) { if (pRet && pRet->init(editor)) {
@ -2105,6 +2116,14 @@ class GJGameLevel : cocos2d::CCNode {
GJDifficulty getAverageDifficulty() = win 0xbd9b0; GJDifficulty getAverageDifficulty() = win 0xbd9b0;
gd::string getUnpackedLevelDescription() = win 0xbf890; gd::string getUnpackedLevelDescription() = win 0xbf890;
static GJGameLevel* getCurrent() {
auto playLayer = PlayLayer::get();
if (playLayer) return playLayer->m_level;
auto editorLayer = LevelEditorLayer::get();
if (editorLayer) return editorLayer->m_level;
return nullptr;
}
cocos2d::CCDictionary* m_lastBuildSave; cocos2d::CCDictionary* m_lastBuildSave;
int m_levelIDRand; int m_levelIDRand;
int m_levelIDSeed; int m_levelIDSeed;
@ -3550,10 +3569,10 @@ class LevelCell : TableViewCell {
} }
class LevelCommentDelegate { class LevelCommentDelegate {
virtual void loadCommentsFinished(cocos2d::CCArray *, const char*) {} virtual void loadCommentsFinished(cocos2d::CCArray*, char const*) {}
virtual void loadCommentsFailed(const char*) {} virtual void loadCommentsFailed(char const*) {}
virtual void updateUserScoreFinished() {} virtual void updateUserScoreFinished() {}
virtual void setupPageInfo(gd::string, const char*) {} virtual void setupPageInfo(gd::string, char const*) {}
} }
class LevelDeleteDelegate { class LevelDeleteDelegate {
@ -3855,6 +3874,12 @@ class LevelSettingsObject : cocos2d::CCNode {
static LevelSettingsObject* objectFromString(gd::string) = mac 0x945a0, win 0x16f440; static LevelSettingsObject* objectFromString(gd::string) = mac 0x945a0, win 0x16f440;
void setupColorsFromLegacyMode(cocos2d::CCDictionary*) = mac 0xa6a30, win 0x170050; void setupColorsFromLegacyMode(cocos2d::CCDictionary*) = mac 0xa6a30, win 0x170050;
static LevelSettingsObject* get() {
auto baseLayer = GJBaseGameLayer::get();
if (baseLayer) return baseLayer->m_levelSettings;
return nullptr;
}
gd::string getSaveString() = mac 0x979c0, win 0x16ebf0; gd::string getSaveString() = mac 0x979c0, win 0x16ebf0;
GJEffectManager* m_effectManager; GJEffectManager* m_effectManager;
@ -4035,7 +4060,7 @@ class MusicDownloadManager : cocos2d::CCNode, PlatformDownloadDelegate {
cocos2d::CCDictionary* m_unknownDict; cocos2d::CCDictionary* m_unknownDict;
cocos2d::CCArray* m_handlers; cocos2d::CCArray* m_handlers;
cocos2d::CCDictionary* m_songsDict; cocos2d::CCDictionary* m_songsDict;
int m_unknown; int m_priority;
} }
class NumberInputDelegate { class NumberInputDelegate {
@ -5190,6 +5215,19 @@ class TableView : CCScrollLayerExt, CCScrollLayerExtDelegate {
static TableView* create(TableViewDelegate*, TableViewDataSource*, cocos2d::CCRect) = mac 0x37eb30, win 0x30ed0; static TableView* create(TableViewDelegate*, TableViewDataSource*, cocos2d::CCRect) = mac 0x37eb30, win 0x30ed0;
void reloadData() = mac 0x37f970, win 0x317e0; void reloadData() = mac 0x37f970, win 0x317e0;
virtual void onEnter() = mac 0x37ff30, ios 0x21dcac;
virtual void onExit() = mac 0x37ff40, ios 0x21dcb0;
virtual bool ccTouchBegan(cocos2d::CCTouch*, cocos2d::CCEvent*) = mac 0x380120, ios 0x21de24, win 0x31de0;
virtual void ccTouchMoved(cocos2d::CCTouch*, cocos2d::CCEvent*) = mac 0x380be0, ios 0x21e5e8;
virtual void ccTouchEnded(cocos2d::CCTouch*, cocos2d::CCEvent*) = mac 0x3809a0, ios 0x21e46c;
virtual void ccTouchCancelled(cocos2d::CCTouch*, cocos2d::CCEvent*) = mac 0x380b20, ios 0x21e580;
virtual void registerWithTouchDispatcher() = mac 0x37ff50, ios 0x21dcb4;
virtual void scrollWheel(float, float) = mac 0x380cd0, ios 0x21e6b4;
virtual void scrllViewWillBeginDecelerating(CCScrollLayerExt*) = mac 0x3818a0, ios 0x21efd4;
virtual void scrollViewDidEndDecelerating(CCScrollLayerExt*) = mac 0x3818c0, ios 0x21efdc;
virtual void scrollViewTouchMoving(CCScrollLayerExt*) = mac 0x3818e0, ios 0x21efe4;
virtual void scrollViewDidEndMoving(CCScrollLayerExt*) = mac 0x381900, ios 0x21efec;
bool m_touchOutOfBoundary; bool m_touchOutOfBoundary;
cocos2d::CCTouch* m_touchStart; cocos2d::CCTouch* m_touchStart;
cocos2d::CCPoint m_touchStartPosition2; cocos2d::CCPoint m_touchStartPosition2;
@ -5261,7 +5299,7 @@ class TeleportPortalObject : GameObject {
bool m_teleportEase; bool m_teleportEase;
} }
class TextAlertPopup { class TextAlertPopup : cocos2d::CCNode {
static TextAlertPopup* create(gd::string const& text, float time, float scale) = win 0x1450b0; static TextAlertPopup* create(gd::string const& text, float time, float scale) = win 0x1450b0;
} }
@ -5372,4 +5410,4 @@ class VideoOptionsLayer : FLAlertLayer {
class LevelTools { class LevelTools {
static gd::string base64DecodeString(gd::string) = mac 0x294510, win 0x18b3b0; static gd::string base64DecodeString(gd::string) = mac 0x294510, win 0x18b3b0;
} }
// clang-format on

View file

@ -1,25 +1,27 @@
#include "Shared.hpp" #include "Shared.hpp"
#include "TypeOpt.hpp" #include "TypeOpt.hpp"
#include <iostream> #include <iostream>
#include <set> #include <set>
namespace { namespace format_strings { namespace {
char const* wrap_start = R"GEN( namespace format_strings {
char const* wrap_start = R"GEN(
namespace wrap { namespace wrap {
)GEN"; )GEN";
char const* wrap_declare_identifier = R"GEN( char const* wrap_declare_identifier = R"GEN(
#ifndef GEODE_WRAP_{function_name} #ifndef GEODE_WRAP_{function_name}
#define GEODE_WRAP_{function_name} #define GEODE_WRAP_{function_name}
GEODE_WRAPPER_FOR_IDENTIFIER({function_name}) GEODE_WRAPPER_FOR_IDENTIFIER({function_name})
#endif #endif
)GEN"; )GEN";
char const* wrap_end = R"GEN( char const* wrap_end = R"GEN(
} }
)GEN"; )GEN";
// requires: class_name // requires: class_name
char const* modify_start = R"GEN(#pragma once char const* modify_start = R"GEN(#pragma once
#include <Geode/modify/Modify.hpp> #include <Geode/modify/Modify.hpp>
#include <Geode/modify/Field.hpp> #include <Geode/modify/Field.hpp>
#include <Geode/modify/InternalMacros.hpp> #include <Geode/modify/InternalMacros.hpp>
@ -28,100 +30,89 @@ using namespace geode::modifier;
namespace geode::modifier {{ namespace geode::modifier {{
{wrap} {wrap}
template<class Derived> template<class Derived>
struct Modify<Derived, {class_name}> : ModifyBase<Modify<Derived, {class_name}>> {{ struct ModifyDerive<Derived, {class_name}> : ModifyBase<ModifyDerive<Derived, {class_name}>> {{
using ModifyBase<Modify<Derived, {class_name}>>::ModifyBase; using ModifyBase<ModifyDerive<Derived, {class_name}>>::ModifyBase;
using Base = {class_name}; using Base = {class_name};
static void apply() {{ static void apply() {{
using namespace geode::core::meta; using namespace geode::core::meta;
)GEN"; )GEN";
// requires: index, class_name, arg_types, function_name, raw_arg_types, non_virtual // requires: index, class_name, arg_types, function_name, raw_arg_types, non_virtual
char const* apply_function = R"GEN( char const* apply_function = R"GEN(
GEODE_APPLY_MODIFY_FOR_FUNCTION({addr_index}, {pure_index}, {function_convention}, {class_name}, {function_name}))GEN"; GEODE_APPLY_MODIFY_FOR_FUNCTION({addr_index}, {pure_index}, {function_convention}, {class_name}, {function_name}))GEN";
char const* modify_end = R"GEN( char const* modify_end = R"GEN(
} }
}; };
} }
)GEN"; )GEN";
char const* modify_include = R"GEN(#include "modify/{file_name}" char const* modify_include = R"GEN(#include "modify/{file_name}"
)GEN"; )GEN";
}} }
}
std::string generateModifyHeader(Root& root, ghc::filesystem::path const& singleFolder) { std::string generateModifyHeader(Root& root, ghc::filesystem::path const& singleFolder) {
std::string output; std::string output;
TypeBank bank; TypeBank bank;
bank.loadFrom(root); bank.loadFrom(root);
for (auto c : root.classes) { for (auto c : root.classes) {
if (c.name == "cocos2d") if (c.name == "cocos2d") continue;
continue;
std::string filename = (codegen::getUnqualifiedClassName(c.name) + ".hpp"); std::string filename = (codegen::getUnqualifiedClassName(c.name) + ".hpp");
output += fmt::format(format_strings::modify_include, output += fmt::format(format_strings::modify_include, fmt::arg("file_name", filename));
fmt::arg("file_name", filename)
);
std::string single_output; std::string single_output;
std::string wrap; std::string wrap;
// wrap // wrap
wrap += format_strings::wrap_start; wrap += format_strings::wrap_start;
std::set<std::string> used; std::set<std::string> used;
for (auto& f : c.fields) { for (auto& f : c.fields) {
if (auto fn = f.get_fn()) { if (auto fn = f.get_fn()) {
if (fn->type == FunctionType::Normal && !used.count(fn->name)) { if (fn->type == FunctionType::Normal && !used.count(fn->name)) {
used.insert(fn->name); used.insert(fn->name);
wrap += fmt::format(format_strings::wrap_declare_identifier, wrap += fmt::format(
fmt::arg("function_name", fn->name) format_strings::wrap_declare_identifier, fmt::arg("function_name", fn->name)
); );
} }
} }
} }
wrap += format_strings::wrap_end; wrap += format_strings::wrap_end;
single_output += fmt::format(format_strings::modify_start, single_output += fmt::format(
fmt::arg("class_name", c.name), format_strings::modify_start, fmt::arg("class_name", c.name), fmt::arg("wrap", wrap)
fmt::arg("wrap", wrap) );
);
// modify // modify
for (auto& f : c.fields) { for (auto& f : c.fields) {
if (codegen::getStatus(f) != BindStatus::Unbindable) { if (codegen::getStatus(f) != BindStatus::Unbindable) {
auto begin = f.get_fn(); auto begin = f.get_fn();
std::string function_name; std::string function_name;
switch (begin->type) { switch (begin->type) {
case FunctionType::Normal: case FunctionType::Normal: function_name = begin->name; break;
function_name = begin->name; case FunctionType::Ctor: function_name = "constructor"; break;
break; case FunctionType::Dtor: function_name = "destructor"; break;
case FunctionType::Ctor: }
function_name = "constructor";
break;
case FunctionType::Dtor:
function_name = "destructor";
break;
}
single_output += fmt::format(format_strings::apply_function, single_output += fmt::format(
fmt::arg("addr_index", f.field_id), format_strings::apply_function, fmt::arg("addr_index", f.field_id),
fmt::arg("pure_index", bank.getPure(*begin, c.name)), fmt::arg("pure_index", bank.getPure(*begin, c.name)),
fmt::arg("class_name", c.name), fmt::arg("class_name", c.name), fmt::arg("function_name", function_name),
fmt::arg("function_name", function_name), fmt::arg("function_convention", codegen::getConvention(f))
fmt::arg("function_convention", codegen::getConvention(f)) );
); }
} }
}
single_output += format_strings::modify_end; single_output += format_strings::modify_end;
writeFile(singleFolder / filename, single_output); writeFile(singleFolder / filename, single_output);
} }
return output; return output;
} }

View file

@ -10,30 +10,84 @@ namespace geode {
class Mod; class Mod;
class Event; class Event;
enum class PassThrough : bool { enum class ListenerResult {
Propagate, Propagate,
Stop, Stop
}; };
struct GEODE_DLL BasicEventHandler { struct GEODE_DLL EventListenerProtocol {
virtual PassThrough passThrough(Event*) = 0; virtual void enable();
virtual void disable();
virtual ListenerResult passThrough(Event*) = 0;
};
virtual ~BasicEventHandler(); template <typename C, typename T>
struct to_member;
void listen();
void unlisten(); template <typename C, typename R, typename ...Args>
struct to_member<C, R(Args...)> {
using value = R(C::*)(Args...);
};
template <typename T>
concept is_event = std::is_base_of_v<Event, T>;
template <is_event T>
class EventFilter {
public:
using Callback = ListenerResult(T*);
using Event = T;
ListenerResult handle(std::function<Callback> fn, T* e) {
return fn(e);
}
};
template <typename T>
concept is_filter =
std::is_base_of_v<EventFilter<typename T::Event>, T> &&
requires(T a) {
a.handle(std::declval<typename T::Callback>(), std::declval<typename T::Event*>());
};
template <is_filter T>
class EventListener : public EventListenerProtocol {
public:
using Callback = typename T::Callback;
template <typename C> requires std::is_class_v<C>
using MemberFn = typename to_member<C, Callback>::value;
ListenerResult passThrough(Event* e) override {
if (auto myev = dynamic_cast<typename T::Event*>(e)) {
return m_filter.handle(m_callback, myev);
}
return ListenerResult::Propagate;
}
EventListener(T filter = T()) {}
EventListener(std::function<Callback> fn, T filter = T()) : m_callback(fn), m_filter(filter) {}
EventListener(Callback* fnptr, T filter = T()) : m_callback(fnptr), m_filter(filter) {}
template <class C>
EventListener(C* cls, MemberFn<C> fn, T filter = T()) : EventListener(std::bind(fn, cls, std::placeholders::_1), filter) {}
void bind(std::function<Callback> fn) {
m_callback = fn;
}
template <typename C>
void bind(C* cls, MemberFn<C> fn) {
m_callback = std::bind(fn, cls, std::placeholders::_1);
}
protected:
std::function<Callback> m_callback;
T m_filter;
}; };
class GEODE_DLL Event { class GEODE_DLL Event {
static std::unordered_set<BasicEventHandler*> s_handlers; static std::unordered_set<EventListenerProtocol*> s_listeners;
friend BasicEventHandler;
Mod* m_sender; Mod* m_sender;
friend EventListenerProtocol;
public: public:
static std::unordered_set<BasicEventHandler*> const& getHandlers();
void postFrom(Mod* sender); void postFrom(Mod* sender);
inline void post() { inline void post() {
@ -44,21 +98,4 @@ namespace geode {
virtual ~Event(); virtual ~Event();
}; };
template <typename T>
class EventHandler : public BasicEventHandler {
public:
virtual PassThrough handle(T*) = 0;
PassThrough passThrough(Event* ev) override {
if (auto myev = dynamic_cast<T*>(ev)) {
return handle(myev);
}
return PassThrough::Propagate;
}
EventHandler() {
listen();
}
};
} }

View file

@ -34,6 +34,7 @@ namespace geode {
static constexpr std::string_view GEODE_CONFIG_DIRECTORY = "config"; static constexpr std::string_view GEODE_CONFIG_DIRECTORY = "config";
static constexpr std::string_view GEODE_TEMP_DIRECTORY = "temp"; static constexpr std::string_view GEODE_TEMP_DIRECTORY = "temp";
static constexpr std::string_view GEODE_MOD_EXTENSION = ".geode"; static constexpr std::string_view GEODE_MOD_EXTENSION = ".geode";
static constexpr std::string_view GEODE_INDEX_DIRECTORY = "index";
class Mod; class Mod;
class Hook; class Hook;
@ -41,7 +42,7 @@ namespace geode {
class VersionInfo; class VersionInfo;
namespace modifier { namespace modifier {
template <class, class, class> template <class, class>
class FieldIntermediate; class FieldIntermediate;
} }
} }
@ -101,7 +102,7 @@ namespace geode {
size_t getFieldIndexForClass(size_t hash); size_t getFieldIndexForClass(size_t hash);
template <class, class, class> template <class, class>
friend class modifier::FieldIntermediate; friend class modifier::FieldIntermediate;
void updateModResources(Mod* mod); void updateModResources(Mod* mod);

View file

@ -468,11 +468,19 @@ namespace geode {
} \ } \
} }
// clang-format off
template <class T> template <class T>
T getBuiltInSettingValue(const std::shared_ptr<Setting> setting) { T getBuiltInSettingValue(const std::shared_ptr<Setting> setting) {
GEODE_INT_BUILTIN_SETTING_IF(Bool, getValue(), std::is_same_v<T, bool>) 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 { else GEODE_INT_BUILTIN_SETTING_IF(Float, getValue(), std::is_floating_point_v<T>)
static_assert(!std::is_same_v<T, T>, "todo: implement"); 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(); return T();
} }
@ -480,10 +488,17 @@ namespace geode {
template <class T> template <class T>
void setBuiltInSettingValue(const std::shared_ptr<Setting> setting, T const& value) { 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>) 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 { else GEODE_INT_BUILTIN_SETTING_IF(Float, setValue(value), std::is_floating_point_v<T>)
static_assert(!std::is_same_v<T, T>, "todo: implement"); 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) #pragma warning(pop)

View file

@ -18,41 +18,37 @@ namespace geode {
std::shared_ptr<Setting> getSetting() const; std::shared_ptr<Setting> getSetting() const;
}; };
template <class T> template <typename T = Setting, typename = std::enable_if_t<std::is_base_of_v<Setting, T>>>
class SettingChangedEventHandler : public EventHandler<SettingChangedEvent> { class SettingChangedFilter : public EventFilter<SettingChangedEvent> {
public: public:
using Consumer = void (*)(std::shared_ptr<T>); using Callback = void(std::shared_ptr<T>);
using Event = SettingChangedEvent;
static_assert(std::is_base_of_v<Setting, T>, "Setting must inherit from the Setting class"); ListenerResult handle(std::function<Callback> fn, SettingChangedEvent* event) {
protected:
Consumer m_consumer;
std::string m_modID;
std::optional<std::string> m_targetKey;
public:
PassThrough handle(SettingChangedEvent* event) override {
if (m_modID == event->getModID() && if (m_modID == event->getModID() &&
(!m_targetKey || m_targetKey.value() == event->getSetting()->getKey())) { (!m_targetKey || m_targetKey.value() == event->getSetting()->getKey())) {
m_consumer(std::static_pointer_cast<T>(event->getSetting())); fn(std::static_pointer_cast<T>(event->getSetting()));
} }
return PassThrough::Propagate; return ListenerResult::Propagate;
} }
/** /**
* Listen to changes on a specific setting * Listen to changes on a specific setting
*/ */
SettingChangedEventHandler( SettingChangedFilter(
std::string const& modID, std::string const& settingID, Consumer handler std::string const& modID, std::string const& settingID
) : ) :
m_modID(modID), m_modID(modID),
m_targetKey(settingID), m_consumer(handler) {} m_targetKey(settingID) {}
/** /**
* Listen to changes on all of a mods' settings * Listen to changes on all of a mods' settings
*/ */
SettingChangedEventHandler(std::string const& modID, Consumer handler) : SettingChangedFilter(std::string const& modID) :
m_modID(modID), m_targetKey(std::nullopt), m_consumer(handler) {} m_modID(modID), m_targetKey(std::nullopt) {}
protected:
std::string m_modID;
std::optional<std::string> m_targetKey;
}; };
template <class T> template <class T>
@ -61,14 +57,14 @@ namespace geode {
std::string const& settingID, void (*callback)(std::shared_ptr<T>) std::string const& settingID, void (*callback)(std::shared_ptr<T>)
) { ) {
Loader::get()->scheduleOnModLoad(getMod(), [=]() { Loader::get()->scheduleOnModLoad(getMod(), [=]() {
static SettingChangedEventHandler<T> _(getMod()->getID(), settingID, callback); static auto _ = EventListener(callback, SettingChangedFilter<T>(getMod()->getID(), settingID));
}); });
return std::monostate(); return std::monostate();
} }
static std::monostate listenForAllSettingChanges(void (*callback)(std::shared_ptr<Setting>)) { static std::monostate listenForAllSettingChanges(void (*callback)(std::shared_ptr<Setting>)) {
Loader::get()->scheduleOnModLoad(getMod(), [=]() { Loader::get()->scheduleOnModLoad(getMod(), [=]() {
static SettingChangedEventHandler<Setting> _(getMod()->getID(), callback); static auto _ = EventListener(callback, SettingChangedFilter(getMod()->getID()));
}); });
return std::monostate(); return std::monostate();
} }

View file

@ -42,7 +42,7 @@ namespace geode::modifier {
} }
}; };
template <class Base, class Intermediate, class Parent> template <class Parent, class Base>
class FieldIntermediate { class FieldIntermediate {
// Padding used for guaranteeing any member of parents // Padding used for guaranteeing any member of parents
// will be in between sizeof(Intermediate) and sizeof(Parent) // will be in between sizeof(Intermediate) and sizeof(Parent)
@ -54,22 +54,22 @@ namespace geode::modifier {
auto parent = new (parentContainer.data()) Parent(); auto parent = new (parentContainer.data()) Parent();
parent->Intermediate::~Intermediate(); parent->Base::~Base();
std::memcpy( std::memcpy(
offsetField, std::launder(&parentContainer[sizeof(Intermediate)]), offsetField, std::launder(&parentContainer[sizeof(Base)]),
sizeof(Parent) - sizeof(Intermediate) sizeof(Parent) - sizeof(Base)
); );
} }
static void fieldDestructor(void* offsetField) { static void fieldDestructor(void* offsetField) {
std::array<std::byte, sizeof(Parent)> parentContainer; std::array<std::byte, sizeof(Parent)> parentContainer;
auto parent = new (parentContainer.data()) Intermediate(); auto parent = new (parentContainer.data()) Base();
std::memcpy( std::memcpy(
std::launder(&parentContainer[sizeof(Intermediate)]), offsetField, std::launder(&parentContainer[sizeof(Base)]), offsetField,
sizeof(Parent) - sizeof(Intermediate) sizeof(Parent) - sizeof(Base)
); );
static_cast<Parent*>(parent)->Parent::~Parent(); static_cast<Parent*>(parent)->Parent::~Parent();
@ -87,15 +87,14 @@ namespace geode::modifier {
auto offsetField = container->getField(index); auto offsetField = container->getField(index);
if (!offsetField) { if (!offsetField) {
offsetField = container->setField( offsetField = container->setField(
index, sizeof(Parent) - sizeof(Intermediate), index, sizeof(Parent) - sizeof(Base), &FieldIntermediate::fieldDestructor
&FieldIntermediate::fieldDestructor
); );
FieldIntermediate::fieldConstructor(offsetField); FieldIntermediate::fieldConstructor(offsetField);
} }
return reinterpret_cast<Parent*>( return reinterpret_cast<Parent*>(
reinterpret_cast<std::byte*>(offsetField) - sizeof(Intermediate) reinterpret_cast<std::byte*>(offsetField) - sizeof(Base)
); );
} }
}; };

View file

@ -8,15 +8,9 @@
* struct hook0 {}; * struct hook0 {};
* namespace { * namespace {
* struct hook0Parent {}; * struct hook0Parent {};
* Modify<hook0<hook0Parent>, MenuLayer> hook0Apply;
* struct GEODE_HIDDEN hook0Intermediate: public MenuLayer {
* geode::modifier::FieldIntermediate<MenuLayer,
* hook0<hook0Intermediate>, hook0<hook0Parent>
* > m_fields;
* };
* } * }
* template<> * template<>
* struct GEODE_HIDDEN hook0<hook0Parent>: hook0Intermediate { * struct GEODE_HIDDEN hook0<hook0Parent> : Modify<hook0<hook0Parent>, MenuLayer> {
* // code stuff idk * // code stuff idk
* }; * };
* *
@ -24,33 +18,19 @@
* I am bad at this stuff * I am bad at this stuff
*/ */
#define GEODE_MODIFY_DECLARE_ANONYMOUS(base, derived) \ #define GEODE_MODIFY_DECLARE_ANONYMOUS(base, derived) \
derived##Dummy; \ derived##Dummy; \
template <class> \ template <class> \
struct derived {}; \ struct derived {}; \
namespace { \ namespace { \
struct derived##Parent {}; \ struct derived##Parent {}; \
Modify<derived<derived##Parent>, base> derived##Apply; \ } \
struct GEODE_HIDDEN derived##Intermediate : base { \ template <> \
mutable geode::modifier::FieldIntermediate< \ struct GEODE_HIDDEN derived<derived##Parent> : geode::Modify<derived<derived##Parent>, base>
base, derived##Intermediate, derived<derived##Parent>> \
m_fields; \
}; \
} \
template <> \
struct GEODE_HIDDEN derived<derived##Parent> : derived##Intermediate
#define GEODE_MODIFY_DECLARE(base, derived) \ #define GEODE_MODIFY_DECLARE(base, derived) \
derived##Dummy; \ derived##Dummy; \
struct derived; \ struct GEODE_HIDDEN derived : geode::Modify<derived, base>
namespace { \
Modify<derived, base> derived##Apply; \
struct GEODE_HIDDEN derived##Intermediate : base { \
mutable geode::modifier::FieldIntermediate<base, derived##Intermediate, derived> \
m_fields; \
}; \
} \
struct GEODE_HIDDEN derived : derived##Intermediate
#define GEODE_MODIFY_REDIRECT4(base, derived) GEODE_MODIFY_DECLARE(base, derived) #define GEODE_MODIFY_REDIRECT4(base, derived) GEODE_MODIFY_DECLARE(base, derived)
#define GEODE_MODIFY_REDIRECT3(base, derived) GEODE_MODIFY_DECLARE_ANONYMOUS(base, derived) #define GEODE_MODIFY_REDIRECT3(base, derived) GEODE_MODIFY_DECLARE_ANONYMOUS(base, derived)

View file

@ -1,6 +1,7 @@
#pragma once #pragma once
#include "../meta/meta.hpp" #include "../meta/meta.hpp"
#include "Addresses.hpp" #include "Addresses.hpp"
#include "Field.hpp"
#include "Types.hpp" #include "Types.hpp"
#include "Wrapper.hpp" #include "Wrapper.hpp"
@ -22,7 +23,7 @@
namespace geode::modifier { namespace geode::modifier {
template <class Derived, class Base> template <class Derived, class Base>
class Modify; class ModifyDerive;
template <class Derived> template <class Derived>
class ModifyBase { class ModifyBase {
@ -34,15 +35,29 @@ namespace geode::modifier {
}); });
} }
template <class, class> template <class, class>
friend class Modify; friend class ModifyDerive;
// explicit Modify(Property property) idea // explicit Modify(Property property) idea
}; };
template <class Derived, class Base> template <class Derived, class Base>
class Modify { class ModifyDerive {
public: public:
Modify() { ModifyDerive() {
static_assert(core::meta::always_false<Derived>, "Custom Modify not implemented."); static_assert(core::meta::always_false<Derived>, "Custom Modify not implemented.");
} }
}; };
} }
namespace geode {
template <class Derived, class Base>
class Modify : public Base {
private:
static inline modifier::ModifyDerive<Derived, Base> s_apply;
// because for some reason we need it
static inline auto s_applyRef = &Modify::s_apply;
public:
modifier::FieldIntermediate<Derived, Base> m_fields;
};
}

View file

@ -101,9 +101,32 @@ namespace geode {
bool operator==(T* other) const { bool operator==(T* other) const {
return m_obj == other; return m_obj == other;
} }
bool operator==(Ref<T> const& other) const { bool operator==(Ref<T> const& other) const {
return m_obj == other.m_obj; 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());
}
}; };
} }

View file

@ -60,4 +60,9 @@ namespace geode {
std::string toString() const; std::string toString() const;
}; };
inline std::ostream& operator<<(std::ostream& stream, VersionInfo const& version) {
stream << version.toString();
return stream;
}
} }

View file

@ -9,7 +9,7 @@ namespace geode::cast {
/** /**
* Alias for static_cast * Alias for static_cast
*/ */
template <typename T, typename F> template <class T, class F>
static constexpr T as(F const v) { static constexpr T as(F const v) {
return static_cast<T>(v); return static_cast<T>(v);
} }
@ -18,8 +18,8 @@ namespace geode::cast {
* Cast from anything to anything else, * Cast from anything to anything else,
* provided they are the same size * provided they are the same size
*/ */
template <typename T, typename F> template <class T, class F>
static constexpr T union_cast(F v) { static constexpr T union_cast(F const v) {
static_assert(sizeof(F) == sizeof(T), "union_cast: R and T don't match in size!"); static_assert(sizeof(F) == sizeof(T), "union_cast: R and T don't match in size!");
union { union {
@ -36,27 +36,40 @@ namespace geode::cast {
* cast but uses reference syntactic sugar to * cast but uses reference syntactic sugar to
* look cleaner. * look cleaner.
*/ */
template <typename T, typename F> template <class T, class F>
static constexpr T reference_cast(F v) { static constexpr T reference_cast(F v) {
return reinterpret_cast<T&>(v); return reinterpret_cast<T&>(v);
} }
/** /**
* Cast an adjusted this pointer to it's base pointer * Cast based on RTTI. Casts an adjusted this pointer
* to it's non offset form.
*/ */
template <typename T, typename F> template <class T, class F>
static constexpr T base_cast(F obj) { static constexpr T base_cast(F const obj) {
return reinterpret_cast<T>(dynamic_cast<void*>(obj)); return static_cast<T>(dynamic_cast<void*>(obj));
} }
/** /**
* Cast based on RTTI. This is a replacement for * Cast based on RTTI. This is used to check
* dynamic_cast, since it doesn't work for gd. * if an object is exactly the class needed. Returns
* nullptr on failure.
*/ */
template <typename T, typename F> template <class T, class F>
static T typeid_cast(F obj) { static T exact_cast(F const obj) {
if (std::string(typeid(*obj).name()) == typeid(std::remove_pointer_t<T>).name()) if (std::strcmp(typeid(*obj).name(), typeid(std::remove_pointer_t<T>).name()) == 0) {
return reinterpret_cast<T>(obj); return base_cast<T>(obj);
else return nullptr; }
return nullptr;
}
/**
* Cast based on RTTI. This behaves as a replacement
* of dynamic_cast for cocos and gd classes,
* and must be used for expected results.
*/
template <class T, class F>
static T safe_cast(F const obj) {
return typeinfo_cast<T>(obj);
} }
} }

View file

@ -86,6 +86,7 @@ namespace geode::utils::web {
mutable std::mutex m_mutex; mutable std::mutex m_mutex;
std::variant<std::monostate, std::ostream*, ghc::filesystem::path> m_target = std::variant<std::monostate, std::ostream*, ghc::filesystem::path> m_target =
std::monostate(); std::monostate();
std::vector<std::string> m_httpHeaders;
template <class T> template <class T>
friend class AsyncWebResult; friend class AsyncWebResult;
@ -134,6 +135,7 @@ namespace geode::utils::web {
AsyncCancelled m_cancelled = nullptr; AsyncCancelled m_cancelled = nullptr;
bool m_sent = false; bool m_sent = false;
std::variant<std::monostate, std::ostream*, ghc::filesystem::path> m_target; std::variant<std::monostate, std::ostream*, ghc::filesystem::path> m_target;
std::vector<std::string> m_httpHeaders;
template <class T> template <class T>
friend class AsyncWebResult; friend class AsyncWebResult;
@ -159,6 +161,12 @@ namespace geode::utils::web {
* @returns Same AsyncWebRequest * @returns Same AsyncWebRequest
*/ */
AsyncWebRequest& join(std::string const& requestID); AsyncWebRequest& join(std::string const& requestID);
/**
* In order to specify a http header to the request, give it here.
* Can be called more than once.
*/
AsyncWebRequest& header(std::string const& header);
/** /**
* URL to fetch from the internet asynchronously * URL to fetch from the internet asynchronously
* @param url URL of the data to download. Redirects will be * @param url URL of the data to download. Redirects will be

View file

@ -28,6 +28,15 @@ namespace geode {
return rect; 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) { static cocos2d::CCPoint operator/=(cocos2d::CCPoint& pos, float div) {
pos.x /= div; pos.x /= div;
pos.y /= div; pos.y /= div;

View file

@ -4,6 +4,9 @@
#include <algorithm> #include <algorithm>
#include <string> #include <string>
#undef min
#undef max
namespace geode::utils::ranges { namespace geode::utils::ranges {
template <class C> template <class C>
concept ValidConstContainer = requires(C const& c) { concept ValidConstContainer = requires(C const& c) {
@ -205,4 +208,57 @@ namespace geode::utils::ranges {
std::transform(from.begin(), from.end(), res.end(), mapper); std::transform(from.begin(), from.end(), res.end(), mapper);
return res; 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);
}
} }

View file

@ -1,7 +1,7 @@
{ {
"geode": "v@PROJECT_VERSION@", "geode": "@PROJECT_VERSION@",
"id": "geode.loader", "id": "geode.loader",
"version": "v@PROJECT_VERSION@", "version": "@PROJECT_VERSION@",
"name": "Geode", "name": "Geode",
"developer": "Geode Team", "developer": "Geode Team",
"description": "The Geode mod loader", "description": "The Geode mod loader",

View file

@ -68,7 +68,7 @@ public:
// proxy forwards // proxy forwards
// clang-format off // clang-format off
#include <Geode/modify/CCNode.hpp> #include <Geode/modify/CCNode.hpp>
class $modify(ProxyCCNode, CCNode) { struct ProxyCCNode : Modify<ProxyCCNode, CCNode> {
virtual CCObject* getUserObject() { virtual CCObject* getUserObject() {
return GeodeNodeMetadata::set(this)->m_userObject; return GeodeNodeMetadata::set(this)->m_userObject;
} }

View file

@ -9,9 +9,10 @@
USE_GEODE_NAMESPACE(); USE_GEODE_NAMESPACE();
#pragma warning(disable : 4217) #pragma warning(disable : 4217)
template <class T = CCNode>
requires std::is_base_of_v<CCNode, T> T* setIDSafe(CCNode* node, int index, char const* id) { template <class T = CCNode>
requires std::is_base_of_v<CCNode, T>
T* setIDSafe(CCNode* node, int index, char const* id) {
if constexpr (std::is_same_v<CCNode, T>) { if constexpr (std::is_same_v<CCNode, T>) {
if (auto child = getChild(node, index)) { if (auto child = getChild(node, index)) {
child->setID(id); child->setID(id);
@ -29,7 +30,7 @@ requires std::is_base_of_v<CCNode, T> T* setIDSafe(CCNode* node, int index, char
// clang-format off // clang-format off
#include <Geode/modify/LevelSearchLayer.hpp> #include <Geode/modify/LevelSearchLayer.hpp>
class $modify(LevelSearchLayer) { struct LevelSearchLayerIDs : Modify<LevelSearchLayerIDs, LevelSearchLayer> {
bool init() { bool init() {
if (!LevelSearchLayer::init()) if (!LevelSearchLayer::init())
return false; return false;
@ -99,4 +100,5 @@ class $modify(LevelSearchLayer) {
return true; return true;
} }
}; };
// clang-format on // clang-format on

View file

@ -3,26 +3,23 @@
USE_GEODE_NAMESPACE(); USE_GEODE_NAMESPACE();
// clang-format off
#include <Geode/modify/LoadingLayer.hpp> #include <Geode/modify/LoadingLayer.hpp>
class $modify(CustomLoadingLayer, LoadingLayer) {
struct CustomLoadingLayer : Modify<CustomLoadingLayer, LoadingLayer> {
bool m_updatingResources; bool m_updatingResources;
CustomLoadingLayer() : m_updatingResources(false) {} CustomLoadingLayer() : m_updatingResources(false) {}
bool init(bool fromReload) { bool init(bool fromReload) {
if (!LoadingLayer::init(fromReload)) if (!LoadingLayer::init(fromReload)) return false;
return false;
auto winSize = CCDirector::sharedDirector()->getWinSize(); auto winSize = CCDirector::sharedDirector()->getWinSize();
auto count = Loader::get()->getAllMods().size(); auto count = Loader::get()->getAllMods().size();
auto label = CCLabelBMFont::create( auto label = CCLabelBMFont::create(
CCString::createWithFormat( CCString::createWithFormat("Geode: Loaded %lu mods", static_cast<unsigned long>(count))
"Geode: Loaded %lu mods", ->getCString(),
static_cast<unsigned long>(count)
)->getCString(),
"goldFont.fnt" "goldFont.fnt"
); );
label->setPosition(winSize.width / 2, 30.f); label->setPosition(winSize.width / 2, 30.f);
@ -31,13 +28,10 @@ class $modify(CustomLoadingLayer, LoadingLayer) {
this->addChild(label); this->addChild(label);
// verify loader resources // verify loader resources
if (!InternalLoader::get()->verifyLoaderResources( if (!InternalLoader::get()->verifyLoaderResources(std::bind(
std::bind( &CustomLoadingLayer::updateResourcesProgress, this, std::placeholders::_1,
&CustomLoadingLayer::updateResourcesProgress, this, std::placeholders::_2, std::placeholders::_3
std::placeholders::_1, std::placeholders::_2, ))) {
std::placeholders::_3
)
)) {
// auto bg = CCScale9Sprite::create( // auto bg = CCScale9Sprite::create(
// "square02b_001.png", { 0.0f, 0.0f, 80.0f, 80.0f } // "square02b_001.png", { 0.0f, 0.0f, 80.0f, 80.0f }
// ); // );
@ -73,17 +67,10 @@ class $modify(CustomLoadingLayer, LoadingLayer) {
// ); // );
} }
void updateResourcesProgress( void updateResourcesProgress(UpdateStatus status, std::string const& info, uint8_t progress) {
UpdateStatus status,
std::string const& info,
uint8_t progress
) {
switch (status) { switch (status) {
case UpdateStatus::Progress: { case UpdateStatus::Progress: {
this->setUpdateText( this->setUpdateText("Downloading Resources: " + std::to_string(progress) + "%");
"Downloading Resources: " +
std::to_string(progress) + "%"
);
} break; } break;
case UpdateStatus::Finished: { case UpdateStatus::Finished: {
@ -95,9 +82,10 @@ class $modify(CustomLoadingLayer, LoadingLayer) {
case UpdateStatus::Failed: { case UpdateStatus::Failed: {
InternalLoader::platformMessageBox( InternalLoader::platformMessageBox(
"Error updating resources", "Error updating resources",
"Unable to update Geode resources: " + info + ".\n" "Unable to update Geode resources: " + info +
"The game will be loaded as normal, but please be aware " ".\n"
"that it may very likely crash." "The game will be loaded as normal, but please be aware "
"that it may very likely crash."
); );
this->setUpdateText("Resource Download Failed"); this->setUpdateText("Resource Download Failed");
m_fields->m_updatingResources = false; m_fields->m_updatingResources = false;
@ -106,12 +94,10 @@ class $modify(CustomLoadingLayer, LoadingLayer) {
} }
} }
void loadAssets() { void loadAssets() {
if (m_fields->m_updatingResources) { if (m_fields->m_updatingResources) {
return; return;
} }
LoadingLayer::loadAssets(); LoadingLayer::loadAssets();
} }
}; };
// clang-format on

View file

@ -64,7 +64,7 @@ static void updateIndexProgress(UpdateStatus status, std::string const& info, ui
} }
#include <Geode/modify/MenuLayer.hpp> #include <Geode/modify/MenuLayer.hpp>
class $modify(CustomMenuLayer, MenuLayer) { class CustomMenuLayer : Modify<CustomMenuLayer, MenuLayer> {
void destructor() { void destructor() {
g_geodeButton = nullptr; g_geodeButton = nullptr;
MenuLayer::~MenuLayer(); MenuLayer::~MenuLayer();
@ -160,4 +160,3 @@ class $modify(CustomMenuLayer, MenuLayer) {
ModListLayer::scene(); ModListLayer::scene();
} }
}; };
// clang-format on

View file

@ -1,15 +1,15 @@
USE_GEODE_NAMESPACE(); USE_GEODE_NAMESPACE();
// clang-format off
#include <Geode/modify/CCTouchDispatcher.hpp> #include <Geode/modify/CCTouchDispatcher.hpp>
class $modify(CCTouchDispatcher) {
void addTargetedDelegate(CCTouchDelegate *delegate, int priority, bool swallowsTouches) { struct ForcePrioRevert : Modify<ForcePrioRevert, CCTouchDispatcher> {
void addTargetedDelegate(CCTouchDelegate* delegate, int priority, bool swallowsTouches) {
m_bForcePrio = false; m_bForcePrio = false;
if (m_pTargetedHandlers->count() > 0) { if (m_pTargetedHandlers->count() > 0) {
auto handler = static_cast<CCTouchHandler*>(m_pTargetedHandlers->objectAtIndex(0)); auto handler = static_cast<CCTouchHandler*>(m_pTargetedHandlers->objectAtIndex(0));
priority = handler->getPriority() - 2; priority = handler->getPriority() - 2;
} }
CCTouchDispatcher::addTargetedDelegate(delegate, priority, swallowsTouches); CCTouchDispatcher::addTargetedDelegate(delegate, priority, swallowsTouches);
} }
@ -21,4 +21,3 @@ class $modify(CCTouchDispatcher) {
m_bForcePrio = false; m_bForcePrio = false;
} }
}; };
// clang-format on

View file

@ -1,14 +1,12 @@
// this is the fix for the dynamic_cast problems // this is the fix for the dynamic_cast problems
// TODO: completely replace dynamic_cast on macos // TODO: completely replace dynamic_cast on macos
#include <Geode/DefaultInclude.hpp>
using namespace cocos2d;
using namespace geode::modifier;
#if defined(GEODE_IS_IOS) || defined(GEODE_IS_MACOS) #if defined(GEODE_IS_IOS) || defined(GEODE_IS_MACOS)
using namespace geode::cast; USE_GEODE_NAMESPACE();
#define HandlerFixFor(CCUtility) \ #define HandlerFixFor(CCUtility) \
class $modify(CCUtility##HandlerTypeinfoFix, CCUtility##Handler) { \ struct CCUtility##HandlerFix : Modify<CCUtility##HandlerFix, CCUtility##Handler> { \
void destructor() { \ void destructor() { \
if (m_pDelegate) { \ if (m_pDelegate) { \
cocos2d::CCObject* pObject = base_cast<cocos2d::CCObject*>(m_pDelegate); \ cocos2d::CCObject* pObject = base_cast<cocos2d::CCObject*>(m_pDelegate); \
@ -55,7 +53,7 @@ HandlerFixFor(CCKeyboard);
HandlerFixFor(CCMouse); HandlerFixFor(CCMouse);
#include <Geode/modify/CCTargetedTouchHandler.hpp> #include <Geode/modify/CCTargetedTouchHandler.hpp>
class $modify(CCTargetedTouchHandlerTypeinfoFix, CCTargetedTouchHandler) { struct CCTargetedTouchHandlerFix : Modify<CCTargetedTouchHandlerFix, CCTargetedTouchHandler> {
void destructor() { void destructor() {
if (m_pDelegate) { if (m_pDelegate) {
cocos2d::CCObject* pObject = base_cast<cocos2d::CCObject*>(m_pDelegate); cocos2d::CCObject* pObject = base_cast<cocos2d::CCObject*>(m_pDelegate);
@ -101,7 +99,7 @@ class $modify(CCTargetedTouchHandlerTypeinfoFix, CCTargetedTouchHandler) {
}; };
#include <Geode/modify/CCStandardTouchHandler.hpp> #include <Geode/modify/CCStandardTouchHandler.hpp>
class $modify(CCStandardTouchHandlerTypeinfoFix, CCStandardTouchHandler) { struct CCStandardTouchHandlerFix : Modify<CCStandardTouchHandlerFix, CCStandardTouchHandler> {
void destructor() { void destructor() {
if (m_pDelegate) { if (m_pDelegate) {
cocos2d::CCObject* pObject = base_cast<cocos2d::CCObject*>(m_pDelegate); cocos2d::CCObject* pObject = base_cast<cocos2d::CCObject*>(m_pDelegate);
@ -140,6 +138,7 @@ class $modify(CCStandardTouchHandlerTypeinfoFix, CCStandardTouchHandler) {
return pHandler; return pHandler;
} }
}; };
// clang-format on // clang-format on
#endif #endif

View file

@ -2,12 +2,11 @@
USE_GEODE_NAMESPACE(); USE_GEODE_NAMESPACE();
// clang-format off
#include <Geode/modify/AchievementNotifier.hpp> #include <Geode/modify/AchievementNotifier.hpp>
class $modify(AchievementNotifier) {
struct SceneSwitch : Modify<SceneSwitch, AchievementNotifier> {
void willSwitchToScene(CCScene* scene) { void willSwitchToScene(CCScene* scene) {
AchievementNotifier::willSwitchToScene(scene); AchievementNotifier::willSwitchToScene(scene);
SceneManager::get()->willSwitchToScene(scene); SceneManager::get()->willSwitchToScene(scene);
} }
}; };
// clang-format on

View file

@ -2,9 +2,9 @@
USE_GEODE_NAMESPACE(); USE_GEODE_NAMESPACE();
// clang-format off
#include <Geode/modify/AppDelegate.hpp> #include <Geode/modify/AppDelegate.hpp>
class $modify(AppDelegate) {
struct SaveLoader : Modify<SaveLoader, AppDelegate> {
void trySaveGame() { void trySaveGame() {
log::log(Severity::Info, Loader::getInternalMod(), "Saving..."); log::log(Severity::Info, Loader::getInternalMod(), "Saving...");
@ -14,8 +14,7 @@ class $modify(AppDelegate) {
} }
log::log(Severity::Info, Loader::getInternalMod(), "Saved"); log::log(Severity::Info, Loader::getInternalMod(), "Saved");
return AppDelegate::trySaveGame(); return AppDelegate::trySaveGame();
} }
}; };
// clang-format on

View file

@ -1,12 +1,12 @@
#include <InternalLoader.hpp> #include <InternalLoader.hpp>
USE_GEODE_NAMESPACE(); USE_GEODE_NAMESPACE();
// clang-format off
#include <Geode/modify/CCScheduler.hpp> #include <Geode/modify/CCScheduler.hpp>
class $modify(CCScheduler) {
struct FunctionQueue : Modify<FunctionQueue, CCScheduler> {
void update(float dt) { void update(float dt) {
InternalLoader::get()->executeGDThreadQueue(); InternalLoader::get()->executeGDThreadQueue();
return CCScheduler::update(dt); return CCScheduler::update(dt);
} }
}; };
// clang-format on

View file

@ -4,21 +4,12 @@
USE_GEODE_NAMESPACE(); USE_GEODE_NAMESPACE();
class $modify(GameManager) { struct ResourcesUpdate : Modify<ResourcesUpdate, LoadingLayer> {
void reloadAllStep2() { void loadAssets() {
GameManager::reloadAllStep2(); LoadingLayer::loadAssets();
Loader::get()->updateResourcePaths(); // this is in case the user refreshes texture quality at runtime
} if (this->m_loadStep == 10) {
Loader::get()->updateResources();
}
}
}; };
class $modify(LoadingLayer) {
bool init(bool fromReload) {
// this is in case the user refreshes texture quality at runtime
// resources are loaded first so things like texture packs override
// the GD textures
Loader::get()->waitForModsToBeLoaded();
Loader::get()->updateResources();
return LoadingLayer::init(fromReload);
}
};
// clang-format on

View file

@ -12,6 +12,7 @@
#include <Geode/utils/ranges.hpp> #include <Geode/utils/ranges.hpp>
#include <Geode/utils/string.hpp> #include <Geode/utils/string.hpp>
#include <Geode/utils/vector.hpp> #include <Geode/utils/vector.hpp>
#include <fmt/format.h>
#include <hash.hpp> #include <hash.hpp>
#include <thread> #include <thread>
@ -86,7 +87,7 @@ void Index::updateIndex(IndexUpdateCallback callback, bool force) {
// create directory for the local clone of // create directory for the local clone of
// the index // the index
auto indexDir = Loader::get()->getGeodeDirectory() / "index"; auto indexDir = Loader::get()->getGeodeSaveDirectory() / GEODE_INDEX_DIRECTORY;
ghc::filesystem::create_directories(indexDir); ghc::filesystem::create_directories(indexDir);
#if GITHUB_DONT_RATE_LIMIT_ME_PLS == 1 #if GITHUB_DONT_RATE_LIMIT_ME_PLS == 1
@ -104,39 +105,32 @@ void Index::updateIndex(IndexUpdateCallback callback, bool force) {
#endif #endif
// read sha of currently installed commit
std::string currentCommitSHA = "";
if (ghc::filesystem::exists(indexDir / "current")) {
auto data = utils::file::readString(indexDir / "current");
if (data) {
currentCommitSHA = data.value();
}
}
web::AsyncWebRequest() web::AsyncWebRequest()
.join("index-update") .join("index-update")
.fetch("https://api.github.com/repos/geode-sdk/mods/commits") .header(fmt::format("If-None-Match: \"{}\"", currentCommitSHA))
.json() .header("Accept: application/vnd.github.sha")
.then([this, force, callback](nlohmann::json const& json) { .fetch("https://api.github.com/repos/geode-sdk/mods/commits/main")
auto indexDir = Loader::get()->getGeodeDirectory() / "index"; .text()
.then([this, force, callback, currentCommitSHA](std::string const& upcomingCommitSHA) {
auto indexDir = Loader::get()->getGeodeSaveDirectory() / GEODE_INDEX_DIRECTORY;
// check if rate-limited (returns object) // gee i sure hope no one does 60 commits to the mod index an hour and download every
JsonChecker checkerObj(json); // single one of them
auto obj = checkerObj.root("[geode-sdk/mods/commits]").obj(); if (upcomingCommitSHA == "") {
if (obj.has("documentation_url") && obj.has("message")) { m_upToDate = true;
RETURN_ERROR(obj.has("message").get<std::string>()); m_updating = false;
}
// get sha of latest commit if (callback) callback(UpdateStatus::Finished, "", 100);
JsonChecker checker(json); return;
auto root = checker.root("[geode-sdk/mods/commits]").array();
std::string upcomingCommitSHA;
if (auto first = root.at(0).obj().needs("sha")) {
upcomingCommitSHA = first.get<std::string>();
}
else {
RETURN_ERROR("Unable to get hash from latest commit: " + checker.getError());
}
// read sha of currently installed commit
std::string currentCommitSHA = "";
if (ghc::filesystem::exists(indexDir / "current")) {
auto data = utils::file::readString(indexDir / "current");
if (data) {
currentCommitSHA = data.value();
}
} }
// update if forced or latest commit has // update if forced or latest commit has
@ -224,16 +218,34 @@ void Index::addIndexItemFromFolder(ghc::filesystem::path const& dir) {
return; return;
} }
auto info = ModInfo::createFromFile(dir / "mod.json"); auto infoRes = ModInfo::createFromFile(dir / "mod.json");
if (!info) { if (!infoRes) {
log::warn("{}: {}, skipping", dir, info.error()); log::warn("{}: {}, skipping", dir, infoRes.error());
return; 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; IndexItem item;
item.m_path = dir; item.m_path = dir;
item.m_info = info.value(); item.m_info = info;
if (!json.contains("download") || !json["download"].is_object()) { if (!json.contains("download") || !json["download"].is_object()) {
log::warn("[index.json].download is not an object, skipping"); log::warn("[index.json].download is not an object, skipping");
@ -284,7 +296,7 @@ void Index::addIndexItemFromFolder(ghc::filesystem::path const& dir) {
Result<> Index::updateIndexFromLocalCache() { Result<> Index::updateIndexFromLocalCache() {
m_items.clear(); m_items.clear();
auto baseIndexDir = Loader::get()->getGeodeDirectory() / "index"; auto baseIndexDir = Loader::get()->getGeodeSaveDirectory() / GEODE_INDEX_DIRECTORY;
// load geode.json (index settings) // load geode.json (index settings)
if (auto baseIndexJson = readJSON(baseIndexDir / "geode.json")) { if (auto baseIndexJson = readJSON(baseIndexDir / "geode.json")) {
@ -529,7 +541,7 @@ void InstallItems::error(std::string const& info) {
void InstallItems::finish(bool replaceFiles) { void InstallItems::finish(bool replaceFiles) {
// move files from temp dir to geode directory // move files from temp dir to geode directory
auto tempDir = Loader::get()->getGeodeDirectory() / "index" / "temp"; auto tempDir = Loader::get()->getGeodeSaveDirectory() / GEODE_INDEX_DIRECTORY / "temp";
for (auto& file : ghc::filesystem::directory_iterator(tempDir)) { for (auto& file : ghc::filesystem::directory_iterator(tempDir)) {
try { try {
auto modDir = Loader::get()->getGeodeDirectory() / "mods"; auto modDir = Loader::get()->getGeodeDirectory() / "mods";
@ -605,7 +617,7 @@ InstallItems::CallbackID InstallItems::start(ItemInstallCallback callback, bool
// by virtue of running this function we know item must be valid // by virtue of running this function we know item must be valid
auto item = Index::get()->getKnownItem(inst); auto item = Index::get()->getKnownItem(inst);
auto indexDir = Loader::get()->getGeodeDirectory() / "index"; auto indexDir = Loader::get()->getGeodeSaveDirectory() / GEODE_INDEX_DIRECTORY;
(void)file::createDirectoryAll(indexDir / "temp"); (void)file::createDirectoryAll(indexDir / "temp");
auto tempFile = indexDir / "temp" / item.m_download.m_filename; auto tempFile = indexDir / "temp" / item.m_download.m_filename;

View file

@ -7,6 +7,7 @@
#include <Geode/loader/Log.hpp> #include <Geode/loader/Log.hpp>
#include <Geode/utils/fetch.hpp> #include <Geode/utils/fetch.hpp>
#include <Geode/utils/file.hpp> #include <Geode/utils/file.hpp>
#include <fmt/format.h>
#include <hash.hpp> #include <hash.hpp>
#include <iostream> #include <iostream>
#include <sstream> #include <sstream>
@ -95,67 +96,38 @@ void InternalLoader::downloadLoaderResources(IndexUpdateCallback callback) {
web::AsyncWebRequest() web::AsyncWebRequest()
.join("update-geode-loader-resources") .join("update-geode-loader-resources")
.fetch("https://api.github.com/repos/geode-sdk/geode/releases/tags/" + version) .fetch(fmt::format(
.json() "https://github.com/geode-sdk/geode/releases/download/{}/resources.zip", version
.then([tempResourcesZip, resourcesDir, callback](nlohmann::json const& json) { ))
auto checker = JsonChecker(json); .into(tempResourcesZip)
auto root = checker.root("[matching geode release]").obj(); .then([tempResourcesZip, resourcesDir, callback](auto) {
// unzip resources zip
// find resources.zip and download it auto unzip = file::unzipTo(tempResourcesZip, resourcesDir);
for (auto asset : root.needs("assets").iterate()) { if (!unzip) {
auto obj = asset.obj(); if (callback)
if (obj.needs("name").get<std::string>() == "resources.zip") { callback(
auto url = obj.needs("browser_download_url").get<std::string>(); UpdateStatus::Failed, "Unable to unzip new resources: " + unzip.error(), 0
if (url.size()) { );
web::AsyncWebRequest() return;
.fetch(url) }
.into(tempResourcesZip) // delete resources zip
.then([tempResourcesZip, resourcesDir, callback](auto) { try {
// unzip resources zip ghc::filesystem::remove(tempResourcesZip);
auto unzip = file::unzipTo(tempResourcesZip, resourcesDir); }
if (!unzip) { catch (...) {
if (callback)
callback(
UpdateStatus::Failed,
"Unable to unzip new resources: " + unzip.error(), 0
);
return;
}
// delete resources zip
try {
ghc::filesystem::remove(tempResourcesZip);
}
catch (...) {
}
Loader::get()->updateResources();
if (callback) callback(
UpdateStatus::Finished,
"Resources updated",
100
);
})
.expect([callback](std::string const& info) {
if (callback) callback(UpdateStatus::Failed, info, 0);
})
.progress([callback](auto&, double now, double total) {
if (callback)
callback(
UpdateStatus::Progress, "Downloading resources",
static_cast<uint8_t>(now / total * 100.0)
);
});
}
}
} }
if (checker.isError()) { if (callback) callback(UpdateStatus::Finished, "Resources updated", 100);
if (callback) callback(UpdateStatus::Failed, checker.getError(), 0);
}
}) })
.expect([callback](std::string const& info) { .expect([callback](std::string const& info) {
if (callback) callback(UpdateStatus::Failed, info, 0); if (callback) callback(UpdateStatus::Failed, info, 0);
})
.progress([callback](auto&, double now, double total) {
if (callback)
callback(
UpdateStatus::Progress, "Downloading resources",
static_cast<uint8_t>(now / total * 100.0)
);
}); });
} }

View file

@ -3,14 +3,14 @@
USE_GEODE_NAMESPACE(); USE_GEODE_NAMESPACE();
std::unordered_set<BasicEventHandler*> Event::s_handlers = {}; std::unordered_set<EventListenerProtocol*> Event::s_listeners = {};
void BasicEventHandler::listen() { void EventListenerProtocol::enable() {
Event::s_handlers.insert(this); Event::s_listeners.insert(this);
} }
void BasicEventHandler::unlisten() { void EventListenerProtocol::disable() {
Event::s_handlers.erase(this); Event::s_listeners.erase(this);
} }
BasicEventHandler::~BasicEventHandler() { BasicEventHandler::~BasicEventHandler() {
@ -22,8 +22,8 @@ Event::~Event() {}
void Event::postFrom(Mod* m) { void Event::postFrom(Mod* m) {
if (m) m_sender = m; if (m) m_sender = m;
for (auto h : Event::s_handlers) { for (auto h : Event::s_listeners) {
if (h->passThrough(this) == PassThrough::Stop) { if (h->passThrough(this) == ListenerResult::Stop) {
break; break;
} }
} }
@ -32,7 +32,3 @@ void Event::postFrom(Mod* m) {
Mod* Event::getSender() { Mod* Event::getSender() {
return m_sender; return m_sender;
} }
std::unordered_set<BasicEventHandler*> const& Event::getHandlers() {
return Event::s_handlers;
}

View file

@ -37,6 +37,10 @@ void Loader::createDirectories() {
auto tempDir = this->getGeodeDirectory() / GEODE_TEMP_DIRECTORY; auto tempDir = this->getGeodeDirectory() / GEODE_TEMP_DIRECTORY;
auto confDir = this->getGeodeDirectory() / GEODE_CONFIG_DIRECTORY; auto confDir = this->getGeodeDirectory() / GEODE_CONFIG_DIRECTORY;
#ifdef GEODE_IS_MACOS
ghc::filesystem::create_directory(this->getSaveDirectory());
#endif
ghc::filesystem::create_directories(resDir); ghc::filesystem::create_directories(resDir);
ghc::filesystem::create_directory(confDir); ghc::filesystem::create_directory(confDir);
ghc::filesystem::create_directory(modDir); ghc::filesystem::create_directory(modDir);
@ -465,7 +469,12 @@ ghc::filesystem::path Loader::getGameDirectory() const {
} }
ghc::filesystem::path Loader::getSaveDirectory() const { ghc::filesystem::path Loader::getSaveDirectory() const {
// not using ~/Library/Caches
#ifdef GEODE_IS_MACOS
return ghc::filesystem::path("/Users/Shared/Geode");
#else
return ghc::filesystem::path(CCFileUtils::sharedFileUtils()->getWritablePath().c_str()); return ghc::filesystem::path(CCFileUtils::sharedFileUtils()->getWritablePath().c_str());
#endif
} }
ghc::filesystem::path Loader::getGeodeDirectory() const { ghc::filesystem::path Loader::getGeodeDirectory() const {

View file

@ -507,45 +507,33 @@ float TextRenderer::adjustLineAlignment() {
auto anchor = node->getAnchorPoint().y; auto anchor = node->getAnchorPoint().y;
switch (this->getCurrentVerticalAlign()) { switch (this->getCurrentVerticalAlign()) {
case TextAlignment::Begin: case TextAlignment::Begin:
default: default: {
{ node->setPositionY(m_cursor.y - height * (1.f - anchor));
node->setPositionY(m_cursor.y - height * (1.f - anchor)); } break;
}
break;
case TextAlignment::Center: case TextAlignment::Center: {
{ node->setPositionY(m_cursor.y - maxHeight / 2 + height * (.5f - anchor));
node->setPositionY(m_cursor.y - maxHeight / 2 + height * (.5f - anchor)); } break;
}
break;
case TextAlignment::End: case TextAlignment::End: {
{ node->setPositionY(m_cursor.y - maxHeight + height * anchor);
node->setPositionY(m_cursor.y - maxHeight + height * anchor); } break;
}
break;
} }
switch (this->getCurrentHorizontalAlign()) { switch (this->getCurrentHorizontalAlign()) {
case TextAlignment::Begin: case TextAlignment::Begin:
default: default: {
{ // already correct
// already correct } break;
}
break;
case TextAlignment::Center: case TextAlignment::Center: {
{ node->setPositionX(node->getPositionX() + (m_size.width - maxWidth) / 2);
node->setPositionX(node->getPositionX() + (m_size.width - maxWidth) / 2); } break;
}
break;
case TextAlignment::End: case TextAlignment::End: {
{ node->setPositionX(
node->setPositionX( node->getPositionX() + m_size.width - maxWidth - this->getCurrentIndent()
node->getPositionX() + m_size.width - maxWidth - this->getCurrentIndent() );
); } break;
}
break;
} }
} }
return maxHeight; return maxHeight;

View file

@ -126,37 +126,29 @@ Result<ccColor3B> geode::cocos::cc3bFromHexString(std::string const& hexValue) {
return Err("Invalid hex value"); return Err("Invalid hex value");
} }
switch (hexValue.size()) { switch (hexValue.size()) {
case 6: case 6: {
{ auto r = static_cast<uint8_t>((numValue & 0xFF0000) >> 16);
auto r = static_cast<uint8_t>((numValue & 0xFF0000) >> 16); auto g = static_cast<uint8_t>((numValue & 0x00FF00) >> 8);
auto g = static_cast<uint8_t>((numValue & 0x00FF00) >> 8); auto b = static_cast<uint8_t>((numValue & 0x0000FF));
auto b = static_cast<uint8_t>((numValue & 0x0000FF)); return Ok(ccc3(r, g, b));
return Ok(ccc3(r, g, b)); } break;
}
break;
case 3: case 3: {
{ auto r = static_cast<uint8_t>(((numValue & 0xF00) >> 8) * 17);
auto r = static_cast<uint8_t>(((numValue & 0xF00) >> 8) * 17); auto g = static_cast<uint8_t>(((numValue & 0x0F0) >> 4) * 17);
auto g = static_cast<uint8_t>(((numValue & 0x0F0) >> 4) * 17); auto b = static_cast<uint8_t>(((numValue & 0x00F)) * 17);
auto b = static_cast<uint8_t>(((numValue & 0x00F)) * 17); return Ok(ccc3(r, g, b));
return Ok(ccc3(r, g, b)); } break;
}
break;
case 2: case 2: {
{ auto num = static_cast<uint8_t>(numValue);
auto num = static_cast<uint8_t>(numValue); return Ok(ccc3(num, num, num));
return Ok(ccc3(num, num, num)); } break;
}
break;
case 1: case 1: {
{ auto num = static_cast<uint8_t>(numValue) * 17;
auto num = static_cast<uint8_t>(numValue) * 17; return Ok(ccc3(num, num, num));
return Ok(ccc3(num, num, num)); } break;
}
break;
default: return Err("Invalid hex size, expected 1, 2, 3, or 6"); default: return Err("Invalid hex size, expected 1, 2, 3, or 6");
} }
@ -177,57 +169,45 @@ Result<ccColor4B> geode::cocos::cc4bFromHexString(std::string const& hexValue) {
return Err("Invalid hex value"); return Err("Invalid hex value");
} }
switch (hexValue.size()) { switch (hexValue.size()) {
case 8: case 8: {
{ auto r = static_cast<uint8_t>((numValue & 0xFF000000) >> 24);
auto r = static_cast<uint8_t>((numValue & 0xFF000000) >> 24); auto g = static_cast<uint8_t>((numValue & 0x00FF0000) >> 16);
auto g = static_cast<uint8_t>((numValue & 0x00FF0000) >> 16); auto b = static_cast<uint8_t>((numValue & 0x0000FF00) >> 8);
auto b = static_cast<uint8_t>((numValue & 0x0000FF00) >> 8); auto a = static_cast<uint8_t>((numValue & 0x000000FF));
auto a = static_cast<uint8_t>((numValue & 0x000000FF)); return Ok(ccc4(r, g, b, a));
return Ok(ccc4(r, g, b, a)); } break;
}
break;
case 6: case 6: {
{ auto r = static_cast<uint8_t>((numValue & 0xFF0000) >> 16);
auto r = static_cast<uint8_t>((numValue & 0xFF0000) >> 16); auto g = static_cast<uint8_t>((numValue & 0x00FF00) >> 8);
auto g = static_cast<uint8_t>((numValue & 0x00FF00) >> 8); auto b = static_cast<uint8_t>((numValue & 0x0000FF));
auto b = static_cast<uint8_t>((numValue & 0x0000FF)); return Ok(ccc4(r, g, b, 255));
return Ok(ccc4(r, g, b, 255)); } break;
}
break;
case 4: case 4: {
{ auto r = static_cast<uint8_t>(((numValue & 0xF000) >> 12) * 17);
auto r = static_cast<uint8_t>(((numValue & 0xF000) >> 12) * 17); auto g = static_cast<uint8_t>(((numValue & 0x0F00) >> 8) * 17);
auto g = static_cast<uint8_t>(((numValue & 0x0F00) >> 8) * 17); auto b = static_cast<uint8_t>(((numValue & 0x00F0) >> 4) * 17);
auto b = static_cast<uint8_t>(((numValue & 0x00F0) >> 4) * 17); auto a = static_cast<uint8_t>(((numValue & 0x000F)) * 17);
auto a = static_cast<uint8_t>(((numValue & 0x000F)) * 17); return Ok(ccc4(r, g, b, a));
return Ok(ccc4(r, g, b, a)); } break;
}
break;
case 3: case 3: {
{ auto r = static_cast<uint8_t>(((numValue & 0xF00) >> 8) * 17);
auto r = static_cast<uint8_t>(((numValue & 0xF00) >> 8) * 17); auto g = static_cast<uint8_t>(((numValue & 0x0F0) >> 4) * 17);
auto g = static_cast<uint8_t>(((numValue & 0x0F0) >> 4) * 17); auto b = static_cast<uint8_t>(((numValue & 0x00F)) * 17);
auto b = static_cast<uint8_t>(((numValue & 0x00F)) * 17); return Ok(ccc4(r, g, b, 255));
return Ok(ccc4(r, g, b, 255)); } break;
}
break;
case 2: case 2: {
{ auto num = static_cast<uint8_t>(numValue);
auto num = static_cast<uint8_t>(numValue); return Ok(ccc4(num, num, num, 255));
return Ok(ccc4(num, num, num, 255)); } break;
}
break;
case 1: case 1: {
{ auto num = static_cast<uint8_t>(numValue) * 17;
auto num = static_cast<uint8_t>(numValue) * 17; return Ok(ccc4(num, num, num, 255));
return Ok(ccc4(num, num, num, 255)); } break;
}
break;
default: return Err("Invalid hex size, expected 1, 2, 3, 4, 6, or 8"); default: return Err("Invalid hex size, expected 1, 2, 3, 4, 6, or 8");
} }

View file

@ -127,7 +127,7 @@ static std::unordered_map<std::string, SentAsyncWebRequestHandle> RUNNING_REQUES
static std::mutex RUNNING_REQUESTS_MUTEX; static std::mutex RUNNING_REQUESTS_MUTEX;
SentAsyncWebRequest::SentAsyncWebRequest(AsyncWebRequest const& req, std::string const& id) : SentAsyncWebRequest::SentAsyncWebRequest(AsyncWebRequest const& req, std::string const& id) :
m_id(id), m_url(req.m_url), m_target(req.m_target) { m_id(id), m_url(req.m_url), m_target(req.m_target), m_httpHeaders(req.m_httpHeaders) {
#define AWAIT_RESUME() \ #define AWAIT_RESUME() \
while (m_paused) {} \ while (m_paused) {} \
if (m_cancelled) { \ if (m_cancelled) { \
@ -178,6 +178,12 @@ SentAsyncWebRequest::SentAsyncWebRequest(AsyncWebRequest const& req, std::string
curl_easy_setopt(curl, CURLOPT_NOPROGRESS, 0); curl_easy_setopt(curl, CURLOPT_NOPROGRESS, 0);
curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1); curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1);
curl_slist* headers = nullptr;
for (auto& header : m_httpHeaders) {
headers = curl_slist_append(headers, header.c_str());
}
curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers);
struct ProgressData { struct ProgressData {
SentAsyncWebRequest* self; SentAsyncWebRequest* self;
std::ofstream* file; std::ofstream* file;
@ -293,6 +299,11 @@ AsyncWebRequest& AsyncWebRequest::join(std::string const& requestID) {
return *this; return *this;
} }
AsyncWebRequest& AsyncWebRequest::header(std::string const& header) {
m_httpHeaders.push_back(header);
return *this;
}
AsyncWebResponse AsyncWebRequest::fetch(std::string const& url) { AsyncWebResponse AsyncWebRequest::fetch(std::string const& url) {
m_url = url; m_url = url;
return AsyncWebResponse(*this); return AsyncWebResponse(*this);

View file

@ -15,16 +15,15 @@ USE_GEODE_NAMESPACE();
#endif #endif
VersionInfo::VersionInfo(char const* versionString) { VersionInfo::VersionInfo(char const* versionString) {
if (!THE_SSCANF(versionString, "v%d.%d.%d", &this->m_major, &this->m_minor, &this->m_patch)) if (!THE_SSCANF(versionString, "v%d.%d.%d", &this->m_major, &this->m_minor, &this->m_patch)) {
if (!THE_SSCANF(versionString, "%d.%d.%d", &this->m_major, &this->m_minor, &this->m_patch)) THE_SSCANF(versionString, "%d.%d.%d", &this->m_major, &this->m_minor, &this->m_patch);
THE_SSCANF(versionString, "%d %d %d", &this->m_major, &this->m_minor, &this->m_patch); }
} }
bool VersionInfo::validate(std::string const& string) { bool VersionInfo::validate(std::string const& string) {
int buf0, buf1, buf2; int buf0, buf1, buf2;
if (THE_SSCANF(string.c_str(), "v%d.%d.%d", &buf0, &buf1, &buf2)) return true; if (THE_SSCANF(string.c_str(), "v%d.%d.%d", &buf0, &buf1, &buf2)) return true;
if (THE_SSCANF(string.c_str(), "%d.%d.%d", &buf0, &buf1, &buf2)) return true; if (THE_SSCANF(string.c_str(), "%d.%d.%d", &buf0, &buf1, &buf2)) return true;
if (THE_SSCANF(string.c_str(), "%d %d %d", &buf0, &buf1, &buf2)) return true;
return false; return false;
} }

View file

@ -3,31 +3,26 @@
USE_GEODE_NAMESPACE(); USE_GEODE_NAMESPACE();
#include <Geode/modify/MenuLayer.hpp> #include <Geode/modify/MenuLayer.hpp>
class $modify(MenuLayer) {
void onMoreGames(CCObject*) { struct MyMenuLayer : Modify<MyMenuLayer, MenuLayer> {
if (Mod::get()->getSettingValue<bool>("its-raining-after-all")) { void onMoreGames(CCObject*) {
FLAlertLayer::create( if (Mod::get()->getSettingValue<bool>("its-raining-after-all")) {
"Damn", FLAlertLayer::create("Damn", ":(", "OK")->show();
":(", }
"OK" else {
)->show(); FLAlertLayer::create("Yay", "The weather report said it wouldn't rain today :)", "OK")
} else { ->show();
FLAlertLayer::create( }
"Yay", }
"The weather report said it wouldn't rain today :)",
"OK"
)->show();
}
}
}; };
GEODE_API bool GEODE_CALL geode_load(Mod*) { GEODE_API bool GEODE_CALL geode_load(Mod*) {
// Dispatcher::get()->addFunction<void(GJGarageLayer*)>("test-garage-open", [](GJGarageLayer* gl) { // Dispatcher::get()->addFunction<void(GJGarageLayer*)>("test-garage-open", [](GJGarageLayer*
// auto label = CCLabelBMFont::create("Dispatcher works!", "bigFont.fnt"); // gl) { auto label = CCLabelBMFont::create("Dispatcher works!", "bigFont.fnt");
// label->setPosition(100, 80); // label->setPosition(100, 80);
// label->setScale(.4f); // label->setScale(.4f);
// label->setZOrder(99999); // label->setZOrder(99999);
// gl->addChild(label); // gl->addChild(label);
// }); // });
return true; return true;
} }

View file

@ -1,6 +1,6 @@
{ {
"geode": "v0.4.1", "geode": "0.4.1",
"version": "v1.0.0", "version": "1.0.0",
"id": "geode.testdep", "id": "geode.testdep",
"name": "Geode Test Dependency", "name": "Geode Test Dependency",
"developer": "Geode Team", "developer": "Geode Team",

View file

@ -2,90 +2,91 @@
USE_GEODE_NAMESPACE(); USE_GEODE_NAMESPACE();
auto test = [](){ auto test = []() {
log::info("Static logged"); log::info("Static logged");
return 0; return 0;
}; };
// Exported functions // Exported functions
GEODE_API bool GEODE_CALL geode_enable() { GEODE_API bool GEODE_CALL geode_enable() {
log::info("Enabled"); log::info("Enabled");
return true; return true;
} }
GEODE_API bool GEODE_CALL geode_disable() { GEODE_API bool GEODE_CALL geode_disable() {
log::info("Disabled"); log::info("Disabled");
return true; return true;
} }
GEODE_API bool GEODE_CALL geode_load(Mod*) { GEODE_API bool GEODE_CALL geode_load(Mod*) {
log::info("Loaded"); log::info("Loaded");
return true; return true;
} }
GEODE_API bool GEODE_CALL geode_unload() { GEODE_API bool GEODE_CALL geode_unload() {
log::info("Unloaded"); log::info("Unloaded");
return true; return true;
} }
// Modify // Modify
#include <Geode/modify/GJGarageLayer.hpp> #include <Geode/modify/GJGarageLayer.hpp>
class $modify(GJGarageLayerTest, GJGarageLayer) {
GJGarageLayerTest() :
myValue(1907) {}
int myValue; struct GJGarageLayerTest : Modify<GJGarageLayerTest, GJGarageLayer> {
std::string myString = "yeah have fun finding a better thing for this"; GJGarageLayerTest() : myValue(1907) {}
bool init() {
if (!GJGarageLayer::init()) return false;
auto label = CCLabelBMFont::create("Modify works!", "bigFont.fnt"); int myValue;
label->setPosition(100, 110); std::string myString = "yeah have fun finding a better thing for this";
label->setScale(.4f);
label->setZOrder(99999);
addChild(label);
if (m_fields->myValue == 1907 && m_fields->myString != "") { bool init() {
auto label = CCLabelBMFont::create("Field default works!", "bigFont.fnt"); if (!GJGarageLayer::init()) return false;
label->setPosition(100, 100);
label->setScale(.4f);
label->setZOrder(99999);
addChild(label);
}
// Data Store auto label = CCLabelBMFont::create("Modify works!", "bigFont.fnt");
auto ds = Mod::get()->getDataStore(); label->setPosition(100, 110);
int out = ds["times-opened"]; label->setScale(.4f);
ds["times-opened"] = out + 1; label->setZOrder(99999);
addChild(label);
std::string text = std::string("Times opened: ") + std::to_string(out); if (m_fields->myValue == 1907 && m_fields->myString != "") {
auto label = CCLabelBMFont::create("Field default works!", "bigFont.fnt");
label->setPosition(100, 100);
label->setScale(.4f);
label->setZOrder(99999);
addChild(label);
}
auto label2 = CCLabelBMFont::create(text.c_str(), "bigFont.fnt"); // Data Store
label2->setPosition(100, 90); auto ds = Mod::get()->getDataStore();
label2->setScale(.4f); int out = ds["times-opened"];
label2->setZOrder(99999); ds["times-opened"] = out + 1;
addChild(label2);
// Dispatch system pt. 1 std::string text = std::string("Times opened: ") + std::to_string(out);
// auto fn = Dispatcher::get()->getFunction<void(GJGarageLayer*)>("test-garage-open");
// fn(this);
return true; auto label2 = CCLabelBMFont::create(text.c_str(), "bigFont.fnt");
} label2->setPosition(100, 90);
label2->setScale(.4f);
label2->setZOrder(99999);
addChild(label2);
// Dispatch system pt. 1
// auto fn = Dispatcher::get()->getFunction<void(GJGarageLayer*)>("test-garage-open");
// fn(this);
return true;
}
}; };
/*// Event system pt. 2 /*// Event system pt. 2
int a = (0, []() { int a = (0, []() {
Dispatcher::get()->addSelector("test-garage-open", [](GJGarageLayer* gl) { Dispatcher::get()->addSelector("test-garage-open", [](GJGarageLayer* gl) {
auto label = CCLabelBMFont::create("EventCenter works!", "bigFont.fnt"); auto label = CCLabelBMFont::create("EventCenter works!", "bigFont.fnt");
label->setPosition(100, 80); label->setPosition(100, 80);
label->setScale(.4f); label->setScale(.4f);
label->setZOrder(99999); label->setZOrder(99999);
gl->addChild(label); gl->addChild(label);
TestDependency::depTest(gl); TestDependency::depTest(gl);
}); });
// Event system pt. 2 // Event system pt. 2
// $observe("test-garage-open", GJGarageLayer*, evt) { // $observe("test-garage-open", GJGarageLayer*, evt) {
@ -99,6 +100,5 @@ int a = (0, []() {
// // API pt. 2 // // API pt. 2
// TestDependency::depTest(gl); // TestDependency::depTest(gl);
// } // }
return 0; return 0;
}());*/ }());*/

View file

@ -1,6 +1,6 @@
{ {
"geode": "v0.4.1", "geode": "0.4.1",
"version": "v1.0.0", "version": "1.0.0",
"id": "geode.test", "id": "geode.test",
"name": "Geode Test", "name": "Geode Test",
"developer": "Geode Team", "developer": "Geode Team",
@ -11,7 +11,7 @@
"dependencies": [ "dependencies": [
{ {
"id": "geode.testdep", "id": "geode.testdep",
"version": "v1.0.*", "version": "1.0.*",
"required": true "required": true
} }
] ]