Skip to content

Commit

Permalink
Add repl server skeleton
Browse files Browse the repository at this point in the history
  • Loading branch information
bamboo committed Nov 20, 2024
1 parent a75e019 commit 1207231
Show file tree
Hide file tree
Showing 4 changed files with 149 additions and 14 deletions.
14 changes: 10 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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=<port-number>` (and/or `--s7-tcp-address=<bind-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=<port-number>` and/or `--s7-tcp-address=<bind-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
Expand Down
54 changes: 44 additions & 10 deletions src/register_types.cpp
Original file line number Diff line number Diff line change
@@ -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 <gdextension_interface.h>
#include <godot_cpp/classes/engine.hpp>
#include <godot_cpp/classes/resource_loader.hpp>
#include <godot_cpp/core/class_db.hpp>
#include <godot_cpp/core/defs.hpp>
Expand All @@ -13,10 +15,7 @@ using namespace godot;

static Ref<SchemeScriptLoader> 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);
Expand All @@ -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,
Expand All @@ -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();
}
Expand Down
63 changes: 63 additions & 0 deletions src/scheme_repl_server.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
#include "scheme_repl_server.hpp"
#include <godot_cpp/classes/engine.hpp>
#include <godot_cpp/classes/os.hpp>
#include <godot_cpp/classes/stream_peer_tcp.hpp>
#include <godot_cpp/variant/utility_functions.hpp>

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=<number> and/or --s7-tcp-address=<address> are present in OS::get_cmdline_args()

String tcp_bind_address = "127.0.0.1";
int tcp_port = 0;

Ref<TCPServer> 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;
}
32 changes: 32 additions & 0 deletions src/scheme_repl_server.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
#ifndef GODOT_S7_SCHEME_SCHEME_REPL_SERVER_H
#define GODOT_S7_SCHEME_SCHEME_REPL_SERVER_H

#include "s7.hpp"
#include <godot_cpp/classes/object.hpp>
#include <godot_cpp/classes/stream_peer_tcp.hpp>
#include <godot_cpp/classes/tcp_server.hpp>
#include <godot_cpp/classes/thread.hpp>

namespace godot {

class SchemeReplServer : public Object {
GDCLASS(SchemeReplServer, Object);

static SchemeReplServer *singleton;

private:
mutable bool exit_thread;
Ref<Thread> 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

0 comments on commit 1207231

Please sign in to comment.