From 120723115c89339b2f2cac721eb3026f7e4d1758 Mon Sep 17 00:00:00 2001 From: "Rodrigo B. de Oliveira" Date: Wed, 20 Nov 2024 06:50:58 -0300 Subject: [PATCH] Add repl server skeleton --- README.md | 14 ++++++--- src/register_types.cpp | 54 ++++++++++++++++++++++++++------ src/scheme_repl_server.cpp | 63 ++++++++++++++++++++++++++++++++++++++ src/scheme_repl_server.hpp | 32 +++++++++++++++++++ 4 files changed, 149 insertions(+), 14 deletions(-) create mode 100644 src/scheme_repl_server.cpp create mode 100644 src/scheme_repl_server.hpp diff --git a/README.md b/README.md index 665bac2..2372c0d 100644 --- a/README.md +++ b/README.md @@ -85,16 +85,22 @@ Install [Geiser](https://www.nongnu.org/geiser/) then add the following to your The Emacs extension automatically recognize Scheme files inside Godot project directories as `Godot s7 Scheme` files. -### Connecting +### Connecting to the editor -1. Add a `SchemeReplServer` to your scene (preferably as a child of a `Scheme` node) and set its `Auto Start` property to `true`. +1. Start Godot with `--s7-tcp-port=` (and/or `--s7-tcp-address=`). 2. Check the port number in the Godot output window. -3. `M-x connect-to-godot-s7` +3. In Emacs, `M-x connect-to-godot-s7` + +### Connecting to a running scene + +1. In Godot, select `Debug / Customize Run Instances... / Main Run Args` + - Add `--s7-tcp-port=` and/or `--s7-tcp-address=`. +2. Steps 2 and 3 as above. ## Roadmap - [x] use Godot API from Scheme -- [o] live coding interface via Emacs (wip) +- [x] live coding interface via Emacs - [ ] expose tree-sitter API to Scheme - [ ] Scheme editor with syntax highlighting - [ ] Scheme notebooks diff --git a/src/register_types.cpp b/src/register_types.cpp index 9391e85..369cbb4 100644 --- a/src/register_types.cpp +++ b/src/register_types.cpp @@ -1,9 +1,11 @@ #include "register_types.h" #include "scheme.hpp" #include "scheme_object.hpp" +#include "scheme_repl_server.hpp" #include "scheme_script.hpp" #include "scheme_script_loader.hpp" #include +#include #include #include #include @@ -13,10 +15,7 @@ using namespace godot; static Ref script_loader; -void initialize_gdextension_types(ModuleInitializationLevel p_level) { - if (p_level != MODULE_INITIALIZATION_LEVEL_SCENE) { - return; - } +void initialize_scene_types() { GDREGISTER_CLASS(SchemeScript); GDREGISTER_CLASS(SchemeScriptLoader); GDREGISTER_CLASS(Scheme); @@ -26,15 +25,50 @@ void initialize_gdextension_types(ModuleInitializationLevel p_level) { ResourceLoader::get_singleton()->add_resource_format_loader(script_loader); } -void uninitialize_gdextension_types(ModuleInitializationLevel p_level) { - if (p_level != MODULE_INITIALIZATION_LEVEL_SCENE) { - return; - } - +void uninitialize_scene_types() { ResourceLoader::get_singleton()->remove_resource_format_loader(script_loader); script_loader.unref(); } +static SchemeReplServer *repl_server; + +void initialize_server_types() { + GDREGISTER_CLASS(SchemeReplServer); + repl_server = memnew(SchemeReplServer); + repl_server->init(); +} + +void uninitialize_server_types() { + repl_server->finish(); + memdelete(repl_server); +} + +void initialize_gdextension_types(ModuleInitializationLevel p_level) { + switch (p_level) { + case MODULE_INITIALIZATION_LEVEL_SCENE: + initialize_scene_types(); + break; + case MODULE_INITIALIZATION_LEVEL_EDITOR: + initialize_server_types(); + break; + default: + break; + } +} + +void uninitialize_gdextension_types(ModuleInitializationLevel p_level) { + switch (p_level) { + case MODULE_INITIALIZATION_LEVEL_SCENE: + uninitialize_scene_types(); + break; + case MODULE_INITIALIZATION_LEVEL_EDITOR: + uninitialize_server_types(); + break; + default: + break; + } +} + extern "C" { GDExtensionBool GDE_EXPORT godot_s7_scheme_library_init( GDExtensionInterfaceGetProcAddress p_get_proc_address, @@ -43,7 +77,7 @@ GDExtensionBool GDE_EXPORT godot_s7_scheme_library_init( GDExtensionBinding::InitObject init_obj(p_get_proc_address, p_library, r_initialization); init_obj.register_initializer(initialize_gdextension_types); init_obj.register_terminator(uninitialize_gdextension_types); - init_obj.set_minimum_library_initialization_level(MODULE_INITIALIZATION_LEVEL_SCENE); + init_obj.set_minimum_library_initialization_level(MODULE_INITIALIZATION_LEVEL_EDITOR); return init_obj.init(); } diff --git a/src/scheme_repl_server.cpp b/src/scheme_repl_server.cpp new file mode 100644 index 0000000..bfbe2e6 --- /dev/null +++ b/src/scheme_repl_server.cpp @@ -0,0 +1,63 @@ +#include "scheme_repl_server.hpp" +#include +#include +#include +#include + +using namespace godot; +using gd = godot::UtilityFunctions; + +void SchemeReplServer::_bind_methods() { + ClassDB::bind_method(D_METHOD("server_loop"), &SchemeReplServer::server_loop); +} + +void SchemeReplServer::server_loop() { + // TODO: only start server when --s7-tcp-port= and/or --s7-tcp-address=
are present in OS::get_cmdline_args() + + String tcp_bind_address = "127.0.0.1"; + int tcp_port = 0; + + Ref tcp_server; + tcp_server.instantiate(); + + auto error = tcp_server->listen(tcp_port, tcp_bind_address); + ERR_FAIL_COND_MSG(error != OK, ("Failed to start scheme repl server: " + UtilityFunctions::error_string(error))); + + uint64_t msdelay = 250; + + while (!exit_thread) { + // TODO: accept client connections + // TODO: handle pending data + OS::get_singleton()->delay_msec(msdelay); + } + + tcp_server->stop(); +} + +Error SchemeReplServer::init() { + ERR_FAIL_COND_V_MSG(thread.is_valid(), ERR_BUG, "Scheme repl server can only be started once!"); + exit_thread = false; + thread.instantiate(); + return thread->start(Callable::create(this, "server_loop"), Thread::PRIORITY_LOW); +} + +void SchemeReplServer::finish() { + if (thread.is_null()) { + return; + } + + exit_thread = true; + thread->wait_to_finish(); + + thread.unref(); +} + +SchemeReplServer *SchemeReplServer::singleton = NULL; + +SchemeReplServer *SchemeReplServer::get_singleton() { + return singleton; +} + +SchemeReplServer::SchemeReplServer() { + singleton = this; +} diff --git a/src/scheme_repl_server.hpp b/src/scheme_repl_server.hpp new file mode 100644 index 0000000..5243241 --- /dev/null +++ b/src/scheme_repl_server.hpp @@ -0,0 +1,32 @@ +#ifndef GODOT_S7_SCHEME_SCHEME_REPL_SERVER_H +#define GODOT_S7_SCHEME_SCHEME_REPL_SERVER_H + +#include "s7.hpp" +#include +#include +#include +#include + +namespace godot { + +class SchemeReplServer : public Object { + GDCLASS(SchemeReplServer, Object); + + static SchemeReplServer *singleton; + +private: + mutable bool exit_thread; + Ref thread; + +public: + static SchemeReplServer *get_singleton(); + SchemeReplServer(); + Error init(); + void finish(); + +protected: + static void _bind_methods(); + void server_loop(); +}; +} //namespace godot +#endif //GODOT_S7_SCHEME_SCHEME_REPL_SERVER_H