العودة إلى المدونة

تسريع تطوير Godot Addon باستخدام JetBrains Rider: GDExtensions بلغة C++ Native و Editor Plugins

نُشر في 29 مايو 2026
تسريع تطوير Godot Addon باستخدام JetBrains Rider: GDExtensions بلغة C++ Native و Editor Plugins

باختصار

يستعرض المقال كيف يسهل تحديث JetBrains Rider 2026.2 تطوير إضافات محرك Godot عبر توفير قوالب جاهزة وتكامل CMake وتصحيح أخطاء متزامن للغتي GDScript و C++. كما يناقش التحديات المرتبطة ببناء GDExtension وإدارة دورة حياة الأدوات وتجنب تسريبات الذاكرة في بيئة التطوير. وأخيرًا، يقدم أفضل الممارسات الميدانية لتجنب تعارض الـ Namespace وتحسين أداء الاتصالات في الوقت الفعلي مع تسليط الضوء على دمج حلول Backend جاهزة مثل horizOn لتسريع دورة الإنتاج.

عادةً ما يبدأ بناء Godot editor plugin مخصص أو GDExtension بلغة C++ Native بإدراك مفاجئ ومؤلم: لقد أمضيت ثلاث ساعات في تهيئة سكربتات تجميع SCons، ومحاربة flags المترجم headless، وتحليل أخطاء cross-compilation الغامضة بدلاً من كتابة المنطق الفعلي لـ plugin الخاص بك. في حين أن بنية Godot 4 قد فتحت آفاقًا وإمكانيات مذهلة لـ extensions، إلا أن تجربة المطور (developer experience) كانت تاريخياً تبدو وكأنك تقوم بتهيئة رف خادم (server rack) باستخدام عود أسنان. ومع إطلاق Godot Asset Store الجديد، بدأت الشركات المزودة لـ IDEs أخيرًا في التعامل مع تطوير addon كمواطن من الدرجة الأولى.

دخل JetBrains Rider 2026.2 الساحة رسميًا كأحد أول مزودي أدوات IDE الكبار الذين يقدمون قوالب مخصصة، وأتمتة CMake، و multi-language debugging لـ Godot Asset Store. بالنسبة للمطورين المستقلين (indie developers) ومهندسي أدوات الفرق (team tools engineers)، يعني هذا نهاية جحيم إعدادات boilerplate وبداية النمذجة السريعة للأدوات (rapid tool prototyping). من خلال تقليل أوقات الإعداد والتكوين إلى معالج نقرة واحدة (single-click wizard)، لم يكن العائق أمام توسيع واجهة محرر Godot الأساسية أقل مما هو عليه الآن.

عائق GDExtension: لماذا كانت أدوات Godot مؤلمة في السابق

في الكواليس (Under the hood)، يستخدم Godot 4 تقنية GDExtension ليتيح للمطورين كتابة أكواد C++ أو Rust عالية الأداء تتصل مباشرة بالبنى الأساسية للمحرك دون الحاجة لإعادة تجميع المحرك بأكمله. يقوم بذلك عن طريق تحميل المكتبات الديناميكية (dynamic libraries) — مثل .dll على Windows، و .so على Linux، و .dylib على macOS — ورسم خرائط لها (mapping) إلى واجهات GDScript. ومع ذلك، فإن إعداد هذا يدويًا يتطلب سحب مستودع bindings لـ godot-cpp، ومطابقة header نسخة المحرك بدقة، وكتابة سكربتات SCons أو CMake مخصصة، وتهيئة ملف إعداد .gdextension لرسم مسارات المكتبة عبر خمس منصات مستهدفة مختلفة.

والأسوأ من ذلك، أن عمل debugging لهذه الملفات الثنائية native كان عرضة للانهيار (crash-prone) بشكل سيئ السمعة. يتضمن سير عمل استكشاف الأخطاء وإصلاحها النموذجي تشغيل محرر Godot تحت debugger منفصل (مثل GDB أو LLDB)، وتعيين breakpoints في محرر خارجي، وتمني ألا يؤدي hot-reload إلى إحداث panic في thread المحرك الرئيسي والتسبب في hard crash. عندما يقوم المطورون ببناء أدوات مخصصة — خاصة مزامنات قواعد البيانات المعقدة، أو واجهات Netcode ذات زمن انتقال منخفض (low-latency)، أو asset pipelines — فإن هذا الاحتكاك يدمر الإنتاجية تمامًا.

Rider 2026.2: تحليل لسلسلة أدوات Addon الجديدة

قوالب مشاريع جاهزة للاستخدام المباشر (Out-of-the-Box)

