#snake #unix-socket #unix #pipe #user-input #tcp-socket #game

bin+lib snakepipe

遵循 Unix 哲学的基于 stdin/stdout 的贪吃蛇游戏

7 个版本 (稳定)

2.2.0 2024 年 5 月 29 日
2.1.0 2024 年 3 月 12 日
1.1.0 2024 年 2 月 17 日
0.2.0 2024 年 2 月 14 日
0.1.0 2024 年 2 月 12 日

62游戏

Download history 65/week @ 2024-05-23 58/week @ 2024-05-30 8/week @ 2024-06-06 3/week @ 2024-06-13 1/week @ 2024-07-04

每月 581 次下载

MIT 许可证

115KB
2K SLoC

Rust 1.5K SLoC // 0.0% comments JavaScript 352 SLoC // 0.2% comments TypeScript 156 SLoC // 0.1% comments

snake-pipe-rust

crates.io Docs Build

不仅仅是一个终端中的贪吃蛇游戏 😉。

https://github.com/topheman/snake-pipe-rust/assets/985982/76161595-1c3a-4252-9cbd-25e144bf185c

这个项目遵循 Unix 哲学

  • snakepipe gamestate 接受用户输入,计算游戏状态并将其写入 stdout
  • snakepipe renderstdin 读取游戏状态并在终端上渲染它
  • snakepipe throttlestdin 读取预先录制的游戏,并将每个时钟周期写入 stdout,以便 snakepipe render 可以检索它
  • snakepipe render-browser 启动服务器,并通过服务器发送事件将 stdin 发送到浏览器中的 JavaScript 渲染器
  • snakepipe stream-sse 连接到由 render-browser 启动的服务器,并将服务器发送事件流回终端
  • snakepipe socket-play 从 stdin 接受游戏状态并将其推送到 Unix 套接字
  • snakepipe socket-watch 从 Unix 套接字读取游戏状态
  • snakepipe tcp-play 从 stdin 接受游戏状态并将其推送到 TCP 套接字
  • snakepipe tcp-watch 从 TCP 套接字读取游戏状态
  • snakepipe pipeline <command> 打印最常见的管道(命令组合),以便您可以直接 pbcopy/粘贴它们

这样

  • 您可以编写自己的 gamestaterender 命令的版本,并用任何编程语言实现它,使其与我的版本兼容
  • 在 Rust 中处理流序列化/反序列化是一个很好的练习

动机

我已经完成了一些Rust项目(使用WebAssembly或bevy),然而,我想要一个需要直接处理以下内容的项目:

  • I/O
  • 解析
  • 并行处理
  • 异步编程
  • 处理管道、stdin、stdout、信号...
  • 进程间通信

安装

任何操作系统 - 如果你的Rust版本 >= 1.75.0 - 如何安装Rust(如果你还没有的话)

cargo install snakepipe

在MacOS上,使用Homebrew(自带zsh、bash和fish的shell补全

brew install topheman/tap/snakepipe

其他操作系统:见发布版

使用方法

管道

🎮 在终端中玩

# basic usage
snakepipe gamestate|snakepipe render

# change the defaults
snakepipe gamestate --frame-duration 80 --width 70 --height 20 --snakepipe-length 15|snakepipe render

# call help on any of the commands
snakepipe --help

📼 你甚至可以使用基本的管道来录制和回放

# record a game into a file using the builtin `tee` command utility
snakepipe gamestate|tee /tmp/snakepipe-output|snakepipe render

# replay the game you recorded
cat /tmp/snakepipe-output|snakepipe throttle|snakepipe render

🖥 你可以将你的游戏终端镜像到可以在浏览器中打开的服务器

snakepipe gamestate|snakepipe render-browser|snakepipe render

然后打开https://127.0.0.1:8080。当你正在终端中玩游戏时,你可以在浏览器中切换渲染器(多亏了服务器推送事件)。

🖼 你可以将你的游戏终端镜像到另一个终端,通过http

打开两个终端

# main terminal:
# - accepts user inputs
# - spawns an http server that streams stdin to server-sent events
# - renders the game to the terminal so you can play
snakepipe gamestate|snakepipe render-browser|snakepipe render
# mirroring terminal (not necessary the same device, only need to be on the same network):
# - connects to the http server and streams server-sent events to sdout
# - render the gamestate retrieved from the server
snakepipe stream-sse|snakepipe render

你可以在你的局域网中共享你的游戏!

IPC(进程间通信)

TCP

打开两个终端。 snakepipe tcp-play 将暴露一个接受TCP连接的进程(默认端口为8050)。你可以通过netcatnc命令)连接到它,它将TCP流输出到stdout。

# main terminal
snakepipe gamestate|snakepipe tcp-play|snakepipe render
# mirroring terminal
nc localhost 8050|snakepipe render # with netcat
snakepipe tcp-watch|snakepipe render # or with snakepipe itself

Unix域套接字

打开两个终端。 snakepipe socket-play 将暴露一个Unix域套接字(默认在/tmp/snakepipe.sock)。你可以通过netcatnc命令)连接到它,它将套接字流输出到stdout。

# main terminal
snakepipe gamestate|snakepipe socket-play|snakepipe render
# mirroring terminal
nc -U /tmp/snakepipe.sock|snakepipe render # with netcat
snakepipe socket-watch|snakepipe render # or with snakepipe itself

其他

