Skip to content

[1.21.1] Share structure previews 共用结构渲染与只读界面预览 - #283

Merged
Gu-ZT merged 1 commit into
Anvil-Dev:dev/1.21.1from
WhereisFff:codex/shared-preview-1.21.1
Nov 14, 2026
Merged

Gu-ZT merged 1 commit into
Anvil-Dev:dev/1.21.1from
WhereisFff:codex/shared-preview-1.21.1

Conversation

@WhereisFff

Copy link
Copy Markdown
Contributor

将结构渲染、隔离沙盒、相机拾取和只读 GUI 浮窗移入砧库,让手册与机器界面共用实现,避免机器功能依赖手册模组。

  • 提供特殊方块 GUI 注册、减少动态效果参数及附加模型和后处理接口
  • 适配 1.21.1 的原生渲染与透明度补丁,保留结构中的方块实体和实体
  • 提供通用翻译、接口说明和只读作用域回归检查

验证::anvillib-rendering-neoforge-1.21.1:build(含 previewTest)与 git diff --check 通过;已对照当前版本源码核查 Mixin 目标。客户端画面尚未实测。

- 迁移隔离结构沙盒、相机拾取与原生界面浮窗
- 提供自定义界面注册和附加渲染接口,解除对手册的依赖
- 适配当前游戏版本的渲染与透明度补丁
- 补充通用翻译、接口说明及只读作用域回归检查
@Gugle2308

Copy link
Copy Markdown

🌿 Roseau API Breaking Change Report

Module Status Breaking Changes
codec ✅ Compatible 0
collision ✅ Compatible 0
cube ✅ Compatible 0
config ✅ Compatible 0
integration ✅ Compatible 0
moveable-entity-block ✅ Compatible 0
network ✅ Compatible 0
rendering ✅ Compatible 0
font ✅ Compatible 0
explosion ✅ Compatible 0
rpc ✅ Compatible 0
space-select ✅ Compatible 0
util ✅ Compatible 0
math ✅ Compatible 0
multiblock ✅ Compatible 0
recipe ✅ Compatible 0
registrum ✅ Compatible 0
sync ✅ Compatible 0
ui ✅ Compatible 0
wheel ✅ Compatible 0
main ✅ Compatible 0

Full CSVs: see the Artifacts section of this workflow run.

@Gugle2308 Gugle2308 left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

结论:需要修改(尚未合并;head 57052c2e,32 个文件 +3553/−0,CI build / build ✅)。核对后确认,4 个 mixin 目标与两处关键覆写的前提都成立,真正的阻塞项集中在「渲染状态没有还原、mixin 作用面过宽、可选依赖缺少版本防护」三处;另有约 220 行代码、214 条 lang 在本 PR 内没有任何生产调用方。

一、已核对的基础事实(先确认前提成立)

断言 核对结果
本 PR 仍是开放状态且尚未合并 state: open / merged: false / draft: false,1 个提交
改动范围 API changed_files=32 / +3553 / −0,与收到的 diff 文件数一致,覆盖完整
GuiGraphics.setColor 存在 1.21.1 mappings 190:192:void setColor(float,float,float,float) -> a,并且该类只有这一个 setColor 重载 ⇒ 只写方法名而不写描述符时不会匹配到多个目标,argsOnly=true, ordinal=3 对应第 4 个 float 形参(alpha)✅
Screen.renderTransparentBackground(GuiGraphics) 存在 377:378:void renderTransparentBackground(GuiGraphics) -> b ✅
RecipeBookComponent.isVisibleAccordingToBookData() 存在 179:179:boolean isVisibleAccordingToBookData() -> k ✅
RenderStateShard.setupRenderState() 存在且全层级只声明一次 mappings 全文件仅 1 处 ⇒ 基类 TAIL 注入确实覆盖每个 shard(没有被子类覆写绕过)✅
SandboxLevelChunk.setBlockState 真的构成覆写 ChunkAccess 里该方法无行号 = abstract,LevelChunk(223:297)/EmptyLevelChunk(33:33) 各自实现 ⇒ 子类覆写成立。这条是整个预览能画出内容的前提(覆写里才更新 filledBlocks),若当初是包私有方法就整块失效
StructureTemplate.placeInWorld(ServerLevelAccessor,…) 签名 204:350 存在;settings.setIgnoreEntities(true) 与手工重建实体路径自洽
语言文件 en_us / zh_cn 各 107 条 tooltip.*,无单向缺键 ✅
CI head sha check-runs 共 1 项,conclusion: success

