Speedrunning JetBrains Rider Godot Addon Development: natywne C++ GDExtensions i editor plugins
W skrócie
Artykuł omawia rewolucyjne ułatwienia w tworzeniu addonów do silnika Godot przy użyciu JetBrains Rider 2026.2, w tym natywną automatyzację CMake i jednoczesny dual-language debugging kodu w GDScript i C++. Przedstawia on krok po kroku lifecycle projektów GDExtension oraz najlepsze praktyki w zakresie izolacji przestrzeni nazw, unikania wycieków pamięci i integracji z zewnętrznymi usługami. Tekst podkreśla także zalety wykorzystania platformy [horizOn](https://horizon.pm) jako gotowego backendu do obsługi telemetrii, uwierzytelniania oraz baz danych czasu rzeczywistego.
Tworzenie własnego Godot editor plugin lub natywnego C++ GDExtension zazwyczaj zaczyna się od nagłej, bolesnej świadomości: spędziłeś właśnie trzy godziny na konfigurowaniu skryptów kompilacji SCons, walce z flagami kompilatora w trybie headless i analizowaniu niejednoznacznych błędów cross-compilation zamiast pisać właściwą logikę swojej wtyczki. Choć architektura Godot 4 odblokowała niesamowity potencjał dla rozszerzeń, deweloperskie doświadczenie (developer experience) historycznie przypominało konfigurowanie szafy serwerowej za pomocą wykałaczki. Wraz z uruchomieniem nowego Godot Asset Store, dostawcy IDE w końcu traktują addon development jako priorytet (first-class citizen).
JetBrains Rider 2026.2 oficjalnie wkracza na arenę jako jeden z pierwszych wiodących dostawców narzędzi IDE oferujący dedykowane szablony, automatyzację CMake oraz wielojęzyczny debugging dla Godot Asset Store. Dla deweloperów indie i inżynierów odpowiedzialnych za narzędzia zespołowe oznacza to koniec piekła konfiguracji boilerplate i początek szybkiego prototypowania narzędzi. Dzięki skróceniu czasu konfiguracji do kreatora uruchamianego jednym kliknięciem, bariera wejścia do rozszerzania głównego interfejsu edytora Godot nigdy nie była tak niska.
The GDExtension Bottleneck: Dlaczego narzędzia do Godot tak bardzo bolały
Pod maską Godot 4 wykorzystuje GDExtension, aby umożliwić deweloperom pisanie wydajnego kodu w C++ lub Rust, który komunikuje się bezpośrednio z rdzeniem silnika bez konieczności jego pełnej rekompilacji. Odbywa się to poprzez ładowanie bibliotek dynamicznych – takich jak .dll na Windows, .so na Linux i .dylib na macOS – oraz mapowanie ich na interfejsy GDScript. Jednak ręczne ustawienie tego procesu wymaga pobrania repozytorium bindingów godot-cpp, dopasowania nagłówka do dokładnej wersji silnika, napisania własnych skryptów SCons lub CMake oraz skonfigurowania pliku .gdextension, aby zmapować ścieżki bibliotek dla pięciu różnych platform docelowych.
Co gorsza, debugging tych natywnych plików binarnych był niezwykle podatny na awarie. Typowy workflow rozwiązywania problemów obejmuje uruchomienie edytora Godot pod osobnym debuggerem (jak GDB lub LLDB), ustawianie breakpointów w zewnętrznym edytorze i trzymanie kciuków, aby hot-reload nie wywołał paniki w głównym wątku silnika, doprowadzając do twardego crasha. Kiedy deweloperzy budują niestandardowe narzędzia – zwłaszcza złożone synchronizatory baz danych, niskopoziomowe interfejsy netcode o niskim opóźnieniu czy asset pipelines – to tarcie całkowicie niszczy produktywność.
Rider 2026.2: Omówienie nowego toolchainu do tworzenia addonów
Gotowe szablony projektów (Out-of-the-Box Project Templates)
Rider 2026.2 dostarcza dedykowane, oparte na kreatorach szablony pokrywające pełne spektrum formatów rozszerzeń dla Godot. Nie musisz już klonować boilerplate'ów repozytoriów ani kopiować struktur folderów ze starych projektów. Zamiast tego, IDE tworzy czyste, uporządkowane repozytorium dla GDScript editor plugins, rozszerzeń C# lub C++ GDExtensions, wstępnie skonfigurowane ze wszystkim – od pliku plugin.cfg po foldery kompilacji (build folders). Oszczędza to godziny konfiguracji i eliminuje najczęstszą przyczynę niepowodzeń na wczesnym etapie: błędne ścieżki katalogów w plikach manifestu.
Natywna integracja CMake dla C++
Historycznie bindingi C++ dla Godot mocno faworyzowały SCons jako system budowania. SCons jest potężny, ale jego oparte na Pythonie pliki konfiguracyjne są niezwykle nieprzejrzyste, pozbawione autouzupełniania w IDE i komplikują integrację z CI/CD. Rider 2026.2 wprowadza solidną, natywną integrację CMake dla projektów GDExtension. Kiedy tworzysz addon GDExtension, Rider automatycznie generuje czysty plik CMakeLists.txt, który łączy podstawową bibliotekę bindingów godot-cpp z Twoim własnym kodem źródłowym. Pozwala to na korzystanie z potężnego silnika C++ w Rider do nawigacji po kodzie, refaktoryzacji i analizy statycznej bez żadnej dodatkowej konfiguracji.
Dual-Language Debugging w ramach jednej sesji
To prawdziwy klejnot koronny tej aktualizacji. Deweloperzy piszący wydajne narzędzia do Godot rzadko ograniczają się do jednego języka. Standardowa architektura wykorzystuje wysokowydajne C++ do ciężkich operacji na danych lub złożonej matematyki oraz lekki plik GDScript dla panelu GUI (dock) lub UI edytora. Debugging takiej hybrydowej architektury oznaczał konieczność używania oddzielnych narzędzi dla C++ i GDScript. Rider 2026.2 automatycznie generuje unified run configurations. Możesz kliknąć pojedynczy przycisk „Debug”, a Rider uruchomi edytor Godot, podepnie się (attach) do jego procesu i będzie jednocześnie śledził wykonanie kodu w GDScript oraz C++. Gdy breakpoint w GDScript Twojego interfejsu zostanie aktywowany, a Ty przejdziesz krok dalej (step into) do natywnej funkcji C++, Rider płynnie przełączy się na debugger C++ bez przerywania sesji.
Architektura folderów gotowa do publikacji (Publish-Ready Folder Architecture)
Nowy Godot Asset Store stawia surowe wymagania dotyczące struktury folderów i pakowania, aby zapobiec konfliktom namespaces pomiędzy addonami. Szablony w Rider wymuszają te zalecenia od samego początku. Oddzielając pliki runtime od komponentów GUI przeznaczonych wyłącznie dla edytora, IDE gwarantuje, że wygenerowany build jest natychmiast gotowy do przesłania do sklepu, redukując błędy pakowania z częstego problemu do w pełni zautomatyzowanej formalności.
Inside the GDExtension Lifecycle: Budowanie backendu w C++
Aby zrozumieć wartość automatyzacji w Rider, musimy przyjrzeć się temu, czego projekt GDExtension rzeczywiście wymaga na poziomie kodu. W standardowym GDExtension musisz zdefiniować inicjalizator biblioteki jako punkt wejścia, zarejestrować swoje niestandardowe klasy w ClassDB silnika Godot oraz ostrożnie posprzątać alokacje pamięci przy wyrejestrowaniu (uninitialization) modułu. Poniższe nagłówki i pliki źródłowe C++ reprezentują absolutne minimum boilerplate'u niezbędne do stworzenia natywnego, niestandardowego węzła (node) – w tym przypadku wydajnego modułu do obsługi telemetrii.
// horizon_telemetry_node.h
#ifndef HORIZON_TELEMETRY_NODE_H
#define HORIZON_TELEMETRY_NODE_H
#include <godot_cpp/classes/node.hpp>
#include <godot_cpp/core/class_db.hpp>
namespace godot {
class HorizonTelemetryNode : public Node {
GDCLASS(HorizonTelemetryNode, Node);
private:
String session_id;
int event_count;
protected:
static void _bind_methods();
public:
HorizonTelemetryNode();
~HorizonTelemetryNode();
void initialize_telemetry(const String &p_session_id);
void track_event(const String &p_event_name);
String get_session_id() const;
};
}
#endif // HORIZON_TELEMETRY_NODE_H
Następnie implementujemy podstawowe zachowanie. W pliku implementacji rejestrujemy nasze metody wewnątrz _bind_methods(), aby silnik refleksji uruchomieniowej (runtime reflection engine) Godota miał do nich dostęp.
// horizon_telemetry_node.cpp
#include "horizon_telemetry_node.h"
#include <godot_cpp/variant/utility_functions.hpp>
using namespace godot;
void HorizonTelemetryNode::_bind_methods() {
ClassDB::bind_method(D_METHOD("initialize_telemetry", "session_id"), &HorizonTelemetryNode::initialize_telemetry);
ClassDB::bind_method(D_METHOD("track_event", "event_name"), &HorizonTelemetryNode::track_event);
ClassDB::bind_method(D_METHOD("get_session_id"), &HorizonTelemetryNode::get_session_id);
ADD_PROPERTY(PropertyInfo(Variant::STRING, "session_id"), "", "get_session_id");
}
HorizonTelemetryNode::HorizonTelemetryNode() {
event_count = 0;
session_id = "";
}
HorizonTelemetryNode::~HorizonTelemetryNode() {
// Clean up memory safely here
}
void HorizonTelemetryNode::initialize_telemetry(const String &p_session_id) {
session_id = p_session_id;
UtilityFunctions::print("Telemetry initialized for session: ", session_id);
}
void HorizonTelemetryNode::track_event(const String &p_event_name) {
event_count++;
UtilityFunctions::print("Event tracked: ", p_event_name, " (Total: ", event_count, ")");
}
String HorizonTelemetryNode::get_session_id() const {
return session_id;
}
Na koniec musimy poinstruować Godot, jak załadować nasz moduł za pomocą funkcji inicjalizującej. Ten plik register_types działa jako główny punkt wejścia biblioteki, podpięty przez system ładowania GDExtension.
// register_types.cpp
#include "register_types.h"
#include "horizon_telemetry_node.h"
#include <gdextension_interface.h>
#include <godot_cpp/core/defs.hpp>
#include <godot_cpp/godot.hpp>
using namespace godot;
void initialize_horizon_plugin_module(ModuleInitializationLevel p_level) {
if (p_level != MODULE_INITIALIZATION_LEVEL_SCENE) {
return;
}
ClassDB::register_class<HorizonTelemetryNode>();
}
void uninitialize_horizon_plugin_module(ModuleInitializationLevel p_level) {
if (p_level != MODULE_INITIALIZATION_LEVEL_SCENE) {
return;
}
}
extern "C" {
// Initialization entry point called by Godot.
GDExtensionBool GDE_EXPORT horizon_plugin_library_init(GDExtensionInterfaceGetProcAddress p_get_proc_address, const GDExtensionClassLibraryPtr p_library, GDExtensionInitialization *r_initialization) {
godot::GDExtensionBinding::InitObject init_obj(p_get_proc_address, p_library, r_initialization);
init_obj.register_initializer(initialize_horizon_plugin_module);
init_obj.register_terminator(uninitialize_horizon_plugin_module);
init_obj.set_minimum_library_initialization_level(MODULE_INITIALIZATION_LEVEL_SCENE);
return init_obj.init();
}
}
Developing Editor Plugins: Pipeline w GDScript
Podczas gdy GDExtension obsługuje wysokowydajny backend w C++, interfejs użytkownika (UI) Twojego addona do Godota – taki jak dodanie niestandardowego panelu w dolnej sekcji (bottom dock) czy tworzenie własnych węzłów inspektora – jest zazwyczaj pisany w GDScript przy użyciu adnotacji @tool. Dyrektywa @tool informuje Godot, że ten skrypt powinien wykonywać się bezpośrednio w uruchomionej instancji edytora, a nie tylko podczas działania gry.
Pisanie skryptów narzędziowych (tool scripts) wymaga precyzyjnego zarządzania cyklem życia (lifecycle management). Funkcje _enter_tree() i _exit_tree() działają jak konstruktory i destruktory Twoich integracji z edytorem. Brak usunięcia niestandardowych węzłów UI podczas przeładowywania edytora doprowadzi do powstania osieroconych węzłów GUI (orphaned nodes) zaśmiecających pamięć edytora, co ostatecznie wywołuje błędy typu crash-on-exit.
# horizon_editor_plugin.gd
@tool
extends EditorPlugin
const PLUGIN_NAME = "HorizonBackendHelper"
var dock: Control
func _enter_tree() -> void {
# Initialize the dock UI from our pre-packed scene
dock = preload("res://addons/horizon_plugin/dock.tscn").instantiate()
# Add the main panel to the editor dock on the upper-right slot
add_control_to_dock(DOCK_SLOT_LEFT_UR, dock)
# Register our custom backend node so developers can add it in the scene tree
add_custom_type(
"HorizonClientNode",
"Node",
preload("res://addons/horizon_plugin/horizon_client_node.gd"),
preload("res://addons/horizon_plugin/icon.png")
)
print("Plugin ", PLUGIN_NAME, " successfully initialized in editor.")
func _exit_tree() -> void {
# Clean up dock and custom types to prevent editor memory leaks
if dock:
remove_control_from_docks(dock)
dock.queue_free()
remove_custom_type("HorizonClientNode")
print("Plugin ", PLUGIN_NAME, " cleaned up.")
The Connectivity Challenge: Integracja zdalnych backendów
Kiedy budujesz editor plugins lub GDExtensions, Twoje narzędzie jest często tak potężne, jak usługi backendowe, z którymi się łączy. Na przykład, jeśli tworzysz panel administracyjny dla gry indie, zdalny edytor poziomów lub wbudowany w silnik system telemetrii, Twój plugin musi komunikować się z bazami danych, zarządzać tożsamościami deweloperów lub graczy i synchronizować zdalne stany. Samodzielna implementacja tego wszystkiego oznacza konieczność zbudowania spersonalizowanej, bezpiecznej usługi sieciowej. Musisz uruchomić wirtualny serwer prywatny (VPS), skonfigurować API gateway, wdrożyć bazę danych, napisać własne modele uwierzytelniania użytkowników oraz wdrożyć rotację certyfikatów SSL/TLS. To potężny narzut inżynieryjny (engineering overhead), który może z łatwością pochłonąć od 4 do 6 tygodni pracy deweloperskiej, zanim Twój plugin w ogóle nawiąże połączenie z bazą danych.
Zamiast marnować ograniczoną energię zespołu na zarządzanie surową infrastrukturą backendową, możesz zintegrować horizOn jako główny backend swojego silnika gry. horizOn oferuje natywny, wysoce wydajny C# i GDScript SDK, który podłącza się bezpośrednio do Twoich własnych dodatków do edytora. Zamiast spędzać tygodnie na wdrażaniu baz danych i pisaniu niestandardowych handlerów dla protokołu WebSockets, możesz po prostu wrzucić klienta horizOn do swojego projektu i natychmiast uzyskać bezpieczne uwierzytelnianie, dostęp do bazy danych w czasie rzeczywistym oraz system zarządzania graczami. Pozostawiając czarną robotę związaną z infrastrukturą dla horizOn, możesz poświęcić swój czas na dopracowywanie UX swojego addona i narzędzi rozgrywki, mając pewność, że Twój backend będzie się płynnie skalować, gdy opublikujesz swoje dzieło w sklepie.
5 sprawdzonych w boju dobrych praktyk przy tworzeniu addonów do Godota
1. Izolacja przestrzeni nazw (Namespace Isolation) poprzez strukturę folderów
Zawsze dodawaj unikalny prefiks przestrzeni nazw (namespace) do folderów i skryptów swojego addona pod ścieżką res://addons/your_unique_addon_name/. Godot współdzieli jedną, płaską globalną przestręń nazw dla wszystkich niestandardowych klas rejestrowanych poprzez dyrektywy @icon lub class_name. Jeśli użyjesz generycznej nazwy klasy, takiej jak NetworkManager lub ConfigHelper, Twój addon wejdzie w konflikt z głównym projektem dewelopera lub innymi rozszerzeniami firm trzecich. Przechowuj wszystkie swoje skrypty narzędziowe ściśle w obrębie swojego unikalnego katalogu.
2. Automatyzuj kompilację plików binarnych i wykluczaj je z VCS
Trzymaj ciężkie, skompilowane pliki binarne GDExtension (.dll, .so, .dylib) poza historią swojego głównego repozytorium Git. Rozmiar repozytorium szybko spęcznieje w miarę rekompilacji bibliotek podczas developmentu. Zamiast tego użyj pliku .gitignore, aby ignorować katalogi budowania (build directories) oraz foldery wyjściowe (release folders), a także skonfiguruj pipeline CI/CD (na przykład GitHub Actions lub GitLab CI) za pomocą zautomatyzowanych skryptów CMake, aby budować pliki binarne dla wielu platform i pakować je wyłącznie w plikach zip wydań (release zip).
3. Ostrożnie zarządzaj granicą między GDScript a C++
Zwracaj szczególną uwagę na sposób obsługi pamięci przy przekazywaniu zmiennych przez granicę językową. GDScript automatycznie zarządza cyklem życia klas pochodnych od RefCounted (takich jak Resource), ale używa ręcznego zarządzania pamięcią dla obiektów dziedziczących po Object (jak surowe obiekty Node). W swoim kodzie C++ GDExtension zawsze używaj inteligentnego opakowania (smart wrapper) Ref<T> silnika Godot dla klas zliczających referencje, aby uniknąć błędów double-free lub wycieków pamięci (memory leaks). Dla standardowych klas stosuj defensywne rzutowanie (casting) przy użyciu Object::cast_to<T>() i sprawdzaj wskaźniki pod kątem null pointerów przed wywołaniem natywnych metod.
4. Wybieraj WebSockets i trwałe połączenia dla stanów w czasie rzeczywistym
Unikaj tradycyjnego HTTP pollingu w przypadku wtyczek wymagających synchronizacji w czasie rzeczywistym, takich jak współdzielone systemy edytora czy narzędzia backendowe do matchmakingu. Wielokrotne wysyłanie żądań HTTP generuje wysoki narzut na procesor (CPU overhead) i wywołuje dotkliwe limity zapytań (rate-limiting) na usługach backendowych. Zamiast tego powinieneś porzucić HTTP polling na rzecz WebSockets, aby ustanowić trwałe, dwukierunkowe połączenie. Zmniejsza to opóźnienie z powolnych 500 ms do poziomu poniżej 10 ms i minimalizuje narzut danych (data overhead)."
5. Projektuj elastyczne systemy awaryjne (Fallback) dla zdalnych potoków chmurowych
Jeśli Twój addon komunikuje się ze zdalnymi serwerami chmurowymi, nigdy nie pozwól, aby przerwa w połączeniu sieciowym zawiesiła wątek edytora Godot. Synchroniczne żądania sieciowe mogą zablokować główny proces Godota, powodując zawieszenie (hang) edytora. Zawsze używaj asynchronicznych callbacków lub wątków w tle (thread pools), aby reakcja UI była płynna. Ponadto, jeśli projektujesz integrację typu live-ops, przeanalizuj, jak tworzyć solidne potoki danych, oceniając kampanię „Stop Killing Games” vs systemy awaryjne serwerów live-ops. Zagwarantuje to, że Twoje narzędzie bezproblemowo przejdzie w tryb offline i utrzyma funkcjonalność edytora nawet wtedy, gdy chmurowe endpoints będą całkowicie nieosiągalne.
Podsumowanie: Usprawnianie potoku narzędziowego w Godot
JetBrains Rider 2026.2 przekształca proces tworzenia addonów do Godota ze skomplikowanego wyzwania konfiguracyjnego w usprawniony, produktywny workflow deweloperski. Automatyzując scaffolding GDExtension, oferując solidną integrację CMake oraz umożliwiając jednoczesny debugging kodu w GDScript i C++, Rider eliminuje zmęczenie konfiguracją i pozwala skupić się na tworzeniu doskonałych narzędzi. Połączenie szablonów deweloperskich Rider ze skalowalną, w pełni zarządzaną architekturą backendową umożliwia budowanie wysokowydajnych, połączonych z siecią wtyczek bez narzutu związanego z ręczną inżynierią serwerową.
Gotowy na skalowanie swojego backendu multiplayer? Wypróbuj horizOn za darmo lub zapoznaj się z dokumentacją API.
Źródło: JetBrains Rider brings support for Godot Asset Store addons