Back to skills
SKILL.md
Zinc J
ASecurity本项目 .j 文件中的 Zinc(//! zinc ... //! endzinc)语法与约定:库/结构体写法、匿名函数(无闭包)最佳实践、回调参数传递、资源释放/置空、Hash 表键位与避免不必要新建 hashtable、以及 JASS 到 Zinc 迁移要点。
- 11 stars
- 0 votes
- 0 copies
- 2 views
- Added May 26, 2026
Works with
Security analysis
100/100Pro scans all 6 files and shows the line behind each finding
npx -y skills add Crainax/War3Lib --skill zinc-j --agent claude-codeAre you the author of Zinc J?
Add the live security badge to your README. It updates with every re-scan.
[](https://www.skillsdirectory.com/skills/crainax-zinc-j)---
name: zinc-j
description: 本项目 .j 文件中的 Zinc(//! zinc ... //! endzinc)语法与约定:库/结构体写法、匿名函数(无闭包)最佳实践、回调参数传递、资源释放/置空、Hash 表键位与避免不必要新建 hashtable、以及 JASS 到 Zinc 迁移要点。
---
# Zinc(本项目)语法与约定
## 必须遵守
- 用 `//! zinc` / `//! endzinc` 包裹 Zinc 代码块。
- 禁用 `++/--`;用 `+= 1` / `-= 1`。
- 禁用 `break/continue`;用 `if` 分支或布尔开关改写。
- 数组声明一律用 `type name[];`,不要写大小(例如 `integer a[10]` 是错的)。
- Zinc 的数组维度里不要写表达式(例如 `arr[HERO_COUNT + 1][5]`);需要先把值算成常量再使用,或直接用固定上界。
- 二维数组在底层是线性存储;只要索引映射不冲突(例如 `a[6][5]` 下 `(1,5)` 与 `(2,1)` 映射不同),可以按项目约定安全使用,不必为了“从 1 开始”额外预留整段 `[1]`。
- 返回类型用 `-> type` 写在参数列表后;无返回可省略。
- 局部变量统一在函数/匿名函数开头声明:先基础类型,后句柄类型。
- 函数定义顺序不再作为日常写法的硬性约束:`vjassc` 会处理上方函数直接调用下方函数的场景。
- 仍需兼容 `jasshelper` 时,如果它只因函数顺序报错而 `vjassc` 能过,优先在该调用点改成 `xxxx.evaluate(...)`,少做大范围函数重排。
- 本地场景(例如 UI 本地执行)禁止 `execute`,只能用 `evaluate`,否则有 OOS 风险。
- 仅在本文件内引用的固定长文案/标签,不要另起一个只 `return "..."` 的函数;优先按 `CenterUp.j` 的 `GetResumeLabel()` 风格用函数式 `#define`,并在文件末尾配套 `#undef`。
## 匿名函数(无闭包)
- 匿名函数内不能直接访问外层局部变量;需要显式传参。
- 传参优先用结构体/库的 `static` 成员变量,并在回调结束后立刻清理为默认值(`null/0/""`)。
- 临时触发器回调用 `CreateTrigger` + `TriggerAddCondition` + `TriggerEvaluate`,随后 `DestroyTrigger` 并置 `null`。
## 句柄生命周期(资源释放)
- 创建出来的句柄资源(`timer/trigger/group/effect/location/...`):按对应 `Destroy*` / `Remove*` API 释放,并在作用域末尾 `= null`。
- 可分配结构体(通过 `allocate()` / `create()` 取得实例)析构只写 `method onDestroy()`;外部必须调用实例的 `.destroy()`,使子句柄清理与结构体回收一起执行。
- 不要把 `destroy1()` 当作可分配结构体的析构函数:它只是普通方法,既不会自动触发 `onDestroy()`,也不会回收结构体实例。`destroy1()` 仅可用于 `struct X []` 这类静态管理器的内部 UI 清理流程。
- 不要自定义 `destroy()`。
## 结构体/方法写法
- 静态方法:`static method foo(...) -> type { ... }`
- 实例方法:`method foo(...) -> type { ... }`
- 纯静态“管理器”可用 `struct X [] { ... }`(避免 `create/destroy`)。
## 跨位置调用与性能约束
- `execute`:不带返回值(底层是 `TriggerExecute` 触发 action)。
- `evaluate`:可用于需要返回值或不需要返回值的调用场景。
- `jasshelper` 会把 `evaluate` 降成 trigger/condition 包装,存在额外资源开销;`vjassc` 通常能把这类调用智能降成直接调用。
- 不要为了旧的函数顺序习惯重排大段代码;只有在需要通过 `jasshelper` 或解决循环调用链时才引入 `evaluate` / `execute`。
## 玩家工具与日志名
- 判断地图有效玩家或在线用户时,直接调用 War3Lib `PlayerUtils.cfg` 注入的 `IsValidPlayer(player)` / `IsOnlineUser(player)`;不要在地图模块内重复定义,也不要重复手写 `GetPlayerSlotState(...) == PLAYER_SLOT_STATE_PLAYING && GetPlayerController(...) == MAP_CONTROL_USER`。
- Xlimon 中通过 `DzWriteLog`、`CrainaxLogTrace/Debug/Info/Warn/Error` 或项目日志包装函数记录玩家名时,统一写 `PlayerName[GetConvertedPlayerId(p)]`(已有 `pid/index` 时优先直接用对应数组下标);禁止用 `GetPlayerName(p)`,避免日志出现平台前缀(如 `[初雪境#0]`)。该约束只针对日志,不要机械改动 UI 文案、权限判断、随机种子等非日志语境。
## JASS -> Zinc 迁移要点
- JASS 默认 `public`;Zinc 默认 `private`:需要对外可见的函数/全局变量要显式 `public`。
- `loop/exitwhen/endloop` 优先改写为 Zinc 的范围 `for (a <= i <= b)` 或常规 for。
- timer/trigger 的一次性逻辑优先改成匿名回调。
## Hash 表键位
- 没有必须隔离生命周期或键空间的理由时,不要在 Xlimon 本地新建 `InitHashtable()`;优先复用 War3Lib 的共享表(如 `HASH_ITEM`/`HASH_UNIT`/`HASH_BIGINT`),并把新增子键跨仓定义到对应的 `D:/War3/Library/War3Lib/Jass/core/table/Hash_*Define.j` 文件,避免以后生成代码又在本地创建重复小表。
- 只有确实无法通过共享表完成的任务,才新建独立 hashtable;例如需要独立 flush 整个 child 且不能逐键清理,或键空间/生命周期必须与共享表隔离。
- 新增或迁移 `HASH_UNIT`/`HASH_ITEM`/`HASH_BIGINT` 子键时,先检查对应 `D:/War3/Library/War3Lib/Jass/core/table/Hash_*Define.j` 内是否已有相同数值;同一个 hashtable 内数值冲突会导致完全不同系统互相覆盖。
- `Hash_UnitDefine.j`、`Hash_ItemDefine.j`、`Hash_BIDefine.j` 是不同 hashtable 的键名空间;跨表数值相同通常不是问题,但同文件内数值和宏名都必须唯一。
- 修改共享键位前,先搜索旧宏名的全部 `Save*`/`Load*`/`RemoveSaved*` 调用,确认应该移动新键还是保留既有系统键位。
- 可用以下命令快速检查三张表内重复数值和重复宏名:
```powershell
$files = 'D:\War3\Library\War3Lib\Jass\core\table\Hash_UnitDefine.j','D:\War3\Library\War3Lib\Jass\core\table\Hash_ItemDefine.j','D:\War3\Library\War3Lib\Jass\core\table\Hash_BIDefine.j'; foreach ($file in $files) { Write-Host "FILE $file"; $defs = Select-String -Path $file -Pattern '^\s*#define\s+(\S+)\s+(-?\d+)\b' | ForEach-Object { [pscustomobject]@{ Line=$_.LineNumber; Name=$_.Matches[0].Groups[1].Value; Value=[int64]$_.Matches[0].Groups[2].Value; Text=$_.Line.Trim() } }; $defs | Group-Object Value | Where-Object Count -gt 1 | ForEach-Object { Write-Host " DUP VALUE $($_.Name)"; $_.Group | ForEach-Object { Write-Host (" L{0}: {1}" -f $_.Line, $_.Text) } }; $defs | Group-Object Name | Where-Object Count -gt 1 | ForEach-Object { Write-Host " DUP NAME $($_.Name)"; $_.Group | ForEach-Object { Write-Host (" L{0}: {1}" -f $_.Line, $_.Text) } } }
```
## References(按需加载)
- `references/jass-to-zinc.md`
- `references/anonymous-functions.md`
- `references/callback-params.md`
- `references/method-syntax.md`
- `references/function-order-and-indirect-call.md`
Files in this skill
- SKILL.md
- references/anonymous-functions.md
- references/callback-params.md
- references/function-order-and-indirect-call.md
- references/jass-to-zinc.md
- references/method-syntax.md
Attribution
Comments
Loading comments…
Newsletter
Get the weekly list of safe new skills, and the ones we pulled.