二、必须修改(合并前)

1. StructurePreviewRenderer 泄漏渲染状态,作为库会在调用方同帧内留下脏状态

StructurePreviewRenderer.java(新增 441 行)里:

  • render(level, cameraRig)(2 参便捷重载):RenderSystem.clear(GlConst.GL_DEPTH_BUFFER_BIT, Minecraft.ON_OSX) —— 在 GUI pass 里清整屏深度,且此时没有 enableScissor 限定区域,前面已绘制的 GUI/物品深度被抹掉,后续依赖深度的绘制会错位。
  • render(..., int, int, Effects):setShaderFogColor(1,1,1,0) / setShaderFogStart(0) / setShaderFogEnd(1000) / setShaderFogShape(FogShape.SPHERE) 与 setShaderGameTime(...) 设置后没有还原;finally 只 popMatrix + applyModelViewMatrix + restoreProjectionMatrix + Lighting.setupFor3DItems()。
  • PreviewLightmapAtlas.bind() 把预览图集绑到纹理单元 2,同样不还原原版 lightmap。

对「机器界面/手册共用」的库来说,这三处都会作用到调用方后续的 GUI 绘制与同帧其它模组。建议:把 clear 放进 enableScissor 的预览区域内(或直接去掉,改画自己的四边形);在 finally 里把 fog/着色器时间/纹理单元 2 恢复到 GUI 期望值(1.21.1 无 fog getter,需按「GUI 默认」常量回写并在 javadoc 写明),或在 README 明确要求调用方自行 reset。

2. StructureGuiRenderStateMixin 的作用面超出了它想做的事

@Inject(method = "setupRenderState", at = @At("TAIL"))
private void anvillib$blendPreview(CallbackInfo ci) {
    if (StructureGuiSession.isRendering() && StructureGuiSession.opacity() < 1) {
        RenderSystem.enableBlend(); RenderSystem.defaultBlendFunc();
    }
}

setupRenderState 在 RenderStateShard 里只声明一次(已核),因此在预览作用域内每个 shard 执行完都会被强行打开混合——包括显式 disableBlend() 的 NO_TRANSPARENCY、LIGHTMAP/OVERLAY 等并不需要混合的 shard,而且打开后不关闭,会带到同帧后续绘制。建议改成不依赖全局 shard 的点位:在 StructureGuiPreview.render 里包住 screen.render(...) 手工 enableBlend/defaultBlendFunc/disableBlend(作用域与生命周期都在自己手里),或至少把条件收窄到「方块/物品的 texture+transparency 组合」。

3. SodiumSpriteBridge 只挡「没装」,挡不住「装的是别的版本」

compileOnly 'maven.modrinth:sodium:mc1.21.1-0.6.13-neoforge' + ModList.get().isLoaded("sodium") 后直接 SpriteUtil.INSTANCE.markSpriteActive(sprite):Sodium 是可选依赖,玩家可能装其它 0.6.x(甚至 Embeddium 生态的替代实现),SpriteUtil 的包路径/方法一旦对不上就是预览渲染路径上的 NoSuchMethodError / NoClassDefFoundError。建议 try { … } catch (Throwable ignored) {}(这是纯粹的动画纹理提示,失败无害),或先用 ModList.getModContainerById("sodium") 校验版本区间再调用。

