fix: 用户可见的操作提示统一为 uv run 前缀
项目由 uv 管理,依赖装在 .venv。此前各处的提示语仍写作 `python main.py ...`,用户若在未激活虚拟环境时照抄,会因缺少 fastapi 而失败——这是正确性问题,不只是措辞。 涉及:main.py(12 行,含 prog 与各条 hint)、ai_player.py(5 行)、farm/client.py、farm/server/__main__.py(3 行,含 prog)、farm/server/__init__.py、farm/server/daemon.py、README(4 处示例与目录树注释)。 刻意保留 main.py:26 与 README:49 的「没装 uv?」兜底说明,那里 `python main.py` 是正确写法。 验收:ruff 全通过 / format 26 文件无改动 / pyright 0 错误 / 97 项测试通过。
This commit is contained in:
1 parent
d7a7107b0a
commit
c414db4217
7 files changed
+27
-27
No files matched your search
@@ -84,7 +84,7 @@ uv run main.py solo # 本地引擎 + 终端界面,不开
|
||||
**TUI 是纯客户端**:它通过 WebSocket 订阅状态、通过 WebSocket/HTTP 提交动作,
|
||||
自己不持有任何游戏状态。所以:
|
||||
|
||||
* 服务端可以在另一台机器上,`python main.py play --url http://10.0.0.5:8765 --farm alice`;
|
||||
* 服务端可以在另一台机器上,`uv run main.py play --url http://10.0.0.5:8765 --farm alice`;
|
||||
* 关掉 TUI 不会影响服务器,你的牧场继续按时间生长;
|
||||
* 你在 TUI 里操作的同时,AI 可以对同一个牧场操作,双方立刻能看到对方的结果。
|
||||
|
||||
@@ -113,7 +113,7 @@ uv run main.py solo # 本地引擎 + 终端界面,不开
|
||||
实例:
|
||||
|
||||
```bash
|
||||
$ python main.py actions
|
||||
$ uv run main.py actions
|
||||
牧场 default · 第 3 天 · 金币 96 · 行动点 7/10
|
||||
共 9 个候选动作:
|
||||
|
||||
@@ -123,7 +123,7 @@ $ python main.py actions
|
||||
4 号地空闲,可种小麦
|
||||
...
|
||||
|
||||
$ python main.py do harvest '{"plot":2}'
|
||||
$ uv run main.py do harvest '{"plot":2}'
|
||||
✓ 收获 2 份胡萝卜(丰收!),获得 104 金币 (0.01s)
|
||||
第 3 天 · 金币 200 · 行动点 7/10 · 净资产 512.0
|
||||
```
|
||||
@@ -347,7 +347,7 @@ farm/
|
||||
hub.py WebSocket 连接池与状态推送(线程 → 事件循环的桥)
|
||||
daemon.py 守护进程:start / stop / status / 日志 / pid
|
||||
webui.py 内嵌网页客户端(单页,原生 JS + WebSocket)
|
||||
__main__.py python -m farm.server(前台运行)
|
||||
__main__.py uv run python -m farm.server(前台运行)
|
||||
tools/
|
||||
preview_tui.py 把 TUI 渲染成文本(--url 可连真实服务器)
|
||||
ws_demo.py 最小 WebSocket 客户端,验证协议
|
||||
|
||||
+5
-5
@@ -6,9 +6,9 @@
|
||||
|
||||
用法
|
||||
----
|
||||
python ai_player.py # 连本机 default 牧场
|
||||
python ai_player.py --farm ai-1 --minutes 5 # 经营 ai-1 五分钟
|
||||
python ai_player.py --url http://10.0.0.5:8765 --farm my-bot
|
||||
uv run ai_player.py # 连本机 default 牧场
|
||||
uv run ai_player.py --farm ai-1 --minutes 5 # 经营 ai-1 五分钟
|
||||
uv run ai_player.py --url http://10.0.0.5:8765 --farm my-bot
|
||||
|
||||
策略在哪里
|
||||
----------
|
||||
@@ -32,7 +32,7 @@ from farm.engine import GameError
|
||||
|
||||
def build_parser() -> argparse.ArgumentParser:
|
||||
parser = argparse.ArgumentParser(
|
||||
prog="python ai_player.py",
|
||||
prog="uv run ai_player.py",
|
||||
description="通过 HTTP/WebSocket 接口参与 SimpleFarm 的机器人玩家",
|
||||
)
|
||||
parser.add_argument("--url", default="http://127.0.0.1:8765", help="服务器地址")
|
||||
@@ -59,7 +59,7 @@ def main(argv=None) -> int:
|
||||
)
|
||||
except GameError as exc:
|
||||
print(f"✗ 连不上服务器:{exc.message}", file=sys.stderr)
|
||||
print(" 先启动:python main.py serve --daemon", file=sys.stderr)
|
||||
print(" 先启动:uv run main.py serve --daemon", file=sys.stderr)
|
||||
return 2
|
||||
|
||||
snapshot = client.snapshot()
|
||||
|
||||
+1
-1
@@ -322,7 +322,7 @@ class RemoteFarm:
|
||||
self.connected = False
|
||||
raise GameError(
|
||||
"network",
|
||||
f"连不上服务器 {self.endpoint}({exc})。请先启动:python main.py serve --daemon",
|
||||
f"连不上服务器 {self.endpoint}({exc})。请先启动:uv run main.py serve --daemon",
|
||||
status=503,
|
||||
) from exc
|
||||
try:
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
* :mod:`farm.server.webui` —— 内嵌网页客户端
|
||||
|
||||
``app`` 依赖 FastAPI,而 ``daemon`` 只依赖标准库。为了让
|
||||
``python main.py stop`` 在没有装 FastAPI 的机器上也能用,这里采用**惰性导出**:
|
||||
``uv run main.py stop`` 在没有装 FastAPI 的机器上也能用,这里采用**惰性导出**:
|
||||
真正 import 到用例时才加载对应子模块。
|
||||
"""
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
"""``python -m farm.server`` —— 服务器进程入口(前台运行)。
|
||||
"""``uv run python -m farm.server`` —— 服务器进程入口(前台运行)。
|
||||
|
||||
守护进程模式会由 :class:`farm.server.daemon.DaemonManager` 用同样的命令行
|
||||
再拉起一个本模块的实例,只是不带 ``--daemon``。
|
||||
@@ -17,7 +17,7 @@ from .daemon import DEFAULT_LOG_FILE, DEFAULT_PID_FILE, PROJECT_ROOT, DaemonMana
|
||||
|
||||
def build_parser() -> argparse.ArgumentParser:
|
||||
parser = argparse.ArgumentParser(
|
||||
prog="python -m farm.server",
|
||||
prog="uv run python -m farm.server",
|
||||
description="SimpleFarm 游戏服务器:HTTP 接口给 AI,WebSocket 给实时界面。",
|
||||
formatter_class=argparse.RawDescriptionHelpFormatter,
|
||||
)
|
||||
@@ -50,7 +50,7 @@ def build_parser() -> argparse.ArgumentParser:
|
||||
parser.add_argument("--access-log", action="store_true", help="打印每个 HTTP 请求")
|
||||
parser.add_argument("--no-remote-control", action="store_true", help="禁止通过接口停机")
|
||||
parser.add_argument(
|
||||
"--daemon", action="store_true", help="转到后台运行(等价于 python main.py serve --daemon)"
|
||||
"--daemon", action="store_true", help="转到后台运行(等价于 uv run main.py serve --daemon)"
|
||||
)
|
||||
parser.add_argument(
|
||||
"--pid-file", default=None, help="把本进程 pid 写到该文件(守护进程模式会自动指定)"
|
||||
|
||||
@@ -231,7 +231,7 @@ class DaemonManager:
|
||||
f"服务器已在后台启动(pid {pid})\n"
|
||||
f" 网页 http://{self.host}:{self.port}/\n"
|
||||
f" 日志 {self.log_file}\n"
|
||||
f" 停止 python main.py stop"
|
||||
f" 停止 uv run main.py stop"
|
||||
)
|
||||
if process.poll() is not None and self.read_pid() is None:
|
||||
return False, (
|
||||
|
||||
@@ -88,7 +88,7 @@ def die(message: str, hint: str = "", code: int = 1) -> int:
|
||||
# ---------------------------------------------------------------------- 参数
|
||||
def build_parser() -> argparse.ArgumentParser:
|
||||
parser = argparse.ArgumentParser(
|
||||
prog="python main.py",
|
||||
prog="uv run main.py",
|
||||
description="SimpleFarm:终端农场游戏 + 小型多人游戏服务器(HTTP/WebSocket 供 AI 调用)",
|
||||
formatter_class=argparse.RawDescriptionHelpFormatter,
|
||||
epilog=BANNER,
|
||||
@@ -222,8 +222,8 @@ def make_client(args, realtime: bool = False):
|
||||
except GameError as exc:
|
||||
hint = (
|
||||
f"服务器地址:{args.url}\n"
|
||||
"先启动服务器:python main.py serve --daemon\n"
|
||||
"再确认地址端口:python main.py status"
|
||||
"先启动服务器:uv run main.py serve --daemon\n"
|
||||
"再确认地址端口:uv run main.py status"
|
||||
)
|
||||
raise SystemExit(die(f"无法连接服务器:{exc.message}", hint)) from None
|
||||
|
||||
@@ -286,7 +286,7 @@ def cmd_status(args) -> int:
|
||||
|
||||
if not info["running"]:
|
||||
print(warn("• 服务器未运行"))
|
||||
print(dim(" 启动:python main.py serve --daemon"))
|
||||
print(dim(" 启动:uv run main.py serve --daemon"))
|
||||
print(dim(f" 地址:http://{args.host}:{args.port}/"))
|
||||
return 1
|
||||
|
||||
@@ -336,7 +336,7 @@ def cmd_play(args) -> int:
|
||||
if not (sys.stdin is not None and sys.stdin.isatty()):
|
||||
return die(
|
||||
"当前终端不是交互式终端,无法启动界面。",
|
||||
"在真实终端里运行,或用命令行模式:python main.py actions / do / watch",
|
||||
"在真实终端里运行,或用命令行模式:uv run main.py actions / do / watch",
|
||||
)
|
||||
client = make_client(args, realtime=not args.no_realtime)
|
||||
label = f"{client.farm} @ {client.host}"
|
||||
@@ -392,7 +392,7 @@ def cmd_do(args) -> int:
|
||||
except json.JSONDecodeError as exc:
|
||||
return die(
|
||||
f"参数不是合法 JSON:{exc}",
|
||||
'例子:python main.py do plant \'{"plot":1,"crop":"wheat"}\'',
|
||||
'例子:uv run main.py do plant \'{"plot":1,"crop":"wheat"}\'',
|
||||
)
|
||||
if not isinstance(params, dict):
|
||||
return die("参数必须是 JSON 对象")
|
||||
@@ -403,7 +403,7 @@ def cmd_do(args) -> int:
|
||||
except GameError as exc:
|
||||
return die(
|
||||
f"{args.action} 失败({exc.code}):{exc.message}",
|
||||
"看看现在能做什么:python main.py actions",
|
||||
"看看现在能做什么:uv run main.py actions",
|
||||
)
|
||||
|
||||
print(ok("✓ ") + str(result.get("message", "完成")) + dim(f" ({time.time() - started:.2f}s)"))
|
||||
@@ -432,7 +432,7 @@ def cmd_batch(args) -> int:
|
||||
except json.JSONDecodeError as exc:
|
||||
return die(
|
||||
f"actions 不是合法 JSON:{exc}",
|
||||
"例子:python main.py batch "
|
||||
"例子:uv run main.py batch "
|
||||
'\'[{"action":"harvest_all"},{"action":"buy_feed","params":{"quantity":5}}]\'',
|
||||
)
|
||||
if not isinstance(items, list):
|
||||
@@ -458,7 +458,7 @@ def cmd_actions(args) -> int:
|
||||
)
|
||||
if not items:
|
||||
print(warn("• 当前没有可执行的动作"))
|
||||
print(dim(" 试试:python main.py do sleep (推进一天)"))
|
||||
print(dim(" 试试:uv run main.py do sleep (推进一天)"))
|
||||
return 0
|
||||
print(f"共 {len(items)} 个候选动作:\n")
|
||||
for index, item in enumerate(items, start=1):
|
||||
@@ -467,7 +467,7 @@ def cmd_actions(args) -> int:
|
||||
params_text = json.dumps(item["params"], ensure_ascii=False)
|
||||
print(f" [{index:>2}] {item['action']:<12} {params_text:<42} ({cost_text})")
|
||||
print(dim(f" {item.get('reason', '')}"))
|
||||
print(dim("\n执行方式:python main.py do <动作名> '<参数JSON>'"))
|
||||
print(dim("\n执行方式:uv run main.py do <动作名> '<参数JSON>'"))
|
||||
return 0
|
||||
|
||||
|
||||
@@ -561,7 +561,7 @@ def cmd_bot(args) -> int:
|
||||
print("\n已中断。")
|
||||
summary = {"actions": bot.actions_executed, "failures": bot.failures}
|
||||
print(json.dumps(summary, ensure_ascii=False))
|
||||
print(dim("提示:机器人和你调的是同一套接口,随时可以用 python main.py actions 接手。"))
|
||||
print(dim("提示:机器人和你调的是同一套接口,随时可以用 uv run main.py actions 接手。"))
|
||||
return 0
|
||||
|
||||
|
||||
@@ -670,7 +670,7 @@ def main(argv=None) -> int:
|
||||
|
||||
handler = handlers.get(args.command)
|
||||
if handler is None:
|
||||
return die(f"未知命令 {args.command}", "python main.py --help 查看全部命令")
|
||||
return die(f"未知命令 {args.command}", "uv run main.py --help 查看全部命令")
|
||||
try:
|
||||
return handler(args)
|
||||
except SystemExit as exc: # make_client 里的友好退出
|
||||
|
||||
Reference in new issue
Block a user