Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
# 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.

Class & function documentation is built automatically from [the Geode source code](https://github.com/geode-sdk/geode).

## 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:

Expand Down
2 changes: 1 addition & 1 deletion flash-template/default.css
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ body {
body {
grid-template-columns: 1fr;
}

nav {
position: absolute;
width: 100%;
Expand Down
2 changes: 1 addition & 1 deletion geometrydash/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,4 @@ title: Geometry Dash
order: 6
---

These pages contain documentation for Geometry Dash.
These pages contain documentation for Geometry Dash.
8 changes: 4 additions & 4 deletions getting-started/cpp-stuff.md
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand All @@ -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.

Expand Down Expand Up @@ -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).
2 changes: 1 addition & 1 deletion getting-started/geode-cli.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
```
Expand Down
2 changes: 1 addition & 1 deletion getting-started/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,4 @@ order: 1

# Getting Started

Please read through this chapter in order, starting in [Prerequisites](/getting-started/prerequisites.md).
Please read through this chapter in order, starting in [Prerequisites](/getting-started/prerequisites).
16 changes: 8 additions & 8 deletions getting-started/what-next.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.
1 change: 0 additions & 1 deletion handbook/chap0.md
Original file line number Diff line number Diff line change
Expand Up @@ -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)!**

8 changes: 4 additions & 4 deletions handbook/vol1/chap1_1.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.

Expand Down
2 changes: 1 addition & 1 deletion handbook/vol1/chap1_2.md
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
23 changes: 11 additions & 12 deletions handbook/vol1/chap1_3.md
Original file line number Diff line number Diff line change
@@ -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.

Expand All @@ -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<void*>(address), metadata);
if (wrapped.isErr()) {{
throw std::runtime_error(wrapped.unwrapErr());
}}
return wrapped.unwrap();
auto wrapped = geode::hook::createWrapper(reinterpret_cast<void*>(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),
Expand Down Expand Up @@ -55,17 +55,16 @@ 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.

However, it is also important to note that **you still need to know how to reverse engineer** in order to make GD mods. Even if the addresses and signatures are all available, they still don't tell you what the function actually does, how it works, or where its called.

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**.
Loading