三、建议(不阻塞合并)

  • 未接线代码量偏大:StructureBlockEntityDetails.describe(144 行)、StructureDetailsPager(74 行)、StructureGuiSession.tint(全仓仅被 PreviewRegressionTest 调用)在本 PR 内没有任何生产调用方,却同时带进 107×2 条 nbt.field.* 翻译。要么在机器/手册界面里接上(PR 描述声称「附加模型和后处理接口」已就绪,这部分像是留给下一步),要么先删并留 TODO,否则库使用者会以为这是可用能力。
  • SectionOffsetVertexConsumer 只平移 3 参 addVertex:putBulkData(...)(两个重载)与 11 参 addVertex、addVertex(Matrix4f,…) 都直通不偏移。当前 renderLiquid 走 3 参路径没问题,但类名与注释宣称「转换为世界坐标」,任何走 bulk 的调用都会画到分段原点。
  • SandboxRenderLevel.getFilledBlocks() 返回复用的 MutableBlockPos:内部 getBounds() 用 forEach + cur.set(...) 没问题、StructurePreviewPicker.pick 用了 pos.immutable() ✅,但这是 public 方法,任何 collect(toList()) 的调用方会拿到 N 个同一实例。建议改为返回 BlockPos 的不可变流,或把方法降为包私有 + 提供 forEachFilled。
  • StructureGuiProviders.register 第三方面冲突直接抛 IllegalStateException(客户端初始化期)⇒ 两个模组登记同一方块 id 会变成启动崩溃。库侧更稳妥的是 log + first-wins(回归测试断言「不得覆盖已有提供器」可以保留,但抛异常与「不得覆盖」是两件事)。
  • StructureBlockEntityDetails.appendArray 每次取 getAsByteArray()/getAsIntArray()/getAsLongArray(),lambda 里又取一次 ⇒ 逐元素重复分配数组(MAX_LINES 4096 保护了上界,但可一行提出)。
  • DelegatingServerLevelAccessor.getLevel() 抛 UnsupportedOperationException:ignoreEntities(true) 下不该被调到,StructureGuiPreview.open 也 catch 了 RuntimeException 转 false ✅;但 StructureSandboxFactory.create 自身没有兜底,建议在那里 catch 并给一条带结构尺寸/坐标的 warn,否则用户只能看到「预览打不开」。
  • mixins.json 是 "required": true + defaultRequire: 1,4 个 client mixin 全打在原版类上 ⇒ 1.21.1 的补丁级重命名(如 renderTransparentBackground 改名)会让整个模块加载失败。库可以考虑对「纯视觉增强」的两个注入给 require = 0。

四、测试与验证缺口

PR 自述「客户端画面尚未实测」,而本次新增的 previewTest(已正确挂在 check 上)只覆盖 provider 注册/重复保护、renderOnly 嵌套与异常作用域恢复、easeOut 单调性——完全不覆盖渲染。建议补以下场景:真实容器屏(箱子/熔炉/模组存储)在 100%/150% GUI scale 下的浮窗与 scissor 裁剪;F3 隐藏 GUI;开光影(Iris)与未装 Sodium / 装 Sodium 两条路径;浮窗拖出屏幕边缘;结构含方块实体与实体(含乘客)时的重建;以及同一帧内先渲染预览、再渲染原版 GUI 深度是否被污染。

五、验证边界

未执行 Gradle 构建、runClient 与游戏内实测;结论来自静态读码 + 1.21.1 官方 client_mappings/混淆 client.jar 的成员与抽象性核对(setColor、renderTransparentBackground、isVisibleAccordingToBookData、setupRenderState、ChunkAccess#setBlockState、StructureTemplate#placeInWorld)+ head CI check-runs + 语言文件键集合比对 + 32 文件 +3553 与 API 计数对账。渲染观感、光影/Sodium 叠加表现、浮窗手感必须由实机确认,我无法静态替代。

@Gu-ZT
Gu-ZT merged commit 5ac4513 into Anvil-Dev:dev/1.21.1 Nov 14, 2026
1 check passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants