NPC检测命令：
HashExists，格式：HashExists 标识 键，高效命令，用于替换CheckStringList此类涉及硬盘IO的低效命令
---------------------------------------------------------------------------
检测执行标识的HASH中是否存在“键”
例：
#if
HashExists 标识 <$USERNAME>
#say
存在
#elsesay
不存在



NPC检测命令：
HashModified，格式：HashModified 标识，检测此“标识”的Hash是否被改动，通常用于保存数据用，程序内部其实会自动判断
---------------------------------------------------------------------------
检测指定标识的Hash是否已改动
例：
#if
HashModified 标识
#say
Hash已经被修改过
#elsesay
未改动



NPC执行命令：
HASH，格式：HASH 标识 子命令 键 值
---------------------------------------------------------------------------
子命令，有以下几种：
Load 文件名	// 从文件加载，不要频繁使用，否则有性能问题！！！
Save 文件名	// 保存到文件，不要频繁使用，否则有性能问题！！！
BGSave 文件名	// 设置后台自动保存，通常只有全局Hash才需要
Clear		// 清理对应标识的HASH容器所有键值（谨用）
Delete key	// key为空时，将删除整个HASH（谨用），否则根据key删除
Get key S1	// 获取key对应的值到S1
Set key value 过期时间(秒)(可选) @过期触发(可选) 执行者(G或空)	// 过期时间设置-1不会过期(默认)，>0则将在X秒后过期并触发QF的[@过期触发]字段，执行者写G是全局触发；触发参数：$PARAM(0)=标识，$PARAM(1)=key，$PARAM(2)=value，$PARAM(3)=执行者名称
Set 键1|键2|键3... 值1|值2|值3... 过期时间1|过期时间2|过期时间3... @过期触发1|@过期触发2|@过期触发3... 执行者1|执行者2|执行者3...	// 批量设置数据方法
Inc key increment	// 对一个存在的key对应的值增加increment，值必须是整数才有效
Dec key decrement	// 对一个存在的key对应的值减少decrement，值必须是整数才有效
GetE key M1				// 获取过期秒数，-2键值不存在，-1永久，>=0为剩余时间(秒)
SetE key 过期时间(秒)	// 设置一个已存在键值的过期时间，过期时间>0会在X秒后删除，设置-1不过期
ReplaceKey 原Key 新Key	// 替换key，保持内容不变，用于角色改名后的支持，key严格按此两种格式定义：角色名.XX 或 角色名_XX，才支持替换！

变量：
$HASH(标识)Size			// 获取对应标识下的HASH数量，保留字
$HASH(标识)key			// 根据key获取值，key只能是非变量的键，否则无法直接解析；作用同：Hash 标识 Get key S1



例1，当高效静态列表使用：
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
#act
; 手动添加一些怪物
Hash H_野猪 Set 黑野猪
Hash H_野猪 Set 红野猪
Hash H_野猪 Set 白野猪

; 或者批量写入
Hash H_野猪 Set 黑野猪|红野猪|白野猪

; 手动保存列表，只是演示，实际使用中提前编辑好列表然后在[@OnStart]加载即可
Hash H_野猪 Save ..\QuestDiary\MonData\野猪.csv

; 加载列表，提前编辑好文件然后在QM的[@OnStart]加载才有效率，上面的操作只是为了保存成一个文件方便查看格式：键,值,过期时间,触发者,触发字段
Hash H_野猪 Load ..\QuestDiary\MonData\野猪.csv

; QM脚本
[@OnStart]
#act
; 服务器启动时，加载一些全局列表数据
Hash H_野猪 Load ..\QuestDiary\MonData\野猪.csv

; QF脚本
[@Attack]
#if
; “H_野猪”Hash中存在大量条目也能高效检测
HashExists H_野猪 <$TARINFO_NAME>
#act
sendmsg 7 目标是野猪
#elsesay
sendmsg 7 不是野猪



例2，一个文件存取多人数据，适合使用自动保存，
“键”严格按格式：角色名.XX 或 角色名_XX 两种方式来定义，以支持改名和合区操作！
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
#act
; 写数据
Hash H_角色数据 Set <$USERNAME>_整数 3 10 @整数过期
Hash H_角色数据 Inc <$USERNAME>_整数 2
Hash H_角色数据 Set <$USERNAME>_字符 字符串数据
Hash H_角色数据 Set <$USERNAME>_日期 <$TIMESTAMP>