يوفر Rider 2026.2 قوالب مخصصة تعتمد على معالج (wizard-driven) تغطي كامل نطاق تنسيقات Godot extension. لن تضطر بعد الآن إلى استنساخ boilerplates من المستودعات أو نسخ ولصق هياكل المجلدات من المشاريع القديمة. بدلاً من ذلك، يقوم الـ IDE ببناء مستودع نظيف ومنظم إما لـ GDScript editor plugins، أو C# extensions، أو C++ GDExtensions، مهيأ مسبقًا بكل شيء بدءًا من plugin.cfg وحتى مجلدات build المستهدفة. يوفر هذا ساعات من التهيئة ويقضي على السبب الأكثر شيوعًا للفشل في المراحل المبكرة: عدم تطابق مسارات الدليل في ملفات المانيفست (manifest files).

تكامل CMake native للغة C++

تاريخيًا، كانت bindings الخاصة بلغة C++ في Godot تفضل بشدة SCons كنظام بناء (build system). على الرغم من قوة SCons، إلا أن ملفات التكوين القائمة على Python فيه معتمة بشكل سيئ، وتفتقر إلى ميزة autocomplete في IDE، وتزيد من تعقيد تكامل CI/CD. يقدم Rider 2026.2 تكاملاً قويًا و native لـ CMake لمشاريع GDExtension. عندما تقوم بإنشاء GDExtension addon، يقوم Rider تلقائيًا بإنشاء ملف CMakeLists.txt نظيف يربط مكتبة bindings الأساسية لـ godot-cpp بكود المصدر المخصص لك. يتيح لك هذا الاستفادة من محرك C++ القوي لـ Rider لتصفح الكود (code navigation)، وإعادة الهيكلة (refactoring)، والتحليل الساكن (static analysis) دون أي إعداد إضافي.

ميزة Dual-Language Debugging في جلسة واحدة

هذه هي جوهرة التحديث. نادرًا ما يلتزم المطورون الذين يكتبون أدوات Godot عالية الأداء بلغة واحدة. تستخدم البنية القياسية C++ عالي الأداء للمهام الحسابية الثقيلة للبيانات أو الرياضيات المعقدة، وملف GDScript خفيف الوزن لـ GUI dock أو لوحة واجهة مستخدم المحرر (editor UI panel). كان عمل debugging لهذه البنية الهجينة يعني استخدام أدوات منفصلة لكل من C++ و GDScript. يقوم Rider 2026.2 بإنشاء تكوينات تشغيل موحدة (unified run configurations) تلقائيًا. يمكنك الضغط على زر "Debug" واحد، وسيقوم Rider بتشغيل محرر Godot، والربط بالعملية الخاصة به (attach to process)، وتتبع عمليات التنفيذ في نفس الوقت عبر GDScript و C++. سيتم تفعيل breakpoint في واجهة مستخدم GDScript الخاصة بك، وعندما تنتقل خطوة داخل دالة C++ native، سينتقل Rider بسلاسة إلى C++ debugger دون مقاطعة الجلسة.

بنية مجلدات جاهزة للنشر (Publish-Ready)

يفرض Godot Asset Store الجديد هياكل مجلدات صارمة ومتطلبات تغليف لمنع تداخل addons مع namespaces الخاصة ببعضها البعض. تفرض قوالب Rider هذه التوصيات من اليوم الأول. من خلال فصل ملفات runtime عن مكونات GUI المخصصة للمحرر فقط (editor-only)، يضمن الـ IDE أنه عند إخراج build، فإنه يكون جاهزًا على الفور للرفع إلى المتجر، مما يقلل أخطاء التغليف من صداع متكرر إلى حدث مؤتمت غير مقلق.

داخل دورة حياة GDExtension: بناء Backend بلغة C++

لفهم قيمة الأتمتة التي يوفرها Rider، يجب أن ننظر إلى ما يتطلبه مشروع GDExtension فعليًا على مستوى الكود. في GDExtension القياسي، يجب عليك تحديد نقطة دخول لتهيئة المكتبة (entry point library initializer)، وتسجيل أنواع الفئات المخصصة (custom class types) مع ClassDB الخاص بـ Godot، وتنظيف المخصصات (allocations) بعناية عند إلغاء تهيئة الوحدة (module). تمثل ترويسات وملفات مصدر C++ التالية الحد الأدنى من boilerplate المطلوب لإنشاء node مخصص native — في هذه الحالة، معالج بيانات تتبع عن بعد (telemetry handler) عالي الأداء.

// 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

بعد ذلك، نقوم بتنفيذ السلوك الأساسي. في ملف التنفيذ (implementation file)، نقوم بتسجيل طرقنا (methods) داخل _bind_methods() لضمان إمكانية وصول محرك انعكاس وقت التشغيل (runtime reflection engine) لـ Godot إليها.

// 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;
}

