生物生成限制器(SpawnLimiter)
基本信息
| 属性 | 值 |
|---|---|
| 类型 | 服务端事件拦截器(LivingSpawnEvent.CheckSpawn) |
| 事件优先级 | EventPriority.LOWEST(最后裁决) |
| 触发条件 | 每个生物实体的生成尝试(CheckSpawn) |
| 配置文件 | <configDir>/AMDI/MobSpawnLimits.xml |
| 配置类 | eu.usrv.amdiforge.xml.spawnlimit.SpawnLimitsConfig |
| 工厂类 | SpawnLimitsConfigFactory |
| 状态侧限制 | 仅服务端生效(FMLCommonHandler.instance().getEffectiveSide().isClient() 直接 return) |
工作原理
LivingCheckSpawnEventHandler 注册到 MinecraftForge.EVENT_BUS,对每个 LivingSpawnEvent.CheckSpawn:
- 客户端事件直接 return。
- 非
EntityLiving子类直接 return。 - 若已有
Result.DENY直接 return(让其他拦截器先裁决)。 - 在已加载的
LimitedEntity列表中按类名匹配实体(支持两种匹配模式,见下)。 - 若全局白名单命中 → 允许并加入
EntityCounter计数。 - 否则在匹配实体的
SpawnLimitWorld列表中查找当前维度:- 维度白名单命中 → 允许。
- 否则对比当前实体数与
MaximumSpawnCount:- 已达上限 →
DENY。 - 未达上限 → 维持原
Result(既不强制允许也不强制拒绝,让其他拦截器决定)。
- 已达上限 →
- 实体完全不在配置列表时:
TraceAndExportUnknownEntities = true→ 自动写入配置并落盘。- 否则静默放行,由 Forge 原生规则裁决。
实体匹配规则
LimitedEntity 提供两种匹配模式,通过 ExactNameMatch 属性切换:
| 属性 | 模式 | 匹配方式 |
|---|---|---|
ExactNameMatch="true" |
精确 | 类的 getCanonicalName() 完全相等(忽略大小写)。 |
ExactNameMatch="false"(缺省) |
子串 | 类的 getCanonicalName() 包含配置的子串(忽略大小写)。例:配置 Skeleton 可命中 net.minecraft.entity.monster.EntitySkeleton、EntitySkeleton騎士 等所有 Skeleton 子串类。 |
注:源码注释「
"Skeleton"matches all entities with Skeleton in their classname」。
XML 配置结构
根节点 <LimitedEntities>:
<LimitedEntities>
<LimitedEntity EntityClassName="net.minecraft.entity.monster.EntityCaveSpider" ExactNameMatch="true" GlobalWhitelisted="false">
<World DimensionID="0" MaximumSpawnCount="50" AlwaysAllowSpawn="false"/>
<World DimensionID="-1" MaximumSpawnCount="20" AlwaysAllowSpawn="true"/>
</LimitedEntity>
</LimitedEntities>
| 字段 | 必填 | 含义 |
|---|---|---|
EntityClassName |
是 | Java 类名(精确或子串) |
ExactNameMatch |
否 | true=精确;缺省/其他值=子串 |
GlobalWhitelisted |
否 | true=所有维度强制生成,跳过 MaximumSpawnCount |
World/@DimensionID |
是 | 维度 ID(Overworld=0、Nether=-1、End=1) |
World/@MaximumSpawnCount |
否 | 维度内最大并发数量(-1=不限) |
World/@AlwaysAllowSpawn |
否 | 该维度白名单:忽略计数强制放行 |
初始配置
首次加载时若 MobSpawnLimits.xml 不存在,调用 InitSampleConfig() 写入种子配置:
EntityCaveSpider(精确匹配)→ 维度 0 一条无AlwaysAllowSpawn、未设上限的规则。
随后 SaveSpawnLimits() 通过 JAXB 落盘。
实体计数(EntityCounter)
每次 EntityCounter 单例的定时任务(RunnableManager 每 10 秒触发一次)会遍历所有 WorldServer.loadedEntityList,按 (类名, 维度ID) 汇总当前在线数量;同时 LivingSpawnEvent 通过 trackSpawnEvent 在 Result.ALLOW 时递增一次计数(注意这是事件计数,与实际存活数存在偏差,仅用于 SpawnLimiter 的简单判断)。计数存储在内存中 _mEntities: List<EntityDef>,通过 ReentrantReadWriteLock 保护并发读写。
⚠️ 实现细节:
run()的循环中tFoundWorld = true在命中现有WorldDef后未重置,所以同一实体只会计入第一个WorldDef。已知 Bug,对正确性影响有限。
配置项关联(AMDIConfig)
| 主配置项 | 默认 | 取值范围 | 作用 |
|---|---|---|---|
SpawnLimiter.TraceAndExportUnknownEntities |
false |
bool | 是否将未见过的实体自动追加到 XML |
SpawnLimiter.SpawnEventReportLevel |
0 |
0-2 | 0=不报告;1=仅报告被拒绝;2=报告被拒绝+允许 |
UseMySQL |
true |
bool | 是否启用 MySQL(关闭则跳过数据库初始化) |
注:源码中存在对
DoDebugMessages字段的引用,但AMDIConfig未声明该字段——读取时实际为false,不会打印诊断日志。
配置加载失败保护
ReloadLootGroups() 抛异常 → 触发回退:
- 警告日志
[SpawnLimits] Configuration File seems to be damaged, nothing will be loaded! - 通过
IngameErrorLog.AddErrorLogOnAdminJoin把错误推送给下次登录的管理员 - 调用
InitSampleConfig()写入默认配置以保证系统可继续运行
第三方 mod 集成
- 无直接外部 mod 集成。
LivingCheckSpawnEventHandler仅消费 Forge 自身LivingSpawnEvent,不调用任何第三方 mod API。
相关条目
- /graveadmin 命令 - 读取与墓地档案相关的操作
- /gravehelp 命令 - 玩家侧请求 Lookup Code