From 1281611d2fcaabfa220fc6b29d8e25645a9083ff Mon Sep 17 00:00:00 2001 From: undefined06855 Date: Thu, 30 Apr 2026 21:56:01 +0100 Subject: [PATCH 1/2] fix every single link --- README.md | 4 +- getting-started/cpp-stuff.md | 8 +- getting-started/geode-cli.md | 2 +- getting-started/index.md | 2 +- getting-started/what-next.md | 16 ++-- handbook/vol1/chap1_1.md | 8 +- handbook/vol1/chap1_2.md | 2 +- handbook/vol1/chap1_3.md | 10 +-- handbook/vol1/chap1_4.md | 8 +- handbook/vol1/chap1_5.md | 10 +-- handbook/vol1/chap1_6.md | 12 +-- handbook/vol1/chap1_7.md | 12 +-- handbook/vol2/chap2_1.md | 10 +-- handbook/vol2/chap2_2.md | 4 +- handbook/vol2/chap2_3.md | 2 +- handbook/vol2/chap2_4.md | 4 +- handbook/vol2/chap2_5.md | 2 +- handbook/vol4/chap4_1.md | 4 +- index.md | 6 +- mods/configuring.md | 20 ++--- mods/dependencies.md | 48 ++-------- mods/guidelines-tips.md | 4 +- mods/md-files.md | 2 +- mods/publishing.md | 8 +- mods/resources.md | 4 +- mods/savedata.md | 4 +- mods/settings-old.md | 10 +-- mods/settings.md | 164 +++++++++++++++++------------------ tutorials/coroutines.md | 2 +- tutorials/dictionary.md | 46 +++++----- tutorials/events.md | 6 +- tutorials/hookpriority.md | 4 +- tutorials/layouts.md | 6 +- tutorials/logging.md | 6 +- tutorials/manualhooks.md | 6 +- tutorials/memory.md | 2 +- tutorials/migrate-v5.md | 2 +- tutorials/modify-geode.md | 4 +- tutorials/modify.md | 44 +++++----- tutorials/nodetree.md | 10 +-- tutorials/popup.md | 6 +- tutorials/positioning.md | 6 +- tutorials/tasks.md | 28 +++--- tutorials/utils.md | 6 +- 44 files changed, 271 insertions(+), 303 deletions(-) diff --git a/README.md b/README.md index 1f16c5e0..be911d37 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # Geode Docs -### [Visit the Docs site](https://docs.geode-sdk.org) +### [Visit the Docs site](https://docs.geode-sdk.org/) This is the source code for Geode's docs, containing all the hand-written tutorials. @@ -8,7 +8,7 @@ Class & function documentation is built automatically from [the Geode source cod ## Building -The docs are built using [Flash](https://github.com/geode-sdk/flash). To build the docs, you need Flash, along with [CMake](https://cmake.org/install/) and [Clang](https://clang.llvm.org/). +The docs are built using [Flash](https://github.com/geode-sdk/flash). To build the docs, you need Flash, along with [CMake](https://cmake.org/install) and [Clang](https://clang.llvm.org/). To build the docs, you first need to clone Geode, and then clone the docs inside the Geode root, for a folder structure like this: diff --git a/getting-started/cpp-stuff.md b/getting-started/cpp-stuff.md index 0a5ffa72..b1d9a319 100644 --- a/getting-started/cpp-stuff.md +++ b/getting-started/cpp-stuff.md @@ -7,8 +7,8 @@ order: 2 To be able to use the Geode SDK, you **will** need at least the following: * [A C++ compiler](#compiler) - * [CMake](https://cmake.org/download/) - Version 3.29+ is required - make sure to add to PATH when installing on Windows. - * [Git](https://git-scm.com/downloads) - Hey you. Yes, you! I know a lot of people skip this step **but you will need it**. Don't come at us asking for why you "could not find git for clone of json-populate". + * [CMake](https://cmake.org/resources/) - Version 3.29+ is required - make sure to add to PATH when installing on Windows. + * [Git](https://git-scm.com/install/) - Hey you. Yes, you! I know a lot of people skip this step **but you will need it**. Don't come at us asking for why you "could not find git for clone of json-populate". ## Compiler @@ -30,7 +30,7 @@ LLVM itself does not come with Windows SDK and CRT libraries, so you will need a Please note that Visual Studio **2022** or higher is required. If you have an older version already installed, you should upgrade to the latest available. -Unless you want to install Visual Studio (the editor) itself, we recommend installing just the build tools. Open the VS [download page](https://visualstudio.microsoft.com/downloads), scroll to the bottom, and under "Tools for Visual Studio" download **Build Tools for Visual Studio**. +Unless you want to install Visual Studio (the editor) itself, we recommend installing just the build tools. Open the VS [download page](https://visualstudio.microsoft.com/downloads/), scroll to the bottom, and under "Tools for Visual Studio" download **Build Tools for Visual Studio**. After launching the installer, select **Desktop development with C++** and optionally deselect all features except for **MSVC Build Tools** and **Windows SDK**, like on the screenshot below. Click Install and wait for it to finish. @@ -86,4 +86,4 @@ After installing the CLI, run this command to install all the needed tools: geode sdk install-linux ``` -Now you can proceed to [setting up Geode CLI](/getting-started/geode-cli.md). +Now you can proceed to [setting up Geode CLI](/getting-started/geode-cli). diff --git a/getting-started/geode-cli.md b/getting-started/geode-cli.md index fb67915d..b38ae8b9 100644 --- a/getting-started/geode-cli.md +++ b/getting-started/geode-cli.md @@ -58,7 +58,7 @@ It is recommended that you [set up a profile afterwards](#profile-setup). ## MacOS -You can easily install the CLI via [Brew](https://brew.sh) +You can easily install the CLI via [Brew](https://brew.sh/) ```bash brew install geode-sdk/geode/geode-cli ``` diff --git a/getting-started/index.md b/getting-started/index.md index 36fdb907..a61b5f2e 100644 --- a/getting-started/index.md +++ b/getting-started/index.md @@ -5,4 +5,4 @@ order: 1 # Getting Started -Please read through this chapter in order, starting in [Prerequisites](/getting-started/prerequisites.md). \ No newline at end of file +Please read through this chapter in order, starting in [Prerequisites](/getting-started/prerequisites). \ No newline at end of file diff --git a/getting-started/what-next.md b/getting-started/what-next.md index 83d267de..931893e0 100644 --- a/getting-started/what-next.md +++ b/getting-started/what-next.md @@ -5,18 +5,18 @@ order: 7 # What next? -You should now be set on your journey to develope GD mods! If you are completely new to GD modding, a good place to start is [the Handbook](/handbook/chap0.md), which covers all of the basics of creating a Hello World. +You should now be set on your journey to develope GD mods! If you are completely new to GD modding, a good place to start is [the Handbook](/handbook/chap0), which covers all of the basics of creating a Hello World. ## Geode Features Here are a list of Geode-specific concepts you might like to familiarize yourself with: - * [Hooking with `$modify`](/tutorials/modify.md) and [adding fields](/tutorials/fields.md) - * [Using sprites and other resources](/mods/resources.md) - * [Settings](/mods/settings.md) and [saving data](/mods/savedata.md) - * [String IDs](/tutorials/nodetree.md) and [Layouts](/tutorials/layouts.md) - * [Events](/tutorials/events.md) - * [Using dependencies](/mods/dependencies.md) - * [Publishing mods](/mods/publishing.md) + * [Hooking with `$modify`](/tutorials/modify) and [adding fields](/tutorials/fields) + * [Using sprites and other resources](/mods/resources) + * [Settings](/mods/settings) and [saving data](/mods/savedata) + * [String IDs](/tutorials/nodetree) and [Layouts](/tutorials/layouts) + * [Events](/tutorials/events) + * [Using dependencies](/mods/dependencies) + * [Publishing mods](/mods/publishing) The [Tutorials](/tutorials) category in general is a great source for information about working with Geode mods. diff --git a/handbook/vol1/chap1_1.md b/handbook/vol1/chap1_1.md index 66fc449d..4e41e6d8 100644 --- a/handbook/vol1/chap1_1.md +++ b/handbook/vol1/chap1_1.md @@ -10,22 +10,22 @@ But how exactly does a mod work? The actual executable file for Geometry Dash th Modifying binary code is a bit difficult, however, as it is not meant to be for humans; binary code is strictly for computers, and as such it is unreadable and unmodifiable for a human. Binary code is also **platform-specific**: a mod written purely in binary would only work on one platform, and could not be ported over to others without fully rewriting the entire mod from the ground up. -As a historical curiosity, it should be noted that mods used to be written fully in binary. Or, more accurately, they were written in **assembly**, which is a (somewhat) human-readable form of binary. However, to make it very clear, **no sane person does this anymore**. Working with binary code directly is nowadays only done in rare cases, which will be explained in [Chapter 1.2](/handbook/vol1/chap1_2.md). +As a historical curiosity, it should be noted that mods used to be written fully in binary. Or, more accurately, they were written in **assembly**, which is a (somewhat) human-readable form of binary. However, to make it very clear, **no sane person does this anymore**. Working with binary code directly is nowadays only done in rare cases, which will be explained in [Chapter 1.2](/handbook/vol1/chap1_2). But what's the alternative? We can make modding much easier by knowing two facts: - Binary code is produced through **compilation**. - Geometry Dash is written in **C++**. -**Compilation is the process of turning source code into binary code**. While we can't modify GD's binary code easily, we can write our own source code in C++ and compile it into compatible binary code. Knowing what language GD was made in is vitally important, as not all binary code is equal. Some programming languages such as [Rust](https://www.rust-lang.org/) produce wildly different binary code from C++, and while it is technically compatible, it is much easier to work with a C++ game using C++. (Some modders, however, are working on making Rust usable for modding!) Writing our mods in a higher-level language like C++ also makes porting much easier; **C++ can be compiled to compatible machine code on all the platforms GD is available on**. This does come with multiple caveats though, however those are left for a later chapter. +**Compilation is the process of turning source code into binary code**. While we can't modify GD's binary code easily, we can write our own source code in C++ and compile it into compatible binary code. Knowing what language GD was made in is vitally important, as not all binary code is equal. Some programming languages such as [Rust](https://rust-lang.org/) produce wildly different binary code from C++, and while it is technically compatible, it is much easier to work with a C++ game using C++. (Some modders, however, are working on making Rust usable for modding!) Writing our mods in a higher-level language like C++ also makes porting much easier; **C++ can be compiled to compatible machine code on all the platforms GD is available on**. This does come with multiple caveats though, however those are left for a later chapter. What all of this means is if we write our mod in C++ and then find some way to make GD run the compiled binary code of it, then we have unlocked a path to modding! And luckily, we know how to make GD load extra code: **binary injection**. Almost every platform GD is on has some sort of **dynamic library support**. On Windows, these are known as **.DLL files**; on Mac, they're **.DYLIBs**; on Android, they're **.SO files**. A dynamic library is like a binary executable, but they have one special property: other executables can load them. This is usually done through address tables and the such; these are much too complicated for the purposes of this tutorial, and the specifics are also platform-dependant and modern modloaders use proxy DLLs instead. As such, how binary injection works in detail will not be explained here. All you have to take away is that we have methods to make GD load a dynamic library, and that acts as our entry point to modding the game. -> :information_source: There is one platform that doesn't have dynamic library support: iOS (unless jailbroken). Modding on iOS without jailbreaking requires basically baking all the mods you want into a premodified game, like [iCreate](https://icreate.pro/) has done, but a general mod loader like Geode will likely never see iOS support unless some major changes to the OS happen first. +> :information_source: There is one platform that doesn't have dynamic library support: iOS (unless jailbroken). Modding on iOS without jailbreaking requires basically baking all the mods you want into a premodified binary, like [iCreate](https://icreate.pro/) has done. This is done by the iOS loader! -> :green_book: If you are interested in **learning more about binary injection**, [the Wikipedia article on DLL injection](https://en.wikipedia.org/wiki/DLL_injection) is a pretty good place to start. +> :green_book: If you are interested in **learning more about binary injection**, [the Wikipedia article on DLL injection](https://en.wikipedia.org/wiki/DLL_injection) is a pretty good place to start. Usually, the first custom dynamic library we make GD load is a mod loader; that is, a mod whose purpose is to load other mods. This is because the methods we use for injection are not easily scalable, but once we have our one library running, we can invent much simpler ways of loading more of them. For example, on Windows, loading a library from another library that you control is as simple as calling the `LoadLibrary` function. This can also be done an arbitary number of times within our initial library, so we can for example automatically find all the .DLL files in some directory and load them. This is how old 2.1 mod loaders such as **Mega Hack v7** used to work: they would search for all of the `.dll` files in a predefined folder like `extensions` and call `LoadLibrary` on them. diff --git a/handbook/vol1/chap1_2.md b/handbook/vol1/chap1_2.md index 3eeadb88..65a2a53b 100644 --- a/handbook/vol1/chap1_2.md +++ b/handbook/vol1/chap1_2.md @@ -157,7 +157,7 @@ However, at this point it should be noted that **the syntax for hooking in Geode For now, we can leave hooking be, as before we can find any practical applications for it, we must first **find some functions to hook**. -[Chapter 1.3: Functions & Addresses](/handbook/vol1/chap1_3.md) +[Chapter 1.3: Functions & Addresses](/handbook/vol1/chap1_3) ## Notes diff --git a/handbook/vol1/chap1_3.md b/handbook/vol1/chap1_3.md index aa485569..0f50407c 100644 --- a/handbook/vol1/chap1_3.md +++ b/handbook/vol1/chap1_3.md @@ -1,10 +1,10 @@ # Chapter 1.3: Functions & Addresses -In [the last chapter](/handbook/vol1/chap1_2.md), we looked at hooking and how it works. However, the last chapter only touched hooking in theory. The code shown was not what actuals hooks in your code look like. For instance, we do not have access to GD's source code, so we can't exactly just write `return ourDetour()` at the start of the function we want to hook. Instead, we need to figure out some way to **insert hooks into GD's binary code**. +In [the last chapter](/handbook/vol1/chap1_2), we looked at hooking and how it works. However, the last chapter only touched hooking in theory. The code shown was not what actuals hooks in your code look like. For instance, we do not have access to GD's source code, so we can't exactly just write `return ourDetour()` at the start of the function we want to hook. Instead, we need to figure out some way to **insert hooks into GD's binary code**. ## Manual hooking -The main way to create hooks in Geode is using an abstraction called `$modify` - however, it is a very powerful tool and hard to explain without some preface. It is also just an abstraction; you can create hooks manually in Geode as well through the [`Mod::addHook`](/classes/geode/Mod#addHook) interface - although in practice **you should never be creating manual hooks unless necessary**. +The main way to create hooks in Geode is using an abstraction called `$modify` - however, it is a very powerful tool and hard to explain without some preface. It is also just an abstraction; you can create hooks manually in Geode as well through the [`Mod::hook`](/classes/geode/Mod#hook) interface - although in practice **you should never be creating manual hooks unless necessary**. > :information_source: Manual hooks are sometimes necessary, for example to hook some obscure low-level functions that only exists on one platform, like GLFW on Windows. However, for 99% of mods, you should just be using `$modify`, since it makes your code much more portable and easier to work with. @@ -55,7 +55,7 @@ What this means is that in order to hook a function in GD, **we need to know its ## Addresses -So how do we find out these things? Usually, this is done through **reverse engineering**; however, RE is quite a complex skill, and would take far too much time to explain here, so it has its [own dedicated volume instead](https://docs.geode-sdk.org/handbook/vol2/chap2_1). And on top of that, **most common functions have already been found**. This means that instead of REing the function yourself, you can use the GD bindings that come packaged with Geode. +So how do we find out these things? Usually, this is done through **reverse engineering**; however, RE is quite a complex skill, and would take far too much time to explain here, so it has its [own dedicated volume instead](/handbook/vol2/chap2_1). And on top of that, **most common functions have already been found**. This means that instead of REing the function yourself, you can use the GD bindings that come packaged with Geode. > :information_source: Traditionally, the most common GD header library was [**gd.h**](https://github.com/HJfod/gd.h). However, nowadays **gd.h is completely obsolete**, as it is only for 2.1 and fully unmaintained. @@ -63,9 +63,9 @@ However, it is also important to note that **you still need to know how to rever As noted previously, you shouldn't usually be creating hooks manually. Instead, **Geode comes with a special hooking syntax called `$modify`**. How it works will be explained in a later chapter, but first we must talk a bit about GD's game engine: **Cocos2d**. -[Chapter 1.4: Cocos2d](/handbook/vol1/chap1_4.md) +[Chapter 1.4: Cocos2d](/handbook/vol1/chap1_4) ## Notes -> [Note 1] Hook conflicts are a type of [**race condition**](https://en.m.wikipedia.org/wiki/Race_condition) and it happens when two mods try to hook the same function at the same time. If the mods do this sufficiently close to one another, there is a high chance that **one mod's hook will replace the other's**. The end result of this is that one of the mods functions incorrectly, when it fails to hook the function it expected to. In the best case, this just results in the mod losing functionality, but in the extreme case this **could cause crashes**. +> [Note 1] Hook conflicts are a type of [**race condition**](https://en.wikipedia.org/wiki/Race_condition) and it happens when two mods try to hook the same function at the same time. If the mods do this sufficiently close to one another, there is a high chance that **one mod's hook will replace the other's**. The end result of this is that one of the mods functions incorrectly, when it fails to hook the function it expected to. In the best case, this just results in the mod losing functionality, but in the extreme case this **could cause crashes**. diff --git a/handbook/vol1/chap1_4.md b/handbook/vol1/chap1_4.md index 2f6b03ea..9989328e 100644 --- a/handbook/vol1/chap1_4.md +++ b/handbook/vol1/chap1_4.md @@ -50,11 +50,11 @@ Cocos2d is a **sprite-based** framework, meaning that instead of rendering thing It is important to use the correct `create` function for `CCSprite`, as the wrong one will return `nullptr` and cause an error if not properly handled. If the sprite you're creating is contained in its own file, like `GJ_button_01.png`, then you should use `CCSprite::create`; otherwise, if the sprite is in a spritesheet like `GJ_infoIcon_001.png` in `GJ_GameSheet03`, use `CCSprite::createWithSpriteFrameName` instead. ```cpp -// Uh oh! This will return null as GJ_infoIcon_001.png is not its own file but +// Uh oh! This will return null as GJ_infoIcon_001.png is not its own file but // contained in a spritesheet auto infoSpriteFail = CCSprite::create("GJ_infoIcon_001.png"); -// You don't have to specify the name of the spritesheet you're loading from anywhere, +// You don't have to specify the name of the spritesheet you're loading from anywhere, // as GD has already loaded GJ_GameSheet03 into memory auto infoSpriteCorrect = CCSprite::createWithSpriteFrameName("GJ_infoIcon_001.png"); ``` @@ -65,7 +65,7 @@ How to add your own sprites to use in Geode mods will be discussed in a later ch ## Menus & Buttons -Another important class to know of is `CCMenu`. Most mods need some sort of [buttons](/tutorials/buttons.md) for their UI, and for that, the most common class to use is `CCMenuItemSpriteExtra`. However, all `CCMenuItem`-derived classes **must be part of a `CCMenu` to work**. In practice, this means that all of your buttons must be the direct children of some menu. +Another important class to know of is `CCMenu`. Most mods need some sort of [buttons](/tutorials/buttons) for their UI, and for that, the most common class to use is `CCMenuItemSpriteExtra`. However, all `CCMenuItem`-derived classes **must be part of a `CCMenu` to work**. In practice, this means that all of your buttons must be the direct children of some menu. You can actually see the effects of this in-game; hold down on some button, and then without releasing move your cursor over other buttons in the same scene. You will find that some buttons display their bounce animation indicating they are usable, and others don't. This is because of the Cocos2d **touch system**, which will be discussed in detail later, but in essence, only buttons in the same menu are clickable when you start holding down from one. @@ -89,7 +89,7 @@ layer->addChild(button); At this point, we're getting very close to writing actual mod code. However, before we can get to that, we must first discuss **GD layers** and the `$modify` macro in Geode. -[Chapter 1.5: Layers](/handbook/vol1/chap1_5.md) +[Chapter 1.5: Layers](/handbook/vol1/chap1_5) ## Notes diff --git a/handbook/vol1/chap1_5.md b/handbook/vol1/chap1_5.md index bba671fc..1b6e59cf 100644 --- a/handbook/vol1/chap1_5.md +++ b/handbook/vol1/chap1_5.md @@ -1,20 +1,20 @@ # Chapter 1.5: Layers -In [the previous chapter](/handbook/vol1/chap1_4.md), it was mentioned that at all times there exists **one scene at the top of the node tree**. Usually, in practice, this scene has only one child: some **layer**. Scenes are usually only used as containers for layers and for **transitions**; you will likely never have to inherit from `CCScene` in your code. Instead, the class your layers and GD layers usually inherit from is `CCLayer`. +In [the previous chapter](/handbook/vol1/chap1_4), it was mentioned that at all times there exists **one scene at the top of the node tree**. Usually, in practice, this scene has only one child: some **layer**. Scenes are usually only used as containers for layers and for **transitions**; you will likely never have to inherit from `CCScene` in your code. Instead, the class your layers and GD layers usually inherit from is `CCLayer`. Nearly all layers have their own class; for example, the icon kit is `GJGarageLayer`, the editor is `LevelEditorLayer`, and when you go to play a level you enter `PlayLayer`. The first layer most GD modders start modding and also the first layer in the game (after the loading screen) is **the main menu**; it is an instance of the `MenuLayer` class. ## Finding Layer Names -You can find the **class name** of a given layer using the [DevTools](https://github.com/geode-sdk/devtools) mod in Geode. To see how it works, install the **DevTools** mod in-game through the **Download** tab in Geode and then press **F11** to activate it: +You can find the **class name** of a given layer using the [DevTools](https://github.com/geode-sdk/DevTools) mod in Geode. To see how it works, install the **DevTools** mod in-game through the **Download** tab in Geode and then press **F11** to activate it: ![Image of the DevTools mod open in GD, focused on MenuLayer](/assets/handbook/vol1/DevTools_MenuLayer.png) If you look at the **Tree** view on the left, you will see that the current scene contains one layer named **MenuLayer**, which then further contains a bunch of `CCMenu`s and other nodes. The menus contain all of the buttons in the scene; for example, the bottom center row of buttons is contained in one menu. -One thing you will notice in DevTools is that many of the nodes have **string IDs**; this is a **Geode-specific addition** meant to make modding simpler. You can read more about string IDs [in its dedicated article](/tutorials/nodetree.md). +One thing you will notice in DevTools is that many of the nodes have **string IDs**; this is a **Geode-specific addition** meant to make modding simpler. You can read more about string IDs [in its dedicated article](/tutorials/nodetree). -> :warning: In addition to string IDs, some of the menus in Geode are different from the ones in vanilla GD. This is because of the [layout system](/tutorials/layouts.md) in Geode, which handles positioning - in traditional mods, all position has to be done manually! +> :warning: In addition to string IDs, some of the menus in Geode are different from the ones in vanilla GD. This is because of the [layout system](/tutorials/layouts) in Geode, which handles positioning - in traditional mods, all position has to be done manually! There may also be multiple layers in a scene at once. For example, if you click the profile button in `MenuLayer`, you will find it adds a layer named `ProfilePage` in the scene: @@ -24,5 +24,5 @@ Using DevTools, you can find the name of any layer. Just navigate to the layer w > :information_source: DevTools also comes with many other utilities, such as moving nodes in the scene around. However, this tutorial is not about DevTools, so you will have to look at its (currently non-existent) documentation for that ;) -So now we know that the main menu is called `MenuLayer`, but so what? How can we actually modify it? For that, see [Chapter 1.6: Modifying Layers](/handbook/vol1/chap1_6.md) +So now we know that the main menu is called `MenuLayer`, but so what? How can we actually modify it? For that, see [Chapter 1.6: Modifying Layers](/handbook/vol1/chap1_6) diff --git a/handbook/vol1/chap1_6.md b/handbook/vol1/chap1_6.md index 7dd39297..a29da2e3 100644 --- a/handbook/vol1/chap1_6.md +++ b/handbook/vol1/chap1_6.md @@ -40,7 +40,7 @@ public: }; ``` -Every node, and as such layer, has at least two functions: `create` and `init` [[Note 1]](#notes). `create`, as explained [in the previous chapter](/handbook/vol1/chap1_5.md), is what you use to create instances of the class. +Every node, and as such layer, has at least two functions: `create` and `init` [[Note 1]](#notes). `create`, as explained [in the previous chapter](/handbook/vol1/chap1_5), is what you use to create instances of the class. What we're interested in right now however is `init`. This is the function where the node initializes itself; adds all of its subnodes, sets its delegates, etc.. As we can see in the definition of `create`, every instance of `SomeNode` is first created, and then its `init` function is invoked. On top of this, the `init` function is only called once per node, as it doesn't make sense to initialize the same node multiple times. @@ -68,7 +68,7 @@ You will find that this declaration closely resembles a class, and underlyingly The name and syntax of `$modify` comes from its purpose; it is to **modify classes**. -For example, to modify the main menu, it would look like this (remembering from [the previous chapter](/handbook/vol1/chap1_5.md) that the main menu's layer is called `MenuLayer`): +For example, to modify the main menu, it would look like this (remembering from [the previous chapter](/handbook/vol1/chap1_5) that the main menu's layer is called `MenuLayer`): ```cpp #include @@ -92,7 +92,7 @@ class $modify(MyModifiedMenuLayer, MenuLayer) { If you don't provide a name, Geode will automatically generate a random name for the class, which is meant to not cause any name collisions. -> :warning: As `$modify` does not create a normal class, you should not expect standard C++ class things like adding members to work. Geode does come with [an utility for adding members to classes](/tutorials/fields.md), which is quite close to the normal way of declaring members, but not exactly the same. +> :warning: As `$modify` does not create a normal class, you should not expect standard C++ class things like adding members to work. Geode does come with [an utility for adding members to classes](/tutorials/fields), which is quite close to the normal way of declaring members, but not exactly the same. ## Hooking `init` @@ -113,7 +113,7 @@ class $modify(MenuLayer) { That's it! Now if you compiled this mod, installed it and opened the game, you would find that `MenuLayer` has turned completely blank, as we have overridden its `init` function, which means it can't add any of its nodes. You would also most likely find that the game would crash, since a lot of things depend on `MenuLayer` actually containing stuff. -And, well, we don't want to _override_ MenuLayer; we just want to append stuff to it. Luckily, from [the hooking chapter](/handbook/vol1/chap1_2.md), we know there is a tool for this: just **call the original function**! +And, well, we don't want to _override_ MenuLayer; we just want to append stuff to it. Luckily, from [the hooking chapter](/handbook/vol1/chap1_2), we know there is a tool for this: just **call the original function**! ...well, uh. How exactly do we do that? @@ -137,9 +137,9 @@ class $modify(MenuLayer) { That's it. As previously stated, this is [quite close to the standard node design pattern](#the-structure-of-a-node). -> :warning: It should be noted that not all layer's `init` functions have the same signature, and `$modify` requires the signature to **exactly match** in order to create a hook. Unfortunately, due to implementation problems, it also (currently) doesn't tell you if your signature is wrong, so you may find yourself scratching your head as to why your mod isn't working, only to realize the signature of `init` is off. Check [GeometryDash.bro](https://github.com/geode-sdk/bindings/blob/main/bindings/2.2074/GeometryDash.bro) to make sure the signature of your hook is correct! +> :warning: It should be noted that not all layer's `init` functions have the same signature, and `$modify` requires the signature to **exactly match** in order to create a hook. Unfortunately, due to implementation problems, it also (currently) doesn't tell you if your signature is wrong, so you may find yourself scratching your head as to why your mod isn't working, only to realize the signature of `init` is off. Check [GeometryDash.bro](https://github.com/geode-sdk/bindings/blob/main/bindings/2.2081/GeometryDash.bro) to make sure the signature of your hook is correct! -Now we are at an interesting point; we know how to hook functions, we know what function from a layer to hook in order to modify it, and we know how to work with nodes. So, let's tie all of this together! Only 7 chapters in, [it's time for **Hello, World!**](/handbook/vol1/chap1_7.md) +Now we are at an interesting point; we know how to hook functions, we know what function from a layer to hook in order to modify it, and we know how to work with nodes. So, let's tie all of this together! Only 7 chapters in, [it's time for **Hello, World!**](/handbook/vol1/chap1_7) ## Notes diff --git a/handbook/vol1/chap1_7.md b/handbook/vol1/chap1_7.md index f3f921b5..53dfe6a1 100644 --- a/handbook/vol1/chap1_7.md +++ b/handbook/vol1/chap1_7.md @@ -30,7 +30,7 @@ class $modify(MenuLayer) { bool init() { if (!MenuLayer::init()) return false; - + return true; } }; @@ -38,7 +38,7 @@ class $modify(MenuLayer) { ## Adding the label -Now it's time to actually show the text. As outlined in [Chapter 1.4](/handbook/vol1/chap1_4.md), Cocos2d is node-based; we don't do our own rendering, we leverage other nodes to do it for us. In this case, since we want to display text, we go for the standard `CCLabelBMFont` class: +Now it's time to actually show the text. As outlined in [Chapter 1.4](/handbook/vol1/chap1_4), Cocos2d is node-based; we don't do our own rendering, we leverage other nodes to do it for us. In this case, since we want to display text, we go for the standard `CCLabelBMFont` class: ```cpp #include @@ -49,7 +49,7 @@ class $modify(MenuLayer) { return false; auto label = cocos2d::CCLabelBMFont::create("Hello, World!", "bigFont.fnt"); - + return true; } }; @@ -68,7 +68,7 @@ class $modify(MenuLayer) { return false; auto label = CCLabelBMFont::create("Hello, World!", "bigFont.fnt"); - + return true; } }; @@ -175,7 +175,7 @@ class $modify(MenuLayer) { }; ``` -To try the mod out, [create a new mod using `geode new`](/geode/creating), and then replace the code in `src/main.cpp` with the above. After building the mod, open up GD and you should see this: +To try the mod out, [create a new mod using `geode new`](/getting-started/create-mod#creating-a-new-mod), and then replace the code in `src/main.cpp` with the above. After building the mod, open up GD and you should see this: ![Image showing the main menu in GD with a 'Hello, world' text on top](/assets/handbook/vol1/hello_world.png) @@ -183,4 +183,4 @@ If it works for you, **congratulations!** You have now officially built your fir After drinking your juice however, it's time to get back into business. So we've got a Hello, World! going, that's great. Now it's time to start crafting something actually useful. -In Volume 2 of the tutorial, we will start looking at **reverse engineering** and how making new mods actually works in practice. To start, let's once again ask the following question: [so how exactly does one make a mod?](/handbook/vol2/chap2_1.md). +In Volume 2 of the tutorial, we will start looking at **reverse engineering** and how making new mods actually works in practice. To start, let's once again ask the following question: [so how exactly does one make a mod?](/handbook/vol2/chap2_1). diff --git a/handbook/vol2/chap2_1.md b/handbook/vol2/chap2_1.md index df9de249..97214025 100644 --- a/handbook/vol2/chap2_1.md +++ b/handbook/vol2/chap2_1.md @@ -10,8 +10,8 @@ But that's enough preface. Let's start actually **REing**! There are a lot of tools GD modders use for reverse engineering, but some of the most common ones include: - * [Ghidra](https://ghidra-sre.org/) - * [IDA Pro](https://hex-rays.com/IDA-pro/) (which every modder most definitely has legally bought :wink:) + * [Ghidra](https://github.com/NationalSecurityAgency/ghidra) + * [IDA Pro](https://hex-rays.com/ida-pro) (which every modder most definitely has legally bought :wink:) * [x64dbg](https://x64dbg.com/) (Windows) * [Cheat Engine](https://cheatengine.org/) (Windows) * [ReClass](https://github.com/ReClassNET/ReClass.NET) (Windows) @@ -19,11 +19,11 @@ There are a lot of tools GD modders use for reverse engineering, but some of the * [Slicer](https://github.com/zorgiepoo/Bit-Slicer) (Mac) * [LLDB](https://lldb.llvm.org/) -For this tutorial, we will be using **Ghidra** and **x64dbg**. +For this tutorial, we will be using **Ghidra** and **x64dbg**. ## Setting Up Ghidra -First, [download Ghidra](https://ghidra-sre.org/) and install it on your machine. Open it, **create a new project**, and you should see something like this: +First, [download Ghidra](https://github.com/NationalSecurityAgency/ghidra/releases/latest) and install it on your machine. Open it, **create a new project**, and you should see something like this: ![Image showing the project page of Ghidra](/assets/handbook/vol2/ghidra_start.png) @@ -39,4 +39,4 @@ The three most important windows for our purposes are **Symbol Tree**, **Listing Now that we have Ghidra setup, it's time to learn how we can find a layer's `init` function. -[Chapter 2.2: Finding `MenuLayer::init`](/handbook/vol2/chap2_2.md) +[Chapter 2.2: Finding `MenuLayer::init`](/handbook/vol2/chap2_2) diff --git a/handbook/vol2/chap2_2.md b/handbook/vol2/chap2_2.md index c56a4bc1..43a1c21c 100644 --- a/handbook/vol2/chap2_2.md +++ b/handbook/vol2/chap2_2.md @@ -10,7 +10,7 @@ If you look at the Symbol Tree window in Ghidra, you will see it has a folder na Now, there is one problem with this list: **there are a lot of classes in GD**. Just browsing this list is most likely not going to help you figure out where the function you're looking for is. Instead, first you need to make some guess about where to start. -For finding layers' `init` functions, this guess is very easy to make: just figure out the layer's name using DevTools, and it's probably in that class. +For finding layers' `init` functions, this guess is very easy to make: just figure out the layer's name using DevTools, and it's probably in that class. So, to find the `init` function for `MenuLayer`, lets first navigate to `MenuLayer`: @@ -42,4 +42,4 @@ We can also fix the function's signature by right-clicking on it and selecting ` Congratulations, you have reverse-engineered your first function! However, let's not stop here - let's find some other, a little more obscure `init` functions: -[Chapter 2.3: Finding `LevelSettingsLayer::init`](/handbook/vol2/chap2_3.md) +[Chapter 2.3: Finding `LevelSettingsLayer::init`](/handbook/vol2/chap2_3) diff --git a/handbook/vol2/chap2_3.md b/handbook/vol2/chap2_3.md index 175b0f9c..d9ec46bb 100644 --- a/handbook/vol2/chap2_3.md +++ b/handbook/vol2/chap2_3.md @@ -75,7 +75,7 @@ And besides, what we have done know is still just educated guesses, though they Well, luckily, there is: Android! -[Chapter 2.4: Comparing against Android](/handbook/vol2/chap2_4.md) +[Chapter 2.4: Comparing against Android](/handbook/vol2/chap2_4) ## Notes diff --git a/handbook/vol2/chap2_4.md b/handbook/vol2/chap2_4.md index 66c026ab..780df86a 100644 --- a/handbook/vol2/chap2_4.md +++ b/handbook/vol2/chap2_4.md @@ -12,7 +12,7 @@ Once you have acquired the Android binary, import it to Ghidra the same way you ![Analyzing Android GD, with the `Non returning functions: discovered` option disabled](/assets/handbook/vol2/Android_analyz.png) -So, let's verify the REing we did [in the last chapter](/handbook/vol2/chap2_3.md)! +So, let's verify the REing we did [in the last chapter](/handbook/vol2/chap2_3)! Open up the class list on Android and look up `LevelSettingsLayer`: @@ -64,4 +64,4 @@ Repeat the same for `LevelEditorLayer`, and now Ghidra lets us use them as param And there we go - we can now be pretty much certain that we have found the correct `LevelSettingsLayer::init`, and that our signature is right! -[Chapter 2.5: Finding Callbacks](/handbook/vol2/chap2_5.md) +[Chapter 2.5: Finding Callbacks](/handbook/vol2/chap2_5) diff --git a/handbook/vol2/chap2_5.md b/handbook/vol2/chap2_5.md index 37329e51..abf49a97 100644 --- a/handbook/vol2/chap2_5.md +++ b/handbook/vol2/chap2_5.md @@ -64,4 +64,4 @@ Right now, reverse engineering should seem a little less mystifying, however the Before we can delve into that, we first need to learn a bit about assembly: -[Chapter 2.6: Introduction to Assembly](/handbook/vol2/chap2_6.md) +[Chapter 2.6: Introduction to Assembly](/handbook/vol2/chap2_6) diff --git a/handbook/vol4/chap4_1.md b/handbook/vol4/chap4_1.md index e623d752..6cae28ea 100644 --- a/handbook/vol4/chap4_1.md +++ b/handbook/vol4/chap4_1.md @@ -14,7 +14,7 @@ After figuring out what it is that you are actually going for, it's time to look ## Step 3: Write Some Code -Once you have an idea on what might be a route to achieving what you want, write some code to try it out. If you don't know the exact magic words to write, refer to documentation and [ask around for help](https://discord.gg/AWWCUUfeA7). +Once you have an idea on what might be a route to achieving what you want, write some code to try it out. If you don't know the exact magic words to write, refer to documentation and [ask around for help](https://discord.gg/9e43WMKzhp). ## Step 4: Test @@ -28,4 +28,4 @@ If your code doesn't work, reverse engineer a bit more to find out why, write so These may seem like completely dull and uninteresting steps for some, and perhaps like a glorified [WikiHow](https://www.youtube.com/watch?v=PSKQ3ZNQ_O8) article, but the point of that is to illustrate that there really is no specific process for making a mod. For example, reverse engineering may involve using Ghidra and IDA to analyze functions, or it may involve just looking around with DevTools. The code you write may be some in-depth template-filled C++ behemoth, or it may just be a few quick lines of test code. Testing may involve a larger group or just be you realizing you forgot to call `retain` on an array. Modding isn't an exact science; it's an art. -Even then, there are many important skills and tools every modder should get acquainted with, and in the next chapter we will look at one of the most important ones: [Reverse Engineering](/handbook/vol2/chap2_2.md) +Even then, there are many important skills and tools every modder should get acquainted with, and in the next chapter we will look at one of the most important ones: [Reverse Engineering](/handbook/vol2/chap2_2) diff --git a/index.md b/index.md index f156f5e8..bcf280dd 100644 --- a/index.md +++ b/index.md @@ -1,4 +1,4 @@ -![Geode Logo](https://github.com/geode-sdk.png?size=80) +![Geode Logo](https://github.com/geode-sdk.png?size=80) # Geode SDK @@ -10,7 +10,7 @@ The main goal of Geode is to **end mod incompatibility**. Traditional modding leads very easily to compatibility problems, many of which Geode attempts to address with better solutions. -On top of this, and perhaps more interestingly, **Geode provides much better ergonomics for modding**. Instead of having to deal with calling conventions, trampolines, manually setting hooks (likely in another source file), you can have all the code relevant to hooks in a [nice, clean, readable syntax](/tutorials/modify.md) contained within a single source file. +On top of this, and perhaps more interestingly, **Geode provides much better ergonomics for modding**. Instead of having to deal with calling conventions, trampolines, manually setting hooks (likely in another source file), you can have all the code relevant to hooks in a [nice, clean, readable syntax](/tutorials/modify) contained within a single source file. ## Help, Contributing, Etc. @@ -35,4 +35,4 @@ See [Handbook](/handbook/chap0) for a beginner-friendly tutorial series on using ### Special Thanks * [NachoBIT](https://github.com/TheNachoBIT) - * [RobTop Games](https://twitter.com/RobTopGames/) for making this amazing game and providing us and so many others with hours of entertainment ❤ + * [RobTop Games](https://twitter.com/RobTopGames) for making this amazing game and providing us and so many others with hours of entertainment ❤ diff --git a/mods/configuring.md b/mods/configuring.md index 9354e85f..81ac85e9 100644 --- a/mods/configuring.md +++ b/mods/configuring.md @@ -49,7 +49,7 @@ The target Geode version. Should be in the format of an exact version, such as ` ### `gd` -The target Geometry Dash version exactly, or `*` for **any** GD version (whether your mod actually works on any GD version is your responsibility.) +The target Geometry Dash version exactly, or `*` for **any** GD version (whether your mod actually works on any GD version is your responsibility.) This key is an object for specifying per platform GD version: ```json @@ -80,19 +80,19 @@ The name of the mod that is displayed in-game, and in other situations where the ### `version` -The version of the mod; should follow [semver](https://semver.org), especially if the mod can be used as a dependency (see [the API key](#api)). +The version of the mod; should follow [semver](https://semver.org/), especially if the mod can be used as a dependency (see [the API key](#api)). ### `developer` The name of the mod's developer, displayed on the Installed tab. Should be a single name, like "HJfod" or "Alk". If the mod has multiple developers, this should be a team name like "Geode Team". -### `developers` +### `developers` The name of the mod's developers. Replaces `developer`. Can be a single name, like \["HJfod"\] or \["Alk"\]. If the mod has multiple developers, you should list the names of each developer because, the `developers` property is a list/array. First developer listed is the main developer. If you have 3 or more developers listed, it will show `main developer + 2 more`. If you have 2 developers listed, it will show `Developer 1 & Developer 2`. If you list multiple developers, when someone clicks on your name(s), it will show the list of the developers, then you can click on one to see other mods they have made or are apart of. ### `description` -A short description of the mod. Should only be a single sentence; for longer descriptions, see [about.md](/mods/md-files.md). +A short description of the mod. Should only be a single sentence; for longer descriptions, see [about.md](/mods/md-files). ### `links` @@ -112,11 +112,11 @@ Describes where users can report problems with the mod. Value is an object with ### `dependencies` -The dependencies of a mod; see [Dependencies](/mods/dependencies.md) for details +The dependencies of a mod; see [Dependencies](/mods/dependencies) for details ### `incompatibilities` -The incompatibilities of a mod. Very similar to [dependencies](/mods/dependencies.md). +The incompatibilities of a mod. Very similar to [dependencies](/mods/dependencies). ```json "incompatibilities": { @@ -129,11 +129,11 @@ Superseding is no longer handled by incompatibilities and has been replaced by s ### `settings` -The settings of a mod; see [Settings](/mods/settings.md) for details +The settings of a mod; see [Settings](/mods/settings) for details ### `resources` -The resources of a mod; see [Resources](/mods/resources.md) for details +The resources of a mod; see [Resources](/mods/resources) for details ### `early-load` @@ -141,11 +141,11 @@ If true, specifies that this mod must have finished loading before the loading s ### `api` -Specifies that this mod can be [used as a dependency](/mods/dependencies.md). Value is an object with the following properties: +Specifies that this mod can be [used as a dependency](/mods/dependencies). Value is an object with the following properties: #### `headers` -An array specifying the list of headers that should bundled with this mod. Supports [globbing](/mods/resources.md). +An array specifying the list of headers that should bundled with this mod. Supports [globbing](/mods/resources). ### `tags` diff --git a/mods/dependencies.md b/mods/dependencies.md index 1be6a4a6..1a825ac9 100644 --- a/mods/dependencies.md +++ b/mods/dependencies.md @@ -20,7 +20,7 @@ If a dependency is required, it is **linked to**; this means that for the mod th ## Adding dependencies -Dependencies can be added to your mod by simply adding it to the `dependencies` key in [mod.json](/mods/configuring.md): +Dependencies can be added to your mod by simply adding it to the `dependencies` key in [mod.json](/mods/configuring): ```json { @@ -39,11 +39,11 @@ Dependencies can be added to your mod by simply adding it to the `dependencies` } ``` -Dependencies can be specified by using an object that maps from a mod id to a version, or more information if needed. The dependency may also be set as required, which specifies if the mod is required or [optional](#Optional-Dependencies) for the dependant mod to load. If this is not specified, the dependency is marked as required. +Dependencies can be specified by using an object that maps from a mod id to a version, or more information if needed. The dependency may also be set as required, which specifies if the mod is required or [optional](#Optional-Dependencies) for the dependant mod to load. If this is not specified, the dependency is marked as required. -The `version` field of a dependency may be written as `>=version`, `=version`, or `<=version`. The comparisons work as expected, with the addition that if the major versions are different, the comparison is always false. This means that if you depend on version `>=1.2.5` of a mod, version `v1.8.0` will be considered acceptable but `v2.0.0` will not. For this reason, [if you make a mod that is depended upon, you should follow strict semver](https://semver.org). +The `version` field of a dependency may be written as `>=version`, `=version`, or `<=version`. The comparisons work as expected, with the addition that if the major versions are different, the comparison is always false. This means that if you depend on version `>=1.2.5` of a mod, version `v1.8.0` will be considered acceptable but `v2.0.0` will not. For this reason, [if you make a mod that is depended upon, you should follow strict semver](https://semver.org/). -Once you have added a dependency to your `mod.json`, if you have [Geode CLI v1.4.0 or higher](/geode/installcli), its headers are automatically added to your project. If you have the mod installed, the headers from the installed version will be used. If you don't have the mod installed, Geode will install it from the mods index. The added dependency files for your project can be found in `build/geode-deps/`. Geode automatically adds `build/geode-deps` to your project's include path, and links whatever binaries are found in dependencies, meaning you most likely don't have to configure anything. +Once you have added a dependency to your `mod.json`, if you have [Geode CLI v1.4.0 or higher](/getting-started/geode-cli), its headers are automatically added to your project. If you have the mod installed, the headers from the installed version will be used. If you don't have the mod installed, Geode will install it from the mods index. The added dependency files for your project can be found in `build/geode-deps/`. Geode automatically adds `build/geode-deps` to your project's include path, and links whatever binaries are found in dependencies, meaning you most likely don't have to configure anything. ## Example @@ -74,30 +74,7 @@ In case the dependency is **not required**, it is not linked to, which in turn m The key system Geode provides for optional mod interop are events. For example, a mod that adds support for drag-and-dropping files on the GD window could define a drag-and-drop event that other mods can then listen to. -Usually however, events are defined in code in a way that requires linking by inheriting from the `Event` class. To avoid this, mods that want to support being used optionally should also provide events that are specializations of the `DispatchEvent` class: - -```cpp -using DragDropEvent = geode::DispatchEvent; -using DragDropFilter = geode::DispatchFilter; - -// Posting events in source -DragDropEvent("geode.drag-drop/default", "path/to/file").post(); -``` - -All `DispatchEvent`s have an associated ID, which is specific for each `DispatchEvent` specialization. This can be used to differentiate between events; for example, the drag drop API might use this to let dependencies determine which file types they listen to. - -Mods that use the dependency can now listen for drag-and-drop events: - -```cpp -$execute { - new EventListener(+[](std::filesystem::path const& path) { - log::info("File dropped: {}", path); - return ListenerResult::Propagate; - }, DragDropFilter("geode.drag-drop/default")); -}; -``` - -An example of using dispatch events in practice [can be found in MouseAPI](https://github.com/geode-sdk/MouseAPI/blob/main/src/test.cpp#L54-L94). +See the [docs page on events](/tutorials/events) for more information. ### User Objects @@ -117,19 +94,6 @@ layer->setUserObject("hjfod.cool-scrollbars/enable", CCBool::create(true)); As these are typical `CCObject`s, a mod can store any piece of information within a user object. This can range from objects as simple as a `CCBool` to more complicated, mod-specific ones that store multiple pieces of data. -Mods can also add an event listener to listen for when attributes are added/changed: - -```cpp -$execute { - new EventListener( - +[](UserObjectSetEvent* event) { - addScrollbar(event->node); - }, - AttributeSetFilter("hjfod.cool-scrollbars/enable") - ); -} -``` - ### User Flags User flags are a frequently used alternative to user objects which does not store a value. This makes them a more optimized solution for our aforementioned scrollbar example. @@ -146,8 +110,6 @@ if (layer->getUserFlag("hjfod.cool-scrollbars/enable")) { Do note that user flags and user objects are separate - `getUserFlag` and `getUserObject` are not interchangeable and need to be consistent with the setter used. -Unlike user objects, user flags do not fire an event when set. - ## Event Macro Included in [Geode v4.3.0](https://github.com/geode-sdk/geode/releases/tag/v4.3.0) is a macro to automate the process of exporting functions from API mods, in a way that works for optional dependencies. diff --git a/mods/guidelines-tips.md b/mods/guidelines-tips.md index e540432c..e3a34336 100644 --- a/mods/guidelines-tips.md +++ b/mods/guidelines-tips.md @@ -1,6 +1,6 @@ # Tips for Getting Your Mod Approved -These are an unsorted collection of some tips for making sure your mod adheres to the [index guidelines](/mods/guidelines.md). +These are an unsorted collection of some tips for making sure your mod adheres to the [index guidelines](/mods/guidelines). ## Common pitfalls @@ -24,7 +24,7 @@ While it is unavoidable in some cases (or will cause additional instability), of * Avoid reinventing the wheel. -Hooking functions, especially heavily called ones, to accomplish tasks that can be done by other means may get your mod rejected. One example would be hooking `CCScheduler::update` to run code on each frame (use [`scheduleSelector`](/classes/cocos2d/CCScheduler#scheduleSelector) instead) or to keep a node across scenes (use [`SceneManager::keepAcrossScenes`](/classes/geode/SceneManager#keepAcrossScenes)). Besides creating performance issues, this may also reduce compatibility with other mods. +Hooking functions, especially heavily called ones, to accomplish tasks that can be done by other means may get your mod rejected. One example would be hooking `CCScheduler::update` to run code on each frame (use [`scheduleSelector`](/classes/cocos2d/CCScheduler#scheduleSelector) instead) or to keep a node across scenes (use [`OverlayManager`](/classes/geode/OverlayManager) by calling `addChild`). Besides creating performance issues, this may also reduce compatibility with other mods. While this isn't typically a rejection reason, new developers tend to find themselves "reinventing the wheel" and using platform-specific functions to accomplish tasks that are already covered by Geode utils. For example, [restarting the game](/functions/geode/utils/game/restart/), [opening a file picker](/functions/geode/utils/file/pick) and [splitting a string](/functions/geode/utils/string/split) are all built into the Geode utils. These functions have the most compatibility with other mods and are tested across all supported platforms. A basic list of them can be found [here](/tutorials/utils). diff --git a/mods/md-files.md b/mods/md-files.md index 0580aa53..af97a4e4 100644 --- a/mods/md-files.md +++ b/mods/md-files.md @@ -24,7 +24,7 @@ If you like this mod, please check [my other mod](mod:my.other-mod)! ## Credits -[Join my Discord](https://discord.gg/K9Kuh3hzTC)! Thanks to [Hu Tao](https://www.youtube.com/watch?v=8oap-n_OEgc) for helping with the mod! +[Join my Discord](https://discord.gg/K9Kuh3hzTC)! Thanks to [Heinz Baked Beans](https://www.youtube.com/watch?v=-LVPjtLYia4) for helping with the mod! ``` # `changelog.md` diff --git a/mods/publishing.md b/mods/publishing.md index 83f00f5a..9acff6dc 100644 --- a/mods/publishing.md +++ b/mods/publishing.md @@ -1,13 +1,13 @@ # Publishing Geode Mods -Once your awesome mod is finished, it's time to publish it for all the world to see! Geode comes with an in-game "Download" section where users can download mods from, which gets its content from [the Geode Index](https://api.geode-sdk.org/v1/mods). +Once your awesome mod is finished, it's time to publish it for all the world to see! Geode comes with an in-game "Download" section where users can download mods from, which gets its content from [the Geode Index](https://api.geode-sdk.org/swagger/). -Make sure to [**read the submission guidelines**](/mods/guidelines/) first to avoid common mistakes. +Make sure to [**read the submission guidelines**](/mods/guidelines) first to avoid common mistakes. ## Getting Your Mod on the Repo > :warning: All mods submitted on the index **must provide the source code**! If your mod is open source, just include a link to a Github repository or equivalent. For closed source mods, see [the dedicated section](#what-about-closed-source-mods). - +` Submitting a mod to the official mod index is as follows: ### Using Geode CLI @@ -39,7 +39,7 @@ To release an update using the website, navigate to the mod page (available even If you are using GitHub releases (or any other system), **do not update an existing release** - create a new one instead. Updating an existing release **will break that version of the mod**, as the Geode package is checksummed. -Make sure to **increase your mod version** when updating it! You should be following [Semantic Versioning](https://semver.org), especially if you're developing a mod with a **public API**. +Make sure to **increase your mod version** when updating it! You should be following [Semantic Versioning](https://semver.org/), especially if you're developing a mod with a **public API**. If you are a **verified** developer, then the update will automatically be accepted onto the index, without the need of approval from an **index admin**. For non-verified developers they go through the standard approval process, which can be tracked in the same way as new mod submissions. diff --git a/mods/resources.md b/mods/resources.md index ab8546be..a71e47b8 100644 --- a/mods/resources.md +++ b/mods/resources.md @@ -6,11 +6,11 @@ description: How to include resources in Geode mods Geode mods may include any number of resources/assets in the mod package. Geode also provides utilities for automatically **generating spritesheets**, **turning TTF fonts to Bitmap ones**, and **creating Medium/Low versions of sprites**. -In order for Geode to create the resources, [you must have Geode CLI installed](/geode/installcli). +In order for Geode to create the resources, [you must have Geode CLI installed](/getting-started/geode-cli). ## Adding sprites -Adding sprites is as simple as adding the PNG files somewhere in your mod's source directory (commonly under `resources`) and then adding them to your [mod.json](/mods/configuring.md). +Adding sprites is as simple as adding the PNG files somewhere in your mod's source directory (commonly under `resources`) and then adding them to your [mod.json](/mods/configuring). Geometry Dash needs all sprites to have a High, Medium, and Low quality version for performance. **Geode assumes that the source sprites you provide are UHD quality, and creates the medium / low versions automatically.** You should only include the high quality version of your sprite in resources. diff --git a/mods/savedata.md b/mods/savedata.md index 26fe81f6..3badc142 100644 --- a/mods/savedata.md +++ b/mods/savedata.md @@ -2,11 +2,11 @@ Pretty much every mod will eventually enter the point where it needs to save some data on the user's machine, such as configuration settings or other user-customizable values. Geode has built-in support for **two kinds of savedata**: **user-editable settings** and **non-editable saved values**. -Settings are covered [in their own tutorial](/mods/settings.md) - **this tutorial is about non-user-editable save data**. +Settings are covered [in their own tutorial](/mods/settings) - **this tutorial is about non-user-editable save data**. Unlike settings, save data does not need to be declared in `mod.json`, or anywhere else for that matter. Save data is automatically brought to life when you set it for the first time. Save data is, as the name implies, saved when the game is closed, and its previous state loaded back up the next time the game is opened. -You can save any type of value as long as it implements `matjson::Serialize` (see the [STL container implementations](https://github.com/geode-sdk/json/blob/main/include/matjson/stl_serialize.hpp) for an example). +You can save any type of value as long as it implements `matjson::Serialize` (see the [STL container implementations](https://github.com/geode-sdk/json/blob/main/include/matjson/std.hpp) for an example). ## Setting & getting save data diff --git a/mods/settings-old.md b/mods/settings-old.md index 69b734e7..0feeef21 100644 --- a/mods/settings-old.md +++ b/mods/settings-old.md @@ -6,7 +6,7 @@ Geode mods can have settings, specified in `mod.json`. These settings are automa Settings all have associated with them an ID, as well as a type and default value. There are a number of built-in types for common types of settings, but custom settings are supported aswell. All settings may also have a name and description. Some setting types also provide customizability over controls, such as whether to include a slider or not. -> :information_source: Settings are intended for values the user can customize in-game. If you're looking for just saving some data, [see the tutorial for saving data](/mods/savedata.md). +> :information_source: Settings are intended for values the user can customize in-game. If you're looking for just saving some data, [see the tutorial for saving data](/mods/savedata). ## Example @@ -143,7 +143,7 @@ First, to declare a custom setting, the mod should specify that the setting's ty } ``` -The first class that the mod needs to provide for the setting is one that inherits [SettingValue](/classes/geode/SettingValue). This class manages the current value of the setting as well as saving and loading that value. +The first class that the mod needs to provide for the setting is one that inherits [SettingValue](https://github.com/geode-sdk/geode/blob/v2.0.0-beta.24/loader/include/Geode/loader/Setting.hpp#L222). This class manages the current value of the setting as well as saving and loading that value. ```cpp using namespace geode::prelude; @@ -177,7 +177,7 @@ public: }; ``` -The second class the mod needs to provide is one that inherits [SettingNode](/classes/geode/SettingNode). This should be returned by the call to `MySettingValue::createNode`. It is used by Geode to display the setting's UI. Note that the node should only set the value of the setting when `commit` is called; this allows users to undo accidental changes. +The second class the mod needs to provide is one that inherits [SettingNode](https://github.com/geode-sdk/geode/blob/v2.0.0-beta.24/loader/include/Geode/loader/SettingNode.hpp). This should be returned by the call to `MySettingValue::createNode`. It is used by Geode to display the setting's UI. Note that the node should only set the value of the setting when `commit` is called; this allows users to undo accidental changes. The setting node is provided the current width of the setting layer available in its `init` function; the height of the setting may be as large or as small as it needs. **You must set the content size of the setting node** in order to let Geode know the height of your setting. @@ -246,7 +246,7 @@ public: }; ``` -The last this the mod needs to do is register their setting value class to Geode. This should be done immediately when the mod is loaded, to ensure that the value of the setting is loaded from disk on time. Registering can either be done using the raw [registerCustomSetting](/classes/geode/Mod#registerCustomSetting) method, or the [addCustomSetting](/classes/geode/Mod#addCustomSetting) convenience function. +The last this the mod needs to do is register their setting value class to Geode. This should be done immediately when the mod is loaded, to ensure that the value of the setting is loaded from disk on time. Registering can either be done using the raw [registerCustomSetting](https://github.com/geode-sdk/geode/blob/v2.0.0-beta.24/loader/include/Geode/loader/Mod.hpp#L148) method, or the [addCustomSetting](https://github.com/geode-sdk/geode/blob/v2.0.0-beta.24/loader/include/Geode/loader/Mod.hpp#L162) convenience function. ```cpp $on_mod(Loaded) { @@ -260,4 +260,4 @@ $on_mod(Loaded) { } ``` -You can find an example of custom settings [in the test dependency mod in Geode](https://github.com/geode-sdk/geode/blob/main/loader/test/dependency/main.cpp#L60-L145). +You can find an example of custom settings [in the test dependency mod in Geode](https://github.com/geode-sdk/geode/blob/main/loader/test/dependency/main.cpp#L20-L133). diff --git a/mods/settings.md b/mods/settings.md index c81631cd..96422af2 100644 --- a/mods/settings.md +++ b/mods/settings.md @@ -6,7 +6,7 @@ Mods will often want to have properties that the user can configure, such as how All settings have an associated key, or ID. This is the key of the settings object defined in `mod.json`. Each setting is defined as a JSON object, which contains at least one key: `type`. This key defines the type of the setting, including a wide range of built-in types but also the `custom:` prefix for [defining your own types](#custom-settings). -> :information_source: Settings are intended for values the user can customize in-game. If you're looking for just saving some arbitary data in a quick cross-platform way, [see the tutorial for saving data](/mods/savedata.md). +> :information_source: Settings are intended for values the user can customize in-game. If you're looking for just saving some arbitary data in a quick cross-platform way, [see the tutorial for saving data](/mods/savedata). ![An image showcasing the settings popup for the BetterEdit mod by HJfod](/assets/settings/popup.png) @@ -14,7 +14,7 @@ All settings have an associated key, or ID. This is the key of the settings obje > :information_source: It is heavily recommended to have [the Geode VS Code Extension](/getting-started/ide-setup) installed, as it gives you automatic code completion and checking for settings in `mod.json`. -Adding new settings to your mod happens through `mod.json`. Define the `settings` key as an object and then list out the settings you want to have: +Adding new settings to your mod happens through `mod.json`. Define the `settings` key as an object and then list out the settings you want to have: ```json { // mod.json @@ -155,7 +155,7 @@ Integer settings are a whole number. By default, they have a slider, arrows to i "control": { // Enable the small (green) arrow controls "arrows": true, - // Control how much the small (green) arrows should increment/decrement + // Control how much the small (green) arrows should increment/decrement // the setting's value when clicked "arrow-step": 1, // Enable the secondary (pink) arrow controls @@ -197,7 +197,7 @@ Float settings are just like int settings, but for floats! "control": { // Enable the small (green) arrow controls "arrows": true, - // Control how much the small (green) arrows should increment/decrement + // Control how much the small (green) arrows should increment/decrement // the setting's value when clicked "arrow-step": 0.1, // Enable the secondary (pink) arrow controls @@ -235,10 +235,10 @@ String settings are simple strings that can be controlled by character limits or "default": "Stephen", // A regex pattern the setting must match "match": "[A-Z][a-z]+", - // A list of characters this setting can consist of, same as the + // A list of characters this setting can consist of, same as the // CCTextInputNode character filter "filter": "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ", - // A list of possible values. Turns the setting into an enum, where the + // A list of possible values. Turns the setting into an enum, where the // user can simply use arrows to navigate the list of values "one-of": ["Stephen", "George", "Sarah", "Tiffany"] } @@ -280,10 +280,10 @@ The default value for file settings support a set of known path prefixes to allo "android": "{gd_dir}/groovy.so" }, "control": { - // The dialog to show when the user clicks the file selection button in the + // The dialog to show when the user clicks the file selection button in the // UI. Either "save" or "open" - defaults to "open" "dialog": "open", - // Configure file filters in the open dialog. An "All (*.*)" filter is + // Configure file filters in the open dialog. An "All (*.*)" filter is // added implicitly "filters": [ { @@ -416,7 +416,7 @@ protected: A list of keybind names can be found [in the Geode repository](https://github.com/geode-sdk/geode/blob/main/loader/src/utils/Keyboard.cpp#L6). The supported modifier keys are: -- "Control" or "Ctrl" for the Control key +- "Control" or "Ctrl" for the Control key - "Shift" for the Shift key - "Alt", "Opt", or "Option" for the Alt key (Option on macOS/iOS) - "Super", "Cmd", "Command", "Win", or "Windows" for the Super/Windows key (Command on macOS/iOS) @@ -492,7 +492,7 @@ Custom setting types must define two classes: a `SettingV3`-based definition cla using namespace geode::prelude; -// Settings should have some way to store their value. Here we use an enum, but +// Settings should have some way to store their value. Here we use an enum, but // you can of course use a more complicated type aswell! // The main point is that the type *must implement `matjson::Serialize`*. enum class MyCustomEnum { @@ -500,38 +500,38 @@ enum class MyCustomEnum { OtherValidEnumValue, }; -// The type of the setting must implement `matjson::Serialize` in order to use +// The type of the setting must implement `matjson::Serialize` in order to use // `SettingBaseValueV3` template <> struct matjson::Serialize { - // See https://github.com/geode-sdk/test-mod/blob/main/src/MyCustomSettingV3.cpp#L12-L30 + // See https://github.com/geode-sdk/test-mod/blob/main/src/MyCustomSettingV3.cpp#L12-L30 // for the definition }; -// This is the most important part of the setting - this actually defines what -// properties the setting can have in `mod.json`, as well as keeps track of the +// This is the most important part of the setting - this actually defines what +// properties the setting can have in `mod.json`, as well as keeps track of the // setting's current value. -// You can inherit directly for `SettingV3`, but for most cases you'll want to -// inherit from `SettingBaseValueV3` instead, as it automatically provides the -// `"default"` property, defines most of the required virtuals from `SettingV3`, +// You can inherit directly for `SettingV3`, but for most cases you'll want to +// inherit from `SettingBaseValueV3` instead, as it automatically provides the +// `"default"` property, defines most of the required virtuals from `SettingV3`, // and provides getters and setters for the current value. -// Using `SettingBaseValueV3` requires the value type to implement +// Using `SettingBaseValueV3` requires the value type to implement // `matjson::Serialize`. -// For an example on how to inherit directly from `SettingV3` (for example for +// For an example on how to inherit directly from `SettingV3` (for example for // cosmetic purposes), see below for the `MyButtonSettingV3` example class MyCustomSettingV3 : public SettingBaseValueV3 { protected: - // We don't need to store the value nor the default value ourselves, as + // We don't need to store the value nor the default value ourselves, as // `SettingBaseValueV3` handles that for us :3 - // However, we will add an additional property for the sake of showcasing - // how one would deal with that. This property simply marks whether the + // However, we will add an additional property for the sake of showcasing + // how one would deal with that. This property simply marks whether the // setting should be "splorgy", which by default is false. bool m_splorgy = false; public: - // All settings must provide a `parse` function for parsing the setting - // from its definition in `mod.json`. The signature must match this exactly, - // although the return type should point to a `SettingV3`-derivative rather + // All settings must provide a `parse` function for parsing the setting + // from its definition in `mod.json`. The signature must match this exactly, + // although the return type should point to a `SettingV3`-derivative rather // than `std::shared_ptr` directly static Result> parse( // The key of the setting, as defined in `mod.json` @@ -543,42 +543,42 @@ public: ) { auto res = std::make_shared(); - // It is recommended to use the JSON checking utilities in Geode for - // actually parsing the setting, as this deals with type & error + // It is recommended to use the JSON checking utilities in Geode for + // actually parsing the setting, as this deals with type & error // handling really nicely auto root = checkJson(json, "MyCustomSettingV3"); - // You should basically always call `parseBaseProperties`, as this will - // deal with all of the base keys like `"name"`, `"description"`, etc. - // If you derive from `SettingBaseValueV3`, this will also parse the + // You should basically always call `parseBaseProperties`, as this will + // deal with all of the base keys like `"name"`, `"description"`, etc. + // If you derive from `SettingBaseValueV3`, this will also parse the // `"default"` key appropriately. - // There is also an overload for parsing directly from JSON if you don't - // want to use Geode's JSON checking utilities, as well as a fourth bool - // argument for parsing just the `"name"` and `"description"` properties + // There is also an overload for parsing directly from JSON if you don't + // want to use Geode's JSON checking utilities, as well as a fourth bool + // argument for parsing just the `"name"` and `"description"` properties // if your setting is only cosmetic. - - // If your setting doesn't even have a name or description, you must at + + // If your setting doesn't even have a name or description, you must at // the very least call `res->init(key, modID)`! res->parseBaseProperties(key, modID, root); - // This is where we parse any properties specific to this setting. In + // This is where we parse any properties specific to this setting. In // our case, that will be splorgyness, which is optionally defined: root.has("splorgy").into(res->m_splorgy); - // This logs a warning into the console if the object contains keys you + // This logs a warning into the console if the object contains keys you // haven't defined. You can remove it if you don't like logspam root.checkUnknownKeys(); // Return the resulting instance, or an Err if the JSON parsing failed return root.ok(std::static_pointer_cast(res)); } - - // This is defined at the end of the file, as it needs to know the + + // This is defined at the end of the file, as it needs to know the // definition of the `MyCustomSettingNodeV3` class SettingNodeV3* createNode(float width) override; }; -// If you want to be able to use `Mod::getSettingValue` with your custom setting, +// If you want to be able to use `Mod::getSettingValue` with your custom setting, // specialize this type! // Afterwards, you can simply do `Mod::get()->getSettingValue("my-setting")` template <> @@ -586,22 +586,22 @@ struct geode::SettingTypeForValueType { using SettingType = MyCustomSettingV3; }; -// The second most important part of the setting is defining the node for the -// UI. Again, here we use a helper class, which automatically defines a bunch -// of the required virtual functions for us. Note that using -// `SettingValueNodeV3` requires the setting type to implement +// The second most important part of the setting is defining the node for the +// UI. Again, here we use a helper class, which automatically defines a bunch +// of the required virtual functions for us. Note that using +// `SettingValueNodeV3` requires the setting type to implement // `SettingBaseValueV3`! class MyCustomSettingNodeV3 : public SettingValueNodeV3 { protected: - // If we use `SettingValueNodeV3`, we once again don't need to track the - // current value ourselves - we just use the `getValue` and `setValue` + // If we use `SettingValueNodeV3`, we once again don't need to track the + // current value ourselves - we just use the `getValue` and `setValue` // functions! std::vector m_toggles; bool init(std::shared_ptr setting, float width) { if (!SettingValueNodeV3::init(setting, width)) return false; - + // Create toggles for each of our enum options for (auto value : { std::make_pair(MyCustomEnum::ValidEnumValue, "GJ_starsIcon_001.png"), @@ -616,30 +616,30 @@ protected: toggle->m_notClickable = true; toggle->setTag(static_cast(value.first)); m_toggles.push_back(toggle); - - // You should add all of your menu components to the menu returned by + + // You should add all of your menu components to the menu returned by // `this->getButtonMenu()` this->getButtonMenu()->addChild(toggle); } - // You should resize the menu to fit your content, as this will - // automatically resize the name label to give it the right amount of + // You should resize the menu to fit your content, as this will + // automatically resize the name label to give it the right amount of // space this->getButtonMenu()->setContentWidth(40); this->getButtonMenu()->setLayout(RowLayout::create()); // Remember to always call `updateState` at the end of your init! this->updateState(nullptr); - + return true; } - + void updateState(CCNode* invoker) override { // Remember to always call the base class's `updateState`! SettingValueNodeV3::updateState(invoker); - // If your setting supports "enable-if" schemes, you should make sure - // that the UI is updated to reflect that. If `shouldEnable` returns + // If your setting supports "enable-if" schemes, you should make sure + // that the UI is updated to reflect that. If `shouldEnable` returns // false, all toggles and buttons should be grayed out and disabled! auto shouldEnable = this->getSetting()->shouldEnable(); @@ -660,7 +660,7 @@ protected: this->setValue( // Get the value of the toggle static_cast(sender->getTag()), - // `setValue` eventually calls `updateState`, so it needs to know + // `setValue` eventually calls `updateState`, so it needs to know // what the invoker of the state update was static_cast(sender) ); @@ -697,16 +697,16 @@ If you want to store a more complex value (e.g. a string or a custom class/struc ```cpp struct MyComplexSettingValue { - // In case you're wondering why we can't just define a custom setting with - // `std::string` as the value type: that would conflict with the built-in - // string setting, so `Mod::getSettingValue()` would + // In case you're wondering why we can't just define a custom setting with + // `std::string` as the value type: that would conflict with the built-in + // string setting, so `Mod::getSettingValue()` would // unexpectedly fail std::string value; // Make sure your value type is comparable bool operator==(MyComplexSettingValue const& other) const = default; - // If your value type is a thin wrapper around another type, you can allow + // If your value type is a thin wrapper around another type, you can allow // implicit conversions from your type to the wrapped type operator std::string() const { return value; @@ -722,12 +722,12 @@ struct MyComplexSettingValue { // You'll have to manually implement JSON serialization for the value template<> struct matjson::Serialize { - // Serialize the value into JSON. In this case, we just return the value, + // Serialize the value into JSON. In this case, we just return the value, // as strings are inherently JSON-serializable static matjson::Value toJson(MyComplexSettingValue const& settingValue) { return settingValue.value; } - // Deserialize the value from JSON, again taking advantage of strings being + // Deserialize the value from JSON, again taking advantage of strings being // inherently JSON-serializable static Result fromJson(matjson::Value const& json) { GEODE_UNWRAP_INTO(auto str, json.asString()); @@ -753,7 +753,7 @@ Custom settings do not necessarily need to inherit from the `SettingValueNodeV3< using namespace geode::prelude; -// Inherit from SettingV3 directly over SettingBaseValueV3, as our setting +// Inherit from SettingV3 directly over SettingBaseValueV3, as our setting // doesn't actually control any value, but it just a simple button class MyButtonSettingV3 : public SettingV3 { public: @@ -762,22 +762,22 @@ public: auto res = std::make_shared(); auto root = checkJson(json, "MyButtonSettingV3"); - // `parseBaseProperties` parses all base properties, including - // `requires-restart` - which does not really make sense in our use case, + // `parseBaseProperties` parses all base properties, including + // `requires-restart` - which does not really make sense in our use case, // as there's nothing in this setting that could require a restart. - // So, we instead parse the base properties that actually apply to this + // So, we instead parse the base properties that actually apply to this // setting manually: res->init(key, modID, root); res->parseNameAndDescription(root); res->parseEnableIf(root); - + root.checkUnknownKeys(); return root.ok(std::static_pointer_cast(res)); } // Since there's no data to save or load, these can just return true - // Although you could use these for example to store how many times the - // button has been clicked + // Although you could use these for example to store how many times the + // button has been clicked bool load(matjson::Value const& json) override { return true; } @@ -785,7 +785,7 @@ public: return true; } - // This setting can't ever have anything but the default value, as it has + // This setting can't ever have anything but the default value, as it has // no value bool isDefaultValue() const override { return true; @@ -796,8 +796,8 @@ public: SettingNodeV3* createNode(float width) override; }; -// We are inheriting from `SettingNodeV3` directly again, as we don't need the -// boilerplate `SettingValueNodeV3` fills in for us because our setting has no +// We are inheriting from `SettingNodeV3` directly again, as we don't need the +// boilerplate `SettingValueNodeV3` fills in for us because our setting has no // value! class MyButtonSettingNodeV3 : public SettingNodeV3 { protected: @@ -807,9 +807,9 @@ protected: bool init(std::shared_ptr setting, float width) { if (!SettingNodeV3::init(setting, width)) return false; - + // We just create the button and add it to the setting's menu - + m_buttonSprite = ButtonSprite::create("Click Me!", "goldFont.fnt", "GJ_button_01.png", .8f); m_buttonSprite->setScale(.5f); m_button = CCMenuItemSpriteExtra::create( @@ -820,15 +820,15 @@ protected: this->getButtonMenu()->updateLayout(); this->updateState(nullptr); - + return true; } - + void updateState(CCNode* invoker) override { SettingNodeV3::updateState(invoker); - // In case there's an "enable-if" condition on this button, for example - // if this played a test notification in a notification mod and you + // In case there's an "enable-if" condition on this button, for example + // if this played a test notification in a notification mod and you // want to disable the button if notifications are disabled entirely auto shouldEnable = this->getSetting()->shouldEnable(); m_button->setEnabled(shouldEnable); @@ -848,7 +848,7 @@ protected: )->show(); } - // Both of these can just be no-ops, since they make no sense for our + // Both of these can just be no-ops, since they make no sense for our // setting as it's just a button void onCommit() override {} void onResetToDefault() override {} @@ -864,7 +864,7 @@ public: return nullptr; } - // Both of these can just return false, since they make no sense for our + // Both of these can just return false, since they make no sense for our // setting as it's just a button bool hasUncommittedChanges() const override { return false; @@ -873,8 +873,8 @@ public: return false; } - // This is not necessary, but it makes it so you don't have to do the - // pointer cast every time you want to access the properties of the button + // This is not necessary, but it makes it so you don't have to do the + // pointer cast every time you want to access the properties of the button // setting std::shared_ptr getSetting() const { return std::static_pointer_cast(SettingNodeV3::getSetting()); diff --git a/tutorials/coroutines.md b/tutorials/coroutines.md index f4e055f3..87944553 100644 --- a/tutorials/coroutines.md +++ b/tutorials/coroutines.md @@ -1,6 +1,6 @@ # Coroutines -[Coroutines](https://en.cppreference.com/w/cpp/language/coroutines) are an underutilized feature of C++20 that not many understand. Under the hood, they are very complex, but luckily Geode makes it quite simple for you. Geode lets you leverage the power of coroutines to write clean asynchronous code, tackle Result propagation, and build Python-style generators with ease. +[Coroutines](https://en.cppreference.com/cpp/language/coroutines) are an underutilized feature of C++20 that not many understand. Under the hood, they are very complex, but luckily Geode makes it quite simple for you. Geode lets you leverage the power of coroutines to write clean asynchronous code, tackle Result propagation, and build Python-style generators with ease. ## Task diff --git a/tutorials/dictionary.md b/tutorials/dictionary.md index 7e0f807a..4da761f9 100644 --- a/tutorials/dictionary.md +++ b/tutorials/dictionary.md @@ -10,13 +10,13 @@ The location in the [raw program binary](#binary) where a function or other prog A "human-readable" form of [binary code](#binary). -> Further reading: [Wikipedia](https://en.m.wikipedia.org/wiki/Assembly_language) +> Further reading: [Wikipedia](https://en.wikipedia.org/wiki/Assembly_language) ## Binary Depending on context, may refer to the **program binary** of GD, aka GD's raw machine code found in `GeometryDash.exe` on Windows, or just **binary data** aka 1s and 0s in general. -> Further reading: [Wikipedia](https://en.m.wikipedia.org/wiki/Machine_code) +> Further reading: [Wikipedia](https://en.wikipedia.org/wiki/Machine_code) ## CacaoSDK @@ -42,19 +42,19 @@ A [debugger](#debugger) commonly used by Windows modders. The game engine used by GD. The version used by GD is Cocos2d-x v2.2.3, however with many propietary modifications by RobTop, meaning that you need to use a specially prepared version that accounts for these changes such as [Geode](#geode) or [cocos-headers](#cocos-headers) in order to make mods. -> [Homepage](https://github.com/cocos2d/cocos2d-x/tree/cocos2d-x-2.2.3/) +> [Source](https://github.com/cocos2d/cocos2d-x/tree/cocos2d-x-2.2.3/) ## Cocos-headers A popular library used in [traditional mods](#traditional-modding) consisting of Cocos2d headers with many of RobTop's modifications added. -> [Homepage](https://github.com/HJfod/cocos-headers) +> [Source](https://github.com/HJfod/cocos-headers) ## Debugger A tool used for debugging another application (usually GD) for issues (usually crashes), such as [x64dbg](#x64dbg) or [Cheat Engine](#cheat-engine). -> Further reading: [Wikipedia](https://en.m.wikipedia.org/wiki/Debugger) +> Further reading: [Wikipedia](https://en.wikipedia.org/wiki/Debugger) ## Detour @@ -66,7 +66,7 @@ The code you wrote that is run instead of the original function in a [hook](#hoo The central [manager class](#manager) in Cocos2d that manages the current [scene](#scene) and the nodes in it. -> Further reading: [Cocos2d docs](https://docs.cocos2d-x.org/cocos2d-x/v4/en/basic_concepts/director.html) +> Further reading: [Cocos2d Docs](https://docs.cocos2d-x.org/cocos2d-x/v4/en/basic_concepts/director.html) (Note: These are for Cocos v4, which is more up-to-date than GD's, 2.2.3, so the syntax may differ!) > Related words: [Node tree](#node-tree), [Scene](#scene), [Node](#node), [Menu](#menu), [Popup](#popup), [Layer](#layer) @@ -76,17 +76,17 @@ A different name for mods that is usually used to refer to [Mega Hack](#mega-hac ## GD.h -A popular library used in [traditional mods](#traditional-modding) that contains many GD functions and classes. Originally started by [PoweredByPie](https://github.com/poweredbypie), but the most popular version is the fork by [HJfod](https://github.com/HJfod). +A popular library used in [traditional mods](#traditional-modding) that contains many GD functions and classes. Originally started by [PoweredByPie](https://github.com/poweredbypie), but the most popular version is the [fork by HJfod](https://github.com/HJfod/gd.h). -> [Homepage](https://github.com/HJfod/gd.h) +> [Source](https://github.com/HJfod/gd.h) > Related words: [Geode](#geode), [Traditional modding](#traditional-modding) ## Geode -A framework meant as a replacement for [traditional modding](#traditional-modding) with the aim of standardizing many aspects of modding and fixing [mod incompatabilities](#mod-incompatabilities). +A framework meant as a replacement for [traditional modding](#traditional-modding) with the aim of standardizing many aspects of modding and fixing [mod incompatibilities](#mod-incompatibilities). -> [Homepage](https://github.com/geode-sdk/geode) +> [Homepage](https://geode-sdk.org/) ## Geometry Dash @@ -96,11 +96,11 @@ It's the game. A free tool many modders use for [reverse engineering](#reverse-engineering). -> [Homepage](https://ghidra-sre.org/) +> [Source](https://github.com/nationalsecurityagency/ghidra) ## .GMD Files -A file format for storing GD levels, popularized by [GDShare](https://github.com/HJfod/GDShare-mod/). The level data is stored in plain text as its [Plist data](#plist-files) with a surrounding `` tag. +A file format for storing GD levels, popularized by [GDShare](https://geode-sdk.org/mods/hjfod.gdshare). The level data is stored in plain text as its [Plist data](#plist-files) with a surrounding `` tag. ## .GMD2 Files @@ -112,7 +112,7 @@ Another, more complex file format for storing GD levels, popularized by [GDShare Hijacking the execution of a function in GD and redirecting it to [your own function](#detour). The part of a mod that actually does the modifying; the most fundamental modding concept alongside [patching](#patching). -> Further reading: [Handbook](/handbook/vol1/chap1_2.md), [Wikipedia](https://en.m.wikipedia.org/wiki/Hooking) +> Further reading: [Handbook](/handbook/vol1/chap1_2), [Wikipedia](https://en.wikipedia.org/wiki/Hooking) > Related words: [Detour](#detour), [Patching](#patching), [Trampoline](#trampoline) @@ -130,7 +130,7 @@ An old, never-released framework for creating GD mods. An important influencer t A [paid](https://www.youtube.com/watch?v=i8ju_10NkGY) tool many modders use for [reverse engineering](#reverse-engineering). -> [Homepage](https://hex-rays.com/IDA-pro/) +> [Homepage](https://hex-rays.com/ida-pro) ## Layer @@ -172,9 +172,9 @@ A popular library used for [hooking](#hooking) in [traditional mods](#traditiona A program that modifies GD by adding, changing, and removing content using various techniques such as [hooking](#hooking) and [patching](#patching). [Traditionally made](#traditional-modding) using libraries such as [gd.h](#gdh) and [cocos-headers](#cocos-headers), but may also be made using a framework such as [Geode](#geode). -## Mod Incompatabilities +## Mod Incompatibilities -When two mods become incompatible with each other, for example through a [hook conflict](#hook-conflict) or [accessing the node tree through absolute indices](/tutorials/nodetree.md). +When two mods become incompatible with each other, for example through a [hook conflict](#hook-conflict) or [accessing the node tree through absolute indices](/tutorials/nodetree). ## Mod Loader @@ -190,7 +190,7 @@ An instance of the `CCNode` class or its subclass. A singular visual element on The hierarchy of [nodes](#node) that determines the order of visual elements on screen. At the top of the node tree sits a [scene](#scene), which contains child [layers](#layer) that in turn contain other child nodes. -> Further reading: [Cocos2d docs](https://docs.cocos2d-x.org/cocos2d-x/v4/en/basic_concepts/scene.html) +> Further reading: [Cocos2d Docs](https://docs.cocos2d-x.org/cocos2d-x/v4/en/basic_concepts/scene.html) (Note: These are for Cocos v4, which is more up-to-date than GD's, 2.2.3, so the syntax may differ!) ## Optcall @@ -208,7 +208,7 @@ Also known as **byte patching**; the act of directly changing GD's program code A file format used by GD, invented by Apple, that stores data as XML organized into key-value pairs. -> Further reading: [Wikipedia](https://en.m.wikipedia.org/wiki/Property_list) +> Further reading: [Wikipedia](https://en.wikipedia.org/wiki/Property_list) ## Popup @@ -220,19 +220,19 @@ A [layer](#layer) that visually sits on top of another layer. Usually inherits f When two programs try to affect the same thing simultaniously and the result depends on the order in which they finish. Most commonly encountered in the form of [hook conflicts](#hook-conflict). -> Further reading: [Wikipedia](https://en.m.wikipedia.org/wiki/Race_condition) +> Further reading: [Wikipedia](https://en.wikipedia.org/wiki/Race_condition) ## Register A fixed-length memory location in a CPU that can store a small amount of data. Usually, for a CPU to operate on data that data has to be stored in a register. -> Further reading: [Wikipedia](https://en.m.wikipedia.org/wiki/Processor_register) +> Further reading: [Wikipedia](https://en.wikipedia.org/wiki/Processor_register) ## Reverse Engineering The process of disassembling, decompiling, and understanding how GD works. Usually done using specialized tools such as [Ghidra](#ghidra) or [IDA Pro](#ida-pro), but may also be done using [debuggers](#debugger), trial-and-error modding, or just playing GD. -> Further reading: [Wikipedia](https://en.m.wikipedia.org/wiki/Reverse_engineering) +> Further reading: [Wikipedia](https://en.wikipedia.org/wiki/Reverse_engineering) ## Scene @@ -246,7 +246,7 @@ The node that is at the top of the [node tree](#node-tree). There may only be on A last-in first-out datastructure used for storing program memory. Way too complex of a topic for a simple dictionary. -> Further reading: [Wikipedia](https://en.m.wikipedia.org/wiki/Stack_(abstract_data_type)), [Stack Overflow](https://stackoverflow.com/questions/556714/how-does-the-stack-work-in-assembly-language) +> Further reading: [Wikipedia](https://en.wikipedia.org/wiki/Stack_(abstract_data_type)), [Stack Overflow](https://stackoverflow.com/questions/556714/how-does-the-stack-work-in-assembly-language) ## Thiscall @@ -274,7 +274,7 @@ Also, what most GD modders do once they become experts. Code where the C++ standard does not say what should happen, and as such the results are completely unpredictable. Your code is most likely full of this. -> Further reading: [cppreference.com](https://en.cppreference.com/w/cpp/language/ub) +> Further reading: [cppreference.com](https://en.cppreference.com/cpp/language/ub) ## x64dbg diff --git a/tutorials/events.md b/tutorials/events.md index 0c8822fe..78b2c715 100644 --- a/tutorials/events.md +++ b/tutorials/events.md @@ -6,7 +6,7 @@ For most things in GD, such as `CCTextInputNode`, GD and Cocos2d-x use a **deleg As an alternative to this system, Geode introduces **events**. Events are essentially just small messages broadcast across the whole system: instead of having to install a single delegate, an unlimited number of classes can listen for events. The target that emits the events does not need to have any knowledge of its consumers; it just broadcasts events, and any receivers there are can handle them. -Events are primarily interacted with through three classes: [`Event`](/classes/geode/Event), [`EventListener`](/classes/geode/EventListener), and [`EventFilter`](/classes/geode/EventFilter). `Event` is the base class for the events that are being broadcast; `EventListener` listens for events, and it uses an `EventFilter` to decide which events to listen to. Let's explore how they work through **an example**. +Events are primarily interacted with through three classes: [`Event`](https://github.com/geode-sdk/geode/blob/v4.10.2/loader/include/Geode/loader/Event.hpp#L241), [`EventListener`](https://github.com/geode-sdk/geode/blob/v4.10.2/loader/include/Geode/loader/Event.hpp#L134), and [`EventFilter`](https://github.com/geode-sdk/geode/blob/v4.10.2/loader/include/Geode/loader/Event.hpp#L97). `Event` is the base class for the events that are being broadcast; `EventListener` listens for events, and it uses an `EventFilter` to decide which events to listen to. Let's explore how they work through **an example**. ## Creating events @@ -232,7 +232,7 @@ EventListener m_listener = { When our `DragDropNode` is destroyed, the EventListener is automatically destroyed and unregistered aswell, so you don't need to do anything else. -However, using a member function is not always possible. For example, if you're hooking a class, [event listeners don't work in fields](/tutorials/fields/#note-about-addresses); or if you want to listen for events on an existing node whose class you don't control. +However, using a member function is not always possible. For example, if you're hooking a class, [event listeners don't work in fields](/tutorials/fields#note-about-addresses); or if you want to listen for events on an existing node whose class you don't control. In these cases, there exists a Geode-specific helper called [`CCNode::addEventListener`](/classes/cocos2d/CCNode#addEventListener). You can use this to **add event listeners to any node** - including existing ones by GD! @@ -254,4 +254,4 @@ Any event listener added with `addEventListener` is automatically destroyed aswe ## Dispatched events -There also exist special types of events called **dispatch events** - these are intended for use within optional dependencies. See [the tutorial on dependencies](/mods/dependencies/#events) for more information. +There also exist special types of events called **dispatch events** - these are intended for use within optional dependencies. See [the tutorial on dependencies](/mods/dependencies#events) for more information. diff --git a/tutorials/hookpriority.md b/tutorials/hookpriority.md index abef58d8..41ab46de 100644 --- a/tutorials/hookpriority.md +++ b/tutorials/hookpriority.md @@ -4,7 +4,7 @@ Hook priority allows for a developer to control when their hook will be called i > :warning: This is advanced functionality! Most developers will not need to touch this system. -The syntax, as previously mentioned in the [Hooking tutorial](/tutorials/modify.md), looks like this: +The syntax, as previously mentioned in the [Hooking tutorial](/tutorials/modify), looks like this: ```cpp class $modify(cocos2d::CCLabelBMFont) { @@ -113,7 +113,7 @@ This section details some additional tricks to the hook priority system. - Pre/Post values embedded into `Priority::` do exist, but they are not that recommended as arithmetic on them can be confusing. (Positive becomes negative for Post) -- Manual hooks can have their priorities set through the [Hook::setPriority](/classes/geode/Hook/#setPriority) method. +- Manual hooks can have their priorities set through the [Hook::setPriority](/classes/geode/Hook#setPriority) method. - It is not possible to set the priorities of multiple functions with overloaded parameters in the same modify class (as this would require specifying arguments). In this situation, either split the hooks into separate modify classes or manually hook. diff --git a/tutorials/layouts.md b/tutorials/layouts.md index 5e1908fe..db5e1cc1 100644 --- a/tutorials/layouts.md +++ b/tutorials/layouts.md @@ -4,7 +4,7 @@ One of the biggest pains in all UI design in general is positioning stuff. This The solution Geode introduces for this are **layouts** - small classes added to CCNodes that dictate how that node's children should be positioned. For example, a row layout would lay its children in a horizontal row, whereas a grid layout would lay its children out in a grid. Although, perhaps surprisingly, Geode only adds one type of layout: the [AxisLayout](/classes/geode/AxisLayout) class, which actually handles all common types of layouts, including row, column, grid, and flex. -You can view what layouts are visible on screen using the [DevTools](https://github.com/geode-sdk/devtools) mod, which has an option to show all layouts in the current scene. +You can view what layouts are visible on screen using the [DevTools](https://github.com/geode-sdk/DevTools) mod, which has an option to show all layouts in the current scene. ![Image of the DevTools mod in GD, showcasing the layouts in MenuLayer](/assets/DevTools_layouts.png) @@ -17,7 +17,7 @@ struct $modify(MenuLayer) { bool init() { if (!MenuLayer::init()) return false; - + auto menu = this->getChildByID("bottom-menu"); menu->addChild(/* create button */); menu->updateLayout(); @@ -42,7 +42,7 @@ Geode only adds one type of layout, [AxisLayout](/classes/geode/AxisLayout), whi By default, AxisLayout arranges its children in a single straight line. This can be changed using the [setGrowCrossAxis](/classes/geode/AxisLayout#setGrowCrossAxis) method. -You can play around with the different options AxisLayout offers using the [DevTools](https://github.com/geode-sdk/devtools) mod. +You can play around with the different options AxisLayout offers using the [DevTools](https://github.com/geode-sdk/DevTools) mod. ![Image of the DevTools mod, showing the different options for AxisLayout](/assets/DevTools_layoutAttributes.png) diff --git a/tutorials/logging.md b/tutorials/logging.md index 26f36250..81e431b4 100644 --- a/tutorials/logging.md +++ b/tutorials/logging.md @@ -1,6 +1,6 @@ # Logging -Geode provides a builtin logger with [fmtlib](https://fmt.dev/latest/index.html) formatting, which you can view in real time via the [platform console](#platform-console), or later on by checking the logs in the: +Geode provides a builtin logger with [fmtlib](https://fmt.dev/latest/) formatting, which you can view in real time via the [platform console](#platform-console), or later on by checking the logs in the: * `/geode/logs` directory on `Windows` and `MacOS` * `Android/media/com.geode.launcher/game/geode/logs` directory on `Android` @@ -46,10 +46,10 @@ geode::log::debug("Found node: {}", node->getID()); > :warning: Debug logs are not shown by default. See the [Log filtering](#log-filtering) section to see how you can change this behavior. ## Syntax -The logger uses [fmtlib](https://fmt.dev/latest/syntax.html) under the hood, which means you can use its syntax, which will get forwarded to fmtlib. Geode also provides formatters for a few common types, such as `CCPoint`, `CCNode`, etc. +The logger uses [fmtlib](https://fmt.dev/latest/syntax/) under the hood, which means you can use its syntax, which will get forwarded to fmtlib. Geode also provides formatters for a few common types, such as `CCPoint`, `CCNode`, etc. ![Cheatsheet showing fmtlib syntax](https://hackingcpp.com/cpp/libs/fmt.png) -*Image from [https://hackingcpp.com](https://hackingcpp.com/cpp/cheat_sheets.html)* +*Image from [hackingcpp.com](https://hackingcpp.com/cpp/cheat_sheets)* As such, you are able to do things like this: ```cpp diff --git a/tutorials/manualhooks.md b/tutorials/manualhooks.md index a89c9ab2..26342184 100644 --- a/tutorials/manualhooks.md +++ b/tutorials/manualhooks.md @@ -1,6 +1,6 @@ # Manual Hooks -Sometimes, you need to hook some platform specific function that would be a hassle to manually add to bindings - or you want to provide support for adding hooks through other means than `$modify`, such as an embedded scripting language. In this case, you can also manually add hooks in Geode using [`Mod::addHook`](/classes/geode/Mod#addHook). +Sometimes, you need to hook some platform specific function that would be a hassle to manually add to bindings - or you want to provide support for adding hooks through other means than `$modify`, such as an embedded scripting language. In this case, you can also manually add hooks in Geode using [`Mod::hook`](/classes/geode/Mod#hook). ## Example @@ -11,8 +11,8 @@ The provided example hooks `MenuLayer::onNewgrounds` on Windows. ```cpp void MenuLayer_onNewgrounds(MenuLayer* self, CCObject* sender) { log::info("Hook reached!"); - // You can call the original by calling it - in this case, since the - // original is in bindings, you can just call it the same way as you + // You can call the original by calling it - in this case, since the + // original is in bindings, you can just call it the same way as you // would with $modify // TODO: How to call original manually self->onNewgrounds(sender); diff --git a/tutorials/memory.md b/tutorials/memory.md index 88161f96..4dd0c1d0 100644 --- a/tutorials/memory.md +++ b/tutorials/memory.md @@ -1,6 +1,6 @@ # Memory Management -Managing memory in general with C++ has been partially covered in [C++ tips](/cpp/tips.md), but Cocos2d comes with its own memory management system that is all modders have to learn at some point. On top of this, Geode adds two classes, [`Ref`](/classes/geode/Ref) and [`WeakRef`](/classes/geode/WeakRef), which Geode devs will tell you to use but not how to use them. This tutorial aims to remedy that, explaining how memory management in Cocos2d works in detail. +Managing memory in general with C++ has been partially covered in [C++ tips](/cpp/tips), but Cocos2d comes with its own memory management system that is all modders have to learn at some point. On top of this, Geode adds two classes, [`Ref`](/classes/geode/Ref) and [`WeakRef`](/classes/geode/WeakRef), which Geode devs will tell you to use but not how to use them. This tutorial aims to remedy that, explaining how memory management in Cocos2d works in detail. ## Memory management diff --git a/tutorials/migrate-v5.md b/tutorials/migrate-v5.md index ee08478d..342d8c0c 100644 --- a/tutorials/migrate-v5.md +++ b/tutorials/migrate-v5.md @@ -407,4 +407,4 @@ should now be written as: SceneManager is gone now. Use OverlayManager. It's a CCNode. You use it: like any other CCNode. ## Custom Keybinds -If your mod depends on Custom Keybinds, well, it doesn't anymore! See the [keybind setting documentation](/mods/settings/#keybind) for more info including migration from the old system. +If your mod depends on Custom Keybinds, well, it doesn't anymore! See the [keybind setting documentation](/mods/settings#keybind) for more info including migration from the old system. diff --git a/tutorials/modify-geode.md b/tutorials/modify-geode.md index bd4df0b3..81e0da30 100644 --- a/tutorials/modify-geode.md +++ b/tutorials/modify-geode.md @@ -8,7 +8,7 @@ Geode currently exposes three UI events through the `` hea Note that **all Geode UI events may and will be posted _multiple times_!** This is because Geode UIs often have an initial created "loading" state, and then fetch data from the Geode servers that is updated onto the created node asynchronously. To allow mods to be notified of when this data is loaded, **the UI events are reposted whenever the state of the UI node changes**. For this reason, **mods can never add nodes without checking if they already exist first**. -You can listen to these events by using Geode's events system as usual, using the [`EventFilter`](/classes/geode/EventFilter) helper class. Note that you should always return `ListenerResult::Propagate` to allow other mods to modify the layer as well, like calling the original in a `$modify` hook! +You can listen to these events by using Geode's [events system](/tutorials/events) as usual. Note that you should always return `ListenerResult::Propagate` to allow other mods to modify the layer as well, like calling the original in a `$modify` hook! ## Guidelines @@ -26,6 +26,8 @@ These rules are in place because the Geode UI is a highly volatile place that ** ## Example +> :warning: This is out of date! See the [v5 migration docs](tutorials/migrate-v5#changes-to). + ```cpp #include diff --git a/tutorials/modify.md b/tutorials/modify.md index 27c69883..56a88469 100644 --- a/tutorials/modify.md +++ b/tutorials/modify.md @@ -1,10 +1,10 @@ # Hooking / Modifying classes -At the centre of every modder's toolkit is **hooking**. If you don't know what that is, [read the handbook](/handbook/chap0.md). If you know what that is and have made mods using gd.h + cocos-headers before, you should know how it works in Geode. +At the centre of every modder's toolkit is **hooking**. If you don't know what that is, [read the handbook](/handbook/chap0). If you know what that is and have made mods using gd.h + cocos-headers before, you should know how it works in Geode. ## Modifying a function -Modifying classes is done using the macro `$modify`. If a function inside GD is implemented (or a custom modifier exists, which will be explained a lot later), you can use this macro to alter its behaviour. +Modifying classes is done using the macro `$modify`. If a function inside GD is implemented (or a custom modifier exists, which will be explained a lot later), you can use this macro to alter its behaviour. For example, to change the behaviour of the "More Games" button in GD, all you have to do is this: @@ -15,16 +15,16 @@ class $modify(MenuLayer) { "Geode", "Hello World from my Custom Mod!", "OK" - )->show(); + )->show(); } }; ``` Wait wait wait, so what does this exactly do? -The function we are modifying is `MenuLayer::onMoreGames`, which is called when you press the "More Games" button. This function takes a single parameter, a `cocos2d::CCObject*`, which is the target object the function is called on. For our use case, we don't need this parameter. +The function we are modifying is `MenuLayer::onMoreGames`, which is called when you press the "More Games" button. This function takes a single parameter, a `cocos2d::CCObject*`, which is the target object the function is called on. For our use case, we don't need this parameter. -The syntax is `class $modify(ModifiedClassName)`, which is selected mostly for aesthetics and not breaking the syntax highlighting. Inside this class, we add the functions we will modify. The example will replace the implementation of `MenuLayer::onMoreGames`, making it spawn a [FLAlertLayer](/tutorials/popup.md) instead of the More Games screen. +The syntax is `class $modify(ModifiedClassName)`, which is selected mostly for aesthetics and not breaking the syntax highlighting. Inside this class, we add the functions we will modify. The example will replace the implementation of `MenuLayer::onMoreGames`, making it spawn a [FLAlertLayer](/tutorials/popup) instead of the More Games screen. ## Using the original function @@ -46,11 +46,11 @@ class $modify(MenuLayer) { (or you could be questionable and do `this->MenuLayer::init()` but who am I to judge) -This pattern is quite common inside cocos2d, and therefore in GD too. Here, we modify the `MenuLayer::init` so that it will create our label inside the layer after running the original function. If `MenuLayer::init` returns false something has gone horribly wrong and GD will probably crash but it is important to stick to the idioms for code consistency. +This pattern is quite common inside cocos2d, and therefore in GD too. Here, we modify the `MenuLayer::init` so that it will create our label inside the layer after running the original function. If `MenuLayer::init` returns false something has gone horribly wrong and GD will probably crash but it is important to stick to the idioms for code consistency. ## Giving a class name -You will probably encounter an issue with `$modify` in your very first use case: [**how do you add callbacks for buttons**](/tutorials/buttons.md)? By default, `$modify` gives the inherited class a pretty much random name, but if you want to reference the name of your modified class, that's not very ideal as it's not consistent. Luckily, `$modify` can take a second parameter that specifies the name of the class: +You will probably encounter an issue with `$modify` in your very first use case: [**how do you add callbacks for buttons**](/tutorials/buttons)? By default, `$modify` gives the inherited class a pretty much random name, but if you want to reference the name of your modified class, that's not very ideal as it's not consistent. Luckily, `$modify` can take a second parameter that specifies the name of the class: ```cpp class $modify(MyAwesomeModification, MenuLayer) { @@ -59,7 +59,7 @@ class $modify(MyAwesomeModification, MenuLayer) { "Geode", "Hello World from my Custom Mod!", "OK" - )->show(); + )->show(); } bool init() { @@ -71,6 +71,8 @@ class $modify(MyAwesomeModification, MenuLayer) { menu_selector(MyAwesomeModification::onMyButton) ); + // Note: You should use menus provided by Geode in MenuLayer (or Node IDs in other layers) instead of making your own! + auto menu = CCMenu::create(); menu->addChild(button); menu->setPosition(150, 100); @@ -81,9 +83,9 @@ class $modify(MyAwesomeModification, MenuLayer) { }; ``` -The syntax for this is `class $modify(MyClassName, ClassToModify)`. +The syntax for this is `class $modify(MyClassName, ClassToModify)`. -Creating a `CCMenuItemSpriteExtra` takes a `SEL_MenuHandler`, which is a type alias for `void(cocos2d::CCObject::*)(cocos2d::CCObject*)` (which means a pointer to a member function that has a single parameter of CCObject\*). In order to supply this we need to get access to our function address, which requires us to know the class name. When we don't supply a class name, the macro generates a class which is guaranteed to not create any collision problems. [Read more about menu selectors here](/tutorials/buttons.md) +Creating a `CCMenuItemSpriteExtra` takes a `SEL_MenuHandler`, which is a type alias for `void(cocos2d::CCObject::*)(cocos2d::CCObject*)` (which means a pointer to a member function that has a single parameter of CCObject\*). In order to supply this we need to get access to our function address, which requires us to know the class name. When we don't supply a class name, the macro generates a class which is guaranteed to not create any collision problems. [Read more about menu selectors here](/tutorials/buttons) ## Modifying destructors @@ -121,7 +123,7 @@ class $modify(CreatorLayer) { }; ``` -Do note that you need to initialize the Fields struct manually at some point during the class's lifespan for this to work. See the [Fields tutorial](/tutorials/fields.md) for more information. +Do note that you need to initialize the Fields struct manually at some point during the class's lifespan for this to work. See the [Fields tutorial](/tutorials/fields) for more information. ## Using without macros @@ -134,7 +136,7 @@ struct MyCoolModification : Modify { "Geode", "Hello World from my Custom Mod!", "OK" - )->show(); + )->show(); } }; ``` @@ -143,7 +145,7 @@ Yeah we improved the clean usage a lot, and honestly I prefer this over the macr # Adding members -Geode allows you to add members to your modified classes to extend GD classes nearly seamlessly. [Learn more about fields](/tutorials/fields.md) +Geode allows you to add members to your modified classes to extend GD classes nearly seamlessly. [Learn more about fields](/tutorials/fields) # Advanced usage @@ -174,7 +176,7 @@ See the ModifyBase class to see what functions are available (the ## Setting hook priority -You might want to do this because your code may need to run before others ([Geode string id system](/tutorials/nodetree.md) for example). Or you don't call the original so you might want your function to get called last. This is where the priority system is helpful. Here is how you use it: +You might want to do this because your code may need to run before others ([Geode string id system](/tutorials/nodetree) for example). Or you don't call the original so you might want your function to get called last. This is where the priority system is helpful. Here is how you use it: ```cpp class $modify(GJGarageLayer) { @@ -190,7 +192,7 @@ class $modify(GJGarageLayer) { }; ``` -You can find more details on hook priority at the [Hook Priority tutorial](/tutorials/hookpriority.md). +You can find more details on hook priority at the [Hook Priority tutorial](/tutorials/hookpriority). ## Adding delegates @@ -201,14 +203,14 @@ struct $modify(MyDelegateClass) { struct Fields : TextInputDelegate { MenuLayer* self; std::string myString; - + void textChanged(CCTextInputNode* node) override { // here, this points to m_fields, so you will need that self // variable in order to access the MenuLayer itself myString = node->getString(); } }; - + bool init() { if (!MenuLayer::init()) return false; m_fields->self = this; @@ -226,7 +228,7 @@ struct $modify(MyDelegateClass) { "Geode", "Your text is: " + m_fields->myString, "OK" - )->show(); + )->show(); } }; ``` @@ -251,7 +253,7 @@ class $modify(CopyPlayerObject, PlayerObject) { Not yet implemented -# Common mistakes +# Common mistakes Here are some common mistakes you (and we) may make: @@ -304,6 +306,8 @@ class $modify(MyBrokenClass, MenuLayer) { menu_selector(MyBrokenClass::onMoreGames) ); + // Note: You should use menus provided by Geode in MenuLayer (or Node IDs in other layers) instead of making your own! + auto menu = CCMenu::create(); menu->addChild(button); menu->setPosition(150, 100); @@ -325,7 +329,7 @@ class $modify(MenuLayer) { "Geode", "Hello World from my Custom Mod!",´ "OK" - )->show(); + )->show(); return true; } }; diff --git a/tutorials/nodetree.md b/tutorials/nodetree.md index d676828c..8fb536cc 100644 --- a/tutorials/nodetree.md +++ b/tutorials/nodetree.md @@ -6,7 +6,7 @@ However, this is far from always possible, as not all nodes are stored as member It should be stressed here, at the very start, that **you should __NEVER__ get nodes by their index unless ABSOLUTELY __necessary__**. It should always be left as a last resort. The Cocos2d node tree is _not_ a stabile or predictable environment; child indexes can change rapidly due to game features, Z-order reordering, or most commonly due to other mods. If you do `node->getChildren()->objectAtIndex(5)`, you have no guarantees of getting the same node every time. -Using absolute indexes also requires a lot of work, as the **indexes are shuffled around** after a layer's `init` function due to Z-order reordering. This means that if you look at a node through [CocosExplorer](https://github.com/matcool/CocosExplorer) or [DevTools](https://github.com/geode-sdk/devtools) and see that is is at index 5, it is more than likely that during the layer's `init` function (where you will most likely be working with the node) this will not be the case. +Using absolute indexes also requires a lot of work, as the **indexes are shuffled around** after a layer's `init` function due to Z-order reordering. This means that if you look at a node through [CocosExplorer](https://github.com/matcool/CocosExplorer) or [DevTools](https://github.com/geode-sdk/DevTools) and see that is is at index 5, it is more than likely that during the layer's `init` function (where you will most likely be working with the node) this will not be the case. On top of this, in old GD modding frameworks, there were unfortunately basically no alternatives to members and getting by index. There are node **tags**, however tags are mostly used to implement differing functionality for buttons that use the same callback. The result of this was a lot of mods using raw indexes to position their own additions to GD's UI. Surprise surprise, this lead to [even mods by the same developer messing up their button positions due to indexes being different than expected](https://discord.com/channels/822510988409831486/858820729234391063/881436739250585610). Using raw indexes are one of the biggest sources of **mod incompatability**; and as Geode is meant to help with solving that, it introduces a whole new way of getting nodes: @@ -29,15 +29,15 @@ class $modify(MenuLayer) { ``` Notice that there are no hardcoded indexes. Even if `main-menu` is at a completely different index than expected, as long as the main menu in `MenuLayer` has the ID `main-menu`, the code will always find it and work as expected. -Although, this code does also have another interoperability concern: sure, we can be sure we added our node to the right place, but what if some other mod does the same? If another mod adds a button to the left of the icon kit button, they would overlap. To solve this issue, Geode introduces [layouts](/tutorials/layouts.md), which are discussed in their own tutorial. +Although, this code does also have another interoperability concern: sure, we can be sure we added our node to the right place, but what if some other mod does the same? If another mod adds a button to the left of the icon kit button, they would overlap. To solve this issue, Geode introduces [layouts](/tutorials/layouts), which are discussed in their own tutorial. -This also highlights the **problem with string IDs**: **someone has to assign them**. Whereas raw indexes are an intrinsic property of all CCNodes, the default ID of a node is an empty string (`""`). Geode does come with [default IDs for common classes like `MenuLayer`](https://github.com/geode-sdk/geode/blob/91cecf3843d246939be4057cdf8e7d5d607aeeb1/loader/src/hooks/MenuLayer.cpp#L150-L197), but there are unfortunately too many layers in GD to label all of them. +This also highlights the **problem with string IDs**: **someone has to assign them**. Whereas raw indexes are an intrinsic property of all CCNodes, the default ID of a node is an empty string (`""`). Geode does come with [default IDs for common classes like `MenuLayer`](https://github.com/geode-sdk/geode/blob/main/loader/src/ids/MenuLayer.cpp), but there are unfortunately too many layers in GD to label all of them. -This has, however, been taken in mind when designing the string ID system. If you find a layer you want to get a child of is missing string IDs, what you can do is **add them yourself** (using members or raw indexes), and either [send your code for adding them to Geode](https://github.com/geode-sdk/geode/pulls/new) or [let others know your mod adds these IDs](https://discord.gg/9e43WMKzhp). This way, someone else who wants to get the same children from the same layer can easily piggyback off of your logic. While the string ID system is ultimately and unfortunately based on raw indexes, its point is to **provide a single source of truth for those indexes**; once someone has done the work, no one should have to do it again. +This has, however, been taken in mind when designing the string ID system. If you find a layer you want to get a child of is missing string IDs, what you can do is **add them yourself** (using members or raw indexes), and either [send your code for adding them to Geode](https://github.com/geode-sdk/geode/compare) or [let others know your mod adds these IDs](https://discord.gg/9e43WMKzhp). This way, someone else who wants to get the same children from the same layer can easily piggyback off of your logic. While the string ID system is ultimately and unfortunately based on raw indexes, its point is to **provide a single source of truth for those indexes**; once someone has done the work, no one should have to do it again. ## Viewing String IDs -You can see if a layer's nodes have string IDs using the [DevTools](https://github.com/geode-sdk/devtools) mod, which shows them in the **Tree** view. +You can see if a layer's nodes have string IDs using the [DevTools](https://github.com/geode-sdk/DevTools) mod, which shows them in the **Tree** view. ## String IDs in your own layers diff --git a/tutorials/popup.md b/tutorials/popup.md index 522cbfe3..9a735fd8 100644 --- a/tutorials/popup.md +++ b/tutorials/popup.md @@ -71,7 +71,7 @@ public: ## Colored text -`FLAlertLayer` supports colored text in the content field by default. You can add colors with **color tags**, for example `Hi mom!` will produce yellow text. The built-in color tags in GD are listed [here](../geometrydash/tags.md) +`FLAlertLayer` supports colored text in the content field by default. You can add colors with **color tags**, for example `Hi mom!` will produce yellow text. The built-in color tags in GD are listed [here](/geometrydash/tags) ## Disabling the popup animation @@ -131,5 +131,5 @@ This will make the popup show correctly by adding it as a child to the new `Menu ## Examples - * [`ModSettingsPopup` in Geode, which uses `Popup`](https://github.com/geode-sdk/geode/blob/v4.10.0/loader/src/ui/mods/settings/ModSettingsPopup.hpp) - * [Use of `createQuickPopup` within it](https://github.com/geode-sdk/geode/blob/v4.10.0/loader/src/ui/mods/settings/ModSettingsPopup.cpp#L224-L236) + * [`ModtoberPopup` in Geode, which uses `Popup`](https://github.com/geode-sdk/geode/blob/main/loader/src/ui/mods/popups/ModtoberPopup.hpp) + * [Use of `createQuickPopup` within it](https://github.com/geode-sdk/geode/blob/main/loader/src/ui/mods/popups/ModtoberPopup.cpp#L26-L38) diff --git a/tutorials/positioning.md b/tutorials/positioning.md index 5d370ddf..ef8d9a6e 100644 --- a/tutorials/positioning.md +++ b/tutorials/positioning.md @@ -75,8 +75,8 @@ You can also use **DevTools** to move nodes in real time and check their positio **Content size** dictates how much space a **node and its children** take up. This has many uses. First of all, we can use a node's content size to place children **relative** to the parent's borders, or relative to **other children** inside the layer. There are **2 main approaches** to setting a node's content size. - * Setting the content size **prior** to adding any children, if we know how much space the parent should take beforehand. A great example for this approach are **popups**. If you have read the [Popup Tutorial](/tutorials/popup.md), then you probably remember that when creating a popup, you give it a **width** and a **height**. These values are used to give the popup its **content size**. - * Setting the content size **after** adding and positioning the children. This is usually done by calculating the borders of the parent using the children that are the furthest from (0, 0). A good example of this would be **button menus**. Maybe we have a weird layout, that can't make use of [Layouts](/tutorials/layouts.md), or a menu that has children added to it **after it is initialised**. For that, we would have to calculate the **content size** of the parent. Of course, if you use **Layouts**, you just have to set a content size, and the layout will automatically resize the children to match that **content size** (if auto-scale is enabled), otherwise, you will also have to calculate the content size yourself. + * Setting the content size **prior** to adding any children, if we know how much space the parent should take beforehand. A great example for this approach are **popups**. If you have read the [Popup Tutorial](/tutorials/popup), then you probably remember that when creating a popup, you give it a **width** and a **height**. These values are used to give the popup its **content size**. + * Setting the content size **after** adding and positioning the children. This is usually done by calculating the borders of the parent using the children that are the furthest from (0, 0). A good example of this would be **button menus**. Maybe we have a weird layout, that can't make use of [Layouts](/tutorials/layouts), or a menu that has children added to it **after it is initialised**. For that, we would have to calculate the **content size** of the parent. Of course, if you use **Layouts**, you just have to set a content size, and the layout will automatically resize the children to match that **content size** (if auto-scale is enabled), otherwise, you will also have to calculate the content size yourself. Cocos nodes have different behaviour when it comes to content size. The most popular example to give here is **CCMenu**, which sets its content size to the **screen size**. RobTop doesn't modify this content size, which is a valid approach to this problem, but we are better than this! @@ -106,7 +106,7 @@ bool MyLayer::init() { // We know that the button content size is {40.f, 40.f} (by checking with DevTools), so we will set the menu's content size accordingly. // Just enough to fit a 2x2 grid of buttons, we will place our buttons on a diagonal - menu->setContentSize({ 80.f, 80.f }); + menu->setContentSize({ 80.f, 80.f }); // Ignore this for now menu->ignoreAnchorPointForPosition(false); diff --git a/tutorials/tasks.md b/tutorials/tasks.md index 89b14419..7504e45c 100644 --- a/tutorials/tasks.md +++ b/tutorials/tasks.md @@ -120,9 +120,9 @@ void MyCoolClass::onTask(PointlessTask::Event* event) { else if (int* progress = event->getProgress()) { // The progress callback was called. } - // This check is technically unnecessary, since Tasks can only ever have - // three possible states, but it's good practice to always check it anyway - // in case Task gains more states in the future, or if you get rid of the + // This check is technically unnecessary, since Tasks can only ever have + // three possible states, but it's good practice to always check it anyway + // in case Task gains more states in the future, or if you get rid of the // progress check for example else if (event->isCancelled()) { // The Task was cancelled @@ -150,8 +150,8 @@ SumTask startCalculationHalved() { // Return our result but divided by two return *result / 2; } - // We could also define a callback for mapping the progress value, and a - // callback for handling the mapped Task being cancelled - by default, + // We could also define a callback for mapping the progress value, and a + // callback for handling the mapped Task being cancelled - by default, // progress is just forwarded as-is ); } @@ -184,7 +184,7 @@ Often when working with Task-based code, you will encounter a situation where yo ```cpp SumTask startCalculation() { - // We already know what the sum of 1..10,000,000 is sillyhead! We don't + // We already know what the sum of 1..10,000,000 is sillyhead! We don't // need to calculate that! return SumTask::immediate(49'999'995'000'000u); } @@ -194,20 +194,20 @@ SumTask startCalculation() { Tasks are primarely geared towards running synchronous code in another thread. However, you may sometimes write a Task that needs to wait for another Task or other threaded calculation to finish. For example, maybe instead of writing our number summing logic ourselves, we delegate this responsibility to another library; however, that library's API creates its own thread and takes a callback to be called for the result. -In these cases, you can't really write your Task using `Task::run`, which expects the callback to be synchronous. You could use [`std::condition_variable`](https://en.cppreference.com/w/cpp/thread/condition_variable), but that might be a bit hard to get to work with more complex scenarios. Instead, you can use `Task::runWithCallback`: +In these cases, you can't really write your Task using `Task::run`, which expects the callback to be synchronous. You could use [`std::condition_variable`](https://en.cppreference.com/cpp/thread/condition_variable), but that might be a bit hard to get to work with more complex scenarios. Instead, you can use `Task::runWithCallback`: ```cpp SumTask startCalculation() { return SumTask::runWithCallback([](auto finish, auto progress, auto hasBeenCancelled) { - // Assuming we are using some external library for summing up numbers + // Assuming we are using some external library for summing up numbers // that creates its own thread and calls a callback on finish external_library::sumRange(10'000'000U, [finish](uint64_t value) { - // Note that finish can only be called exactly once; the value can - // never be changed, any new progress posted nor the Task cancelled - // afterwards, so any code you run past this point can no longer + // Note that finish can only be called exactly once; the value can + // never be changed, any new progress posted nor the Task cancelled + // afterwards, so any code you run past this point can no longer // influence the Task itself. finish(value); - // However, if you do need to run some code here like clean up + // However, if you do need to run some code here like clean up // temporary files, that is completely fine and safe }); }, "My epic task that sums up numbers for some reason"); @@ -222,7 +222,7 @@ Sometimes we have a bunch of similar Tasks running in parallel, and need to do s ```cpp Task> startLotsOfCalculations() { - // SumTask::all takes a vector of Tasks and returns a Task that resolves + // SumTask::all takes a vector of Tasks and returns a Task that resolves // into a vector of their results return SumTask::all({ startCalculation(), @@ -277,4 +277,4 @@ Task newTask = ## Coroutines -Tasks can be used in [C++20 coroutines](https://en.cppreference.com/w/cpp/language/coroutines), easily allowing for multiple asynchronous calls to happen within the same code. Note that this may have a little performance overhead compared to regular Task code. See [Coroutines](/tutorials/coroutines) for more information. +Tasks can be used in [C++20 coroutines](https://en.cppreference.com/cpp/language/coroutines), easily allowing for multiple asynchronous calls to happen within the same code. Note that this may have a little performance overhead compared to regular Task code. See [Coroutines](/tutorials/coroutines) for more information. diff --git a/tutorials/utils.md b/tutorials/utils.md index 934ada32..aeb9cd6d 100644 --- a/tutorials/utils.md +++ b/tutorials/utils.md @@ -70,7 +70,7 @@ auto read = clipboard::read(); ``` ### geode::utils::game - + ```cpp game::exit(); game::restart(); @@ -111,7 +111,7 @@ auto hook2 = GEODE_UNWRAP(ObjcHook::create("EAGLView", "initWithFrame:", &MyFunc ## Tasks -Visit the [tasks](tasks.md) page for more information. +Visit the [tasks](/tutorials/tasks) page for more information. ## Cocos related @@ -200,7 +200,7 @@ A wrapper for CallFunc that takes a lambda. ### Touch priority ```cpp -// recursively sets the touch priority of the children based on itself +// recursively sets the touch priority of the children based on itself handleTouchPriority(this) ``` From 2a2c51e001820dc5b4283096f0854c8053b4cb3d Mon Sep 17 00:00:00 2001 From: undefined06855 Date: Thu, 30 Apr 2026 22:04:00 +0100 Subject: [PATCH 2/2] fix formatting everywhere (remove trailing spaces, fix newlines, replace tabs with spaces) --- flash-template/default.css | 2 +- geometrydash/index.md | 2 +- getting-started/index.md | 2 +- handbook/chap0.md | 1 - handbook/vol1/chap1_3.md | 13 ++-- handbook/vol1/chap1_4.md | 1 - handbook/vol1/chap1_5.md | 1 - handbook/vol1/chap1_6.md | 1 - handbook/vol1/chap1_7.md | 100 ++++++++++++++--------------- misc/index.md | 2 +- mods/dependencies.md | 3 - mods/savedata.md | 1 - mods/settings.md | 10 +-- source/index.md | 2 +- source/styling.md | 126 ++++++++++++++++++------------------- tutorials/async.md | 4 +- tutorials/buttons.md | 6 +- tutorials/casting.md | 14 ++--- tutorials/coroutines.md | 48 +++++++------- tutorials/fields.md | 6 +- tutorials/image-loading.md | 2 +- tutorials/migrating.md | 8 +-- tutorials/nodetree.md | 3 - tutorials/positioning.md | 2 +- tutorials/touchpriority.md | 6 +- tutorials/utils.md | 4 +- 26 files changed, 177 insertions(+), 193 deletions(-) diff --git a/flash-template/default.css b/flash-template/default.css index 0518deea..64e9ff6f 100644 --- a/flash-template/default.css +++ b/flash-template/default.css @@ -24,7 +24,7 @@ body { body { grid-template-columns: 1fr; } - + nav { position: absolute; width: 100%; diff --git a/geometrydash/index.md b/geometrydash/index.md index 3c9d85cc..79b83032 100644 --- a/geometrydash/index.md +++ b/geometrydash/index.md @@ -3,4 +3,4 @@ title: Geometry Dash order: 6 --- -These pages contain documentation for Geometry Dash. \ No newline at end of file +These pages contain documentation for Geometry Dash. diff --git a/getting-started/index.md b/getting-started/index.md index a61b5f2e..ea6cbb31 100644 --- a/getting-started/index.md +++ b/getting-started/index.md @@ -5,4 +5,4 @@ order: 1 # Getting Started -Please read through this chapter in order, starting in [Prerequisites](/getting-started/prerequisites). \ No newline at end of file +Please read through this chapter in order, starting in [Prerequisites](/getting-started/prerequisites). diff --git a/handbook/chap0.md b/handbook/chap0.md index 2f691295..4c0fc044 100644 --- a/handbook/chap0.md +++ b/handbook/chap0.md @@ -74,4 +74,3 @@ Many things in this handbook are referred to as "traditional modding". This mean ## Ready, set, go! **And with that, let us start with [Chapter 1.1](/handbook/vol1/chap1_1)!** - diff --git a/handbook/vol1/chap1_3.md b/handbook/vol1/chap1_3.md index 0f50407c..1b6269c8 100644 --- a/handbook/vol1/chap1_3.md +++ b/handbook/vol1/chap1_3.md @@ -13,16 +13,16 @@ The main way to create hooks in Geode is using an abstraction called `$modify` - Let's see a [real-world example](/tutorials/manualhooks) of creating manual hooks in Geode: ```cpp auto wrapFunction(uintptr_t address, tulip::hook::WrapperMetadata const& metadata) { - auto wrapped = geode::hook::createWrapper(reinterpret_cast(address), metadata); - if (wrapped.isErr()) {{ - throw std::runtime_error(wrapped.unwrapErr()); - }} - return wrapped.unwrap(); + auto wrapped = geode::hook::createWrapper(reinterpret_cast(address), metadata); + if (wrapped.isErr()) {{ + throw std::runtime_error(wrapped.unwrapErr()); + }} + return wrapped.unwrap(); } void MenuLayer_onNewgrounds(MenuLayer* self, CCObject* sender) { log::info("Hook reached!"); - static auto original = wrapFunction( + static auto original = wrapFunction( geode::base::get() + 0x27b480, tulip::hook::WrapperMetadata{ .m_convention = geode::hook::createConvention(tulip::hook::TulipConvention::Thiscall), @@ -68,4 +68,3 @@ As noted previously, you shouldn't usually be creating hooks manually. Instead, ## Notes > [Note 1] Hook conflicts are a type of [**race condition**](https://en.wikipedia.org/wiki/Race_condition) and it happens when two mods try to hook the same function at the same time. If the mods do this sufficiently close to one another, there is a high chance that **one mod's hook will replace the other's**. The end result of this is that one of the mods functions incorrectly, when it fails to hook the function it expected to. In the best case, this just results in the mod losing functionality, but in the extreme case this **could cause crashes**. - diff --git a/handbook/vol1/chap1_4.md b/handbook/vol1/chap1_4.md index 9989328e..81868e76 100644 --- a/handbook/vol1/chap1_4.md +++ b/handbook/vol1/chap1_4.md @@ -94,4 +94,3 @@ At this point, we're getting very close to writing actual mod code. However, bef ## Notes > [Note 1] You can also add nodes directly to `CCDirector` and bypass the node tree, but this is very rarely done as nodes in the director don't have access to the touch system or any input for that matter. - diff --git a/handbook/vol1/chap1_5.md b/handbook/vol1/chap1_5.md index 1b6e59cf..7c6333af 100644 --- a/handbook/vol1/chap1_5.md +++ b/handbook/vol1/chap1_5.md @@ -25,4 +25,3 @@ Using DevTools, you can find the name of any layer. Just navigate to the layer w > :information_source: DevTools also comes with many other utilities, such as moving nodes in the scene around. However, this tutorial is not about DevTools, so you will have to look at its (currently non-existent) documentation for that ;) So now we know that the main menu is called `MenuLayer`, but so what? How can we actually modify it? For that, see [Chapter 1.6: Modifying Layers](/handbook/vol1/chap1_6) - diff --git a/handbook/vol1/chap1_6.md b/handbook/vol1/chap1_6.md index a29da2e3..e15955b8 100644 --- a/handbook/vol1/chap1_6.md +++ b/handbook/vol1/chap1_6.md @@ -144,4 +144,3 @@ Now we are at an interesting point; we know how to hook functions, we know what ## Notes > [Note 1] There are some very rare cases of nodes that don't have an `init` or `create` function. However, for the purposes of this tutorial, we will pretend those don't exist. - diff --git a/handbook/vol1/chap1_7.md b/handbook/vol1/chap1_7.md index 53dfe6a1..d48b6e79 100644 --- a/handbook/vol1/chap1_7.md +++ b/handbook/vol1/chap1_7.md @@ -27,12 +27,12 @@ Since we want to add things to the layer, let's hook its `init` function, making #include class $modify(MenuLayer) { - bool init() { - if (!MenuLayer::init()) - return false; + bool init() { + if (!MenuLayer::init()) + return false; - return true; - } + return true; + } }; ``` @@ -44,14 +44,14 @@ Now it's time to actually show the text. As outlined in [Chapter 1.4](/handbook/ #include class $modify(MenuLayer) { - bool init() { - if (!MenuLayer::init()) - return false; + bool init() { + if (!MenuLayer::init()) + return false; - auto label = cocos2d::CCLabelBMFont::create("Hello, World!", "bigFont.fnt"); + auto label = cocos2d::CCLabelBMFont::create("Hello, World!", "bigFont.fnt"); - return true; - } + return true; + } }; ``` @@ -63,14 +63,14 @@ Everything Cocos2d-related lies in the `cocos2d` namespace, so we must prefix ou using namespace geode::prelude; class $modify(MenuLayer) { - bool init() { - if (!MenuLayer::init()) - return false; + bool init() { + if (!MenuLayer::init()) + return false; - auto label = CCLabelBMFont::create("Hello, World!", "bigFont.fnt"); + auto label = CCLabelBMFont::create("Hello, World!", "bigFont.fnt"); - return true; - } + return true; + } }; ``` @@ -84,15 +84,15 @@ Now, the label isn't currently a child of any layer, so it won't show up anywher using namespace geode::prelude; class $modify(MenuLayer) { - bool init() { - if (!MenuLayer::init()) - return false; + bool init() { + if (!MenuLayer::init()) + return false; - auto label = CCLabelBMFont::create("Hello, World!", "bigFont.fnt"); - this->addChild(label); + auto label = CCLabelBMFont::create("Hello, World!", "bigFont.fnt"); + this->addChild(label); - return true; - } + return true; + } }; ``` @@ -106,17 +106,17 @@ The default position for any node is (0, 0) which is at bottom left of the scree using namespace geode::prelude; class $modify(MenuLayer) { - bool init() { - if (!MenuLayer::init()) - return false; + bool init() { + if (!MenuLayer::init()) + return false; - auto winSize = CCDirector::get()->getWinSize(); + auto winSize = CCDirector::get()->getWinSize(); - auto label = CCLabelBMFont::create("Hello, World!", "bigFont.fnt"); - this->addChild(label); + auto label = CCLabelBMFont::create("Hello, World!", "bigFont.fnt"); + this->addChild(label); - return true; - } + return true; + } }; ``` @@ -128,18 +128,18 @@ Next, to actually position our label, we call the `setPosition` method on it, pl using namespace geode::prelude; class $modify(MenuLayer) { - bool init() { - if (!MenuLayer::init()) - return false; + bool init() { + if (!MenuLayer::init()) + return false; - auto winSize = CCDirector::get()->getWinSize(); + auto winSize = CCDirector::get()->getWinSize(); - auto label = CCLabelBMFont::create("Hello, World!", "bigFont.fnt"); - label->setPosition(winSize.width / 2, winSize.height / 2); - this->addChild(label); + auto label = CCLabelBMFont::create("Hello, World!", "bigFont.fnt"); + label->setPosition(winSize.width / 2, winSize.height / 2); + this->addChild(label); - return true; - } + return true; + } }; ``` @@ -160,18 +160,18 @@ And with that, **we have completed our Hello, World! mod**. Here's what the fina using namespace geode::prelude; class $modify(MenuLayer) { - bool init() { - if (!MenuLayer::init()) - return false; + bool init() { + if (!MenuLayer::init()) + return false; - auto winSize = CCDirector::get()->getWinSize(); + auto winSize = CCDirector::get()->getWinSize(); - auto label = CCLabelBMFont::create("Hello, World!", "bigFont.fnt"); - label->setPosition(winSize / 2); - this->addChild(label); + auto label = CCLabelBMFont::create("Hello, World!", "bigFont.fnt"); + label->setPosition(winSize / 2); + this->addChild(label); - return true; - } + return true; + } }; ``` diff --git a/misc/index.md b/misc/index.md index 091ac36c..755886b2 100644 --- a/misc/index.md +++ b/misc/index.md @@ -3,4 +3,4 @@ title: Miscellaneous order: 6 --- -These pages contain miscellaneous but useful information, that does not fit elsewhere. \ No newline at end of file +These pages contain miscellaneous but useful information, that does not fit elsewhere. diff --git a/mods/dependencies.md b/mods/dependencies.md index 1a825ac9..cd3e7238 100644 --- a/mods/dependencies.md +++ b/mods/dependencies.md @@ -172,6 +172,3 @@ void api::doSomething() { // ... } ``` - - - diff --git a/mods/savedata.md b/mods/savedata.md index 3badc142..2c72609f 100644 --- a/mods/savedata.md +++ b/mods/savedata.md @@ -68,4 +68,3 @@ Mods should save other data (files, backups, etc.) to their specific save direct It should be noted that a mod can have a good reason to save data elsewhere - for example a mod that saves created levels as individual files instead of CCLocalLevels would be justified in saving directly under the GD save folder instead of the mod save folder, since the data its saving is not related to the mod. If you have data that the user should be able to edit (for example config files), these should go in the directory provided by `Mod::get()->getConfigDir()`. **Do not flood the main GD folder with config files or save data** - this will almost certainly get your mod rejected from the index unless you have a _very_ good reason for doing so! - diff --git a/mods/settings.md b/mods/settings.md index 96422af2..68a12c6c 100644 --- a/mods/settings.md +++ b/mods/settings.md @@ -19,9 +19,9 @@ Adding new settings to your mod happens through `mod.json`. Define the `settings ```json { // mod.json "geode": "3.5.0", - "id": "me.my-mod", - "name": "My Awesome Mod!", - "version": "1.0.0", + "id": "me.my-mod", + "name": "My Awesome Mod!", + "version": "1.0.0", "settings": { "awesome-title": { "type": "title", @@ -949,7 +949,3 @@ If you have used custom settings for the purpose of creating titles, you should Unless your custom settings are particularly complex, **it is recommended to just rewrite them from scratch**. This will take a bit of effort, but should be pretty easy [if you follow the guide for creating custom settings](#custom-settings). It is also just good practice in general to (if possible) rewrite parts of your codebase every now and then to make sure everything is as refined as possible. It is heavily recommended to follow the practices laid out in the [Custom Settings part of this tutorial](#custom-settings) for setting nodes, as this results in conventional, easy-to-use and easy-to-maintain UIs. However, if you do have a reason to make a setting that has unconventional UI, you can of course always hide the name label and do what you want. - - - - diff --git a/source/index.md b/source/index.md index f9d7ab6a..ebdce74a 100644 --- a/source/index.md +++ b/source/index.md @@ -3,4 +3,4 @@ title: Source order: 5 --- -These pages contain info about the Geode codebase itself. \ No newline at end of file +These pages contain info about the Geode codebase itself. diff --git a/source/styling.md b/source/styling.md index add67043..ef7a286f 100644 --- a/source/styling.md +++ b/source/styling.md @@ -1,7 +1,7 @@ # Styling guidelines ## General -The maximum line limit is 80 characters. In the case of a line exceeding 80 charaters, wrapping rules should be applied. +The maximum line limit is 80 characters. In the case of a line exceeding 80 charaters, wrapping rules should be applied. ## Files & Directories Files should be in pascalcase. @@ -21,8 +21,8 @@ For single line comments, there should be a single space padding. For multiline comments, the contents should start in a next line with a indent. ```cpp /* - This is quite correct. - Isn't it? + This is quite correct. + Isn't it? */ /*This is @@ -54,7 +54,7 @@ float delta = 0.016667; ## Variables -Local variables should be in camelcase. +Local variables should be in camelcase. ```cpp // Correct @@ -86,7 +86,7 @@ static CCNode* g_sharedNode = nullptr; static CCNode* sharedNode = nullptr; ``` -Since global variable initializations between platforms are inconsistent, the use of global variables is entirely discouraged. +Since global variable initializations between platforms are inconsistent, the use of global variables is entirely discouraged. ## Types @@ -138,17 +138,17 @@ If, while and for statements should be on the same line, the scope should start ```cpp // Correct while (thing == 54) { - thing = 53; + thing = 53; } if (thing == 54) thing = 53; for (int thing = 53; thing != 54; ++thing) { - // Stuff + // Stuff } // Wrong -while (thing == 54) +while (thing == 54) { - thing = 53; + thing = 53; } if (thing == 54) { thing = 53; } ``` @@ -158,21 +158,21 @@ Switch statement labels should be on the same line as the statement. Every fallt ```cpp // Correct switch (thing) { - case TestEnum::Xd: [[fallthrough]]; - case TestEnum::Lol: - value = 5; - break; - default: - value = 6; - break; + case TestEnum::Xd: [[fallthrough]]; + case TestEnum::Lol: + value = 5; + break; + default: + value = 6; + break; } // Wrong switch (thing) { case TestEnum::Xd: [[fallthrough]]; case TestEnum::Lol: - value = 5; - break; + value = 5; + break; } ``` @@ -183,7 +183,7 @@ Return type, the function and the parameters should be in the same line. Functio CCObject* copyWithMeme(CCMeme* meme); // Wrong -CCObject* +CCObject* copyWithMeme (CCMeme* meme); ``` @@ -191,27 +191,27 @@ Definitions should start on the same line. ```cpp // Correct CCObject* copyWithMeme(CCMeme* meme) { - return nullptr; // funny meme if i do say so myself + return nullptr; // funny meme if i do say so myself } // Wrong CCObject* copyWithMeme(CCMeme* meme) { - return nullptr; + return nullptr; } ``` -Unless necessary, the trailing return type shouldn't be used. +Unless necessary, the trailing return type shouldn't be used. ```cpp // Correct CCObject* copyWithMeme(CCMeme* meme) { - return nullptr; + return nullptr; } // Wrong auto copyWithMeme(CCMeme* meme) -> CCObject* { - return nullptr; + return nullptr; } ``` @@ -226,7 +226,7 @@ namespace geode { // Wrong namespace the_old_lilac_namespace { - + } ``` @@ -249,7 +249,7 @@ class BasedButton { }; // Wrong -class BasedButton +class BasedButton { }; @@ -260,19 +260,19 @@ Access specifiers should be on the same indentation as the classes. // Correct class BasedButton { public: - // Stuff + // Stuff }; // Wrong class BasedButton { public: - // this one is for you camila + // this one is for you camila }; // Wrong class BasedButton { - public: - // bad stuff + public: + // bad stuff }; ``` @@ -281,22 +281,22 @@ Subclasses should be on the same line. // Correct class BasedButton : CCNode, FLAlertDelegate { public: - // Stuff + // Stuff }; // Wrong class BasedButton : CCNode, FLAlertDelegate { public: - // Stuff + // Stuff }; // Wrong -class BasedButton : - CCNode, - FLAlertDelegate { +class BasedButton : + CCNode, + FLAlertDelegate { public: - // Stuff + // Stuff }; ``` @@ -305,13 +305,13 @@ Classes which shouldn't be extended should be marked as final. // Correct class BasedButton final : CCNode { public: - // I am the last implementer ok + // I am the last implementer ok }; // Wrong class BasedButton : CCNode { public: - // pls dont subclass me okthxbai + // pls dont subclass me okthxbai }; ``` @@ -322,18 +322,18 @@ Same rules of functions apply for member functions. // Correct class BasedButton : CCNode { public: - bool init() override { - return true; - } + bool init() override { + return true; + } }; // Wrong class BasedButton : CCNode { public: - bool init() override - { - return true; - } + bool init() override + { + return true; + } }; ``` @@ -342,38 +342,38 @@ Functions should be const qualified whenever they can. // Correct class BasedButton : CCNode { public: - void baseMyButton(BasedButton const* other) const { - // basing your button - // and not modifying you :) - } + void baseMyButton(BasedButton const* other) const { + // basing your button + // and not modifying you :) + } }; // Wrong class BasedButton : CCNode { public: - void baseMyButton(BasedButton const* other) { - // basing your button - // and not modifying you :) - } + void baseMyButton(BasedButton const* other) { + // basing your button + // and not modifying you :) + } }; ``` -Virtual overrides should be marked as override. +Virtual overrides should be marked as override. ```cpp // Correct class BasedButton : CCNode { public: - bool init() override { - return true; - } + bool init() override { + return true; + } }; // Wrong class BasedButton : CCNode { public: - bool init() { - return true; - } + bool init() { + return true; + } }; ``` @@ -381,13 +381,13 @@ Outofline definitions of member functions have the same rules applied to them. ```cpp // Correct bool BasedButton::init() { - return true; + return true; } // Wrong -bool BasedButton::init() +bool BasedButton::init() { - return true; + return true; } ``` diff --git a/tutorials/async.md b/tutorials/async.md index db83fe3f..208dc7d9 100644 --- a/tutorials/async.md +++ b/tutorials/async.md @@ -7,7 +7,7 @@ Since Geode v5.0.0, the task system has been replaced by a true **asynchronous** Async is **perfect** for any workload that consists of waiting - such as making a request to a server, waiting for a notification from another thread, waiting a few seconds between tasks, etc. -Geode provides some async utilities in the `` header, as well a global async runtime used by mods. You are not forced to use it, but there's rarely a good reason not to. +Geode provides some async utilities in the `` header, as well a global async runtime used by mods. You are not forced to use it, but there's rarely a good reason not to. ## Coroutines / Futures / Pollables @@ -195,7 +195,7 @@ If you must run an operation that blocks and have no way to go around it, use th ```cpp auto handle = async::runtime().spawnBlocking([] { - // simulate some expensive calculation + // simulate some expensive calculation uint64_t x = 1; for (int i = 0; i < 1024; i++) { x = x * (x + i); diff --git a/tutorials/buttons.md b/tutorials/buttons.md index 74d0f22b..055bbb9d 100644 --- a/tutorials/buttons.md +++ b/tutorials/buttons.md @@ -83,7 +83,7 @@ Here is the popular click counter example in cocos2d: ```cpp class MyLayer : public CCLayer { protected: - // Class member that stores how many times + // Class member that stores how many times // the button has been clicked size_t m_clicked = 0; @@ -128,7 +128,7 @@ One of the most common problems encountered when using menu selectors is situati ```cpp class MyLayer : public CCLayer { protected: - // Class member that stores how many times + // Class member that stores how many times // the button has been clicked size_t m_clicked = 0; @@ -185,7 +185,7 @@ If you want to pass something to a callback that can't be passed through tags li ```cpp class MyLayer : public CCLayer { protected: - // Class member that stores how many times + // Class member that stores how many times // the button has been clicked size_t m_clicked = 0; diff --git a/tutorials/casting.md b/tutorials/casting.md index b6ecb50f..8dfb74fc 100644 --- a/tutorials/casting.md +++ b/tutorials/casting.md @@ -8,12 +8,12 @@ Let's say you have a callback, and in that you want to downcast the sender. You ```cpp void MyClass::onClick(CCObject* sender) { - auto node = static_cast(sender); - node->setPosition({0, 0}); + auto node = static_cast(sender); + node->setPosition({0, 0}); } ``` -You are expected to use static cast when downcasting the objects you already know the type of. +You are expected to use static cast when downcasting the objects you already know the type of. ## Unknown original type @@ -21,12 +21,12 @@ You need to downcast an object, but you don't know its original type. Using your ```cpp void MyClass::onClick(CCObject* sender) { - if (auto button = typeinfo_cast(sender)) { - button->setSizeMult(1.2f); - } + if (auto button = typeinfo_cast(sender)) { + button->setSizeMult(1.2f); + } } ``` ## Callbacks -If you tried to make a button, chances are you used `menu_selector`. But what does that do? It reinterpret casts your member function into a `void (CCObject::*)(CCObject*)`. Since it reinterpret casts it, you should never give it an invalid function, like a `void MyClass::onClick(int)`. It will compile, and it might *techincally* work, but this is very bad. Don't do it. \ No newline at end of file +If you tried to make a button, chances are you used `menu_selector`. But what does that do? It reinterpret casts your member function into a `void (CCObject::*)(CCObject*)`. Since it reinterpret casts it, you should never give it an invalid function, like a `void MyClass::onClick(int)`. It will compile, and it might *techincally* work, but this is very bad. Don't do it. diff --git a/tutorials/coroutines.md b/tutorials/coroutines.md index 87944553..96f8ca35 100644 --- a/tutorials/coroutines.md +++ b/tutorials/coroutines.md @@ -47,7 +47,7 @@ coro::spawn << someTask(); // Spawn from a coroutine via operator<< coro::spawn << [] -> Task { - co_return; + co_return; }; // Spawn from a Task via operator() @@ -55,7 +55,7 @@ coro::spawn(someTask()); // Spawn from a coroutine via operator() coro::spawn([] -> Task { - co_return; + co_return; }); ``` @@ -71,14 +71,14 @@ Creating a new function for just the asynchronous bits might get tedious. Luckil ```cpp void logResponseCode(std::string const& url) { - log::info("Starting request..."); + log::info("Starting request..."); - $async(url) { - auto req = web::WebRequest(); - auto res = co_await req.get(url); + $async(url) { + auto req = web::WebRequest(); + auto res = co_await req.get(url); - log::info("Response code: {}", res.code()); - }; + log::info("Response code: {}", res.code()); + }; } ``` @@ -141,13 +141,13 @@ Here's what a basic range generator looks like: #include coro::Generator range(int start, int end) { - for (int i = start; i < end; ++i) { - co_yield i; - } + for (int i = start; i < end; ++i) { + co_yield i; + } } for (int i : range(0, 10)) { - log::info("My number: {}", i); + log::info("My number: {}", i); } ``` @@ -166,20 +166,20 @@ Here's an example of an infinite fibbonacci generator: #include coro::Generator fibbonacci() { - int a = 0; - int b = 1; - while (true) { - co_yield a; - auto next = a + b; - a = b; - b = next; - } + int a = 0; + int b = 1; + while (true) { + co_yield a; + auto next = a + b; + a = b; + b = next; + } } for (int num : fibbonacci()) { - if (num > 1000) break; - log::info("My number: {}", num); + if (num > 1000) break; + log::info("My number: {}", num); } ``` @@ -190,12 +190,12 @@ Generators have two helper functions that quickly allow you to apply transformat ```cpp // Prints 0, -1, -2, ... for (int num : range(0, 10).map(std::negate())) { - log::info("My number: {}", num); + log::info("My number: {}", num); } // Prints only even numbers for (int num : range(0, 10).filter([](int n) { return n % 2 == 0; })) { - log::info("My number: {}", num); + log::info("My number: {}", num); } ``` diff --git a/tutorials/fields.md b/tutorials/fields.md index 0aef69d1..7dceaaa8 100644 --- a/tutorials/fields.md +++ b/tutorials/fields.md @@ -36,7 +36,7 @@ class $modify(PlayerObject) { This code works even if you have multiple `PlayerObject`s, and the counter is initialized as 0 for each one, providing an elegant yet simple solution to the problem. -Fields are declared just like normal member variables, but inside the special `Fields` struct. Even constructors and destructors work\*. +Fields are declared just like normal member variables, but inside the special `Fields` struct. Even constructors and destructors work\*. > :info: Fields are initialized only whenever they're first accessed, **not** when the modified class is originally created. @@ -64,7 +64,7 @@ class $modify(PlayerObject) { Fields can be accessed outside your modified class like usual, with the help of some casting. -> :warning: Do not use `typeinfo_cast` here, as it isn't actually an instance of `MyGameObject`. +> :warning: Do not use `typeinfo_cast` here, as it isn't actually an instance of `MyGameObject`. ```cpp class $modify(MyGameObject, GameObject) { @@ -76,4 +76,4 @@ class $modify(MyGameObject, GameObject) { GameObject* someObject = /*...*/; // `m_fields` is still required! static_cast(someObject)->m_fields->m_myField = 12; -``` \ No newline at end of file +``` diff --git a/tutorials/image-loading.md b/tutorials/image-loading.md index 77f0db2e..a4d2c06a 100644 --- a/tutorials/image-loading.md +++ b/tutorials/image-loading.md @@ -1,6 +1,6 @@ # Image (Down)loading -If you have to display **images** in your mod that are **not included** in the mod file (for example for reducing the **size** of the mod or for showing **user-generated content**), you can use geode's `LazySprite` class. It can **also** be used for ordinary image initialization, either from a path or from raw data, which would have slight performance improvements over `CCSprite::create`, as the image is lazily read and decoded **in the background**, without freezing the game. +If you have to display **images** in your mod that are **not included** in the mod file (for example for reducing the **size** of the mod or for showing **user-generated content**), you can use geode's `LazySprite` class. It can **also** be used for ordinary image initialization, either from a path or from raw data, which would have slight performance improvements over `CCSprite::create`, as the image is lazily read and decoded **in the background**, without freezing the game. It is **recommended** to use this API instead of implementing custom solutions that mess with `CCTextureCache`. It is not straightforward to do this correctly, and multiple mods (and Geode itself) have in the past failed to do this without memory leaks and other bugs. diff --git a/tutorials/migrating.md b/tutorials/migrating.md index eb5d69fb..ffa940e7 100644 --- a/tutorials/migrating.md +++ b/tutorials/migrating.md @@ -18,7 +18,7 @@ CCPoint getShowButtonPosition(EditorUI* self) { self->m_pTrashBtn->getPositionX() + 50.0f, self->m_pTrashBtn->getPositionY() }; - + return { self->m_pPlaybackBtn->getPositionX() + 45.0f, self->m_pPlaybackBtn->getPositionY() @@ -67,7 +67,7 @@ matdash::add_hook<&MenuLayer_init>(base + 0x1907b0) ### GDMake -No one will need this section but I'm adding it for the completeness sake. GDMake uses the `GDMAKE_HOOK` with the address and the symbol parameter to hook a specific function and `GDMAKE_ORIG` keyword to call the original function. +No one will need this section but I'm adding it for the completeness sake. GDMake uses the `GDMAKE_HOOK` with the address and the symbol parameter to hook a specific function and `GDMAKE_ORIG` keyword to call the original function. ```cpp GDMAKE_HOOK(0x1907b0, "_ZN9MenuLayer4initEv") @@ -81,7 +81,7 @@ bool __fastcall MenuLayer_init(gd::MenuLayer* self, void* edx) { Since all of these hooks are static functions, a `self` parameter and a parameter for clobbing the edx register is added (except MAT dash) to match the calling convention for member functions. These parameters need to be removed when moving the hook inside a modify class. Likewise, all uses of `self->` need to be either removed or replaced with `this->`. -Otherwise, all of the hooks can be replaced by a `Modify` class and the original calls can be replaced with a call the `OriginalClass::function` inside the modify hook with the needed parameters. +Otherwise, all of the hooks can be replaced by a `Modify` class and the original calls can be replaced with a call the `OriginalClass::function` inside the modify hook with the needed parameters. ```cpp class $modify(MenuLayer) { @@ -96,7 +96,7 @@ class $modify(MenuLayer) { Patches are pretty easy to migrate. Geode has a `Mod::patch` function that takes in a byte vector and an address, which can be used for mod specific patches. -## Miscellaneous +## Miscellaneous Some function signatures in gd.h are wrong: wrong as in they work for Windows, but not for any other platform. Geode uses the Android binary symbols to infer the function signatures, so the wrong function calls relating to this issue need to be fixed while migrating from gd.h. diff --git a/tutorials/nodetree.md b/tutorials/nodetree.md index 8fb536cc..521c0fb6 100644 --- a/tutorials/nodetree.md +++ b/tutorials/nodetree.md @@ -46,6 +46,3 @@ It is recommended for you to also use string IDs in your own layers, as this mak ## Naming String IDs String IDs you give to nodes should be in kebab case with only lowercase a-z letters and no spaces. - - - diff --git a/tutorials/positioning.md b/tutorials/positioning.md index ef8d9a6e..1756f22d 100644 --- a/tutorials/positioning.md +++ b/tutorials/positioning.md @@ -165,4 +165,4 @@ Second, how does it affect **content size**? It affects the resizing of said con For example, setting an anchor point of `{0.0f, 0.5f}` will prohibit the node's **content size** from expanding to the left. Setting it to `{1.0f, 0.5f}` will prohibit the node's **content size** from expanding to the right. As with positioning, experimenting with **DevTools** is the best way to learn how this works. -Additionally, different anchor points affect how the node's **children** are moved when the content size changes. \ No newline at end of file +Additionally, different anchor points affect how the node's **children** are moved when the content size changes. diff --git a/tutorials/touchpriority.md b/tutorials/touchpriority.md index e0015d0b..0dc973ce 100644 --- a/tutorials/touchpriority.md +++ b/tutorials/touchpriority.md @@ -4,7 +4,7 @@ You probably had this issue of "Why does my button not work?" whenever you added ## What is touch priority? -Every layer that has `setTouchEnabled(true)` has an assigned touch priority to it. This priority is used to determine which layer will consume the given touch. +Every layer that has `setTouchEnabled(true)` has an assigned touch priority to it. This priority is used to determine which layer will consume the given touch. This priority value is completely independent of the Z order of the layer, which would be the preferred way of doing this kind of thing. But we need to deal with what we have. @@ -14,7 +14,7 @@ The smaller the priority value is, the higher its priority. This means a priorit Disregarding all popups, Geometry Dash touch priority is completely equivalent to how Cocos2d handles it. Every layer has touch priority 0 by default. `CCMenu`'s have a touch priority of -128. Other than these, Robtop classes such as `CCTextInputNode` and `SliderTouchLogic` have a touch priority of -500. -### Force priority +### Force priority If you've been in the Geometry Dash modding community enough, you've probably heard of this term at least once. Force priority is the system Robtop implemented into Cocos2d in order to handle his popups. One of the most hated additions of Geometry Dash solely because how unintuitive it is. @@ -34,4 +34,4 @@ If your popup subclasses `geode::Popup`, the registering and unregistering is ha If you're not using `geode::Popup` class and directly subclassing `FLAlertLayer` instead, you should call `FLAlertLayer::init(int opacity)` inside your init. This will handle the registering of the force priority, along with creating the `m_mainLayer`. You should also override `registerWithTouchDispatcher` and call `CCTouchDispatcher::addTargetedDelegate` in it, since that will allow you to register the popup not as a prio targeted delegate. If you leave it the default, `FLAlertLayer` will have one less priority than your layers (-503 vs -502), meaning none of the touches will go to your layers since they will be consumed. And lastly, `~FLAlertLayer` handles unregistering of the force priority. -If you want to set the priorities manually (such as for an overlay), you can call `CCLayer::setTouchPriority`/`CCMenu::setHandlerPriority` on your layer with the `CCTouchDispatcher::getTargetPrio` value. \ No newline at end of file +If you want to set the priorities manually (such as for an overlay), you can call `CCLayer::setTouchPriority`/`CCMenu::setHandlerPriority` on your layer with the `CCTouchDispatcher::getTargetPrio` value. diff --git a/tutorials/utils.md b/tutorials/utils.md index aeb9cd6d..77e7823f 100644 --- a/tutorials/utils.md +++ b/tutorials/utils.md @@ -299,9 +299,9 @@ intToHex numToString numToAbbreviatedString numFromString -timePointAsString` +timePointAsString ``` ### ColorProvider -No idea how to use this one. \ No newline at end of file +No idea how to use this one.