أخيرًا، يجب أن نخبر Godot بكيفية تحميل الوحدة الخاصة بنا باستخدام دالة التهيئة. يعمل ملف register_types هذا كنقطة الدخول الرئيسية للمكتبة، متصلة عبر نظام تحميل 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();
}
}

تطوير Editor Plugins: مسار عمل (Pipeline) لـ GDScript

بينما تتعامل تقنية GDExtension مع C++ Backend عالي الأداء، فإن واجهة المستخدم لـ Godot addon الخاص بك — مثل إضافة لوحة مخصصة في dock السفلي أو إنشاء inspector nodes مخصصة — تُكتب عادةً بلغة GDScript باستخدام تعليق @tool. يخبر توجيه @tool محرك Godot أن هذا السكربت يجب أن يتم تنفيذه مباشرة داخل مثيل المحرر قيد التشغيل، وليس فقط عند تشغيل اللعبة.

تتطلب كتابة سكربتات الأدوات (tool scripts) إدارة نظيفة لدورة الحياة (lifecycle management). تعمل الدالتان _enter_tree() و _exit_tree() بمثابة الـ constructors والـ destructors لتكاملات المحرر الخاصة بك. سيؤدي الفشل في تنظيف UI nodes المخصصة أثناء إلغاء تحميل المحرر إلى بقاء GUI nodes قديمة ويتيمة (orphaned) تملأ مساحة ذاكرة المحرر، مما يؤدي في النهاية إلى حدوث مشكلات 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.")

تحدي الاتصال: دمج الـ Backends البعيدة

عند بناء editor plugins أو GDExtensions، غالبًا ما تكون أداتك قوية بقدر قوة خدمات Backend التي تتصل بها. على سبيل المثال، إذا كنت تقوم ببناء لوحة تحكم إدارية (admin panel) للعبة مستقلة، أو محرّر مستويات عن بُعد، أو متتبع telemetry داخل المحرك، فسيحتاج plugin الخاص بك إلى الاتصال بقواعد البيانات، وإدارة هويات المطورين أو اللاعبين، ومزامنة الحالات البعيدة. يعني تنفيذ ذلك بنفسك بناء خدمة ويب مخصصة وآمنة. يجب عليك تشغيل خادم افتراضي خاص (VPS)، وإعداد بوابة API (API gateway)، ونشر قاعدة بيانات، وكتابة نماذج مصادقة مستخدم مخصصة، وتنفيذ تدوير شهادات SSL/TLS. هذا عبء هندسي هائل يمكن أن يستهلك بسهولة من 4 إلى 6 أسابيع من وقت التطوير المخصص قبل أن يتمكن الـ plugin الخاص بك من التحدث إلى قاعدة البيانات.

بدلاً من توجيه طاقة فريقك المحدودة نحو إدارة البنية التحتية الخام لـ Backend، يمكنك دمج horizOn ليكون Backend الأساسي لمحرك ألعابك. يوفر horizOn مكتبة SDK مخصصة و native عالية الأداء لكل من C# و GDScript تتصل مباشرة بـ editor addons المخصصة لك. بدلاً من قضاء أسابيع في تهيئة قواعد البيانات وكتابة معالجات WebSockets مخصصة، يمكنك ببساطة وضع عميل horizOn في مشروعك والحصول على الفور على مصادقة آمنة، ووصول فوري لقاعدة البيانات في الوقت الفعلي (real-time)، وإدارة اللاعبين. من خلال ترك الأعباء الثقيلة للبنية التحتية لـ horizOn، يمكنك تكريس وقتك لتحسين تجربة المستخدم (UX) لـ addon وأدوات اللعب الخاصة بك، مع العلم أن Backend الخاص بك سوف يتوسع بسلاسة وسلاسة عند النشر على المتجر.

5 ممارسات مجربة وموثوقة (Battle-Tested) لتطوير Godot Addon

1. عزل الـ Namespace عبر هيكل المجلدات

قم دائمًا بوضع بادئة لمجلدات addon وسكربتاتك باستخدام namespace فريد تحت res://addons/your_unique_addon_name/. يشترك محرك Godot في مسار namespace عالمي مسطح وفردي (flat global path namespace) لجميع الكلاسات المخصصة المسجلة عبر توجيهات @icon أو class_name. إذا استخدمت اسم كلاس عام مثل NetworkManager أو ConfigHelper، فسيحدث تعارض بين addon الخاص بك ومشروع المطور الأساسي أو الإضافات الأخرى من جهات خارجية. احتفظ بجميع سكربتات المرافق (utility scripts) الخاصة بك ضمن نطاق مجلدك الفريد بدقة.

2. أتمتة تجميع الملفات الثنائية (Binary Compilation) واستبعادها من VCS

