From 53c249d0598b03ebe6693bb3c1fd60df606d1c27 Mon Sep 17 00:00:00 2001 From: blinry Date: Tue, 23 Feb 2021 13:06:58 +0100 Subject: [PATCH] Start working on a better shell mechanism for Windows It uses a Perl script to keep a bash session open, which attaches to a port the game keeps open. This avoids having to start a new Git bash for each command, improving the execution speed by a factor of 3-4. --- scenes/game.gd | 27 ++++++++++++++++++- scenes/game.tscn | 6 ++++- scenes/helpers.gd | 3 +++ scenes/tcp_server.gd | 16 +++++++----- scenes/tcp_server_shell.gd | 50 ++++++++++++++++++++++++++++++++++++ scenes/tcp_server_shell.tscn | 6 +++++ scenes/terminal.gd | 5 ++-- scripts/net-test | 47 +++++++++++++++++++++++++++++---- 8 files changed, 145 insertions(+), 15 deletions(-) create mode 100644 scenes/tcp_server_shell.gd create mode 100644 scenes/tcp_server_shell.tscn diff --git a/scenes/game.gd b/scenes/game.gd index 370464a..976bb60 100644 --- a/scenes/game.gd +++ b/scenes/game.gd @@ -20,6 +20,8 @@ func _ready(): get_tree().set_auto_accept_quit(false) else: game.toggle_music() + + start_remote_shell() global_shell = Shell.new() @@ -33,7 +35,26 @@ func _ready(): copy_script_to_game_env("hint") load_state() - + +func start_remote_shell(): + var user_dir = ProjectSettings.globalize_path("user://") + var script_content = helpers.read_file("res://scripts/net-test") + var target_filename = user_dir + "net-test" + var target_file = File.new() + target_file.open(target_filename, File.WRITE) + target_file.store_string(script_content) + target_file.close() + helpers.exec_async(_perl_executable(), [target_filename]) + +func _perl_executable(): + if OS.get_name() == "Windows": + return "dependencies/windows/git/usr/bin/perl.exe" + else: + return "perl" + +func shell_received(text): + print(text) + func _notification(what): if what == MainLoop.NOTIFICATION_WM_QUIT_REQUEST: #get_tree().quit() # default behavio @@ -99,3 +120,7 @@ func toggle_music(): music.volume_db -= 100 else: music.volume_db += 100 + + +func shell_test(command): + return $ShellServer.send(command) diff --git a/scenes/game.tscn b/scenes/game.tscn index 3c49aaa..bfdf02f 100644 --- a/scenes/game.tscn +++ b/scenes/game.tscn @@ -1,7 +1,8 @@ -[gd_scene load_steps=3 format=2] +[gd_scene load_steps=4 format=2] [ext_resource path="res://scenes/game.gd" type="Script" id=1] [ext_resource path="res://music/gigantic-greasy-giraffe.ogg" type="AudioStream" id=2] +[ext_resource path="res://scenes/tcp_server_shell.tscn" type="PackedScene" id=3] [node name="Node" type="Node"] script = ExtResource( 1 ) @@ -10,3 +11,6 @@ script = ExtResource( 1 ) stream = ExtResource( 2 ) volume_db = -15.0 autoplay = true + +[node name="ShellServer" parent="." instance=ExtResource( 3 )] +port = 6666 diff --git a/scenes/helpers.gd b/scenes/helpers.gd index ff130f1..28887f1 100644 --- a/scenes/helpers.gd +++ b/scenes/helpers.gd @@ -35,6 +35,9 @@ func exec(command, args=[], crash_on_fail=true): return {"output": output, "exit_code": exit_code} +func exec_async(command, args=[]): + OS.execute(command, args, false) + # Return the contents of a file. If no fallback_string is provided, crash when # the file doesn't exist. func read_file(path, fallback_string=null): diff --git a/scenes/tcp_server.gd b/scenes/tcp_server.gd index 896754a..3e5c114 100644 --- a/scenes/tcp_server.gd +++ b/scenes/tcp_server.gd @@ -1,6 +1,7 @@ extends Node signal data_received(string) +signal new_connection export var port: int @@ -21,21 +22,24 @@ func _process(_delta): helpers.crash("Dropping active connection") _c = _s.take_connection() _connected = true + emit_signal("new_connection") print("connected!") if _connected: if _c.get_status() != StreamPeerTCP.STATUS_CONNECTED: _connected = false print("disconnected") - var available = _c.get_available_bytes() - while available > 0: - var data = _c.get_utf8_string(available) - emit_signal("data_received", data) - available = _c.get_available_bytes() +# var available = _c.get_available_bytes() +# while available > 0: +# var data = _c.get_utf8_string(available) +# emit_signal("data_received", data) +# available = _c.get_available_bytes() func send(text): if _connected: text += "\n" - _c.put_data(text.to_utf8()) + _c.put_utf8_string(text) + var response = _c.get_utf8_string() + emit_signal("data_received", response) else: helpers.crash("Trying to send data on closed connection") diff --git a/scenes/tcp_server_shell.gd b/scenes/tcp_server_shell.gd new file mode 100644 index 0000000..17f26ce --- /dev/null +++ b/scenes/tcp_server_shell.gd @@ -0,0 +1,50 @@ +extends Node + +signal data_received(string) +signal new_connection + +export var port: int + +var _s = TCP_Server.new() +var _c +var _connected = false + +func _ready(): + start() + +func start(): + _s.listen(port) + +func _process(_delta): + if _s.is_connection_available(): + if _connected: + _c.disconnect_from_host() + helpers.crash("Dropping active connection") + _c = _s.take_connection() + _connected = true + emit_signal("new_connection") + print("connected!") + + if _connected: + if _c.get_status() != StreamPeerTCP.STATUS_CONNECTED: + _connected = false + print("disconnected") +# var available = _c.get_available_bytes() +# while available > 0: +# var data = _c.get_utf8_string(available) +# emit_signal("data_received", data) +# available = _c.get_available_bytes() + +func send(text): + if _connected: + _c.put_utf8_string(text) + var response = _c.get_utf8_string() + var exit_code = _c.get_u32() + + var shell_command = ShellCommand.new() + shell_command.command = text + shell_command.output = response + shell_command.exit_code = exit_code + return shell_command + else: + helpers.crash("Trying to send data on closed connection") diff --git a/scenes/tcp_server_shell.tscn b/scenes/tcp_server_shell.tscn new file mode 100644 index 0000000..b064a60 --- /dev/null +++ b/scenes/tcp_server_shell.tscn @@ -0,0 +1,6 @@ +[gd_scene load_steps=2 format=2] + +[ext_resource path="res://scenes/tcp_server_shell.gd" type="Script" id=1] + +[node name="TCPServerShell" type="Node"] +script = ExtResource( 1 ) diff --git a/scenes/terminal.gd b/scenes/terminal.gd index 74911ff..4eda373 100644 --- a/scenes/terminal.gd +++ b/scenes/terminal.gd @@ -85,8 +85,9 @@ func send_command(command): editor_regex.compile("^(vim?|gedit|emacs|kate|nano|code) ") command = editor_regex.sub(command, "fake-editor ") - var cmd = repository.shell.run_async(command, false) - yield(cmd, "done") +# var cmd = repository.shell.run_async(command, false) +# yield(cmd, "done") + var cmd = game.shell_test(command) call_deferred("command_done", cmd) func command_done(cmd): diff --git a/scripts/net-test b/scripts/net-test index 0f0c7f7..dc70d8d 100755 --- a/scripts/net-test +++ b/scripts/net-test @@ -1,24 +1,61 @@ #!/usr/bin/env perl +use IPC::Open2; use IO::Socket; +my $pid = open2(my $out, my $in, 'bash'); + $socket = IO::Socket::INET->new(PeerAddr => "127.0.0.1", PeerPort => 6666, Proto => "tcp", Type => SOCK_STREAM); -$s = "Hey äöü!"; - -my $send_length = pack("L", length($s)); -$socket->send($send_length); -$socket->send($s); +#$s = "Hey äöü!"; +# +#my $send_length = pack("L", length($s)); +#$socket->send($send_length); +#$socket->send($s); while(true) { + #STDOUT->flush(); my $len; $socket->recv($len, 4); my $actual_len = unpack("L", $len); + if ($actual_len == 0) { + print("not connected"); + exit; + } + print("still connected"); my $s2; $socket->recv($s2, ord($len)); + print($s2); STDOUT->flush(); + + #$s = `bash -c '$s2' 2>&1`; + + $s = ""; + $command = $s2 . "\necho MAGIC\n"; + print $in $command; + + inner_while: while (true) { + $line = <$out>; + if ($line eq "MAGIC\n") { + STDOUT->flush(); + last inner_while; + } + $s = $s . $line; + } + print "got complete output"; + print $s; + + print length($s); + STDOUT->flush(); + my $send_length = pack("L", length($s)); + $socket->send($send_length); + $socket->send($s); + + my $exit_code = pack("L", 0); + $socket->send($exit_code); + }