summaryrefslogtreecommitdiff
path: root/lib/chessh/ssh/client/client.ex
diff options
context:
space:
mode:
Diffstat (limited to 'lib/chessh/ssh/client/client.ex')
-rw-r--r--lib/chessh/ssh/client/client.ex116
1 files changed, 116 insertions, 0 deletions
diff --git a/lib/chessh/ssh/client/client.ex b/lib/chessh/ssh/client/client.ex
new file mode 100644
index 0000000..a5f7bec
--- /dev/null
+++ b/lib/chessh/ssh/client/client.ex
@@ -0,0 +1,116 @@
+defmodule Chessh.SSH.Client do
+ alias IO.ANSI
+ alias Chessh.SSH.Client.Menu
+ require Logger
+
+ use GenServer
+
+ @clear_codes [
+ ANSI.clear(),
+ ANSI.reset(),
+ ANSI.home()
+ ]
+
+ @max_terminal_width 255
+ @max_terminal_height 127
+
+ @terminal_bad_dim_msg [
+ @clear_codes | "The dimensions of your terminal are not within in the valid range"
+ ]
+
+ defmodule State do
+ defstruct tui_pid: nil,
+ width: 0,
+ height: 0,
+ player_session: nil,
+ screen_processes: []
+ end
+
+ @impl true
+ def init([%State{tui_pid: tui_pid} = state]) do
+ {:ok, screen_pid} =
+ GenServer.start_link(Chessh.SSH.Client.Menu, [
+ %Chessh.SSH.Client.Menu.State{tui_pid: tui_pid}
+ ])
+
+ {:ok, %{state | screen_processes: [screen_pid]}}
+ end
+
+ @impl true
+ def handle_info(:quit, %State{} = state) do
+ {:stop, :normal, state}
+ end
+
+ @impl true
+ def handle_info(msg, state) do
+ [burst_ms, burst_rate] =
+ Application.get_env(:chessh, RateLimits)
+ |> Keyword.take([:player_session_message_burst_ms, :player_session_message_burst_rate])
+ |> Keyword.values()
+
+ case Hammer.check_rate_inc(
+ "player-session-#{state.player_session.id}-burst-message-rate",
+ burst_ms,
+ burst_rate,
+ 1
+ ) do
+ {:allow, _count} ->
+ handle(msg, state)
+
+ {:deny, _limit} ->
+ {:noreply, state}
+ end
+ end
+
+ def handle(
+ {:data, data},
+ %State{width: width, height: height, screen_processes: [screen_pid | _]} = state
+ ) do
+ action = keymap(data)
+
+ if action == :quit do
+ {:stop, :normal, state}
+ else
+ send(screen_pid, {:input, width, height, action})
+ {:noreply, state}
+ end
+ end
+
+ # def handle(
+ # {:refresh, },
+ # %State{screen_processes: [screen_pid | _] = screen_processes, width: width, height: height} = state
+ # ) do
+ # send(screen_pid, {:render, tui_pid, width, height})
+ # {:noreply, state}
+ # end
+
+ def handle(
+ {:resize, {width, height}},
+ %State{tui_pid: tui_pid, screen_processes: [screen_pid | _]} = state
+ ) do
+ new_state = %State{state | width: width, height: height}
+
+ if height <= @max_terminal_height && width <= @max_terminal_width do
+ send(screen_pid, {:render, width, height})
+ else
+ send(tui_pid, {:send_data, @terminal_bad_dim_msg})
+ end
+
+ {:noreply, new_state}
+ end
+
+ def keymap(key) do
+ case key do
+ # Exit keys - C-c and C-d
+ <<3>> -> :quit
+ <<4>> -> :quit
+ # Arrow keys
+ "\e[A" -> :up
+ "\e[B" -> :down
+ "\e[D" -> :left
+ "\e[C" -> :right
+ "\r" -> :return
+ x -> x
+ end
+ end
+end