احفظ ملفات GDExtension الثنائية الثقيلة المجمعة (.dll و .so و .dylib) خارج سجل مستودع Git الرئيسي. سيتضخم حجم المستودع بسرعة كلما أعدت تجميع المكتبات أثناء التطوير. بدلاً من ذلك، استخدم ملف .gitignore لتجاهل مجلدات build ومجلدات الإصدارات (release folders)، وقم بإعداد خط أنابيب CI/CD (مثل GitHub Actions أو GitLab CI) باستخدام سكربتات CMake مؤتمتة لبناء الملفات الثنائية المستهدفة لمنصات متعددة، وتغليفها فقط داخل ملفات zip الخاصة بالإصدارات.

3. إدارة الحدود بين GDScript و C++ بعناية

كن حذرًا بشأن كيفية التعامل مع الذاكرة عند تمرير المتغيرات عبر حدود اللغتين. تدير لغة GDScript تلقائيًا دورة حياة الكلاسات المشتقة من RefCounted (مثل Resource)، ولكنها تستخدم إدارة الذاكرة اليدوية للكائنات التي ترث من Object (مثل كائنات Node الخام). في كود C++ GDExtension الخاص بك، استخدم دائمًا الغلاف الذكي Ref<T> الخاص بـ Godot للكلاسات التي تعتمد على عد المراجع (reference-counted) لتجنب أخطاء double-free أو تسرب الذاكرة (memory leaks). بالنسبة للكلاسات القياسية، قم بإجراء defensive casting باستخدام Object::cast_to<T>() وتحقق من وجود مؤشرات فارغة (null pointers) قبل استدعاء الطرق native.

4. تفضيل WebSockets والاتصالات المستمرة لحالات الوقت الفعلي (Real-Time)

تجنب استخدام HTTP polling التقليدي لـ plugins التي تتطلب مزامنة في الوقت الفعلي، مثل أنظمة المحرر المشتركة أو أدوات Matchmaking الخاصة بـ backend. يؤدي إرسال طلبات HTTP بشكل متكرر إلى حدوث عبء معالجة (CPU overhead) مرتفع ويؤدي إلى فرض عقوبات صارمة على معدل الطلبات (rate-limiting penalties) في خدمات backend. بدلاً من ذلك، يجب عليك التخلي عن HTTP polling لصالح WebSockets لإنشاء اتصال مستمر وثنائي الاتجاه (bidirectional). يؤدي هذا إلى تقليل زمن الانتقال (latency) من 500ms بطيئة إلى فترات زمنية تقل عن 10ms ويقلل من حجم البيانات المرسلة.

5. تصميم أنظمة Fallback مرنة لـ خطوط أنابيب السحابة البعيدة (Remote Cloud Pipelines)

إذا كان addon الخاص بك يتصل بخوادم سحابية بعيدة، فلا تدع انقطاع الشبكة يتسبب أبدًا في تجميد thread محرر Godot. يمكن أن تؤدي طلبات الويب المتزامنة (synchronous requests) إلى حظر عملية Godot الرئيسية، مما يتسبب في تعليق المحرر. استخدم دائمًا الاستدعاءات غير المتزامنة (asynchronous callbacks) أو thread pools للحفاظ على سلاسة استجابات واجهة المستخدم. علاوة على ذلك، إذا كنت تصمم تكاملاً لـ live-ops، فادرس كيفية تصميم خطوط أنابيب قوية من خلال تقييم حملة stop killing games مقابل live ops server fallbacks. يضمن ذلك خفض مستوى أداتك بسلاسة إلى وضع عدم الاتصال (offline mode) ويبقي المحرر يعمل حتى عندما تكون نقاط النهاية السحابية (cloud endpoints) غير قابلة للوصول تمامًا.

الخاتمة: تبسيط مسار أدوات Godot الخاصة بك

يحول JetBrains Rider 2026.2 عملية تطوير Godot addon من مجرد مهمة معقدة في تكوين النظام إلى سير عمل مطور مبسط ومنتج. من خلال أتمتة GDExtension scaffolding، وتقديم تكامل قوي لـ CMake، وتوفير إمكانية debugging متزامنة لكل من GDScript و C++، يقضي Rider على إرهاق التكوين ويتيح لك التركيز على برمجة أدوات رائعة. يتيح لك الجمع بين قوالب تطوير Rider وبنية backend المدارة بالكامل والقابلة للتوسع إنشاء plugins متصلة وعالية الأداء دون أعباء هندسة الخوادم اليدوية.

هل أنت مستعد لتوسيع نطاق Multiplayer backend الخاص بك؟ جرب horizOn مجانًا أو تحقق من API docs.


المصدر: JetBrains Rider brings support for Godot Asset Store addons