; 等效于上面的批量写数据方法
; 过期数组写“|10”，因为默认-1，等效于“-1|10|-1”
Hash H_角色数据 Set <$USERNAME>_字符|<$USERNAME>_整数|<$USERNAME>_日期 字符串数据|3|<$TIMESTAMP> |10 |@整数过期
Hash H_角色数据 Inc <$USERNAME>_整数 2

; 使用命令方式取值
Hash H_角色数据 Get <$USERNAME>_整数 M1
Hash H_角色数据 Get <$USERNAME>_字符 S1
Hash H_角色数据 Get <$USERNAME>_日期 S2
sendmsg 7 数值：<$STR(M1)>，字符：<$STR(S1)>，日期：<$STR(S2)>

; QF写上触发字段
[@整数过期]
#act
OutputMessage 整数过期：uid=<$PARAM(0)>，key=<$PARAM(1)>，val=<$PARAM(2)>，who=<$PARAM(3)>

; QM写上如下脚本
[@OnStart]
#act
; 服务器启动时，加载全局角色数据
Hash H_角色数据 Load ..\QuestDiary\HumData\H_角色数据.csv
; 设置此Hash后台自动保存到文件
Hash H_角色数据 BGSave ..\QuestDiary\HumData\H_角色数据.csv

[@OnStop]
#act
; 服务器停止时，保存全局角色数据（已设置为自动保存可以省略此操作，留着也无所谓）
Hash H_角色数据 Save ..\QuestDiary\HumData\H_角色数据.csv



例3，一个角色名对应一个文件，“不”适合使用全局自动保存，适合在QM加载/保存数据触发字段存取数据，
此方法也不适合活跃人数多的游戏（保存文件太多），可以考虑用于不保存数据的应用场景。
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
#act
Hash <$USERNAME> Set 秒卡 1 30 @秒卡过期
Hash <$USERNAME> Inc 秒卡 14
#if
!HashExists <$USERNAME> 秒卡创建日期
#act
Hash <$USERNAME> Set 秒卡创建日期 <$TIMESTAMP>

; 使用变量方式取值
sendmsg 7 秒卡剩余：<$HASH($USERNAME)秒卡>（秒）

; QM写上如下脚本
[@LoadData]
#act
; 角色加载数据，“秒卡”时间将继续
Hash <$USERNAME> Load ..\QuestDiary\HumData\<$USERNAME>.csv

[@SaveData]
#act
; 角色保存数据，“秒卡”时间将被保存
Hash <$USERNAME> Save ..\QuestDiary\HumData\<$USERNAME>.csv



例4，多值存取，结合json使用
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
平时各种数值可以使用json操作，最后存取放到hash中来即可，但json效率远不如hash



例5，改名处理
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
[@chgname_success]
#act
; 对于使用角色名作为文件名的处理，重命名文本文件名
RenameFile ..\QuestDiary\HumData\<$PARAM(1)>.csv ..\QuestDiary\HumData\<$PARAM(2)>.csv

; 文本文件里的内容改名，下面写法不够严谨，可能会替换错不想要替换的字符，请自行适配
StringsReplace 文件名 <$PARAM(1)> <$PARAM(2)>

; 加入文本中将角色名用[]括起来，则够严谨，通常不会替换错
StringsReplace 文件名 [<$PARAM(1)>] [<$PARAM(2)>]

; 替换Hash里跟角色名相关的Key，key须严格按两种格式之一定义：角色名.XX 或 角色名_XX，才支持替换！
Hash H_角色数据 ReplaceKey <$PARAM(1)> <$PARAM(2)>



例6，合区
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
更新合区工具后也支持Hash保存数据的合并



例7，累计
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
#if
!HashExists 数量统计 例如某个物品名
#act
; 不存在先建立并设置初始值为1
Hash 数量统计 Set 例如某个物品名 1
#elseact
; 该统计存在，累加数值1
Hash 数量统计 Inc 例如某个物品名 1

#if
true
#act
sendmsg 7 某个物品名的数量：<$HASH(数量统计)例如某个物品名>