Debugging - HimxhMC/HimDSL GitHub Wiki

调试技巧 / Debugging

打开调试 / Enabling Debug

HimDSL 有两个调试开关:

Two debug switches:

/himdsl debug on
/himdsl debug codeon
  • on —— 打开详细日志:脚本解析、函数收集、执行流程都会打印。

  • codeon —— 每次 run 前,把脚本内容完整打印到控制台。

  • on — verbose logs: parsing, function collection, execution flow.

  • codeon — prints the full script text before each run.

关掉:

Turn off:

/himdsl debug off
/himdsl debug codeoff

重启服务器会重置为关闭状态,不持久化。

Server restart resets them to off. Not persisted.


什么时机用哪个 / When to Use Which

debug on 用于:逻辑不对

debug on for: wrong behavior

脚本能运行但结果不对时。日志会显示每一步变量值、函数调用、分支选择。用来追"为什么这里走了 else 分支"这类问题。

Script runs but produces wrong results. Logs show variable values, function calls, branch choices. Use for "why did this go to else".

debug codeon 用于:脚本内容对不上

debug codeon for: mismatch between intent and file

改了脚本但行为没变——往往是因为 /himdsl run 执行的是另一个文件。codeon 会把当前脚本原文打印出来,对照你改的内容是否一致。

Changed the script but behavior did not — usually running the wrong file. codeon prints the actual text, so you can compare.

日常建议:始终打开 debug on。日志不会影响性能太多,出错时省时间。

Daily advice: keep debug on. Logs barely affect performance, save time when things break.


常见错误信息 / Common Error Messages

语法类

信息 原因
语法错误: ... 缺少分号、括号不匹配等
token recognition error 非法字符,例如中文标识符、不支持的语法

编译阶段错误(compile 命令),脚本未运行。

Parse-time errors (from compile); the script never runs.

运行类

信息 原因
变量 X 未定义 用了未声明的变量
字段不存在: X 访问不存在的字段
未定义函数: X 调用未注册的函数
选择器 @x 没有匹配到任何实体 玩家/实体不存在
Entity 字段 X 是只读的 试图给引用字段赋值
未知事件类型: X event(X) 里的事件名错误
函数 X 存在重载 start 或 stop 用在了重载函数上

运行阶段错误,脚本中途停止。

Runtime errors; the script aborts mid-run.

静默失败

有些错误不会报错:

Some failures are silent:

  • runcmd 目标玩家离线 → 命令丢弃。
  • $player(p, naem)$ 拼错占位符名 → 返回 null。
  • papi("unknown") → 返回 ""。
  • 选择器匹配到空列表 → 空列表参与后续逻辑,可能"看起来像没发生"。

这些都靠不住。用显式检查:

Do not rely on silence. Check explicitly:

if (p != null) {
    runcmd(p.name, "spawn");
}

找问题的常用手法 / Common Approaches

用 say 打印中间值

在关键位置插入 runcmd("CONSOLE", "say ..."),把变量值打出来:

Insert runcmd("CONSOLE", "say ...") at key points to print variable values:

var p = @p;
runcmd("CONSOLE", "say [DEBUG] p = " + p);

if (p != null) {
    runcmd("CONSOLE", "say [DEBUG] hp = " + p.health);
}

加 [DEBUG] 前缀,方便日志搜索和清理。

Prefix with [DEBUG] for easy grep and cleanup.

用 fexists 排除"文件不存在"

runcmd("CONSOLE", "say 文件存在: " + fexists("data.txt"));

很多"数据没了"的问题源于文件路径错了。

Many "data vanished" issues are wrong file paths.

二分排查

脚本很长时,注释掉后半段先跑前半段:

For long scripts, comment out the latter half and run the first:

int main() {
    // 前半段
    runcmd("CONSOLE", "say step 1");

    // 后半段——先注释掉
    /*
    runcmd("CONSOLE", "say step 2");
    */
}

逐步放开,找到出问题的位置。

Uncomment incrementally to locate the failure.

检查返回值

任何可能返回 null 或空值的调用都要检查:

Check every call that may return null or empty:

var p = @p;
if (p == null) {
    runcmd("CONSOLE", "say 没有在线玩家");
    return;
}

var lines = freadlines("data.txt");
if (lines.size() == 0) {
    runcmd("CONSOLE", "say 文件为空");
    return;
}

控制台日志的读法 / Reading the Console Log

HimDSL 的日志格式:

HimDSL's log format:

[HH:MM:SS] [Server thread/INFO]: [HimDSL] ...
[HH:MM:SS] [Server thread/ERROR]: [HimDSL] 执行错误: ...

关键信息:

Key fields:

  • 线程:Server thread 是主线程;Async 开头是异步。

  • 级别:INFO 是信息;WARN / ERROR 是问题。

  • 堆栈:出错时有堆栈跟踪,看第一行 Caused by——真正的根源。

  • Thread: Server thread is main; Async... is async.

  • Level: INFO / WARN / ERROR.

  • Stack trace: check the first Caused by for the real cause.


长期调试建议 / Long-Term Tips

保持 debug on

服务器启动后立刻执行一次:

Run once after server start:

/himdsl debug on

后续 run 都有详细日志。调试完再关。

Detailed logs for later runs. Turn off when done.

用命名清晰的变量

int x = 5;                  // 不知道是什么
int maxLevel = 5;           // 一眼看懂

日志里的变量名 = 代码里的变量名。命名清晰,日志自然好用。

Logs show variable names; clear names make logs readable.

关键操作写日志到文件

重要事件(发奖、扣费、状态切换)写进文件,方便事后查:

Important events (rewards, charges, state changes) to a file for post-mortem:

int f = fopen("actions.log", "a");
fwrite(f, "grant " + p.name + " diamond 1 at " + $server(tps)$ + "\n");
fclose(f);

不要用 fwritelines——它会覆盖文件。追加用 fopen(..., "a")。

Do not use fwritelines — it overwrites. Use fopen(..., "a").

拆分脚本

一个脚本只做一件事。100 行的脚本容易读、容易调、容易改。500 行的脚本终会变成负担。

One script, one purpose. 100-line scripts are easy to read and change. 500-line scripts become a liability.


小结 / Summary

  • debug on 打印详细流程;debug codeon 打印脚本原文。

  • 重启后开关重置。

  • 语法错误在 compile 阶段报;运行错误在执行阶段报。

  • 有些错误静默——runcmd 玩家离线、占位符拼错。

  • 排查手法:say 打印、fexists 检查、二分注释。

  • 日志看线程、级别、第一行 Caused by。

  • 保持 debug on;命名清晰;重要日志写文件。

  • 拆分脚本,一个脚本只做一件事。

  • debug on for verbose flow; debug codeon for script text.

  • Switches reset on restart.

  • Parse errors at compile; runtime errors at execution.

  • Some failures are silent — offline runcmd targets, placeholder typos.

  • Approaches: say prints, fexists checks, binary search with comments.

  • Logs: check thread, level, first Caused by.

  • Keep debug on; name things clearly; log important actions to files.

  • Split scripts — one script, one purpose.


下一步 / Next Steps

下一章 [常见陷阱 / Pitfalls] 汇总所有踩坑点,作为最后的速查。

Next, [常见陷阱 / Pitfalls] aggregates all traps as a final reference.