📺 你也可以将你的游戏终端镜像到另一个终端

你应该优先使用IPC。

打开两个将通过一个将被tail并管道到snakepipe render的文件进行通信的终端

# mirroring terminal
cat /dev/null > /tmp/snakepipe-output && tail -f /tmp/snakepipe-output|snakepipe render
# main terminal
snakepipe gamestate|tee /tmp/snakepipe-output|snakepipe render

😉 也许你还会找到其他方法?...

Shell补全

如果你使用Homebrew安装了snakepipe,它自带了zsh、bash和fish的补全,它们将自动安装,无需你做任何事情。

如果你手动安装了snakepipe,你可以使用snakepipe generate-completions命令生成补全文件。

命令手册

snakepipe--help
A snake game based on stdin/stdout following unix philosophy

Usage: snakepipe <COMMAND>

Commands:

gamestate Accepts user inputs (arrow keys to control the snake) and outputs the state of the game to stdout render Reads gamestate from stdin and renders the game on your terminal throttle Reads stdin line by line and outputs each line on stdout each frame_duration ms (usefull for replaying a file) render-browser Let's you render the game in your browser at https://127.0.0.1:8080 by spawning a server and sending stdin via server-sent events to a JavaScript renderer stream-sse Connects to the server spawned by render-browser and streams server-sent events back to the terminal help Print this message or the help of the given subcommand(s)

Options: -h, --help Print help -V, --version Print version

snakepipe gamestate--help
Accepts user inputs (arrow keys to control the snake) and outputs the state of the game to stdout

Usage: snakepipe gamestate [OPTIONS]

Options: --frame-duration <FRAME_DURATION> in ms [default: 120] --width <WIDTH> default 25 --height <HEIGHT> default 25 --snake-length <SNAKE_LENGTH> [default: 2] --fit-terminal

snakepipe render--help
Reads gamestate from stdin and renders the game on your terminal

Usage: snakepipe render

snakepipe throttle--help
Reads stdin line by line and outputs each line on stdout each `frame_duration` ms (usefull for replaying a file)

Usage: snakepipe throttle [OPTIONS]

Options: --frame-duration <FRAME_DURATION> in ms [default: 120] --loop-infinite

snakepipe render-browser--help
Let's you render the game in your browser at https://127.0.0.1:8080 by spawning a server and sending stdin via server-sent events to a JavaScript renderer

Usage: snakepipe render-browser [OPTIONS]

Options: --port [default: 8080]

snakepipe stream-sse--help
Connects to the server spawned by `render-browser` and streams server-sent events back to the terminal

Usage: snakepipe stream-sse [OPTIONS]

Options: --address <ADDRESS> [default: https://127.0.0.1:8080]

snakepipe socket-play--help
Accepts gamestate from stdin and pushes it to a unix socket

Usage: snakepipe socket-play [OPTIONS]

Options: --path <PATH> Unix socket file path [default: /tmp/snakepipe.sock]

snakepipe socket-watch--help
Reads gamestate from a unix socket

Usage: snakepipe socket-watch [OPTIONS]

Options: --path <PATH> Unix socket file path [default: /tmp/snakepipe.sock]

snakepipe tcp-play--help
Accepts gamestate from stdin and pushes it to a tcp socket

Usage: snakepipe tcp-play [OPTIONS]

Options: --port <PORT> Port number [default: 8050] --host <HOST> Tcp host [default: 127.0.0.1]

snakepipe tcp-watch--help
Reads gamestate from a tcp socket

Usage: snakepipe tcp-watch [OPTIONS]

Options: --port <PORT> Port number [default: 8050] --host <HOST> Tcp host [default: 127.0.0.1]

snakepipe pipeline--help
Prints out some common pipelines, so that you can copy/paste them to execute (you can pipe to `pbcopy`)

Usage: snakepipe pipeline [OPTIONS] [COMMAND]

Commands: play Play in the terminal record Record a party in the terminal replay Replay a party you recorded in the terminal file-play Play and share a party via a shared file in realtime file-watch Render the party you are sharing through a file in realtime http-play Play and share a party through an http server http-watch Render the party you shared through the http server, in the terminal

作为库使用

cargo add snakepipe # add it to your project

这个包是一个命令行工具,但它也导出了一个lib,你可以从中导入一些工具,例如snakepipe::stream::parse_gamestate - 直接链接到docs.rs

use snakepipe::stream::{parse_gamestate, Game};

fn main() -> () {
    match parse_gamestate() {
        Ok(stream) => {
            println!(
                "Frame duration {}, Snake length {}, Level {}x{}",
                stream.options.frame_duration,
                stream.options.snake_length,
                stream.options.size.width,
                stream.options.size.height
            );
            for parsed_line in stream.lines {
                do_something(parsed_line);
            }
        }
        Err(e) => {
            eprintln!("Error occurred while parsing stdin: \"{}\"", e);
        }
    }
}

fn do_something(parsed_line: Game) {
    println!("Snake head position {:?}", parsed_line.snake.head)
}

贡献

你可以

  • 使用非Rust语言为终端实现实际的snakepipe render命令
  • snakepipe render-browser命令创建自己的JavaScript渲染器,并向项目提交PR以集成它

本crate的实验性/部分Node.js实现可在topheman/snake-pipe-node找到。

更多详细信息请参阅CONTRIBUTING.md

依赖关系

~25–38MB
~678K SLoC