Compare commits
29 Commits
master
...
develop/gt
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
27048e08bf | ||
|
|
5923000d3b | ||
|
|
680a1ebfd4 | ||
|
|
28c5d0a8f4 | ||
|
|
54e0d8459d | ||
|
|
2821f8d024 | ||
|
|
55c58ff89f | ||
|
|
eace9531ea | ||
|
|
66e815f613 | ||
|
|
5996882b72 | ||
|
|
342a9510d7 | ||
|
|
9f703d89dc | ||
|
|
a4905c2a3f | ||
|
|
e6dd8381bd | ||
|
|
2cf457fde3 | ||
|
|
0b1077a20e | ||
|
|
c9797d3f6c | ||
|
|
9bbeb97b1b | ||
|
|
573a63bab7 | ||
|
|
50d74cafb0 | ||
|
|
9c69bc9175 | ||
|
|
81be9efab4 | ||
|
|
c106cf1be7 | ||
|
|
05f14e8150 | ||
|
|
71b7bca977 | ||
|
|
c48f712c8b | ||
|
|
d8d734bfaf | ||
|
|
0db446b056 | ||
|
|
2e230dcad6 |
91
.github/ISSUE_TEMPLATE/bug_report.yml
vendored
91
.github/ISSUE_TEMPLATE/bug_report.yml
vendored
|
|
@ -1,91 +0,0 @@
|
|||
name: Bug report / 错误报告
|
||||
description: You found a bug or encountered a crash? Please report it here. / 你发现了一个bug或遇到了崩溃?请在这里报告。
|
||||
labels: ["bug"]
|
||||
body:
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: |
|
||||
Provide a summary of the issue in the Title above. / 请在上面的标题中提供问题的摘要。
|
||||
- type: checkboxes
|
||||
id: existing-issue
|
||||
attributes:
|
||||
label: Checked for existing issues / 已检查现有问题
|
||||
description: You have checked for existing reports of this issue, open or closed. / 您已检查过此问题的现有报告(开放或已关闭)。
|
||||
options:
|
||||
- label: I have checked for existing issues, and have found none. / 我已检查现有问题,未发现相同报告。
|
||||
required: true
|
||||
- type: dropdown
|
||||
id: minecraft-version
|
||||
attributes:
|
||||
label: Minecraft Version / Minecraft 版本
|
||||
description: The version of Minecraft you were using when this bug was encountered. If you do not know where to find this, it is in the bottom left of the main menu. Any version not listed is not supported. / 遇到此错误时使用的 Minecraft 版本。如果不知道如何查找,它位于主菜单的左下角。未列出的任何版本均不受支持。
|
||||
options:
|
||||
- "1.20.1 Forge"
|
||||
- "1.21 NeoForge"
|
||||
validations:
|
||||
required: true
|
||||
- type: dropdown
|
||||
id: environment
|
||||
attributes:
|
||||
label: Environment / 环境
|
||||
description: How you were playing on the world. Hybrid servers are unsupported. / 您在世界中游玩的方式。不支持混合服务器。
|
||||
options:
|
||||
- "Singleplayer / 单人游戏"
|
||||
- "Multiplayer - Open to LAN / 多人游戏 - 开放局域网"
|
||||
- "Multiplayer - Dedicated Server / 多人游戏 - 专用服务器"
|
||||
- type: textarea
|
||||
id: mod-version
|
||||
attributes:
|
||||
label: Mod Versions / 模组版本
|
||||
placeholder: |
|
||||
- Applied Energistics 2 version:
|
||||
- ExtendedAE version:
|
||||
- ExtendedAE Plus version:
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
id: other-mods
|
||||
attributes:
|
||||
label: Other Installed Mods / 其他已安装模组
|
||||
description: Enter the name of the modpack you're playing, or list all mods you have installed here. / 输入您正在游玩的整合包名称,或在此处列出所有已安装的模组。
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
id: description
|
||||
attributes:
|
||||
label: Describe the bug / 描述错误
|
||||
description: |
|
||||
Please describe as best as possible. / 请尽可能详细地描述问题
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
id: expected
|
||||
attributes:
|
||||
label: Expected behavior / 预期行为
|
||||
description: |
|
||||
A clear and concise description of what you expected to happen. / 清晰而简洁地描述你期望发生的事情。
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
id: result
|
||||
attributes:
|
||||
label: Actual Behavior / 实际行为
|
||||
description: What happened despite your expectations. Attach screenshots here as necessary. / 与您预期不符的实际发生情况。必要时在此处附加截图。
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
id: reproduce
|
||||
attributes:
|
||||
label: How to reproduce the bug / 重现步骤
|
||||
description: |
|
||||
Please provide the steps to reproduce it, otherwise we might not be able to fix it. / 请提供复制它的步骤,否则我们可能无法修复它。
|
||||
validations:
|
||||
required: true
|
||||
- type: input
|
||||
id: crashlog
|
||||
attributes:
|
||||
label: Crash log
|
||||
description: |
|
||||
Provide a link to a crashlog. E.g. on [Gist](https://gist.github.com/), [pastebin](https://pastebin.com/), or similar.
|
||||
If it is not a crash report, you can hold F3+C for over 10 seconds to force one. This provides with more details to reproduce it without needing to manually acquire them.
|
||||
Please do not have it expire after some time. We might have to go back to it in the future to compare it with other bug reports, or simply have to delay it when being pretty minor.
|
||||
26
.github/ISSUE_TEMPLATE/feature_request.yml
vendored
26
.github/ISSUE_TEMPLATE/feature_request.yml
vendored
|
|
@ -1,26 +0,0 @@
|
|||
name: Feature Request / 功能请求
|
||||
description: A feature request or suggestion to improve ExtendedAE_Plus / 改进ExtendedAE_Plus的功能请求或建议
|
||||
body:
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: |
|
||||
Provide a summary of the issue in the Title above. / 请在上面的标题中提供问题的摘要。
|
||||
- type: dropdown
|
||||
id: minecraft-version
|
||||
attributes:
|
||||
label: Minecraft Version / Minecraft 版本
|
||||
description: The version of Minecraft you wish this feature was added for. / 您希望此功能添加到的 Minecraft 版本。
|
||||
options:
|
||||
- "Both"
|
||||
- "1.20.1 Forge"
|
||||
- "1.21 NeoForge"
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
id: description
|
||||
attributes:
|
||||
label: Feature Description / 功能描述
|
||||
description: "What feature would you like to have? Please describe it here. / 您希望拥有什么功能?请在此描述。"
|
||||
placeholder: "Example: I want to be able to do... / 例如:我希望能够做..."
|
||||
validations:
|
||||
required: true
|
||||
2
.gitignore
vendored
2
.gitignore
vendored
|
|
@ -118,5 +118,3 @@ tmp/
|
|||
libs/*
|
||||
!libs/ExtendedAE-1.20-1.4.2-forge.jar
|
||||
/src/generated/resources/.cache
|
||||
othermods
|
||||
/doc/
|
||||
|
|
|
|||
146
CHANGELOG.md
146
CHANGELOG.md
|
|
@ -1,150 +1,8 @@
|
|||
# Changelog
|
||||
|
||||
## [1.5.4.1]
|
||||
## [Unreleased]
|
||||
|
||||
### Added / 新增
|
||||
- 样板终端按钮显示新增开关控制按钮
|
||||
- Added a toggle button to control Pattern Terminal button visibility.
|
||||
|
||||
## [1.5.4]
|
||||
|
||||
### Added / 新增
|
||||
- 样板终端新增系列倍增按钮
|
||||
- Added series multiplier buttons to the Pattern Terminal.
|
||||
- 镜像绑定工具新增范围绑定功能
|
||||
- Added area binding support to the Mirror Binding Tool.
|
||||
- 新增镜像样板供应器支持绑定 part 类型的样板供应器以及扩展样板供应器
|
||||
- Mirror Pattern Providers can now bind to part-type Pattern Providers and Extended Pattern Providers.
|
||||
- 新增 4 个纪念品玩偶方块
|
||||
- Added 4 souvenir doll blocks.
|
||||
- 唯一匹配时触发自动上传新增开关控制按钮
|
||||
- Added a toggle button for automatic upload when only one match is found.
|
||||
|
||||
### Changed / 变更
|
||||
- 上传合成配方时搜索内容固定为 `crafting`
|
||||
- The search keyword is now fixed to `crafting` when uploading crafting recipes.
|
||||
- 上传合成样板到装配矩阵适配 AE2CS 的陨石样板供应器
|
||||
- Uploading crafting patterns to the Assembler Matrix now supports AE2CS Meteorite Pattern Providers.
|
||||
- 镜像绑定工具操作逻辑和 tips 提示对齐
|
||||
- Aligned Mirror Binding Tool interaction logic with its tips and prompts.
|
||||
- AE2 合成进度界面打开机器 UI 时过滤掉镜像样板供应器
|
||||
- Mirror Pattern Providers are now filtered out when opening machine UIs from the AE2 crafting progress screen.
|
||||
|
||||
### Fixed / 修复
|
||||
- 修复使用标签无线收发器时退出游戏无法保存的问题
|
||||
- Fixed an issue where changes to the Labeled Wireless Transceiver were not saved when leaving the game.
|
||||
- 修复无线收发器满连接时模型顶面异常问题
|
||||
- Fixed abnormal top-face rendering on Wireless Transceivers when fully connected.
|
||||
- 修正虚拟合成卡取消任务逻辑,改为全部发配完毕,且改用 `finishJob` 完成任务
|
||||
- Corrected Virtual Crafting Card task cancellation logic to wait until full dispatch completes and finish the task via `finishJob`.
|
||||
- 修复使用内存卡刷取实体加速卡的 bug
|
||||
- Fixed a bug that allowed Entity Acceleration Cards to be duplicated via the Memory Card.
|
||||
|
||||
### Performance / 性能
|
||||
- 大幅度优化标签无线收发器性能消耗
|
||||
- Significantly optimized the performance cost of the Labeled Wireless Transceiver.
|
||||
- 优化无限磁盘的性能
|
||||
- Optimized infinite disk performance.
|
||||
|
||||
### Docs / 文档
|
||||
- 修复 1.21.1 Guide 中图片只有中文没有英文图片的问题
|
||||
- Fixed missing English images in the 1.21.1 Guide.
|
||||
- 修复 1.21.1 Guide 中部分标签路径错误问题
|
||||
- Fixed several incorrect tag paths in the 1.21.1 Guide.
|
||||
- 超级速度核心 Guide 新增准确描述 `issue#70`
|
||||
- Added a more accurate Guide entry for the Super Speed Core (`issue#70`).
|
||||
|
||||
## [1.5.3]
|
||||
|
||||
### Added / 新增
|
||||
- 增加镜像样板供应器,可通过镜像绑定工具实现与样板供应器/扩展样板供应器的连接
|
||||
- Added the Mirror Pattern Provider, which can be linked to Pattern Providers and Extended Pattern Providers using the Mirror Binding Tool.
|
||||
|
||||
### Changed / 变更
|
||||
- 上传处理样板时,若供应器检索结果仅有一个目标,则自动上传该样板到该供应器
|
||||
- When uploading processing patterns, if provider lookup returns only one target, the pattern is uploaded automatically to that provider.
|
||||
- JEI 书签相关操作适配新版 JEI 历史记录区域中的书签
|
||||
- Updated JEI bookmark-related actions to support bookmarks shown in the new JEI history area.
|
||||
- 更换部分物品的模型材质
|
||||
- Updated models and textures for several items.
|
||||
- 优化 AppFlux 升级槽兼容的实现方案
|
||||
- Optimized the AppFlux upgrade-slot compatibility implementation.
|
||||
- 优化频道卡的连接处理逻辑
|
||||
- Optimized the Channel Card connection handling logic.
|
||||
|
||||
## [1.5.2]
|
||||
|
||||
### Added / 新增
|
||||
- 添加 Ctrl+Q 快捷键从 JEI 编写样板功能(普通书签直接进入背包,带有配方类型的书签会触发样板自动上传)
|
||||
- Added Ctrl+Q shortcut to create patterns from JEI (regular bookmarks go directly to inventory, bookmarks with recipe types trigger automatic pattern upload)
|
||||
- 虚拟合成卡适配量子计算机
|
||||
- Virtual Crafting Card now supports Quantum Computer
|
||||
- 上传样板供应器选择界面新增右键置顶供应器条目功能
|
||||
- Added right-click to pin provider entries in the pattern upload provider selection screen
|
||||
|
||||
### Changed / 变更
|
||||
- F 键搜索改为按键配置
|
||||
- F-key search is now configurable in keybindings
|
||||
- 移除实体加速器的配方
|
||||
- Removed Entity Accelerator recipe
|
||||
|
||||
## [1.5.1]
|
||||
|
||||
### Added / 新增
|
||||
- 标签无线收发器
|
||||
- Added Labeled Wireless Transceiver
|
||||
- JEI 标签中键下单后,若该物品不可自动合成,会自动将物品名称写入 AE 搜索栏
|
||||
- When middle-click ordering from JEI and the item cannot be autocrafted, the item name is now written into the AE search bar automatically
|
||||
|
||||
### Changed / 变更
|
||||
- 反转 Shift 上传样板的操作逻辑
|
||||
- Reversed the Shift pattern-upload behavior
|
||||
- 虚拟合成卡改为完成任务而非取消任务,以便收到计划完成通知
|
||||
- Virtual Crafting Card now completes the task instead of cancelling it, so completion notifications are delivered
|
||||
|
||||
### Fixed / 修复
|
||||
- 修复虚拟合成卡进入世界时的初始化异常问题
|
||||
- Fixed initialization errors when Virtual Crafting Cards load into the world
|
||||
|
||||
## [1.5.0]
|
||||
|
||||
### Added / 新增
|
||||
- 新增超级装配矩阵速度核心
|
||||
- Added Super Assembler Matrix Speed Core
|
||||
- 新增超级装配矩阵合成核心
|
||||
- Added Super Assembler Matrix Crafting Core
|
||||
- 新增超级装配矩阵样板核心
|
||||
- Added Super Assembler Matrix Pattern Core
|
||||
|
||||
### Changed / 变更
|
||||
- 更替无线收发器材质
|
||||
- Updated Wireless Transceiver textures
|
||||
- 更替样板供应器状态控制器材质
|
||||
- Updated Network Pattern Controller textures
|
||||
- 更替模组 logo
|
||||
- Updated mod logo
|
||||
- 更替 ExtendedAE 依赖版本为最低 1.4.6
|
||||
- Raised minimum required ExtendedAE version to 1.4.6
|
||||
|
||||
## [1.4.6]
|
||||
|
||||
### Added / 新增
|
||||
- 添加虚拟合成卡,可在供应器升级槽中终止即将完成的合成
|
||||
- Added the Virtual Crafting Card, allowing providers to abort near-finished jobs from their upgrade slot
|
||||
- 为实体加速器提供红石控制模式,支持信号启停
|
||||
- Added redstone control support for the Entity Accelerator, enabling signal-based start/stop
|
||||
- 新增“中间下单”功能,允许直接在合成计划中插入新的订单
|
||||
- Added the mid-order feature so new requests can be inserted directly into ongoing crafting plans
|
||||
- 样板编码终端上传至装配矩阵需按住 Shift 点击编码按钮才会触发
|
||||
- Uploading crafting patterns to the Assembler Matrix now only triggers when Shift is held during encoding
|
||||
|
||||
### Fixed / 修复
|
||||
- 修复吞噬盘与 AppFlux 联动配方异常的问题
|
||||
- Fixed the Devouring Disk recipe integration issue with AppFlux
|
||||
- 修复合成计划界面与 ExtendedAE 冲突导致显示异常的问题
|
||||
- Fixed visual conflicts between the crafting plan screen and ExtendedAE features
|
||||
|
||||
## [1.4.5]
|
||||
## [1.4.5] / [未发布]
|
||||
|
||||
### Added / 新增
|
||||
- 全新的智能倍增,极端情况下(合成深度 > 100万)不再造成由本模组引起的AE2计算合成树时数据包过大
|
||||
|
|
|
|||
57
build.gradle
57
build.gradle
|
|
@ -70,6 +70,15 @@ repositories {
|
|||
}
|
||||
maven { url "https://repo.spongepowered.org/maven" }
|
||||
maven { url "https://dl.cloudsmith.io/public/geckolib3/geckolib/maven/" }
|
||||
maven { // Registrate
|
||||
url = "https://maven.tterrag.com/"
|
||||
content {
|
||||
// need to be specific here due to version overlaps
|
||||
includeGroup("com.jozufozu.flywheel")
|
||||
includeGroup("com.tterrag.registrate")
|
||||
includeGroup("com.simibubi.create")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
|
|
@ -81,17 +90,18 @@ dependencies {
|
|||
//exendedae前置
|
||||
modImplementation "curse.maven:glodium-957920:${glodium_version}"
|
||||
//extendedAE
|
||||
modImplementation "curse.maven:ex-pattern-provider-892005:7005572"
|
||||
modImplementation files('libs/ExtendedAE-1.20-1.4.2-forge.jar')
|
||||
modRuntimeOnly fileTree(dir: 'libs', includes: ['*.jar'])
|
||||
|
||||
//ae2
|
||||
modImplementation "appeng:appliedenergistics2-forge:${ae2_version}"
|
||||
modImplementation "org.appliedenergistics:guideme:${guideme_version}"
|
||||
modImplementation "curse.maven:applied-energistics-2-wireless-terminals-459929:${wireless_terminals_version}"
|
||||
modCompileOnly "curse.maven:applied-flux-965012:7072853"
|
||||
modImplementation "curse.maven:applied-flux-965012:6755986"
|
||||
modImplementation "curse.maven:mega-cells-622112:${mega_cells_version}"
|
||||
|
||||
//mae2
|
||||
//modRuntimeOnly "curse.maven:modern-ae2-additions-1028068:6827727"
|
||||
modRuntimeOnly "curse.maven:modern-ae2-additions-1028068:6827727"
|
||||
modCompileOnly "curse.maven:modern-ae2-additions-1028068:6827727"
|
||||
|
||||
//aea
|
||||
|
|
@ -99,24 +109,50 @@ dependencies {
|
|||
modRuntimeOnly "curse.maven:advancedae-1084104:6939473"
|
||||
|
||||
modCompileOnly "mezz.jei:jei-${minecraft_version}-forge:${jei_version}"
|
||||
modRuntimeOnly "mezz.jei:jei-${minecraft_version}-forge:15.20.0.129"
|
||||
modRuntimeOnly "mezz.jei:jei-${minecraft_version}-forge:15.20.0.112"
|
||||
modImplementation "mezz.jei:jei-${minecraft_version}-forge:${jei_version}"
|
||||
modImplementation "curse.maven:jade-324717:${jade_version}"
|
||||
|
||||
// GregTech
|
||||
modCompileOnly "curse.maven:gregtechceu-modern-890405:${gregtech_version}"
|
||||
modCompileOnly "curse.maven:ldlib-626676:${ldlib_version}"
|
||||
modImplementation "curse.maven:gregtechceu-modern-890405:${gregtech_version}"
|
||||
modImplementation "curse.maven:ldlib-626676:5775541"
|
||||
modImplementation files('libs/gtlcore-1.2.2.4-fix8.jar')
|
||||
modImplementation("dev:gtmthings-1.3.5.b")
|
||||
modImplementation("dev:resourcefullib-forge-1.20.1-2.1.29")
|
||||
modImplementation("dev:resourcefulconfig-forge-1.20.1-2.1.3")
|
||||
modImplementation("dev:botarium-forge-1.20.1-2.3.4")
|
||||
modImplementation("dev:ad_astra-forge-1.20.1-1.15.20")
|
||||
implementation(annotationProcessor("io.github.llamalad7:mixinextras-common:0.2.0"))
|
||||
implementation(include("io.github.llamalad7:mixinextras-forge:0.2.0"))
|
||||
modImplementation("com.tterrag.registrate:Registrate:MC1.20-1.3.11")
|
||||
modImplementation("curse.maven:kubejs-238086:5454840")
|
||||
modImplementation("curse.maven:rhino-416294:6186971")
|
||||
|
||||
//curios
|
||||
modImplementation "curse.maven:curios-309927:${curios_version}"
|
||||
|
||||
// Runtime test
|
||||
modRuntimeOnly "curse.maven:curios-309927:${curios_version}"
|
||||
modImplementation "curse.maven:jade-324717:5339264"
|
||||
modRuntimeOnly "dev.architectury:architectury-forge:9.2.14"
|
||||
modRuntimeOnly "curse.maven:cloth-config-348521:5729105"
|
||||
|
||||
//jec
|
||||
modCompileClasspath "curse.maven:just-enough-characters-250702:6680042"
|
||||
modRuntimeOnly "curse.maven:just-enough-characters-250702:6680042"
|
||||
modImplementation "curse.maven:just-enough-characters-250702:6680042"
|
||||
modCompileOnly "curse.maven:just-enough-characters-250702:6680042"
|
||||
|
||||
//拼音搜索
|
||||
modRuntimeOnly("dev:jecharacters-1.20.1-forge-4.5.11-dev-shadow")
|
||||
//输入法冲突
|
||||
modRuntimeOnly("curse.maven:cloth-config-348521:5729105")
|
||||
modRuntimeOnly("curse.maven:IMBlocker-483760:6922546")
|
||||
|
||||
//mae2
|
||||
// modRuntimeOnly "curse.maven:modern-ae2-additions-1028068:6827727"
|
||||
modCompileOnly "curse.maven:modern-ae2-additions-1028068:6827727"
|
||||
|
||||
//aea
|
||||
modImplementation "curse.maven:advancedae-1084104:6939473"
|
||||
|
||||
//geckolib
|
||||
modRuntimeOnly "curse.maven:geckolib-388172:6920925"
|
||||
|
|
@ -130,11 +166,10 @@ dependencies {
|
|||
//ftbteams
|
||||
modCompileOnly "curse.maven:ftb-teams-forge-404468:6130786"
|
||||
modCompileOnly "curse.maven:ftb-library-forge-404465:6807424"
|
||||
modCompileOnly "curse.maven:ftb-chunk-forge-314906:6431735"
|
||||
modRuntimeOnly "curse.maven:ftb-teams-forge-404468:6130786"
|
||||
modRuntimeOnly "curse.maven:ftb-library-forge-404465:6807424"
|
||||
|
||||
//MEK
|
||||
modImplementation "curse.maven:mekanism-268560:6552911"
|
||||
modRuntimeOnly "curse.maven:ftb-chunk-forge-314906:6431735"
|
||||
}
|
||||
|
||||
compileJava {
|
||||
|
|
|
|||
|
|
@ -1,9 +1,9 @@
|
|||
# Done to increase the memory available to Gradle.
|
||||
org.gradle.jvmargs=-Xmx1G
|
||||
loom.platform = forge
|
||||
org.gradle.parallel=true
|
||||
|
||||
mod_version = 1.5.4
|
||||
# Mod properties
|
||||
mod_version = 1.4.5-gtl
|
||||
maven_group = com.extendedae_plus
|
||||
archives_name = extendedae_plus
|
||||
|
||||
|
|
@ -16,7 +16,7 @@ glodium_version=5006780
|
|||
ae2_version=15.4.5
|
||||
guideme_version=20.1.7
|
||||
wireless_terminals_version=5162352
|
||||
jei_version=15.20.0.129
|
||||
jei_version=15.19.5.99
|
||||
applied_flux_version=5329825
|
||||
mega_cells_version=5320730
|
||||
jade_version=4768593
|
||||
|
|
@ -26,7 +26,7 @@ rei_version=12.0.622
|
|||
cloth_config_version=9.0.94
|
||||
projecte_version=4901949
|
||||
appliede_version=5364294
|
||||
gregtech_version=5369020
|
||||
gregtech_version=5753724
|
||||
ldlib_version=5394816
|
||||
ie_version=5224387
|
||||
mixin_version=0.8.4
|
||||
|
|
|
|||
BIN
libs/ad_astra-forge-1.20.1-1.15.20.jar
Normal file
BIN
libs/ad_astra-forge-1.20.1-1.15.20.jar
Normal file
Binary file not shown.
BIN
libs/botarium-forge-1.20.1-2.3.4.jar
Normal file
BIN
libs/botarium-forge-1.20.1-2.3.4.jar
Normal file
Binary file not shown.
BIN
libs/gtlcore-1.2.2.4-fix8.jar
Normal file
BIN
libs/gtlcore-1.2.2.4-fix8.jar
Normal file
Binary file not shown.
BIN
libs/gtmthings-1.3.5.b.jar
Normal file
BIN
libs/gtmthings-1.3.5.b.jar
Normal file
Binary file not shown.
BIN
libs/jecharacters-1.20.1-forge-4.5.11-dev-shadow.jar
Normal file
BIN
libs/jecharacters-1.20.1-forge-4.5.11-dev-shadow.jar
Normal file
Binary file not shown.
BIN
libs/resourcefulconfig-forge-1.20.1-2.1.3.jar
Normal file
BIN
libs/resourcefulconfig-forge-1.20.1-2.1.3.jar
Normal file
Binary file not shown.
BIN
libs/resourcefullib-forge-1.20.1-2.1.29.jar
Normal file
BIN
libs/resourcefullib-forge-1.20.1-2.1.29.jar
Normal file
Binary file not shown.
|
|
@ -1,35 +0,0 @@
|
|||
{
|
||||
"parent": "minecraft:recipes/root",
|
||||
"criteria": {
|
||||
"has_quantum_ring": {
|
||||
"conditions": {
|
||||
"items": [
|
||||
{
|
||||
"items": [
|
||||
"ae2:quantum_ring"
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
"trigger": "minecraft:inventory_changed"
|
||||
},
|
||||
"has_the_recipe": {
|
||||
"conditions": {
|
||||
"recipe": "extendedae_plus:assembler_matrix_crafter_plus"
|
||||
},
|
||||
"trigger": "minecraft:recipe_unlocked"
|
||||
}
|
||||
},
|
||||
"requirements": [
|
||||
[
|
||||
"has_quantum_ring",
|
||||
"has_the_recipe"
|
||||
]
|
||||
],
|
||||
"rewards": {
|
||||
"recipes": [
|
||||
"extendedae_plus:assembler_matrix_crafter_plus"
|
||||
]
|
||||
},
|
||||
"sends_telemetry_event": false
|
||||
}
|
||||
|
|
@ -1,35 +0,0 @@
|
|||
{
|
||||
"parent": "minecraft:recipes/root",
|
||||
"criteria": {
|
||||
"has_quantum_ring": {
|
||||
"conditions": {
|
||||
"items": [
|
||||
{
|
||||
"items": [
|
||||
"ae2:quantum_ring"
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
"trigger": "minecraft:inventory_changed"
|
||||
},
|
||||
"has_the_recipe": {
|
||||
"conditions": {
|
||||
"recipe": "extendedae_plus:assembler_matrix_pattern_plus"
|
||||
},
|
||||
"trigger": "minecraft:recipe_unlocked"
|
||||
}
|
||||
},
|
||||
"requirements": [
|
||||
[
|
||||
"has_quantum_ring",
|
||||
"has_the_recipe"
|
||||
]
|
||||
],
|
||||
"rewards": {
|
||||
"recipes": [
|
||||
"extendedae_plus:assembler_matrix_pattern_plus"
|
||||
]
|
||||
},
|
||||
"sends_telemetry_event": false
|
||||
}
|
||||
|
|
@ -1,12 +1,24 @@
|
|||
{
|
||||
"parent": "minecraft:recipes/root",
|
||||
"criteria": {
|
||||
"has_quantum_ring": {
|
||||
"has_entity_speed_card_x2": {
|
||||
"conditions": {
|
||||
"items": [
|
||||
{
|
||||
"items": [
|
||||
"ae2:quantum_ring"
|
||||
"extendedae_plus:entity_speed_card"
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
"trigger": "minecraft:inventory_changed"
|
||||
},
|
||||
"has_singularity": {
|
||||
"conditions": {
|
||||
"items": [
|
||||
{
|
||||
"items": [
|
||||
"ae2:singularity"
|
||||
]
|
||||
}
|
||||
]
|
||||
|
|
@ -15,20 +27,21 @@
|
|||
},
|
||||
"has_the_recipe": {
|
||||
"conditions": {
|
||||
"recipe": "extendedae_plus:assembler_matrix_speed_plus"
|
||||
"recipe": "extendedae_plus:entity_speed_ticker"
|
||||
},
|
||||
"trigger": "minecraft:recipe_unlocked"
|
||||
}
|
||||
},
|
||||
"requirements": [
|
||||
[
|
||||
"has_quantum_ring",
|
||||
"has_entity_speed_card_x2",
|
||||
"has_singularity",
|
||||
"has_the_recipe"
|
||||
]
|
||||
],
|
||||
"rewards": {
|
||||
"recipes": [
|
||||
"extendedae_plus:assembler_matrix_speed_plus"
|
||||
"extendedae_plus:entity_speed_ticker"
|
||||
]
|
||||
},
|
||||
"sends_telemetry_event": false
|
||||
|
|
@ -1,35 +0,0 @@
|
|||
{
|
||||
"parent": "minecraft:recipes/root",
|
||||
"criteria": {
|
||||
"has_the_recipe": {
|
||||
"conditions": {
|
||||
"recipe": "extendedae_plus:labeled_wireless_transceiver"
|
||||
},
|
||||
"trigger": "minecraft:recipe_unlocked"
|
||||
},
|
||||
"has_wireless_transceiver": {
|
||||
"conditions": {
|
||||
"items": [
|
||||
{
|
||||
"items": [
|
||||
"extendedae_plus:wireless_transceiver"
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
"trigger": "minecraft:inventory_changed"
|
||||
}
|
||||
},
|
||||
"requirements": [
|
||||
[
|
||||
"has_wireless_transceiver",
|
||||
"has_the_recipe"
|
||||
]
|
||||
],
|
||||
"rewards": {
|
||||
"recipes": [
|
||||
"extendedae_plus:labeled_wireless_transceiver"
|
||||
]
|
||||
},
|
||||
"sends_telemetry_event": false
|
||||
}
|
||||
|
|
@ -1,35 +0,0 @@
|
|||
{
|
||||
"parent": "minecraft:recipes/root",
|
||||
"criteria": {
|
||||
"has_mirror_pattern_binding_tool": {
|
||||
"conditions": {
|
||||
"items": [
|
||||
{
|
||||
"items": [
|
||||
"extendedae_plus:mirror_pattern_binding_tool"
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
"trigger": "minecraft:inventory_changed"
|
||||
},
|
||||
"has_the_recipe": {
|
||||
"conditions": {
|
||||
"recipe": "extendedae_plus:mirror_pattern_binding_tool"
|
||||
},
|
||||
"trigger": "minecraft:recipe_unlocked"
|
||||
}
|
||||
},
|
||||
"requirements": [
|
||||
[
|
||||
"has_mirror_pattern_binding_tool",
|
||||
"has_the_recipe"
|
||||
]
|
||||
],
|
||||
"rewards": {
|
||||
"recipes": [
|
||||
"extendedae_plus:mirror_pattern_binding_tool"
|
||||
]
|
||||
},
|
||||
"sends_telemetry_event": false
|
||||
}
|
||||
|
|
@ -1,35 +0,0 @@
|
|||
{
|
||||
"parent": "minecraft:recipes/root",
|
||||
"criteria": {
|
||||
"has_mirror_pattern_provider": {
|
||||
"conditions": {
|
||||
"items": [
|
||||
{
|
||||
"items": [
|
||||
"extendedae_plus:mirror_pattern_provider"
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
"trigger": "minecraft:inventory_changed"
|
||||
},
|
||||
"has_the_recipe": {
|
||||
"conditions": {
|
||||
"recipe": "extendedae_plus:mirror_pattern_provider"
|
||||
},
|
||||
"trigger": "minecraft:recipe_unlocked"
|
||||
}
|
||||
},
|
||||
"requirements": [
|
||||
[
|
||||
"has_mirror_pattern_provider",
|
||||
"has_the_recipe"
|
||||
]
|
||||
],
|
||||
"rewards": {
|
||||
"recipes": [
|
||||
"extendedae_plus:mirror_pattern_provider"
|
||||
]
|
||||
},
|
||||
"sends_telemetry_event": false
|
||||
}
|
||||
|
|
@ -1,35 +0,0 @@
|
|||
{
|
||||
"parent": "minecraft:recipes/root",
|
||||
"criteria": {
|
||||
"has_the_recipe": {
|
||||
"conditions": {
|
||||
"recipe": "extendedae_plus:virtual_crafting_card"
|
||||
},
|
||||
"trigger": "minecraft:recipe_unlocked"
|
||||
},
|
||||
"has_virtual_crafting_card_ingredients": {
|
||||
"conditions": {
|
||||
"items": [
|
||||
{
|
||||
"items": [
|
||||
"ae2:advanced_card"
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
"trigger": "minecraft:inventory_changed"
|
||||
}
|
||||
},
|
||||
"requirements": [
|
||||
[
|
||||
"has_virtual_crafting_card_ingredients",
|
||||
"has_the_recipe"
|
||||
]
|
||||
],
|
||||
"rewards": {
|
||||
"recipes": [
|
||||
"extendedae_plus:virtual_crafting_card"
|
||||
]
|
||||
},
|
||||
"sends_telemetry_event": false
|
||||
}
|
||||
|
|
@ -1,24 +0,0 @@
|
|||
{
|
||||
"type": "minecraft:crafting_shaped",
|
||||
"category": "misc",
|
||||
"key": {
|
||||
"B": {
|
||||
"item": "expatternprovider:assembler_matrix_wall"
|
||||
},
|
||||
"L": {
|
||||
"item": "minecraft:nether_star"
|
||||
},
|
||||
"R": {
|
||||
"item": "expatternprovider:assembler_matrix_crafter"
|
||||
}
|
||||
},
|
||||
"pattern": [
|
||||
"BRB",
|
||||
"RLR",
|
||||
"BRB"
|
||||
],
|
||||
"result": {
|
||||
"item": "extendedae_plus:assembler_matrix_crafter_plus"
|
||||
},
|
||||
"show_notification": true
|
||||
}
|
||||
|
|
@ -1,24 +0,0 @@
|
|||
{
|
||||
"type": "minecraft:crafting_shaped",
|
||||
"category": "misc",
|
||||
"key": {
|
||||
"B": {
|
||||
"item": "expatternprovider:assembler_matrix_wall"
|
||||
},
|
||||
"L": {
|
||||
"item": "minecraft:nether_star"
|
||||
},
|
||||
"R": {
|
||||
"item": "expatternprovider:assembler_matrix_pattern"
|
||||
}
|
||||
},
|
||||
"pattern": [
|
||||
"BRB",
|
||||
"RLR",
|
||||
"BRB"
|
||||
],
|
||||
"result": {
|
||||
"item": "extendedae_plus:assembler_matrix_pattern_plus"
|
||||
},
|
||||
"show_notification": true
|
||||
}
|
||||
|
|
@ -1,24 +0,0 @@
|
|||
{
|
||||
"type": "minecraft:crafting_shaped",
|
||||
"category": "misc",
|
||||
"key": {
|
||||
"B": {
|
||||
"item": "expatternprovider:assembler_matrix_wall"
|
||||
},
|
||||
"L": {
|
||||
"item": "minecraft:nether_star"
|
||||
},
|
||||
"R": {
|
||||
"item": "expatternprovider:assembler_matrix_speed"
|
||||
}
|
||||
},
|
||||
"pattern": [
|
||||
"BRB",
|
||||
"RLR",
|
||||
"BRB"
|
||||
],
|
||||
"result": {
|
||||
"item": "extendedae_plus:assembler_matrix_speed_plus"
|
||||
},
|
||||
"show_notification": true
|
||||
}
|
||||
|
|
@ -0,0 +1,34 @@
|
|||
{
|
||||
"type": "minecraft:crafting_shaped",
|
||||
"key": {
|
||||
"I": {
|
||||
"item": "expatternprovider:ex_io_port"
|
||||
},
|
||||
"Q": {
|
||||
"item": "ae2:singularity"
|
||||
},
|
||||
"S": {
|
||||
"type": "forge:nbt",
|
||||
"item": "extendedae_plus:entity_speed_card",
|
||||
"nbt": {
|
||||
"EAS:mult": 2
|
||||
}
|
||||
},
|
||||
"X": {
|
||||
"item": "minecraft:nether_star"
|
||||
},
|
||||
"Z": {
|
||||
"item": "ae2:dense_energy_cell"
|
||||
}
|
||||
},
|
||||
"pattern": [
|
||||
"SZS",
|
||||
"QXQ",
|
||||
"SIS"
|
||||
],
|
||||
"result": {
|
||||
"type": "forge:partial_nbt",
|
||||
"item": "extendedae_plus:entity_speed_ticker"
|
||||
},
|
||||
"show_notification": true
|
||||
}
|
||||
|
|
@ -1,24 +0,0 @@
|
|||
{
|
||||
"type": "minecraft:crafting_shaped",
|
||||
"category": "misc",
|
||||
"key": {
|
||||
"A": {
|
||||
"item": "minecraft:paper"
|
||||
},
|
||||
"B": {
|
||||
"item": "extendedae_plus:wireless_transceiver"
|
||||
},
|
||||
"C": {
|
||||
"item": "minecraft:emerald"
|
||||
}
|
||||
},
|
||||
"pattern": [
|
||||
"CAC",
|
||||
"ABA",
|
||||
"CAC"
|
||||
],
|
||||
"result": {
|
||||
"item": "extendedae_plus:labeled_wireless_transceiver"
|
||||
},
|
||||
"show_notification": true
|
||||
}
|
||||
|
|
@ -1,27 +0,0 @@
|
|||
{
|
||||
"type": "minecraft:crafting_shaped",
|
||||
"category": "misc",
|
||||
"key": {
|
||||
"A": {
|
||||
"item": "ae2:wireless_receiver"
|
||||
},
|
||||
"B": {
|
||||
"item": "minecraft:iron_ingot"
|
||||
},
|
||||
"C": {
|
||||
"item": "minecraft:redstone"
|
||||
},
|
||||
"D": {
|
||||
"item": "ae2:calculation_processor"
|
||||
}
|
||||
},
|
||||
"pattern": [
|
||||
" A",
|
||||
"BCD",
|
||||
"BBB"
|
||||
],
|
||||
"result": {
|
||||
"item": "extendedae_plus:mirror_pattern_binding_tool"
|
||||
},
|
||||
"show_notification": true
|
||||
}
|
||||
|
|
@ -1,21 +0,0 @@
|
|||
{
|
||||
"type": "minecraft:crafting_shaped",
|
||||
"category": "misc",
|
||||
"key": {
|
||||
"A": {
|
||||
"item": "minecraft:glass"
|
||||
},
|
||||
"B": {
|
||||
"item": "ae2:pattern_provider"
|
||||
}
|
||||
},
|
||||
"pattern": [
|
||||
"AAA",
|
||||
"ABA",
|
||||
"AAA"
|
||||
],
|
||||
"result": {
|
||||
"item": "extendedae_plus:mirror_pattern_provider"
|
||||
},
|
||||
"show_notification": true
|
||||
}
|
||||
|
|
@ -1,18 +0,0 @@
|
|||
{
|
||||
"type": "minecraft:crafting_shapeless",
|
||||
"category": "misc",
|
||||
"ingredients": [
|
||||
{
|
||||
"item": "ae2:advanced_card"
|
||||
},
|
||||
{
|
||||
"item": "minecraft:crafting_table"
|
||||
},
|
||||
{
|
||||
"item": "minecraft:lever"
|
||||
}
|
||||
],
|
||||
"result": {
|
||||
"item": "extendedae_plus:virtual_crafting_card"
|
||||
}
|
||||
}
|
||||
|
|
@ -1,15 +1,10 @@
|
|||
package com.extendedae_plus;
|
||||
|
||||
import appeng.api.storage.StorageCells;
|
||||
import appeng.block.AEBaseEntityBlock;
|
||||
import appeng.menu.locator.MenuLocators;
|
||||
import com.extendedae_plus.api.storage.InfinityBigIntegerCellHandler;
|
||||
import com.extendedae_plus.client.ClientRegistrar;
|
||||
import com.extendedae_plus.client.ModKeybindings;
|
||||
import com.extendedae_plus.config.ModConfig;
|
||||
import com.extendedae_plus.content.ae2.MirrorPatternProviderBlockEntity;
|
||||
import com.extendedae_plus.content.matrix.CrafterCorePlusBlockEntity;
|
||||
import com.extendedae_plus.content.matrix.PatternCorePlusBlockEntity;
|
||||
import com.extendedae_plus.init.*;
|
||||
import com.extendedae_plus.menu.locator.CuriosItemLocator;
|
||||
import com.extendedae_plus.util.command.InfinityDiskGiveCommand;
|
||||
|
|
@ -17,14 +12,12 @@ import com.extendedae_plus.util.storage.InfinityStorageManager;
|
|||
import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraftforge.api.distmarker.Dist;
|
||||
import net.minecraftforge.client.event.ModelEvent;
|
||||
import net.minecraftforge.client.event.RegisterKeyMappingsEvent;
|
||||
import net.minecraftforge.common.MinecraftForge;
|
||||
import net.minecraftforge.event.RegisterCommandsEvent;
|
||||
import net.minecraftforge.event.TickEvent;
|
||||
import net.minecraftforge.eventbus.api.IEventBus;
|
||||
import net.minecraftforge.eventbus.api.SubscribeEvent;
|
||||
import net.minecraftforge.fml.common.Mod;
|
||||
import net.minecraftforge.fml.ModList;
|
||||
import net.minecraftforge.fml.event.lifecycle.FMLClientSetupEvent;
|
||||
import net.minecraftforge.fml.event.lifecycle.FMLCommonSetupEvent;
|
||||
import net.minecraftforge.fml.javafmlmod.FMLJavaModLoadingContext;
|
||||
|
|
@ -87,39 +80,6 @@ public class ExtendedAEPlus {
|
|||
null,
|
||||
null
|
||||
);
|
||||
|
||||
ModBlocks.ASSEMBLER_MATRIX_SPEED_PLUS.get().setBlockEntity(
|
||||
com.extendedae_plus.content.matrix.SpeedCorePlusBlockEntity.class,
|
||||
ModBlockEntities.ASSEMBLER_MATRIX_SPEED_PLUS_BE.get(),
|
||||
null,
|
||||
null
|
||||
);
|
||||
|
||||
ModBlocks.ASSEMBLER_MATRIX_CRAFTER_PLUS.get().setBlockEntity(
|
||||
CrafterCorePlusBlockEntity.class,
|
||||
ModBlockEntities.ASSEMBLER_MATRIX_CRAFTER_PLUS_BE.get(),
|
||||
null,
|
||||
null
|
||||
);
|
||||
|
||||
ModBlocks.ASSEMBLER_MATRIX_PATTERN_PLUS.get().setBlockEntity(
|
||||
PatternCorePlusBlockEntity.class,
|
||||
ModBlockEntities.ASSEMBLER_MATRIX_PATTERN_PLUS_BE.get(),
|
||||
null,
|
||||
null
|
||||
);
|
||||
|
||||
((AEBaseEntityBlock) ModBlocks.MIRROR_PATTERN_PROVIDER.get()).setBlockEntity(
|
||||
MirrorPatternProviderBlockEntity.class,
|
||||
ModBlockEntities.MIRROR_PATTERN_PROVIDER_BE.get(),
|
||||
null,
|
||||
(level, pos, state, blockEntity) -> MirrorPatternProviderBlockEntity.serverTick(
|
||||
level,
|
||||
pos,
|
||||
state,
|
||||
(MirrorPatternProviderBlockEntity) blockEntity)
|
||||
);
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
|
|
@ -151,13 +111,6 @@ public class ExtendedAEPlus {
|
|||
|
||||
// 菜单 -> 屏幕 绑定
|
||||
ClientRegistrar.registerMenuScreens();
|
||||
|
||||
event.enqueueWork(() -> {
|
||||
if (ModList.get().isLoaded("jei")) {
|
||||
MinecraftForge.EVENT_BUS.register(com.extendedae_plus.client.InputEvents.class);
|
||||
MinecraftForge.EVENT_BUS.register(com.extendedae_plus.client.event.CtrlQPatternKeyHandler.class);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@SubscribeEvent
|
||||
|
|
@ -168,11 +121,6 @@ public class ExtendedAEPlus {
|
|||
ModItems.registerPartModels();
|
||||
} catch (Exception ignored) {}
|
||||
}
|
||||
|
||||
@SubscribeEvent
|
||||
public static void onRegisterKeyMappings(final RegisterKeyMappingsEvent event) {
|
||||
ModKeybindings.register(event);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -21,13 +21,12 @@ import net.minecraftforge.registries.ForgeRegistries;
|
|||
*/
|
||||
public class EntitySpeedTickerMenu extends UpgradeableMenu<EntitySpeedTickerPart> {
|
||||
@GuiSync(716) public boolean accelerateEnabled = true; // 是否启用加速
|
||||
@GuiSync(717) public boolean redstoneControlEnabled = false; // 是否启用红石控制
|
||||
@GuiSync(718) public int entitySpeedCardCount; // 已安装的实体加速卡数量
|
||||
@GuiSync(719) public int energyCardCount; // 已安装的能量卡数量
|
||||
@GuiSync(720) public int effectiveSpeed = 1; // 当前生效的加速倍率
|
||||
@GuiSync(721) public double multiplier = 1.0; // 目标方块的配置倍率
|
||||
@GuiSync(722) public boolean targetBlacklisted = false; // 目标方块是否在黑名单中
|
||||
@GuiSync(723) public boolean networkEnergySufficient = true; // 网络能量是否充足
|
||||
@GuiSync(717) public int entitySpeedCardCount; // 已安装的实体加速卡数量
|
||||
@GuiSync(718) public int energyCardCount; // 已安装的能量卡数量
|
||||
@GuiSync(719) public int effectiveSpeed = 1; // 当前生效的加速倍率
|
||||
@GuiSync(720) public double multiplier = 1.0; // 目标方块的配置倍率
|
||||
@GuiSync(721) public boolean targetBlacklisted = false; // 目标方块是否在黑名单中
|
||||
@GuiSync(722) public boolean networkEnergySufficient = true; // 网络能量是否充足
|
||||
|
||||
/**
|
||||
* 构造函数,初始化菜单并绑定部件。
|
||||
|
|
@ -40,7 +39,6 @@ public class EntitySpeedTickerMenu extends UpgradeableMenu<EntitySpeedTickerPart
|
|||
if (host != null) {
|
||||
host.menu = this; // 绑定菜单到部件
|
||||
this.accelerateEnabled = host.getAccelerateEnabled(); // 同步初始开关状态
|
||||
this.redstoneControlEnabled = host.getRedstoneControlEnabled(); // 同步红石控制状态
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -52,10 +50,6 @@ public class EntitySpeedTickerMenu extends UpgradeableMenu<EntitySpeedTickerPart
|
|||
return this.accelerateEnabled;
|
||||
}
|
||||
|
||||
public boolean getRedstoneControlEnabled() {
|
||||
return this.redstoneControlEnabled;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置加速开关状态,并同步到部件。
|
||||
* @param enabled 是否启用加速
|
||||
|
|
@ -68,14 +62,6 @@ public class EntitySpeedTickerMenu extends UpgradeableMenu<EntitySpeedTickerPart
|
|||
broadcastChanges(); // 广播状态变化
|
||||
}
|
||||
|
||||
public void setRedstoneControlEnabled(boolean enabled) {
|
||||
this.redstoneControlEnabled = enabled;
|
||||
if (getHost() != null) {
|
||||
getHost().setRedstoneControlEnabled(enabled); // 同步到部件
|
||||
}
|
||||
broadcastChanges(); // 广播状态变化
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新网络能量充足状态并广播到客户端。
|
||||
* @param sufficient 是否能量充足
|
||||
|
|
@ -95,9 +81,6 @@ public class EntitySpeedTickerMenu extends UpgradeableMenu<EntitySpeedTickerPart
|
|||
updateTargetStatus(); // 更新目标方块的黑名单和倍率
|
||||
updateEffectiveSpeed(); // 计算生效速度
|
||||
updateNetworkEnergyStatus(); // 同步能量状态
|
||||
if (!isClientSide() && getHost() != null) {
|
||||
this.redstoneControlEnabled = getHost().getRedstoneControlEnabled(); // 同步红石控制状态
|
||||
}
|
||||
if (isClientSide()) {
|
||||
refreshClientGui(); // 客户端刷新界面
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,7 +5,6 @@ import appeng.api.config.PowerMultiplier;
|
|||
import appeng.api.config.YesNo;
|
||||
import appeng.api.networking.GridFlags;
|
||||
import appeng.api.networking.IGridNode;
|
||||
import appeng.api.networking.IGridNodeListener;
|
||||
import appeng.api.networking.energy.IEnergyService;
|
||||
import appeng.api.networking.security.IActionSource;
|
||||
import appeng.api.networking.ticking.IGridTickable;
|
||||
|
|
@ -13,10 +12,8 @@ import appeng.api.networking.ticking.TickRateModulation;
|
|||
import appeng.api.networking.ticking.TickingRequest;
|
||||
import appeng.api.parts.IPartCollisionHelper;
|
||||
import appeng.api.parts.IPartItem;
|
||||
|
||||
import appeng.api.parts.IPartModel;
|
||||
import appeng.api.storage.MEStorage;
|
||||
import appeng.api.upgrades.IUpgradeInventory;
|
||||
import appeng.api.upgrades.IUpgradeableObject;
|
||||
import appeng.core.definitions.AEItems;
|
||||
import appeng.items.parts.PartModels;
|
||||
|
|
@ -26,29 +23,21 @@ import appeng.parts.PartModel;
|
|||
import appeng.parts.automation.UpgradeablePart;
|
||||
import com.extendedae_plus.ExtendedAEPlus;
|
||||
import com.extendedae_plus.ae.menu.EntitySpeedTickerMenu;
|
||||
import com.extendedae_plus.ae.wireless.WirelessSlaveLink;
|
||||
import com.extendedae_plus.ae.wireless.endpoint.GenericNodeEndpointImpl;
|
||||
import com.extendedae_plus.api.bridge.IInterfaceWirelessLinkBridge;
|
||||
import com.extendedae_plus.api.config.Settings;
|
||||
import com.extendedae_plus.config.ModConfig;
|
||||
import com.extendedae_plus.init.ModItems;
|
||||
import com.extendedae_plus.init.ModMenuTypes;
|
||||
import com.extendedae_plus.items.materials.ChannelCardItem;
|
||||
import com.extendedae_plus.util.Logger;
|
||||
import com.extendedae_plus.util.ModCheckUtils;
|
||||
import com.extendedae_plus.util.entitySpeed.ConfigParsingUtils;
|
||||
import com.extendedae_plus.util.entitySpeed.PowerUtils;
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.core.HolderLookup;
|
||||
import net.minecraft.network.chat.Component;
|
||||
import net.minecraft.nbt.CompoundTag;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraft.world.InteractionHand;
|
||||
import net.minecraft.world.MenuProvider;
|
||||
import net.minecraft.world.entity.player.Inventory;
|
||||
import net.minecraft.world.entity.player.Player;
|
||||
import net.minecraft.world.inventory.AbstractContainerMenu;
|
||||
import net.minecraft.world.level.BlockGetter;
|
||||
import net.minecraft.world.level.block.entity.BlockEntity;
|
||||
import net.minecraft.world.level.block.entity.BlockEntityTicker;
|
||||
import net.minecraft.world.level.block.entity.BlockEntityType;
|
||||
|
|
@ -60,16 +49,12 @@ import org.jetbrains.annotations.Nullable;
|
|||
import java.lang.invoke.MethodHandle;
|
||||
import java.lang.invoke.MethodHandles;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.Objects;
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* 实体加速器部件,用于加速目标方块实体的 tick 速率,消耗 AE 网络能量,支持加速卡和能量卡升级。
|
||||
* 灵感来源于 <a href="https://github.com/GilbertzRivi/crazyae2addons">Crazy AE2 Addons</a>。
|
||||
*/
|
||||
public class EntitySpeedTickerPart extends UpgradeablePart implements IGridTickable, MenuProvider, IUpgradeableObject,
|
||||
IInterfaceWirelessLinkBridge {
|
||||
|
||||
public class EntitySpeedTickerPart extends UpgradeablePart implements IGridTickable, MenuProvider, IUpgradeableObject {
|
||||
public static final ResourceLocation MODEL_BASE = new ResourceLocation(ExtendedAEPlus.MODID, "part/entity_speed_ticker_part");
|
||||
|
||||
@PartModels
|
||||
|
|
@ -81,8 +66,6 @@ public class EntitySpeedTickerPart extends UpgradeablePart implements IGridTicka
|
|||
|
||||
private static volatile MethodHandle cachedFEExtractHandle;
|
||||
private static volatile boolean FE_UNAVAILABLE;
|
||||
// 红石信号状态
|
||||
private YesNo redstoneState = YesNo.UNDECIDED;
|
||||
|
||||
// 静态块:初始化缓存
|
||||
static {
|
||||
|
|
@ -105,11 +88,6 @@ public class EntitySpeedTickerPart extends UpgradeablePart implements IGridTicka
|
|||
private int cachedEnergyCardCount = -1; // 缓存的能量卡数量
|
||||
private BlockEntity cachedTarget = null;
|
||||
private BlockPos cachedTargetPos = null;
|
||||
private WirelessSlaveLink wirelessLink;
|
||||
private long lastChannelFrequency = -1L;
|
||||
private UUID lastChannelOwner;
|
||||
private boolean wirelessClientConnected = false;
|
||||
private boolean wirelessPendingInit = true;
|
||||
|
||||
/**
|
||||
* 构造函数,初始化部件并设置网络节点属性。
|
||||
|
|
@ -125,22 +103,13 @@ public class EntitySpeedTickerPart extends UpgradeablePart implements IGridTicka
|
|||
|
||||
// 注册可记忆的配置(YES/NO)
|
||||
this.getConfigManager().registerSetting(
|
||||
Settings.ACCELERATE,
|
||||
com.extendedae_plus.api.config.Settings.ACCELERATE,
|
||||
YesNo.YES
|
||||
);
|
||||
// 注册红石控制配置
|
||||
this.getConfigManager().registerSetting(
|
||||
Settings.REDSTONE_CONTROL,
|
||||
YesNo.NO
|
||||
);
|
||||
}
|
||||
|
||||
public boolean getAccelerateEnabled() {
|
||||
return this.getConfigManager().getSetting(Settings.ACCELERATE) == YesNo.YES;
|
||||
}
|
||||
|
||||
public boolean getRedstoneControlEnabled() {
|
||||
return this.getConfigManager().getSetting(Settings.REDSTONE_CONTROL) == YesNo.YES;
|
||||
return this.getConfigManager().getSetting(com.extendedae_plus.api.config.Settings.ACCELERATE) == YesNo.YES;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -149,20 +118,12 @@ public class EntitySpeedTickerPart extends UpgradeablePart implements IGridTicka
|
|||
* @param enabled 是否启用加速
|
||||
*/
|
||||
public void setAccelerateEnabled(boolean enabled) {
|
||||
this.getConfigManager().putSetting(Settings.ACCELERATE, enabled ? YesNo.YES : YesNo.NO);
|
||||
this.getConfigManager().putSetting(com.extendedae_plus.api.config.Settings.ACCELERATE, enabled ? YesNo.YES : YesNo.NO);
|
||||
if (menu != null) {
|
||||
menu.setAccelerateEnabled(enabled);
|
||||
}
|
||||
}
|
||||
|
||||
public void setRedstoneControlEnabled(boolean enabled) {
|
||||
this.getConfigManager().putSetting(Settings.REDSTONE_CONTROL, enabled ? YesNo.YES : YesNo.NO);
|
||||
if (menu != null) {
|
||||
// 需要在EntitySpeedTickerMenu中添加对应的更新方法
|
||||
menu.broadcastChanges();
|
||||
}
|
||||
}
|
||||
|
||||
public boolean getNetworkEnergySufficient() {
|
||||
return this.networkEnergySufficient;
|
||||
}
|
||||
|
|
@ -245,16 +206,6 @@ public class EntitySpeedTickerPart extends UpgradeablePart implements IGridTicka
|
|||
if (menu != null) {
|
||||
menu.broadcastChanges();
|
||||
}
|
||||
|
||||
scheduleWirelessInit();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onMainNodeStateChanged(IGridNodeListener.State reason) {
|
||||
super.onMainNodeStateChanged(reason);
|
||||
if (reason == IGridNodeListener.State.GRID_BOOT) {
|
||||
scheduleWirelessInit();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -266,18 +217,9 @@ public class EntitySpeedTickerPart extends UpgradeablePart implements IGridTicka
|
|||
*/
|
||||
@Override
|
||||
public TickRateModulation tickingRequest(IGridNode iGridNode, int ticksSinceLastCall) {
|
||||
handleWirelessLogic();
|
||||
|
||||
if (!getAccelerateEnabled()) {
|
||||
return TickRateModulation.IDLE;
|
||||
}
|
||||
|
||||
// 检查红石控制
|
||||
if (getRedstoneControlEnabled() && !getRedstoneState()) {
|
||||
// 如果启用了红石控制且没有红石信号,则不执行加速
|
||||
return TickRateModulation.IDLE;
|
||||
}
|
||||
|
||||
updateCachedTarget();
|
||||
if (cachedTarget != null && isActive()) {
|
||||
ticker(cachedTarget);
|
||||
|
|
@ -512,165 +454,5 @@ public class EntitySpeedTickerPart extends UpgradeablePart implements IGridTicka
|
|||
super.removeFromWorld();
|
||||
cachedTarget = null;
|
||||
cachedTargetPos = null;
|
||||
if (wirelessLink != null) {
|
||||
wirelessLink.onUnloadOrRemove();
|
||||
wirelessLink = null;
|
||||
}
|
||||
}
|
||||
|
||||
// 获取红石信号状态
|
||||
private boolean getRedstoneState() {
|
||||
// 每次调用都更新红石状态,确保及时性
|
||||
updateRedstoneState();
|
||||
return redstoneState == YesNo.YES;
|
||||
}
|
||||
|
||||
// 更新红石信号状态
|
||||
private void updateRedstoneState() {
|
||||
var be = this.getHost().getBlockEntity();
|
||||
if (be != null && be.getLevel() != null) {
|
||||
redstoneState = be.getLevel().hasNeighborSignal(be.getBlockPos())
|
||||
? YesNo.YES
|
||||
: YesNo.NO;
|
||||
}
|
||||
}
|
||||
|
||||
private void handleWirelessLogic() {
|
||||
if (!isServerEnvironmentReady()) {
|
||||
return;
|
||||
}
|
||||
if (wirelessPendingInit) {
|
||||
initializeWirelessLink();
|
||||
} else {
|
||||
eap$updateWirelessLink();
|
||||
}
|
||||
}
|
||||
|
||||
private boolean isServerEnvironmentReady() {
|
||||
var be = getBlockEntity();
|
||||
return be != null && be.getLevel() != null && !be.getLevel().isClientSide();
|
||||
}
|
||||
|
||||
private void scheduleWirelessInit() {
|
||||
wirelessPendingInit = true;
|
||||
}
|
||||
|
||||
private void resetWirelessState() {
|
||||
lastChannelFrequency = -1L;
|
||||
lastChannelOwner = null;
|
||||
scheduleWirelessInit();
|
||||
}
|
||||
|
||||
private void initializeWirelessLink() {
|
||||
if (!isServerEnvironmentReady()) {
|
||||
return;
|
||||
}
|
||||
|
||||
wirelessPendingInit = false;
|
||||
|
||||
try {
|
||||
IUpgradeInventory upgrades = this.getUpgrades();
|
||||
long channel = 0L;
|
||||
UUID ownerUUID = null;
|
||||
boolean found = false;
|
||||
for (var stack : upgrades) {
|
||||
if (!stack.isEmpty() && stack.getItem() == ModItems.CHANNEL_CARD.get()) {
|
||||
channel = ChannelCardItem.getChannel(stack);
|
||||
ownerUUID = ChannelCardItem.getOwnerUUID(stack);
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!found) {
|
||||
disconnectWirelessLink();
|
||||
return;
|
||||
}
|
||||
|
||||
if (wirelessLink == null) {
|
||||
var endpoint = new GenericNodeEndpointImpl(
|
||||
() -> {
|
||||
var host = this.getHost();
|
||||
return host != null ? host.getBlockEntity() : null;
|
||||
},
|
||||
this::getActionableNode
|
||||
);
|
||||
wirelessLink = new WirelessSlaveLink(endpoint);
|
||||
Logger.EAP$LOGGER.debug("[服务端] EntitySpeedTicker 创建无线链接");
|
||||
}
|
||||
|
||||
boolean changed = lastChannelFrequency != channel || !Objects.equals(lastChannelOwner, ownerUUID);
|
||||
|
||||
wirelessLink.setPlacerId(ownerUUID);
|
||||
wirelessLink.setFrequency(channel);
|
||||
wirelessLink.updateStatus();
|
||||
lastChannelFrequency = channel;
|
||||
lastChannelOwner = ownerUUID;
|
||||
|
||||
if (changed) {
|
||||
markPartForUpdate();
|
||||
Logger.EAP$LOGGER.debug("[服务端] EntitySpeedTicker 设置频道: {}, connected={}",
|
||||
channel, wirelessLink.isConnected());
|
||||
}
|
||||
} catch (Exception e) {
|
||||
Logger.EAP$LOGGER.error("[服务端] EntitySpeedTicker 初始化频道链接失败", e);
|
||||
}
|
||||
}
|
||||
|
||||
private void disconnectWirelessLink() {
|
||||
lastChannelFrequency = 0L;
|
||||
lastChannelOwner = null;
|
||||
if (wirelessLink != null) {
|
||||
wirelessLink.setFrequency(0L);
|
||||
wirelessLink.updateStatus();
|
||||
}
|
||||
markPartForUpdate();
|
||||
}
|
||||
|
||||
private void markPartForUpdate() {
|
||||
var host = this.getHost();
|
||||
if (host != null) {
|
||||
host.markForUpdate();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void eap$updateWirelessLink() {
|
||||
if (wirelessLink != null && isServerEnvironmentReady()) {
|
||||
wirelessLink.updateStatus();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean eap$isWirelessConnected() {
|
||||
if (this.isClientSide()) {
|
||||
return wirelessClientConnected;
|
||||
}
|
||||
return wirelessLink != null && wirelessLink.isConnected();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void eap$setClientWirelessState(boolean connected) {
|
||||
this.wirelessClientConnected = connected;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean eap$hasTickInitialized() {
|
||||
return !wirelessPendingInit;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void eap$setTickInitialized(boolean initialized) {
|
||||
this.wirelessPendingInit = !initialized;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void eap$initializeChannelLink() {
|
||||
scheduleWirelessInit();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void eap$handleDelayedInit() {
|
||||
handleWirelessLogic();
|
||||
}
|
||||
}
|
||||
|
|
@ -24,9 +24,7 @@ import java.util.Map;
|
|||
*/
|
||||
public class EntitySpeedTickerScreen<C extends EntitySpeedTickerMenu> extends UpgradeableScreen<C> {
|
||||
private boolean eap$entitySpeedTickerEnabled = false; // 本地缓存的加速开关状态
|
||||
private boolean eap$redstoneControlEnabled = false; // 本地缓存的红石控制状态
|
||||
private final SettingToggleButton<YesNo> eap$entitySpeedTickerToggle; // 加速开关按钮
|
||||
private final SettingToggleButton<YesNo> eap$redstoneControlToggle; // 红石控制开关按钮
|
||||
|
||||
/**
|
||||
* 构造函数,初始化界面和控件。
|
||||
|
|
@ -39,14 +37,13 @@ public class EntitySpeedTickerScreen<C extends EntitySpeedTickerMenu> extends Up
|
|||
super((C) menu, playerInventory, title, style);
|
||||
this.addToLeftToolbar(CommonButtons.togglePowerUnit()); // 添加功率单位切换按钮
|
||||
this.eap$entitySpeedTickerEnabled = menu.getAccelerateEnabled();
|
||||
this.eap$redstoneControlEnabled = menu.getRedstoneControlEnabled();
|
||||
|
||||
// 初始化加速开关按钮
|
||||
eap$entitySpeedTickerToggle = new SettingToggleButton<>(
|
||||
Settings.BLOCKING_MODE,
|
||||
this.eap$entitySpeedTickerEnabled ? YesNo.YES : YesNo.NO,
|
||||
(btn, backwards) ->
|
||||
ModNetwork.CHANNEL.sendToServer(new ToggleEntityTickerC2SPacket(ToggleEntityTickerC2SPacket.Setting.accelerateEnabled))
|
||||
ModNetwork.CHANNEL.sendToServer(new ToggleEntityTickerC2SPacket())
|
||||
) {
|
||||
@Override
|
||||
public List<Component> getTooltipMessage() {
|
||||
|
|
@ -72,30 +69,6 @@ public class EntitySpeedTickerScreen<C extends EntitySpeedTickerMenu> extends Up
|
|||
};
|
||||
eap$entitySpeedTickerToggle.set(this.eap$entitySpeedTickerEnabled ? YesNo.YES : YesNo.NO);
|
||||
this.addToLeftToolbar(eap$entitySpeedTickerToggle);
|
||||
|
||||
// 初始化加速开关按钮
|
||||
eap$redstoneControlToggle = new SettingToggleButton<>(
|
||||
Settings.BLOCKING_MODE,
|
||||
this.eap$redstoneControlEnabled ? YesNo.YES : YesNo.NO,
|
||||
(btn, backwards) ->
|
||||
ModNetwork.CHANNEL.sendToServer(new ToggleEntityTickerC2SPacket(ToggleEntityTickerC2SPacket.Setting.redstoneControlEnabled))
|
||||
) {
|
||||
@Override
|
||||
public List<Component> getTooltipMessage() {
|
||||
boolean enabled = eap$redstoneControlEnabled;
|
||||
return List.of(
|
||||
Component.translatable("extendedae_plus.gui.redstone_control.title"),
|
||||
enabled ? Component.translatable("extendedae_plus.gui.redstone_control.enabled") :
|
||||
Component.translatable("extendedae_plus.gui.redstone_control.disabled") );
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Icon getIcon() {
|
||||
return this.getCurrentValue() == YesNo.YES ? Icon.REDSTONE_LOW : Icon.REDSTONE_IGNORE;
|
||||
}
|
||||
};
|
||||
eap$redstoneControlToggle.set(this.eap$redstoneControlEnabled ? YesNo.YES : YesNo.NO);
|
||||
this.addToLeftToolbar(eap$redstoneControlToggle);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -110,12 +83,6 @@ public class EntitySpeedTickerScreen<C extends EntitySpeedTickerMenu> extends Up
|
|||
eap$entitySpeedTickerToggle.set(menu.targetBlacklisted ? YesNo.NO : (eap$entitySpeedTickerEnabled ? YesNo.YES : YesNo.NO));
|
||||
eap$entitySpeedTickerToggle.active = !menu.targetBlacklisted;
|
||||
}
|
||||
|
||||
if (eap$redstoneControlToggle != null && menu != null) {
|
||||
eap$redstoneControlEnabled = menu.getRedstoneControlEnabled();
|
||||
eap$redstoneControlToggle.set(eap$redstoneControlEnabled ? YesNo.YES : YesNo.NO);
|
||||
}
|
||||
|
||||
textData();
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,150 +0,0 @@
|
|||
package com.extendedae_plus.ae.wireless;
|
||||
|
||||
import appeng.api.networking.GridHelper;
|
||||
import appeng.api.networking.IGridConnection;
|
||||
import appeng.api.networking.IGridNode;
|
||||
import appeng.me.service.helpers.ConnectionWrapper;
|
||||
import net.minecraft.resources.ResourceKey;
|
||||
import net.minecraft.server.level.ServerLevel;
|
||||
import net.minecraft.world.level.Level;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
/**
|
||||
* 标签无线收发器的连接器。
|
||||
* - 将 BE 的 in-world 节点连接到标签网络的虚拟节点。
|
||||
* - 不负责注册/反注册标签,只负责维持物理连接。
|
||||
*/
|
||||
public class LabelLink {
|
||||
private final IWirelessEndpoint host;
|
||||
private final ConnectionWrapper connection = new ConnectionWrapper(null);
|
||||
|
||||
@Nullable
|
||||
private LabelNetworkRegistry.LabelNetwork target;
|
||||
|
||||
public LabelLink(IWirelessEndpoint host) {
|
||||
this.host = host;
|
||||
}
|
||||
|
||||
public void setTarget(@Nullable LabelNetworkRegistry.LabelNetwork target) {
|
||||
this.target = target;
|
||||
updateStatus();
|
||||
}
|
||||
|
||||
public void clearTarget() {
|
||||
setTarget(null);
|
||||
}
|
||||
|
||||
public boolean isConnected() {
|
||||
return connection.getConnection() != null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 建议在 serverTick 或标签变化时调用。
|
||||
*/
|
||||
public void updateStatus() {
|
||||
if (host.isEndpointRemoved()) {
|
||||
destroyConnection();
|
||||
return;
|
||||
}
|
||||
if (target == null) {
|
||||
destroyConnection();
|
||||
return;
|
||||
}
|
||||
|
||||
final ServerLevel hostLevel = host.getServerLevel();
|
||||
if (hostLevel == null) {
|
||||
destroyConnection();
|
||||
return;
|
||||
}
|
||||
|
||||
// 维度校验:未开启跨维且维度不匹配则断开
|
||||
ResourceKey<Level> targetDim = target.dim();
|
||||
if (targetDim != null && targetDim != hostLevel.dimension()) {
|
||||
destroyConnection();
|
||||
return;
|
||||
}
|
||||
|
||||
IGridNode hostNode = host.getGridNode();
|
||||
IGridNode targetNode = target.node();
|
||||
if (hostNode == null || targetNode == null) {
|
||||
destroyConnection();
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
var current = connection.getConnection();
|
||||
if (current != null) {
|
||||
var a = current.a();
|
||||
var b = current.b();
|
||||
if ((a == hostNode || b == hostNode) && (a == targetNode || b == targetNode)) {
|
||||
return; // 已经正确连接
|
||||
}
|
||||
current.destroy();
|
||||
connection.setConnection(null);
|
||||
}
|
||||
connection.setConnection(GridHelper.createConnection(hostNode, targetNode));
|
||||
} catch (IllegalStateException ignore) {
|
||||
destroyConnection();
|
||||
}
|
||||
}
|
||||
|
||||
public void onUnloadOrRemove() {
|
||||
this.target = null;
|
||||
destroyConnection();
|
||||
}
|
||||
|
||||
private void destroyConnection() {
|
||||
var current = connection.getConnection();
|
||||
if (current != null) {
|
||||
var a = current.a();
|
||||
var b = current.b();
|
||||
current.destroy();
|
||||
try {
|
||||
if (a != null && a.getGrid() != null) {
|
||||
a.getGrid().getTickManager().wakeDevice(a);
|
||||
}
|
||||
} catch (Throwable ignored) {}
|
||||
try {
|
||||
if (b != null && b.getGrid() != null) {
|
||||
b.getGrid().getTickManager().wakeDevice(b);
|
||||
}
|
||||
} catch (Throwable ignored) {}
|
||||
connection.setConnection(null);
|
||||
} else {
|
||||
try {
|
||||
IGridNode hostNode = this.host.getGridNode();
|
||||
IGridNode targetNode = this.target == null ? null : this.target.node();
|
||||
if (hostNode != null && targetNode != null) {
|
||||
IGridConnection existing = this.findExistingConnection(hostNode, targetNode);
|
||||
if (existing != null) {
|
||||
existing.destroy();
|
||||
try {
|
||||
if (hostNode.getGrid() != null) {
|
||||
hostNode.getGrid().getTickManager().wakeDevice(hostNode);
|
||||
}
|
||||
} catch (Throwable ignored) {}
|
||||
try {
|
||||
if (targetNode.getGrid() != null) {
|
||||
targetNode.getGrid().getTickManager().wakeDevice(targetNode);
|
||||
}
|
||||
} catch (Throwable ignored) {}
|
||||
}
|
||||
}
|
||||
} catch (Throwable ignored) {}
|
||||
}
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private IGridConnection findExistingConnection(IGridNode a, IGridNode b) {
|
||||
try {
|
||||
for (IGridConnection gc : a.getConnections()) {
|
||||
var ga = gc.a();
|
||||
var gb = gc.b();
|
||||
if ((ga == a || gb == a) && (ga == b || gb == b)) {
|
||||
return gc;
|
||||
}
|
||||
}
|
||||
} catch (Throwable ignored) {}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,355 +0,0 @@
|
|||
package com.extendedae_plus.ae.wireless;
|
||||
|
||||
import appeng.api.networking.GridFlags;
|
||||
import appeng.api.networking.GridHelper;
|
||||
import appeng.api.networking.IGridNode;
|
||||
import appeng.api.networking.IGridNodeListener;
|
||||
import appeng.api.networking.IInWorldGridNodeHost;
|
||||
import appeng.api.networking.IManagedGridNode;
|
||||
import com.extendedae_plus.ExtendedAEPlus;
|
||||
import com.extendedae_plus.config.ModConfig;
|
||||
import com.extendedae_plus.util.wireless.WirelessTeamUtil;
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.nbt.CompoundTag;
|
||||
import net.minecraft.nbt.ListTag;
|
||||
import net.minecraft.resources.ResourceKey;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraft.server.MinecraftServer;
|
||||
import net.minecraft.server.level.ServerLevel;
|
||||
import net.minecraft.world.level.Level;
|
||||
import net.minecraft.world.level.saveddata.SavedData;
|
||||
import net.minecraft.core.registries.Registries;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.Comparator;
|
||||
|
||||
/**
|
||||
* 标签无线网络注册中心(SavedData)。
|
||||
* - 负责标签到频道(频率)的分配与复用;
|
||||
* - 创建/销毁虚拟节点,所有收发器连接到同一虚拟节点;
|
||||
* - 记录在线端点集合,端点卸载后需调用 unregister。
|
||||
*/
|
||||
public class LabelNetworkRegistry extends SavedData {
|
||||
public static final String SAVE_ID = ExtendedAEPlus.MODID + "_label_networks";
|
||||
|
||||
private static final long CHANNEL_START = 1_000_000L;
|
||||
|
||||
private final Map<Key, LabelNetwork> networks = new HashMap<>();
|
||||
private long nextChannel = CHANNEL_START;
|
||||
|
||||
/* 入口 API */
|
||||
|
||||
public static LabelNetworkRegistry get(MinecraftServer server) {
|
||||
ServerLevel level = server.getLevel(ServerLevel.OVERWORLD);
|
||||
return level.getDataStorage().computeIfAbsent(LabelNetworkRegistry::load, LabelNetworkRegistry::new, SAVE_ID);
|
||||
}
|
||||
|
||||
public record LabelNetworkSnapshot(String label, long channel) {}
|
||||
|
||||
public static LabelNetworkRegistry get(ServerLevel level) {
|
||||
return get(level.getServer());
|
||||
}
|
||||
|
||||
/**
|
||||
* 规范化标签:trim;保持大小写敏感。
|
||||
*/
|
||||
public static String normalizeLabel(String raw) {
|
||||
if (raw == null) return null;
|
||||
String t = raw.trim();
|
||||
if (t.isEmpty()) return null;
|
||||
if (t.length() > 64) {
|
||||
t = t.substring(0, 64);
|
||||
}
|
||||
// 过滤非法字符
|
||||
for (int i = 0; i < t.length(); i++) {
|
||||
char c = t.charAt(i);
|
||||
if (!(Character.isLetterOrDigit(c) || c == '_' || c == '-')) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
return t;
|
||||
}
|
||||
|
||||
/**
|
||||
* 注册/切换标签。
|
||||
*/
|
||||
public synchronized LabelNetwork register(ServerLevel beLevel, String rawLabel, @Nullable UUID placerId, IWirelessEndpoint endpoint) {
|
||||
String label = normalizeLabel(rawLabel);
|
||||
if (label == null) return null;
|
||||
|
||||
UUID owner = placerId == null ? WirelessMasterRegistry.PUBLIC_NETWORK_UUID : WirelessTeamUtil.getNetworkOwnerUUID(beLevel, placerId);
|
||||
ResourceKey<Level> dimKey = ModConfig.INSTANCE.wirelessCrossDimEnable ? null : beLevel.dimension();
|
||||
Key key = new Key(dimKey, label, owner);
|
||||
|
||||
LabelNetwork network = networks.get(key);
|
||||
if (network == null) {
|
||||
long channel = allocateChannel();
|
||||
network = new LabelNetwork(dimKey, label, owner, channel);
|
||||
if (!network.ensureVirtualNode(beLevel)) {
|
||||
return null;
|
||||
}
|
||||
networks.put(key, network);
|
||||
setDirty();
|
||||
} else {
|
||||
// 确保虚拟节点存在
|
||||
network.ensureVirtualNode(beLevel);
|
||||
}
|
||||
|
||||
network.endpoints.add(new EndpointRef(dimKey, endpoint.getBlockPos()));
|
||||
setDirty();
|
||||
return network;
|
||||
}
|
||||
|
||||
/**
|
||||
* 注销端点;不自动删除网络,网络的移除需显式调用 removeNetwork。
|
||||
*/
|
||||
public synchronized void unregister(IWirelessEndpoint endpoint) {
|
||||
ServerLevel level = endpoint.getServerLevel();
|
||||
if (level == null) return;
|
||||
ResourceKey<Level> dimKey = ModConfig.INSTANCE.wirelessCrossDimEnable ? null : level.dimension();
|
||||
BlockPos pos = endpoint.getBlockPos();
|
||||
for (LabelNetwork net : networks.values()) {
|
||||
net.endpoints.removeIf(ref -> ref.matches(dimKey, pos));
|
||||
}
|
||||
setDirty();
|
||||
}
|
||||
|
||||
public synchronized LabelNetwork getNetwork(ServerLevel level, String rawLabel, @Nullable UUID placerId) {
|
||||
String label = normalizeLabel(rawLabel);
|
||||
if (label == null) return null;
|
||||
UUID owner = placerId == null ? WirelessMasterRegistry.PUBLIC_NETWORK_UUID : WirelessTeamUtil.getNetworkOwnerUUID(level, placerId);
|
||||
ResourceKey<Level> dimKey = ModConfig.INSTANCE.wirelessCrossDimEnable ? null : level.dimension();
|
||||
Key key = new Key(dimKey, label, owner);
|
||||
return networks.get(key);
|
||||
}
|
||||
|
||||
/**
|
||||
* 显式删除一个网络(销毁虚拟节点),仅在 UI “删除” 时调用。
|
||||
*/
|
||||
public synchronized boolean removeNetwork(ServerLevel level, String rawLabel, @Nullable UUID placerId) {
|
||||
String label = normalizeLabel(rawLabel);
|
||||
if (label == null) return false;
|
||||
UUID owner = placerId == null ? WirelessMasterRegistry.PUBLIC_NETWORK_UUID : WirelessTeamUtil.getNetworkOwnerUUID(level, placerId);
|
||||
ResourceKey<Level> dimKey = ModConfig.INSTANCE.wirelessCrossDimEnable ? null : level.dimension();
|
||||
Key key = new Key(dimKey, label, owner);
|
||||
LabelNetwork net = networks.remove(key);
|
||||
if (net != null) {
|
||||
net.destroyVirtualNode();
|
||||
setDirty();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取当前玩家所属网络列表(按标签排序)。
|
||||
*/
|
||||
public synchronized List<LabelNetworkSnapshot> listNetworks(ServerLevel level, @Nullable UUID placerId) {
|
||||
UUID owner = placerId == null ? WirelessMasterRegistry.PUBLIC_NETWORK_UUID : WirelessTeamUtil.getNetworkOwnerUUID(level, placerId);
|
||||
ResourceKey<Level> dimKey = ModConfig.INSTANCE.wirelessCrossDimEnable ? null : level.dimension();
|
||||
List<LabelNetworkSnapshot> list = new ArrayList<>();
|
||||
for (Map.Entry<Key, LabelNetwork> entry : networks.entrySet()) {
|
||||
Key key = entry.getKey();
|
||||
if (!Objects.equals(key.owner(), owner)) continue;
|
||||
if (!Objects.equals(key.dim(), dimKey)) continue;
|
||||
list.add(new LabelNetworkSnapshot(key.label(), entry.getValue().channel()));
|
||||
}
|
||||
list.sort(Comparator.comparingLong(LabelNetworkSnapshot::channel));
|
||||
return list;
|
||||
}
|
||||
|
||||
/* 序列化 */
|
||||
|
||||
@Override
|
||||
public @NotNull CompoundTag save(@NotNull CompoundTag tag) {
|
||||
tag.putLong("nextChannel", nextChannel);
|
||||
ListTag list = new ListTag();
|
||||
networks.forEach((k, v) -> {
|
||||
CompoundTag nbt = new CompoundTag();
|
||||
nbt.putString("label", k.label());
|
||||
if (k.dim() != null) {
|
||||
nbt.putString("dim", k.dim().location().toString());
|
||||
}
|
||||
nbt.putUUID("owner", k.owner());
|
||||
nbt.putLong("channel", v.channel);
|
||||
nbt.put("endpoints", v.saveEndpoints());
|
||||
list.add(nbt);
|
||||
});
|
||||
tag.put("networks", list);
|
||||
return tag;
|
||||
}
|
||||
|
||||
public static LabelNetworkRegistry load(CompoundTag tag) {
|
||||
LabelNetworkRegistry reg = new LabelNetworkRegistry();
|
||||
reg.nextChannel = tag.getLong("nextChannel");
|
||||
ListTag list = tag.getList("networks", CompoundTag.TAG_COMPOUND);
|
||||
for (int i = 0; i < list.size(); i++) {
|
||||
CompoundTag nbt = list.getCompound(i);
|
||||
String label = nbt.getString("label");
|
||||
ResourceKey<Level> dim = nbt.contains("dim") ? ResourceKey.create(Registries.DIMENSION, new ResourceLocation(nbt.getString("dim"))) : null;
|
||||
UUID owner = nbt.getUUID("owner");
|
||||
long channel = nbt.getLong("channel");
|
||||
LabelNetwork net = new LabelNetwork(dim, label, owner, channel);
|
||||
net.loadEndpoints(nbt.getList("endpoints", CompoundTag.TAG_COMPOUND));
|
||||
reg.networks.put(new Key(dim, label, owner), net);
|
||||
}
|
||||
return reg;
|
||||
}
|
||||
|
||||
private long allocateChannel() {
|
||||
// 全局递增,不复用历史频道,从 1_000_000 起
|
||||
if (nextChannel < CHANNEL_START) {
|
||||
nextChannel = CHANNEL_START;
|
||||
}
|
||||
long ch = nextChannel++;
|
||||
return ch;
|
||||
}
|
||||
|
||||
/* 内部类型 */
|
||||
|
||||
public record Key(@Nullable ResourceKey<Level> dim, String label, UUID owner) {}
|
||||
|
||||
public static class LabelNetwork {
|
||||
private final ResourceKey<Level> dim; // null 表示跨维共用
|
||||
private final String label;
|
||||
private final UUID owner;
|
||||
private final long channel;
|
||||
private final Set<EndpointRef> endpoints = new HashSet<>();
|
||||
|
||||
@Nullable
|
||||
private IManagedGridNode managedNode;
|
||||
@Nullable
|
||||
private VirtualLabelNodeHost virtualHost;
|
||||
|
||||
LabelNetwork(@Nullable ResourceKey<Level> dim, String label, UUID owner, long channel) {
|
||||
this.dim = dim;
|
||||
this.label = label;
|
||||
this.owner = owner;
|
||||
this.channel = channel;
|
||||
}
|
||||
|
||||
public long channel() {
|
||||
return channel;
|
||||
}
|
||||
|
||||
public ResourceKey<Level> dim() {
|
||||
return dim;
|
||||
}
|
||||
|
||||
public @Nullable IGridNode node() {
|
||||
return managedNode == null ? null : managedNode.getNode();
|
||||
}
|
||||
|
||||
/**
|
||||
* 确保虚拟节点存在;若已存在则复用。
|
||||
*/
|
||||
public boolean ensureVirtualNode(ServerLevel level) {
|
||||
if (managedNode != null && managedNode.getNode() != null) {
|
||||
return true;
|
||||
}
|
||||
ServerLevel hostLevel = dim == null ? level.getServer().getLevel(ServerLevel.OVERWORLD) : level.getServer().getLevel(dim);
|
||||
if (hostLevel == null) return false;
|
||||
|
||||
// 创建虚拟节点(非 in-world)
|
||||
this.virtualHost = new VirtualLabelNodeHost();
|
||||
this.managedNode = GridHelper.createManagedNode(virtualHost, NodeListener.INSTANCE);
|
||||
this.virtualHost.setManagedNode(this.managedNode);
|
||||
// 虚拟节点不占用频道,且提供致密(32)频道能力
|
||||
this.managedNode.setFlags(GridFlags.DENSE_CAPACITY);
|
||||
this.managedNode.setIdlePowerUsage(0.0);
|
||||
this.managedNode.setInWorldNode(false);
|
||||
this.managedNode.setVisualRepresentation(com.extendedae_plus.init.ModItems.LABELED_WIRELESS_TRANSCEIVER.get().getDefaultInstance());
|
||||
this.managedNode.setTagName("label_net_" + label);
|
||||
this.managedNode.create(hostLevel, null);
|
||||
return true;
|
||||
}
|
||||
|
||||
public void destroyVirtualNode() {
|
||||
if (managedNode != null) {
|
||||
managedNode.destroy();
|
||||
}
|
||||
managedNode = null;
|
||||
virtualHost = null;
|
||||
}
|
||||
|
||||
public ListTag saveEndpoints() {
|
||||
ListTag list = new ListTag();
|
||||
for (EndpointRef ref : endpoints) {
|
||||
list.add(ref.save());
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
public void loadEndpoints(ListTag list) {
|
||||
endpoints.clear();
|
||||
for (int i = 0; i < list.size(); i++) {
|
||||
endpoints.add(EndpointRef.load(list.getCompound(i)));
|
||||
}
|
||||
}
|
||||
|
||||
public int endpointCount() {
|
||||
return endpoints.size();
|
||||
}
|
||||
}
|
||||
|
||||
public record EndpointRef(@Nullable ResourceKey<Level> dim, BlockPos pos) {
|
||||
public boolean matches(@Nullable ResourceKey<Level> currentDim, BlockPos currentPos) {
|
||||
if (!Objects.equals(dim, currentDim)) return false;
|
||||
return pos.equals(currentPos);
|
||||
}
|
||||
|
||||
public CompoundTag save() {
|
||||
CompoundTag tag = new CompoundTag();
|
||||
if (dim != null) {
|
||||
tag.putString("dim", dim.location().toString());
|
||||
}
|
||||
tag.putLong("pos", pos.asLong());
|
||||
return tag;
|
||||
}
|
||||
|
||||
public static EndpointRef load(CompoundTag tag) {
|
||||
ResourceKey<Level> d = tag.contains("dim")
|
||||
? ResourceKey.create(Registries.DIMENSION, new ResourceLocation(tag.getString("dim")))
|
||||
: null;
|
||||
BlockPos p = BlockPos.of(tag.getLong("pos"));
|
||||
return new EndpointRef(d, p);
|
||||
}
|
||||
}
|
||||
|
||||
enum NodeListener implements IGridNodeListener<VirtualLabelNodeHost> {
|
||||
INSTANCE;
|
||||
|
||||
@Override
|
||||
public void onSaveChanges(VirtualLabelNodeHost host, IGridNode node) {}
|
||||
|
||||
@Override
|
||||
public void onStateChanged(VirtualLabelNodeHost host, IGridNode node, IGridNodeListener.State state) {}
|
||||
|
||||
@Override
|
||||
public void onInWorldConnectionChanged(VirtualLabelNodeHost host, IGridNode node) {}
|
||||
|
||||
@Override
|
||||
public void onGridChanged(VirtualLabelNodeHost host, IGridNode node) {}
|
||||
|
||||
@Override
|
||||
public void onOwnerChanged(VirtualLabelNodeHost host, IGridNode node) {}
|
||||
}
|
||||
|
||||
/**
|
||||
* 虚拟标签网络节点宿主,不在世界中放置实体。
|
||||
*/
|
||||
static class VirtualLabelNodeHost implements IInWorldGridNodeHost {
|
||||
private IManagedGridNode managedNode;
|
||||
|
||||
void setManagedNode(IManagedGridNode managedNode) {
|
||||
this.managedNode = managedNode;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable IGridNode getGridNode(@Nullable net.minecraft.core.Direction dir) {
|
||||
return managedNode == null ? null : managedNode.getNode();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -47,11 +47,4 @@ public interface IInterfaceWirelessLinkBridge {
|
|||
default void eap$handleDelayedInit() {
|
||||
// 默认实现为空
|
||||
}
|
||||
|
||||
/**
|
||||
* 指示宿主是否需要保持慢速 tick 以维持无线连接。
|
||||
*/
|
||||
default boolean eap$shouldKeepTicking() {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +0,0 @@
|
|||
package com.extendedae_plus.api.bridge;
|
||||
|
||||
public interface PatternProviderLogicSyncBridge {
|
||||
long eap$getPatternSyncVersion();
|
||||
}
|
||||
|
|
@ -11,7 +11,6 @@ import java.util.Map;
|
|||
public final class Settings {
|
||||
private static final Map<String, Setting<?>> SETTINGS = new HashMap<>();
|
||||
public static final Setting<YesNo> ACCELERATE = register("accelerate", YesNo.NO, YesNo.YES);
|
||||
public static final Setting<YesNo> REDSTONE_CONTROL = register("redstoneControl", YesNo.NO, YesNo.YES);
|
||||
|
||||
|
||||
private Settings() {
|
||||
|
|
|
|||
|
|
@ -1,8 +1,6 @@
|
|||
package com.extendedae_plus.api.storage;
|
||||
|
||||
import appeng.api.config.Actionable;
|
||||
import appeng.api.config.FuzzyMode;
|
||||
import appeng.api.config.IncludeExclude;
|
||||
import appeng.api.networking.security.IActionSource;
|
||||
import appeng.api.stacks.AEItemKey;
|
||||
import appeng.api.stacks.AEKey;
|
||||
|
|
@ -10,18 +8,16 @@ import appeng.api.stacks.KeyCounter;
|
|||
import appeng.api.storage.cells.CellState;
|
||||
import appeng.api.storage.cells.ISaveProvider;
|
||||
import appeng.api.storage.cells.StorageCell;
|
||||
import appeng.api.upgrades.IUpgradeInventory;
|
||||
import appeng.core.definitions.AEItems;
|
||||
import appeng.util.ConfigInventory;
|
||||
import appeng.util.prioritylist.IPartitionList;
|
||||
import appeng.core.AELog;
|
||||
import com.extendedae_plus.ExtendedAEPlus;
|
||||
import com.extendedae_plus.items.InfinityBigIntegerCellItem;
|
||||
import com.extendedae_plus.util.storage.InfinityConstants;
|
||||
import com.extendedae_plus.util.storage.InfinityDataStorage;
|
||||
import com.extendedae_plus.util.storage.InfinityStorageManager;
|
||||
import it.unimi.dsi.fastutil.objects.Object2ObjectMap;
|
||||
import it.unimi.dsi.fastutil.objects.Object2ObjectMaps;
|
||||
import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap;
|
||||
import net.minecraft.nbt.CompoundTag;
|
||||
import net.minecraft.nbt.ListTag;
|
||||
import net.minecraft.network.chat.Component;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
|
||||
|
|
@ -41,39 +37,35 @@ public class InfinityBigIntegerCellInventory implements StorageCell {
|
|||
private final ItemStack self;
|
||||
// AE2 提供的保存提供者,用于在容器中批量保存时触发回调
|
||||
private final ISaveProvider container;
|
||||
private final IPartitionList partitionList;
|
||||
private final IncludeExclude partitionListMode;
|
||||
// 存储物品键和数量的映射
|
||||
private Object2ObjectMap<AEKey, BigInteger> AEKey2AmountsMap;
|
||||
// 存储的物品种类数量
|
||||
private int totalAEKeyType;
|
||||
// 存储的物品总数
|
||||
private BigInteger totalAEKey2Amounts = BI_ZERO;
|
||||
// 仅用于控制 ItemStack 摘要字段是否需要刷新
|
||||
// 标记是否已持久化到 SavedData
|
||||
private boolean isPersisted = true;
|
||||
|
||||
private static final BigInteger BI_ZERO = BigInteger.ZERO;
|
||||
private static final BigInteger BI_LONG_MAX = BigInteger.valueOf(Long.MAX_VALUE);
|
||||
|
||||
public InfinityBigIntegerCellInventory(InfinityBigIntegerCellItem cell, ItemStack stack, ISaveProvider saveProvider) {
|
||||
this.cell = cell;
|
||||
this.self = stack;
|
||||
this.container = saveProvider;
|
||||
|
||||
var builder = IPartitionList.builder();
|
||||
var upgrades = this.getUpgradesInventory();
|
||||
var config = this.getConfigInventory();
|
||||
boolean hasInverter = upgrades.isInstalled(AEItems.INVERTER_CARD);
|
||||
boolean isFuzzy = upgrades.isInstalled(AEItems.FUZZY_CARD);
|
||||
if (isFuzzy) {
|
||||
builder.fuzzyMode(this.getFuzzyMode());
|
||||
}
|
||||
builder.addAll(config.keySet());
|
||||
this.partitionListMode = hasInverter ? IncludeExclude.BLACKLIST : IncludeExclude.WHITELIST;
|
||||
this.partitionList = builder.build();
|
||||
this.initData();
|
||||
public InfinityBigIntegerCellInventory(InfinityBigIntegerCellItem cell, ItemStack stack, ISaveProvider saveProvider) {
|
||||
// 保存存储单元类型(InfinityBigIntegerCellItem 实例),用于访问磁盘属性
|
||||
this.cell = cell;
|
||||
// 保存物品堆栈,表示磁盘本身,包含运行时的 NBT 数据
|
||||
this.self = stack;
|
||||
// 保存提供者,用于触发数据保存
|
||||
this.container = saveProvider;
|
||||
// 初始化 storedAmounts 为 null,延迟加载物品数据
|
||||
this.AEKey2AmountsMap = null;
|
||||
// 初始化磁盘数据
|
||||
initData();
|
||||
}
|
||||
|
||||
// 将 BigInteger 格式化为带单位的字符串,保留两位小数
|
||||
public static String formatBigInteger(BigInteger number) {
|
||||
// 使用方法局部的 DecimalFormat,避免静态共享的非线程安全问题
|
||||
java.text.DecimalFormat df = new java.text.DecimalFormat("#.##");
|
||||
BigDecimal bd = new BigDecimal(number);
|
||||
BigDecimal thousand = new BigDecimal(1000);
|
||||
|
|
@ -89,284 +81,321 @@ public class InfinityBigIntegerCellInventory implements StorageCell {
|
|||
return df.format(bd.doubleValue()) + units[idx];
|
||||
}
|
||||
|
||||
@Override
|
||||
public Component getDescription() {
|
||||
return self.getHoverName();
|
||||
// 获取磁盘的 InfinityDataStorage 数据
|
||||
private InfinityDataStorage getCellStorage() {
|
||||
// 如果磁盘有 UUID,返回对应的 InfinityDataStorage
|
||||
if (getUUID() != null) {
|
||||
return getStorageManagerInstance().getOrCreateCell(getUUID());
|
||||
} else {
|
||||
// 否则返回空的 InfinityDataStorage
|
||||
return InfinityDataStorage.EMPTY;
|
||||
}
|
||||
}
|
||||
|
||||
public static InfinityBigIntegerCellInventory createInventory(ItemStack stack, ISaveProvider saveProvider) {
|
||||
Objects.requireNonNull(stack, "Cannot create cell inventory for null itemstack");
|
||||
if (!(stack.getItem() instanceof InfinityBigIntegerCellItem cell)) {
|
||||
return null;
|
||||
// 初始化磁盘数据
|
||||
private void initData() {
|
||||
// 如果磁盘有 UUID,加载存储的物品数据
|
||||
if (hasUUID()) {
|
||||
InfinityDataStorage storage = getCellStorage();
|
||||
this.totalAEKeyType = storage.amounts.size();
|
||||
this.totalAEKey2Amounts = storage.itemCount.equals(BI_ZERO) ?
|
||||
BI_ZERO :
|
||||
storage.itemCount;
|
||||
|
||||
} else {
|
||||
// 否则初始化为空
|
||||
this.totalAEKeyType = 0;
|
||||
this.totalAEKey2Amounts = BI_ZERO;
|
||||
// 加载物品数据
|
||||
getCellStoredMap();
|
||||
}
|
||||
return new InfinityBigIntegerCellInventory(cell, stack, saveProvider);
|
||||
}
|
||||
|
||||
// 获取存储单元的状态(空、部分填充)
|
||||
@Override
|
||||
public CellState getStatus() {
|
||||
this.refreshCachedStateFromStorage();
|
||||
// 如果没有存储任何物品,返回空状态
|
||||
if (this.getTotalAEKey2Amounts().equals(BI_ZERO)) {
|
||||
return CellState.EMPTY;
|
||||
}
|
||||
// 否则返回满状态
|
||||
return CellState.NOT_EMPTY;
|
||||
}
|
||||
|
||||
// 获取存储单元的待机能耗
|
||||
@Override
|
||||
public double getIdleDrain() {
|
||||
return 512;
|
||||
}
|
||||
|
||||
// 持久化存储单元数据到全局存储
|
||||
@Override
|
||||
public void persist() {
|
||||
this.refreshCachedStateFromStorage();
|
||||
if (this.isPersisted) {
|
||||
if (this.isPersisted)
|
||||
return;
|
||||
|
||||
if (totalAEKey2Amounts.equals(BI_ZERO)) {
|
||||
if (hasUUID()) {
|
||||
getStorageManagerInstance().removeCell(getUUID());
|
||||
if (self.hasTag()) {
|
||||
var tag = self.getTag();
|
||||
// remove persisted identifiers and cached summary fields from the ItemStack
|
||||
tag.remove(InfinityConstants.INFINITY_CELL_UUID);
|
||||
tag.remove(InfinityConstants.INFINITY_ITEM_TOTAL);
|
||||
tag.remove(InfinityConstants.INFINITY_ITEM_TYPES);
|
||||
// backward compat: also remove internal cell item count key if present
|
||||
tag.remove(InfinityConstants.INFINITY_CELL_ITEM_COUNT);
|
||||
}
|
||||
initData();
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
CompoundTag tag = self.getOrCreateTag();
|
||||
if (this.totalAEKey2Amounts.equals(BI_ZERO)) {
|
||||
tag.remove(InfinityConstants.INFINITY_ITEM_TOTAL);
|
||||
tag.remove(InfinityConstants.INFINITY_ITEM_TYPES);
|
||||
// backward compat
|
||||
tag.remove(InfinityConstants.INFINITY_CELL_ITEM_COUNT);
|
||||
} else {
|
||||
byte[] itemCountBytes = this.totalAEKey2Amounts.toByteArray();
|
||||
tag.putByteArray(InfinityConstants.INFINITY_ITEM_TOTAL, itemCountBytes);
|
||||
tag.putInt(InfinityConstants.INFINITY_ITEM_TYPES, this.totalAEKeyType);
|
||||
tag.putByteArray(InfinityConstants.INFINITY_CELL_ITEM_COUNT, itemCountBytes);
|
||||
// 创建物品键列表
|
||||
ListTag keys = new ListTag();
|
||||
// 创建物品数量列表
|
||||
ListTag amounts = new ListTag();
|
||||
// 初始化物品总数
|
||||
BigInteger itemCount = BI_ZERO;
|
||||
|
||||
for (var entry : this.AEKey2AmountsMap.object2ObjectEntrySet()) {
|
||||
BigInteger amount = entry.getValue();
|
||||
// 如果数量大于 0,添加到键和数量列表
|
||||
if (amount.compareTo(BI_ZERO) > 0) {
|
||||
keys.add(entry.getKey().toTagGeneric());
|
||||
CompoundTag amountTag = new CompoundTag();
|
||||
amountTag.putByteArray("value", amount.toByteArray());
|
||||
amounts.add(amountTag);
|
||||
|
||||
itemCount = itemCount.add(amount);
|
||||
}
|
||||
}
|
||||
|
||||
if (keys.isEmpty()) {
|
||||
getStorageManagerInstance().updateCell(getUUID(), new InfinityDataStorage());
|
||||
} else {
|
||||
getStorageManagerInstance().modifyDisk(getUUID(), keys, amounts, itemCount);
|
||||
}
|
||||
|
||||
// 更新存储的物品种类数量
|
||||
this.totalAEKeyType = this.AEKey2AmountsMap.size();
|
||||
// 更新存储的物品总数
|
||||
this.totalAEKey2Amounts = itemCount;
|
||||
// 将物品总数与种类数量存入物品堆栈的 NBT(用于快捷查看/tooltip),同时保留旧字段以兼容历史版本
|
||||
var tag = self.getOrCreateTag();
|
||||
tag.putByteArray(InfinityConstants.INFINITY_ITEM_TOTAL, itemCount.toByteArray());
|
||||
tag.putInt(InfinityConstants.INFINITY_ITEM_TYPES, this.totalAEKeyType);
|
||||
// backward compat storage field (kept for legacy readers)
|
||||
tag.putByteArray(InfinityConstants.INFINITY_CELL_ITEM_COUNT, itemCount.toByteArray());
|
||||
|
||||
// 标记数据已持久化
|
||||
this.isPersisted = true;
|
||||
}
|
||||
|
||||
// 获取存储单元的描述(此处返回null,可自定义)
|
||||
@Override
|
||||
public Component getDescription() {
|
||||
return null;
|
||||
}
|
||||
|
||||
// 静态方法,创建存储单元库存
|
||||
public static InfinityBigIntegerCellInventory createInventory(ItemStack stack, ISaveProvider saveProvider) {
|
||||
// 检查物品堆栈是否为空
|
||||
Objects.requireNonNull(stack, "Cannot create cell inventory for null itemstack");
|
||||
// 检查物品是否为 IDISKCellItem 类型
|
||||
if (!(stack.getItem() instanceof InfinityBigIntegerCellItem cell)) {
|
||||
return null;
|
||||
}
|
||||
// 创建并返回新的 DISKCellInventory 实例
|
||||
return new InfinityBigIntegerCellInventory(cell, stack, saveProvider);
|
||||
}
|
||||
|
||||
// 获取存储的物品总数
|
||||
public BigInteger getTotalAEKey2Amounts() {
|
||||
this.refreshCachedStateFromStorage();
|
||||
return this.totalAEKey2Amounts;
|
||||
}
|
||||
|
||||
// 获取存储的物品种类数量
|
||||
public int getTotalAEKeyType() {
|
||||
this.refreshCachedStateFromStorage();
|
||||
return this.totalAEKeyType;
|
||||
}
|
||||
|
||||
// 判断物品堆栈是否有UUID
|
||||
public boolean hasUUID() {
|
||||
return self.hasTag() && self.getOrCreateTag().contains(InfinityConstants.INFINITY_CELL_UUID);
|
||||
}
|
||||
|
||||
// 获取物品堆栈的UUID
|
||||
public UUID getUUID() {
|
||||
if (this.hasUUID()) {
|
||||
return self.getOrCreateTag().getUUID(InfinityConstants.INFINITY_CELL_UUID);
|
||||
}
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
|
||||
private void refreshCachedStateFromStorage() {
|
||||
var cellStorage = this.getExistingCellStorage();
|
||||
if (cellStorage != null) {
|
||||
this.totalAEKeyType = cellStorage.amounts.size();
|
||||
this.totalAEKey2Amounts = cellStorage.itemCount == null ? BI_ZERO : cellStorage.itemCount;
|
||||
} else {
|
||||
this.totalAEKeyType = 0;
|
||||
this.totalAEKey2Amounts = BI_ZERO;
|
||||
}
|
||||
}
|
||||
|
||||
private void initData() {
|
||||
this.refreshCachedStateFromStorage();
|
||||
}
|
||||
|
||||
// 获取或初始化存储映射
|
||||
private Object2ObjectMap<AEKey, BigInteger> getCellStoredMap() {
|
||||
var cellStorage = this.getExistingCellStorage();
|
||||
if (cellStorage == null) {
|
||||
return Object2ObjectMaps.emptyMap();
|
||||
if (AEKey2AmountsMap == null) {
|
||||
AEKey2AmountsMap = new Object2ObjectOpenHashMap<>(512, 0.6f);
|
||||
this.loadCellStoredMap();
|
||||
}
|
||||
return cellStorage.amounts;
|
||||
return AEKey2AmountsMap;
|
||||
}
|
||||
|
||||
// 获取所有可用的物品堆栈及其数量
|
||||
@Override
|
||||
public void getAvailableStacks(KeyCounter out) {
|
||||
for (var entry : this.getCellStoredMap().object2ObjectEntrySet()) {
|
||||
var map = getCellStoredMap();
|
||||
if (map == null || map.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (var entry : map.object2ObjectEntrySet()) {
|
||||
AEKey key = entry.getKey();
|
||||
BigInteger amount = entry.getValue();
|
||||
|
||||
// 如果当前要添加的数量本身就超过 Long.MAX_VALUE,直接设为 MAX
|
||||
if (amount.compareTo(BI_LONG_MAX) > 0) {
|
||||
out.set(key, Long.MAX_VALUE);
|
||||
continue;
|
||||
}
|
||||
|
||||
long addAmount = amount.longValue();
|
||||
long existing = out.get(key);
|
||||
|
||||
// 如果已有值已是 MAX,直接跳过
|
||||
if (existing == Long.MAX_VALUE) {
|
||||
continue;
|
||||
}
|
||||
|
||||
long addAmount = amount.longValue();
|
||||
// 计算总和,防止溢出
|
||||
long sum = existing + addAmount;
|
||||
if (sum < 0 || sum < existing) {
|
||||
if (sum < 0 || sum < existing) { // 溢出检测
|
||||
out.set(key, Long.MAX_VALUE);
|
||||
} else if (addAmount != 0) {
|
||||
out.add(key, addAmount);
|
||||
} else {
|
||||
out.add(key, addAmount); // 安全添加
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private InfinityStorageManager getStorageManagerInstance() {
|
||||
// 从存储中加载物品映射
|
||||
private void loadCellStoredMap() {
|
||||
boolean dataCorruption = false;
|
||||
if (!self.hasTag()) return;
|
||||
|
||||
var keys = getCellStorage().keys;
|
||||
var amounts = getCellStorage().amounts;
|
||||
// 数据损坏
|
||||
if (keys.size() != amounts.size()) {
|
||||
AELog.warn("Loading storage cell with mismatched amounts/tags: %d != %d", amounts.size(), keys.size());
|
||||
}
|
||||
// 遍历数量和键,加载到 AEKey2AmountsMap
|
||||
for (int i = 0; i < amounts.size(); i++) {
|
||||
AEKey key = AEKey.fromTagGeneric(keys.getCompound(i));
|
||||
BigInteger amount = new BigInteger(amounts.getCompound(i).getByteArray("value"));
|
||||
// 检查数据是否损坏
|
||||
if (amount.compareTo(BI_ZERO) <= 0 || key == null) {
|
||||
dataCorruption = true;
|
||||
} else {
|
||||
AEKey2AmountsMap.put(key, amount);
|
||||
}
|
||||
}
|
||||
if (dataCorruption) {
|
||||
this.saveChanges();
|
||||
}
|
||||
}
|
||||
|
||||
// 获取全局存储实例
|
||||
private static InfinityStorageManager getStorageManagerInstance() {
|
||||
return ExtendedAEPlus.STORAGE_INSTANCE;
|
||||
}
|
||||
|
||||
private InfinityDataStorage getExistingCellStorage() {
|
||||
UUID uuid = this.getUUID();
|
||||
InfinityStorageManager storageManager = this.getStorageManagerInstance();
|
||||
if (uuid == null || storageManager == null || !storageManager.hasUUID(uuid)) {
|
||||
return null;
|
||||
}
|
||||
return storageManager.getOrCreateCell(uuid);
|
||||
}
|
||||
|
||||
private InfinityDataStorage getWritableCellStorage() {
|
||||
InfinityStorageManager storageManager = this.getStorageManagerInstance();
|
||||
if (storageManager == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
UUID uuid = this.getUUID();
|
||||
if (uuid == null) {
|
||||
uuid = this.assignNewUUID();
|
||||
}
|
||||
return storageManager.getOrCreateCell(uuid);
|
||||
}
|
||||
|
||||
private UUID assignNewUUID() {
|
||||
CompoundTag tag = self.getOrCreateTag();
|
||||
UUID newUUID = UUID.randomUUID();
|
||||
tag.putUUID(InfinityConstants.INFINITY_CELL_UUID, newUUID);
|
||||
return newUUID;
|
||||
}
|
||||
|
||||
private void clearCellData() {
|
||||
UUID uuid = this.getUUID();
|
||||
InfinityStorageManager storageManager = this.getStorageManagerInstance();
|
||||
if (uuid != null && storageManager != null && storageManager.hasUUID(uuid)) {
|
||||
storageManager.removeCell(uuid);
|
||||
}
|
||||
|
||||
this.totalAEKeyType = 0;
|
||||
this.totalAEKey2Amounts = BI_ZERO;
|
||||
|
||||
CompoundTag tag = self.getOrCreateTag();
|
||||
tag.remove(InfinityConstants.INFINITY_CELL_UUID);
|
||||
tag.remove(InfinityConstants.INFINITY_ITEM_TOTAL);
|
||||
tag.remove(InfinityConstants.INFINITY_ITEM_TYPES);
|
||||
// backward compat
|
||||
tag.remove(InfinityConstants.INFINITY_CELL_ITEM_COUNT);
|
||||
this.isPersisted = true;
|
||||
|
||||
if (this.container != null) {
|
||||
this.container.saveChanges();
|
||||
}
|
||||
}
|
||||
|
||||
// 标记数据需要保存,并通知容器或直接持久化
|
||||
private void saveChanges() {
|
||||
this.isPersisted = false;
|
||||
InfinityStorageManager storageManager = this.getStorageManagerInstance();
|
||||
if (storageManager != null) {
|
||||
storageManager.setDirty();
|
||||
// 更新存储的物品种类数量
|
||||
this.totalAEKeyType = this.AEKey2AmountsMap.size();
|
||||
// 重置物品总数
|
||||
this.totalAEKey2Amounts = BI_ZERO;
|
||||
// 计算物品总数
|
||||
for (BigInteger AEKey2Amounts : this.AEKey2AmountsMap.values()) {
|
||||
this.totalAEKey2Amounts = this.totalAEKey2Amounts.add(AEKey2Amounts);
|
||||
}
|
||||
|
||||
// 标记数据未持久化
|
||||
this.isPersisted = false;
|
||||
// 如果有保存提供者,通知保存
|
||||
if (this.container != null) {
|
||||
this.container.saveChanges();
|
||||
} else {
|
||||
// 否则立即持久化
|
||||
this.persist();
|
||||
}
|
||||
}
|
||||
|
||||
private ConfigInventory getConfigInventory() {
|
||||
return this.cell.getConfigInventory(this.self);
|
||||
}
|
||||
|
||||
private IUpgradeInventory getUpgradesInventory() {
|
||||
return this.cell.getUpgrades(this.self);
|
||||
}
|
||||
|
||||
private FuzzyMode getFuzzyMode() {
|
||||
return this.cell.getFuzzyMode(this.self);
|
||||
}
|
||||
|
||||
// 插入物品到存储单元
|
||||
@Override
|
||||
public long insert(AEKey what, long amount, Actionable mode, IActionSource source) {
|
||||
// 数量为0或类型不匹配直接返回
|
||||
if (amount == 0){
|
||||
return 0;
|
||||
}
|
||||
if (!this.partitionList.matchesFilter(what, this.partitionListMode)) {
|
||||
return 0;
|
||||
}
|
||||
// 不允许存储有物品的无限单元
|
||||
if (what instanceof AEItemKey itemKey &&
|
||||
itemKey.getItem() instanceof InfinityBigIntegerCellItem &&
|
||||
itemKey.hasTag()) {
|
||||
itemKey.hasTag()
|
||||
) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
var cellStorage = this.getWritableCellStorage();
|
||||
if (cellStorage == null) {
|
||||
return 0;
|
||||
// 如果没有UUID,尝试在服务器端且存储管理器已就绪时生成UUID并初始化存储
|
||||
if (!this.hasUUID()) {
|
||||
self.getOrCreateTag().putUUID(InfinityConstants.INFINITY_CELL_UUID, UUID.randomUUID());
|
||||
getStorageManagerInstance().getOrCreateCell(getUUID());
|
||||
loadCellStoredMap();
|
||||
}
|
||||
// 获取当前物品数量
|
||||
BigInteger currentAmount = this.getCellStoredMap().getOrDefault(what, BI_ZERO);
|
||||
|
||||
BigInteger currentAmount = cellStorage.amounts.getOrDefault(what, BI_ZERO);
|
||||
if (mode == Actionable.MODULATE) {
|
||||
BigInteger delta = BigInteger.valueOf(amount);
|
||||
if (currentAmount.equals(BI_ZERO)) {
|
||||
this.totalAEKeyType++;
|
||||
}
|
||||
|
||||
BigInteger newAmount = currentAmount.add(delta);
|
||||
cellStorage.amounts.put(what, newAmount);
|
||||
this.totalAEKey2Amounts = this.totalAEKey2Amounts.add(delta);
|
||||
cellStorage.itemCount = this.totalAEKey2Amounts;
|
||||
// 实际插入,更新数量并保存
|
||||
BigInteger newAmount = currentAmount.add(BigInteger.valueOf(amount));
|
||||
getCellStoredMap().put(what, newAmount);
|
||||
this.saveChanges();
|
||||
}
|
||||
return amount;
|
||||
}
|
||||
|
||||
// 从存储单元提取物品
|
||||
@Override
|
||||
public long extract(AEKey what, long amount, Actionable mode, IActionSource source) {
|
||||
var cellStorage = this.getExistingCellStorage();
|
||||
if (cellStorage == null) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
BigInteger currentAmount = cellStorage.amounts.getOrDefault(what, BI_ZERO);
|
||||
if (currentAmount.compareTo(BI_ZERO) <= 0) {
|
||||
return 0;
|
||||
}
|
||||
BigInteger currentAmount = this.getCellStoredMap().getOrDefault(what, BI_ZERO);
|
||||
// 如果有物品可提取
|
||||
if (currentAmount.compareTo(BI_ZERO) > 0) {
|
||||
|
||||
BigInteger requested = BigInteger.valueOf(amount);
|
||||
|
||||
// 如果提取数量大于等于当前数量
|
||||
if (requested.compareTo(currentAmount) >= 0) {
|
||||
if (mode == Actionable.MODULATE) {
|
||||
cellStorage.amounts.remove(what);
|
||||
this.totalAEKeyType--;
|
||||
this.totalAEKey2Amounts = this.totalAEKey2Amounts.subtract(currentAmount);
|
||||
cellStorage.itemCount = this.totalAEKey2Amounts;
|
||||
|
||||
if (cellStorage.amounts.isEmpty()) {
|
||||
this.clearCellData();
|
||||
} else {
|
||||
getCellStoredMap().remove(what);
|
||||
this.saveChanges();
|
||||
}
|
||||
}
|
||||
return currentAmount.compareTo(BI_LONG_MAX) > 0 ? Long.MAX_VALUE : currentAmount.longValue();
|
||||
}
|
||||
|
||||
} else {
|
||||
// 提取部分数量
|
||||
if (mode == Actionable.MODULATE) {
|
||||
BigInteger newAmount = currentAmount.subtract(requested);
|
||||
cellStorage.amounts.put(what, newAmount);
|
||||
this.totalAEKey2Amounts = this.totalAEKey2Amounts.subtract(requested);
|
||||
cellStorage.itemCount = this.totalAEKey2Amounts;
|
||||
getCellStoredMap().put(what, currentAmount.subtract(requested));
|
||||
this.saveChanges();
|
||||
}
|
||||
return requested.longValue();
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
// 获取存储单元内所有物品的总数量(格式化字符串)
|
||||
public String getTotalStorage() {
|
||||
this.refreshCachedStateFromStorage();
|
||||
// 使用缓存的 totalStored,避免每次全表扫描
|
||||
return formatBigInteger(totalAEKey2Amounts);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,17 +0,0 @@
|
|||
package com.extendedae_plus.api.upload;
|
||||
|
||||
/**
|
||||
* 用于访问 GuiExPatternTerminalMixin 中快速上传功能的接口
|
||||
*/
|
||||
public interface IGuiExPatternTerminalUploadAccessor {
|
||||
/**
|
||||
* 获取当前选择的样板供应器ID
|
||||
*/
|
||||
long eap$getCurrentlyChoicePatternProvider();
|
||||
|
||||
/**
|
||||
* 快速上传样板到当前选择的供应器
|
||||
*/
|
||||
void eap$quickUploadPattern(int playerSlotIndex);
|
||||
}
|
||||
|
||||
|
|
@ -1,13 +0,0 @@
|
|||
package com.extendedae_plus.api.upload;
|
||||
|
||||
public interface IPatternEncodingShiftUploadSync {
|
||||
/**
|
||||
* 由客户端发送的编码指令附带的 Shift 状态。
|
||||
*/
|
||||
void eap$clientSetShiftUpload(boolean shiftDown);
|
||||
|
||||
/**
|
||||
* 服务器在处理 encode() 时消费该标记,并在读取后自动复位。
|
||||
*/
|
||||
boolean eap$consumeShiftUploadFlag();
|
||||
}
|
||||
|
|
@ -7,7 +7,6 @@ import com.extendedae_plus.ae.menu.EntitySpeedTickerMenu;
|
|||
import com.extendedae_plus.ae.screen.EntitySpeedTickerScreen;
|
||||
import com.extendedae_plus.client.render.crafting.EPlusCraftingCubeModelProvider;
|
||||
import com.extendedae_plus.client.screen.GlobalProviderModesScreen;
|
||||
import com.extendedae_plus.client.screen.LabeledWirelessTransceiverScreen;
|
||||
import com.extendedae_plus.content.crafting.EPlusCraftingUnitType;
|
||||
import com.extendedae_plus.hooks.BuiltInModelHooks;
|
||||
import com.extendedae_plus.init.ModItems;
|
||||
|
|
@ -15,7 +14,6 @@ import com.extendedae_plus.init.ModMenuTypes;
|
|||
import com.extendedae_plus.items.materials.EntitySpeedCardItem;
|
||||
import net.minecraft.client.gui.screens.MenuScreens;
|
||||
import net.minecraft.client.renderer.item.ItemProperties;
|
||||
import net.minecraftforge.fml.ModList;
|
||||
|
||||
/**
|
||||
* 客户端模型注册,将 formed 模型注册为内置模型。
|
||||
|
|
@ -61,7 +59,6 @@ public final class ClientRegistrar {
|
|||
*/
|
||||
public static void registerMenuScreens() {
|
||||
MenuScreens.register(ModMenuTypes.NETWORK_PATTERN_CONTROLLER.get(), GlobalProviderModesScreen::new);
|
||||
MenuScreens.register(ModMenuTypes.LABELED_WIRELESS_TRANSCEIVER.get(), LabeledWirelessTransceiverScreen::new);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ package com.extendedae_plus.client;
|
|||
import appeng.api.stacks.GenericStack;
|
||||
import appeng.client.gui.me.common.MEStorageScreen;
|
||||
import appeng.integration.modules.jei.GenericEntryStackHelper;
|
||||
import com.extendedae_plus.ExtendedAEPlus;
|
||||
import com.extendedae_plus.init.ModNetwork;
|
||||
import com.extendedae_plus.integration.jei.JeiRuntimeProxy;
|
||||
import com.extendedae_plus.mixin.ae2.accessor.MEStorageScreenAccessor;
|
||||
|
|
@ -13,13 +14,16 @@ import com.glodblock.github.extendedae.client.gui.GuiExPatternTerminal;
|
|||
import mezz.jei.api.ingredients.ITypedIngredient;
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.client.gui.screens.Screen;
|
||||
import net.minecraftforge.api.distmarker.Dist;
|
||||
import net.minecraftforge.client.event.ScreenEvent;
|
||||
import net.minecraftforge.eventbus.api.SubscribeEvent;
|
||||
import net.minecraftforge.fml.ModList;
|
||||
import net.minecraftforge.fml.common.Mod;
|
||||
import org.lwjgl.glfw.GLFW;
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
@Mod.EventBusSubscriber(modid = ExtendedAEPlus.MODID, value = Dist.CLIENT, bus = Mod.EventBusSubscriber.Bus.FORGE)
|
||||
public final class InputEvents {
|
||||
private InputEvents() {}
|
||||
|
||||
|
|
@ -97,10 +101,7 @@ public final class InputEvents {
|
|||
return;
|
||||
}
|
||||
// 注意:不要在 try/catch 之外直接访问 JEI 运行时,避免类加载崩溃
|
||||
// 检查是否按下了填充搜索框的快捷键
|
||||
if (!ModKeybindings.FILL_SEARCH_KEY.matches(event.getKeyCode(), event.getScanCode())) {
|
||||
return;
|
||||
}
|
||||
if (event.getKeyCode() != GLFW.GLFW_KEY_F) return;
|
||||
|
||||
// 仅当鼠标确实悬停在 JEI 配料上时触发
|
||||
try {
|
||||
|
|
|
|||
|
|
@ -1,47 +0,0 @@
|
|||
package com.extendedae_plus.client;
|
||||
|
||||
import com.mojang.blaze3d.platform.InputConstants;
|
||||
import net.minecraft.client.KeyMapping;
|
||||
import net.minecraftforge.client.settings.KeyConflictContext;
|
||||
import net.minecraftforge.client.settings.KeyModifier;
|
||||
import org.lwjgl.glfw.GLFW;
|
||||
|
||||
/**
|
||||
* ExtendedAE Plus 快捷键定义
|
||||
*/
|
||||
public final class ModKeybindings {
|
||||
private ModKeybindings() {}
|
||||
|
||||
/**
|
||||
* Ctrl+Q 快速创建样板快捷键
|
||||
*/
|
||||
public static final KeyMapping CREATE_PATTERN_KEY = new KeyMapping(
|
||||
"key.extendedae_plus.create_pattern", // 翻译键
|
||||
KeyConflictContext.GUI, // 仅在GUI中生效
|
||||
KeyModifier.CONTROL, // Ctrl 修饰键
|
||||
InputConstants.Type.KEYSYM, // 键盘按键类型
|
||||
GLFW.GLFW_KEY_Q, // Q 键
|
||||
"key.categories.extendedae_plus" // 分类
|
||||
);
|
||||
|
||||
/**
|
||||
* 填充JEI物品名称到搜索框快捷键
|
||||
*/
|
||||
public static final KeyMapping FILL_SEARCH_KEY = new KeyMapping(
|
||||
"key.extendedae_plus.fill_search", // 翻译键
|
||||
KeyConflictContext.GUI, // 仅在GUI中生效
|
||||
InputConstants.Type.KEYSYM, // 键盘按键类型
|
||||
GLFW.GLFW_KEY_F, // F 键(默认)
|
||||
"key.categories.extendedae_plus" // 分类
|
||||
);
|
||||
|
||||
/**
|
||||
* 注册所有快捷键
|
||||
*
|
||||
* @param event Forge快捷键注册事件
|
||||
*/
|
||||
public static void register(net.minecraftforge.client.event.RegisterKeyMappingsEvent event) {
|
||||
event.register(CREATE_PATTERN_KEY);
|
||||
event.register(FILL_SEARCH_KEY);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,537 +0,0 @@
|
|||
package com.extendedae_plus.client.event;
|
||||
|
||||
import com.extendedae_plus.client.ModKeybindings;
|
||||
import com.extendedae_plus.init.ModNetwork;
|
||||
import com.extendedae_plus.integration.jei.JeiRuntimeProxy;
|
||||
import com.extendedae_plus.network.pattern.CreateAndUploadPatternC2SPacket;
|
||||
import com.extendedae_plus.network.pattern.CreateCtrlQPatternC2SPacket;
|
||||
import com.extendedae_plus.network.provider.RequestProvidersListC2SPacket;
|
||||
import com.extendedae_plus.util.RecipeFinderUtil;
|
||||
import com.extendedae_plus.util.RecipeInfo;
|
||||
import com.extendedae_plus.util.uploadPattern.RecipeTypeNameConfig;
|
||||
import mezz.jei.api.constants.RecipeTypes;
|
||||
import mezz.jei.api.constants.VanillaTypes;
|
||||
import mezz.jei.api.ingredients.ITypedIngredient;
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.client.gui.screens.Screen;
|
||||
import net.minecraft.network.chat.Component;
|
||||
import net.minecraft.world.item.Item;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
import net.minecraft.world.item.crafting.Recipe;
|
||||
import net.minecraftforge.client.event.ScreenEvent;
|
||||
import net.minecraftforge.eventbus.api.SubscribeEvent;
|
||||
|
||||
import java.sql.SQLOutput;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
/**
|
||||
* Ctrl+Q键快速创建样板事件监听器
|
||||
*
|
||||
* <p>应用 JEI 书签优先级选择材料,优先选择工作台配方</p>
|
||||
*/
|
||||
public class CtrlQPatternKeyHandler {
|
||||
|
||||
@SubscribeEvent
|
||||
public static void onScreenKeyPressed(ScreenEvent.KeyPressed event) {
|
||||
Screen screen = event.getScreen();
|
||||
int keyCode = event.getKeyCode();
|
||||
int scanCode = event.getScanCode();
|
||||
boolean isAllowSubstitutes = Screen.hasShiftDown();
|
||||
boolean isFluidSubstitutes = Screen.hasAltDown();
|
||||
|
||||
// 使用 KeyMapping
|
||||
if (!ModKeybindings.CREATE_PATTERN_KEY.matches(keyCode, scanCode)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// JEI 必须可用
|
||||
if (JeiRuntimeProxy.get() == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
// 检查鼠标下是否为配方书签
|
||||
Optional<?> recipeBookmark = JeiRuntimeProxy.getRecipeBookmarkUnderMouse();
|
||||
|
||||
if (recipeBookmark.isPresent()) {
|
||||
// 配方书签分支:处理带有配方类型的书签
|
||||
handleRecipeBookmark(recipeBookmark.get(),isAllowSubstitutes,isFluidSubstitutes);
|
||||
event.setCanceled(true);
|
||||
return;
|
||||
}
|
||||
|
||||
// 普通书签分支:保持原有逻辑
|
||||
// 获取鼠标悬浮的物品
|
||||
Optional<ITypedIngredient<?>> ingredient = JeiRuntimeProxy.getIngredientUnderMouse();
|
||||
|
||||
if (ingredient.isEmpty()) {
|
||||
Minecraft mc = Minecraft.getInstance();
|
||||
if (mc.player != null) {
|
||||
mc.player.displayClientMessage(
|
||||
Component.translatable("message.extendedae_plus.hover_item_first"),
|
||||
true
|
||||
);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// 查找相关配方(使用新的 API,包含完整数量信息)
|
||||
Minecraft mc = Minecraft.getInstance();
|
||||
List<RecipeInfo> recipes = RecipeFinderUtil.findRecipesByIngredient(
|
||||
ingredient.get()
|
||||
);
|
||||
|
||||
if (recipes.isEmpty()) {
|
||||
if (mc.player != null) {
|
||||
mc.player.displayClientMessage(
|
||||
Component.translatable("message.extendedae_plus.no_recipes_found"),
|
||||
true
|
||||
);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// 自动选择最佳配方(优先工作台配方)
|
||||
RecipeInfo selectedRecipeInfo = RecipeFinderUtil.selectBestRecipe(recipes);
|
||||
if (selectedRecipeInfo == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
// 应用JEI书签优先级选择材料
|
||||
List<ItemStack> selectedIngredients = selectIngredientsWithJeiPriority(selectedRecipeInfo);
|
||||
|
||||
// 获取输出材料(转换为 ItemStack,流体会被包装)
|
||||
List<ItemStack> selectedOutputs = convertOutputsToItemStacks(selectedRecipeInfo);
|
||||
|
||||
|
||||
System.out.println("sendPackage");
|
||||
// 发送网络包到服务器
|
||||
ModNetwork.CHANNEL.sendToServer(new CreateCtrlQPatternC2SPacket(
|
||||
selectedRecipeInfo.getRecipe().getId(),
|
||||
selectedRecipeInfo.isCraftingRecipe(),
|
||||
selectedIngredients,
|
||||
selectedOutputs,
|
||||
false,
|
||||
isAllowSubstitutes,
|
||||
isFluidSubstitutes
|
||||
));
|
||||
|
||||
// 消耗事件,防止传播
|
||||
event.setCanceled(true);
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理配方书签的逻辑
|
||||
*
|
||||
* @param recipeBookmark 配方书签对象(RecipeBookmark<?, ?>)
|
||||
*/
|
||||
private static void handleRecipeBookmark(Object recipeBookmark,boolean isAllowSubstitutes,boolean isFluidSubstitutes) {
|
||||
// 判断配方类型
|
||||
if (isCraftingRecipe(recipeBookmark)) {
|
||||
// 合成配方分支
|
||||
handleCraftingRecipeBookmark(recipeBookmark,isAllowSubstitutes,isFluidSubstitutes);
|
||||
} else {
|
||||
// 其他配方分支(加工配方等)
|
||||
handleProcessingRecipeBookmark(recipeBookmark,isAllowSubstitutes,isFluidSubstitutes);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断配方书签是否为能上传到装配矩阵的配方类型
|
||||
*
|
||||
* @param recipeBookmark 配方书签对象
|
||||
* @return true 如果是合成配方
|
||||
*/
|
||||
private static boolean isCraftingRecipe(Object recipeBookmark) {
|
||||
try {
|
||||
// 通过反射获取 RecipeBookmark 的 recipeCategory
|
||||
var getRecipeCategoryMethod = recipeBookmark.getClass().getMethod("getRecipeCategory");
|
||||
Object recipeCategory = getRecipeCategoryMethod.invoke(recipeBookmark);
|
||||
|
||||
// 获取 recipeCategory 的 recipeType
|
||||
var getRecipeTypeMethod = recipeCategory.getClass().getMethod("getRecipeType");
|
||||
Object recipeType = getRecipeTypeMethod.invoke(recipeCategory);
|
||||
|
||||
// 判断是否为能上传到装配矩阵的配方类型
|
||||
return RecipeTypes.CRAFTING.equals(recipeType) || RecipeTypes.STONECUTTING.equals(recipeType) || RecipeTypes.SMITHING.equals(recipeType);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理合成配方书签
|
||||
*
|
||||
* @param recipeBookmark 配方书签对象
|
||||
*/
|
||||
private static void handleCraftingRecipeBookmark(Object recipeBookmark,boolean isAllowSubstitutes,boolean isFluidSubstitutes) {
|
||||
try {
|
||||
// 1. 获取配方ID
|
||||
net.minecraft.resources.ResourceLocation recipeId = getRecipeIdCompat(recipeBookmark);
|
||||
|
||||
if (recipeId == null) {
|
||||
Minecraft mc = Minecraft.getInstance();
|
||||
if (mc.player != null) {
|
||||
mc.player.displayClientMessage(
|
||||
Component.translatable("message.extendedae_plus.recipe_not_found"),
|
||||
true
|
||||
);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// 2. 获取Minecraft实例
|
||||
Minecraft mc = Minecraft.getInstance();
|
||||
if (mc.level == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
// 3. 验证配方存在
|
||||
var recipeManager = mc.level.getRecipeManager();
|
||||
var recipeOpt = recipeManager.byKey(recipeId);
|
||||
|
||||
if (recipeOpt.isEmpty()) {
|
||||
if (mc.player != null) {
|
||||
mc.player.displayClientMessage(
|
||||
Component.translatable("message.extendedae_plus.recipe_not_found"),
|
||||
true
|
||||
);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// 4. 从JEI获取配方的详细信息(输入输出槽位)
|
||||
// 使用RecipeBookmark的getRecipe方法获取配方对象
|
||||
var getRecipeMethod = recipeBookmark.getClass().getMethod("getRecipe");
|
||||
Object recipe = getRecipeMethod.invoke(recipeBookmark);
|
||||
|
||||
if (recipe == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
// 5. 通过JEI的RecipeManager获取配方布局信息
|
||||
mezz.jei.api.runtime.IJeiRuntime jeiRuntime = JeiRuntimeProxy.get();
|
||||
if (jeiRuntime == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
// 6. 获取配方类别
|
||||
var getRecipeCategoryMethod = recipeBookmark.getClass().getMethod("getRecipeCategory");
|
||||
Object recipeCategory = getRecipeCategoryMethod.invoke(recipeBookmark);
|
||||
|
||||
// 7. 创建RecipeInfo来应用JEI书签优先级
|
||||
List<RecipeInfo> recipeInfos = RecipeFinderUtil.findRecipesByIngredient(
|
||||
JeiRuntimeProxy.getIngredientUnderMouse().orElse(null)
|
||||
);
|
||||
|
||||
// 如果找不到,尝试通过配方输出物品查找
|
||||
if (recipeInfos.isEmpty()) {
|
||||
try {
|
||||
var getDisplayIngredientMethod = recipeBookmark.getClass().getMethod("getDisplayIngredient");
|
||||
Object displayIngredient = getDisplayIngredientMethod.invoke(recipeBookmark);
|
||||
if (displayIngredient instanceof ITypedIngredient<?> typedIngredient) {
|
||||
recipeInfos = RecipeFinderUtil.findRecipesByIngredient(typedIngredient);
|
||||
}
|
||||
} catch (Throwable ignored) {
|
||||
}
|
||||
}
|
||||
|
||||
if (recipeInfos.isEmpty()) {
|
||||
var getRecipeOutputMethod = recipeBookmark.getClass().getMethod("getRecipeOutput");
|
||||
Object recipeOutput = getRecipeOutputMethod.invoke(recipeBookmark);
|
||||
|
||||
if (recipeOutput instanceof mezz.jei.api.ingredients.ITypedIngredient<?> typedIngredient) {
|
||||
recipeInfos = RecipeFinderUtil.findRecipesByIngredient(typedIngredient);
|
||||
}
|
||||
}
|
||||
|
||||
if (recipeInfos.isEmpty()) {
|
||||
if (mc.player != null) {
|
||||
mc.player.displayClientMessage(
|
||||
Component.translatable("message.extendedae_plus.no_recipes_found"),
|
||||
true
|
||||
);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// 8. 找到匹配的RecipeInfo
|
||||
RecipeInfo matchingRecipeInfo = null;
|
||||
for (RecipeInfo info : recipeInfos) {
|
||||
if (info.getRecipe().getId().equals(recipeId)) {
|
||||
matchingRecipeInfo = info;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (matchingRecipeInfo == null) {
|
||||
matchingRecipeInfo = recipeInfos.get(0);
|
||||
}
|
||||
|
||||
// 9. 应用JEI书签优先级选择材料
|
||||
List<ItemStack> selectedIngredients = selectIngredientsWithJeiPriority(matchingRecipeInfo);
|
||||
|
||||
// 10. 获取输出材料
|
||||
List<ItemStack> selectedOutputs = convertOutputsToItemStacks(matchingRecipeInfo);
|
||||
RecipeTypeNameConfig.presetCraftingProviderSearchKey();
|
||||
|
||||
// 11. 发送网络包创建样板并上传到装配矩阵
|
||||
ModNetwork.CHANNEL.sendToServer(new CreateAndUploadPatternC2SPacket(
|
||||
recipeId,
|
||||
matchingRecipeInfo.isCraftingRecipe(),
|
||||
selectedIngredients,
|
||||
selectedOutputs,
|
||||
isAllowSubstitutes,
|
||||
isFluidSubstitutes
|
||||
));
|
||||
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理加工配方书签
|
||||
*
|
||||
* @param recipeBookmark 配方书签对象
|
||||
*/
|
||||
private static void handleProcessingRecipeBookmark(Object recipeBookmark,boolean isAllowSubstitutes,boolean isFluidSubstitutes) {
|
||||
try {
|
||||
net.minecraft.resources.ResourceLocation recipeId = getRecipeIdCompat(recipeBookmark);
|
||||
if (recipeId == null) {
|
||||
Minecraft mc = Minecraft.getInstance();
|
||||
if (mc.player != null) {
|
||||
mc.player.displayClientMessage(
|
||||
Component.translatable("message.extendedae_plus.recipe_not_found"),
|
||||
true
|
||||
);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
Minecraft mc = Minecraft.getInstance();
|
||||
if (mc.level == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
var recipeManager = mc.level.getRecipeManager();
|
||||
var recipeOpt = recipeManager.byKey(recipeId);
|
||||
if (recipeOpt.isEmpty()) {
|
||||
if (mc.player != null) {
|
||||
mc.player.displayClientMessage(
|
||||
Component.translatable("message.extendedae_plus.recipe_not_found"),
|
||||
true
|
||||
);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
Object recipeBase = null;
|
||||
try {
|
||||
var getRecipeMethod = recipeBookmark.getClass().getMethod("getRecipe");
|
||||
recipeBase = getRecipeMethod.invoke(recipeBookmark);
|
||||
} catch (Throwable ignored) {
|
||||
}
|
||||
setLastProcessingNameFromRecipe(recipeBase != null ? recipeBase : recipeOpt.get());
|
||||
|
||||
List<RecipeInfo> recipeInfos = RecipeFinderUtil.findRecipesByIngredient(
|
||||
JeiRuntimeProxy.getIngredientUnderMouse().orElse(null)
|
||||
);
|
||||
|
||||
if (recipeInfos.isEmpty()) {
|
||||
try {
|
||||
var getDisplayIngredientMethod = recipeBookmark.getClass().getMethod("getDisplayIngredient");
|
||||
Object displayIngredient = getDisplayIngredientMethod.invoke(recipeBookmark);
|
||||
if (displayIngredient instanceof ITypedIngredient<?> typedIngredient) {
|
||||
recipeInfos = RecipeFinderUtil.findRecipesByIngredient(typedIngredient);
|
||||
}
|
||||
} catch (Throwable ignored) {
|
||||
}
|
||||
}
|
||||
|
||||
if (recipeInfos.isEmpty()) {
|
||||
var getRecipeOutputMethod = recipeBookmark.getClass().getMethod("getRecipeOutput");
|
||||
Object recipeOutput = getRecipeOutputMethod.invoke(recipeBookmark);
|
||||
if (recipeOutput instanceof ITypedIngredient<?> typedIngredient) {
|
||||
recipeInfos = RecipeFinderUtil.findRecipesByIngredient(typedIngredient);
|
||||
}
|
||||
}
|
||||
|
||||
if (recipeInfos.isEmpty()) {
|
||||
if (mc.player != null) {
|
||||
mc.player.displayClientMessage(
|
||||
Component.translatable("message.extendedae_plus.no_recipes_found"),
|
||||
true
|
||||
);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
RecipeInfo matchingRecipeInfo = null;
|
||||
for (RecipeInfo info : recipeInfos) {
|
||||
if (info.getRecipe().getId().equals(recipeId)) {
|
||||
matchingRecipeInfo = info;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (matchingRecipeInfo == null) {
|
||||
matchingRecipeInfo = recipeInfos.get(0);
|
||||
}
|
||||
|
||||
List<ItemStack> selectedIngredients = selectIngredientsWithJeiPriority(matchingRecipeInfo);
|
||||
List<ItemStack> selectedOutputs = convertOutputsToItemStacks(matchingRecipeInfo);
|
||||
|
||||
ModNetwork.CHANNEL.sendToServer(new CreateCtrlQPatternC2SPacket(
|
||||
recipeId,
|
||||
matchingRecipeInfo.isCraftingRecipe(),
|
||||
selectedIngredients,
|
||||
selectedOutputs,
|
||||
true,
|
||||
isAllowSubstitutes,
|
||||
isFluidSubstitutes
|
||||
));
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
private static net.minecraft.resources.ResourceLocation getRecipeIdCompat(Object recipeBookmark) {
|
||||
if (recipeBookmark == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
try {
|
||||
var getRecipeUidMethod = recipeBookmark.getClass().getMethod("getRecipeUid");
|
||||
Object recipeId = getRecipeUidMethod.invoke(recipeBookmark);
|
||||
if (recipeId instanceof net.minecraft.resources.ResourceLocation rl) {
|
||||
return rl;
|
||||
}
|
||||
} catch (Throwable ignored) {
|
||||
}
|
||||
|
||||
try {
|
||||
Object recipe = recipeBookmark.getClass().getMethod("getRecipe").invoke(recipeBookmark);
|
||||
Object recipeCategory = recipeBookmark.getClass().getMethod("getRecipeCategory").invoke(recipeBookmark);
|
||||
if (recipe != null && recipeCategory != null) {
|
||||
try {
|
||||
Object recipeId = recipeCategory.getClass().getMethod("getRegistryName", Object.class).invoke(recipeCategory, recipe);
|
||||
if (recipeId instanceof net.minecraft.resources.ResourceLocation rl) {
|
||||
return rl;
|
||||
}
|
||||
} catch (Throwable ignored) {
|
||||
for (var m : recipeCategory.getClass().getMethods()) {
|
||||
if (!"getRegistryName".equals(m.getName()) || m.getParameterCount() != 1) {
|
||||
continue;
|
||||
}
|
||||
Object recipeId = m.invoke(recipeCategory, recipe);
|
||||
if (recipeId instanceof net.minecraft.resources.ResourceLocation rl) {
|
||||
return rl;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (Throwable ignored) {
|
||||
}
|
||||
|
||||
// 字段兜底
|
||||
try {
|
||||
var f = recipeBookmark.getClass().getDeclaredField("recipeUid");
|
||||
f.setAccessible(true);
|
||||
Object recipeId = f.get(recipeBookmark);
|
||||
if (recipeId instanceof net.minecraft.resources.ResourceLocation rl) {
|
||||
return rl;
|
||||
}
|
||||
} catch (Throwable ignored) {
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private static void setLastProcessingNameFromRecipe(Object recipeBase) {
|
||||
String name = null;
|
||||
|
||||
// 处理 RecipeHolder 包装(Minecraft 1.20+)
|
||||
if (recipeBase != null && "net.minecraft.world.item.crafting.RecipeHolder".equals(recipeBase.getClass().getName())) {
|
||||
try {
|
||||
var valueMethod = recipeBase.getClass().getMethod("value");
|
||||
Object actualRecipe = valueMethod.invoke(recipeBase);
|
||||
if (actualRecipe != null) {
|
||||
recipeBase = actualRecipe;
|
||||
}
|
||||
} catch (Throwable ignored) {
|
||||
}
|
||||
}
|
||||
|
||||
if (recipeBase instanceof Recipe<?> recipe) {
|
||||
name = RecipeTypeNameConfig.mapRecipeTypeToSearchKey(recipe);
|
||||
} else if (recipeBase != null
|
||||
&& "com.gregtechceu.gtceu.api.recipe.GTRecipe".equals(recipeBase.getClass().getName())) {
|
||||
name = RecipeTypeNameConfig.mapGTCEuRecipeToSearchKey(recipeBase);
|
||||
} else if (recipeBase != null
|
||||
&& "com.gregtechceu.gtceu.integration.jei.recipe.GTRecipeWrapper".equals(recipeBase.getClass().getName())) {
|
||||
try {
|
||||
var field = recipeBase.getClass().getField("recipe");
|
||||
Object inner = field.get(recipeBase);
|
||||
name = RecipeTypeNameConfig.mapGTCEuRecipeToSearchKey(inner);
|
||||
} catch (Throwable ignored) {
|
||||
}
|
||||
}
|
||||
|
||||
if (name == null || name.isBlank()) {
|
||||
name = RecipeTypeNameConfig.deriveSearchKeyFromUnknownRecipe(recipeBase);
|
||||
}
|
||||
if (name != null && !name.isBlank()) {
|
||||
RecipeTypeNameConfig.setLastProcessingName(name);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 应用JEI书签优先级选择配方材料
|
||||
*
|
||||
* <p>对配方的每个输入槽位,选择 JEI 书签中优先级最高的物品</p>
|
||||
* <p>如果没有在书签中,则使用槽位的第一个物品</p>
|
||||
*
|
||||
* @param recipeInfo 配方信息(包含完整的输入输出数量)
|
||||
* @return 选择的材料列表
|
||||
*/
|
||||
private static List<ItemStack> selectIngredientsWithJeiPriority(RecipeInfo recipeInfo) {
|
||||
// 获取JEI书签列表并构建优先级映射
|
||||
List<? extends ITypedIngredient<?>> bookmarks = JeiRuntimeProxy.getBookmarkList();
|
||||
Map<Item, Integer> priorities = new HashMap<>();
|
||||
AtomicInteger index = new AtomicInteger(Integer.MAX_VALUE);
|
||||
|
||||
// 构建优先级映射 (数值越小 = 优先级越高)
|
||||
for (ITypedIngredient<?> ingredient : bookmarks) {
|
||||
ingredient.getIngredient(VanillaTypes.ITEM_STACK).ifPresent(itemStack ->
|
||||
priorities.put(itemStack.getItem(), index.getAndDecrement())
|
||||
);
|
||||
}
|
||||
|
||||
// 使用 RecipeInfo 的方法选择最佳输入
|
||||
return recipeInfo.selectBestInputs(priorities);
|
||||
}
|
||||
|
||||
/**
|
||||
* 将配方输出转换为 ItemStack 列表(用于网络传输)
|
||||
*
|
||||
* <p>物品直接转换,流体会被包装为 GenericStack.wrapInItemStack</p>
|
||||
*
|
||||
* @param recipeInfo 配方信息
|
||||
* @return ItemStack 列表(流体已包装)
|
||||
*/
|
||||
private static List<ItemStack> convertOutputsToItemStacks(RecipeInfo recipeInfo) {
|
||||
return recipeInfo.getOutputs().stream()
|
||||
.map(genericStack -> {
|
||||
if (genericStack.what() instanceof appeng.api.stacks.AEItemKey itemKey) {
|
||||
return itemKey.toStack((int) genericStack.amount());
|
||||
} else {
|
||||
// 流体或其他类型,使用包装
|
||||
return appeng.api.stacks.GenericStack.wrapInItemStack(genericStack);
|
||||
}
|
||||
})
|
||||
.toList();
|
||||
}
|
||||
}
|
||||
|
|
@ -1,98 +0,0 @@
|
|||
package com.extendedae_plus.client.gui.widgets;
|
||||
|
||||
import appeng.client.gui.Icon;
|
||||
import com.mojang.blaze3d.systems.RenderSystem;
|
||||
import net.minecraft.client.gui.GuiGraphics;
|
||||
import net.minecraft.client.gui.components.Button;
|
||||
import net.minecraft.client.gui.components.Tooltip;
|
||||
import net.minecraft.network.chat.Component;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
|
||||
public class ScaledTextureButton extends Button {
|
||||
private final OnPress delegateOnPress;
|
||||
private final ResourceLocation texture;
|
||||
private final int textureWidth;
|
||||
private final int textureHeight;
|
||||
private final int srcX;
|
||||
private final int srcY;
|
||||
private final int srcWidth;
|
||||
private final int srcHeight;
|
||||
private final float scale;
|
||||
|
||||
public ScaledTextureButton(ResourceLocation texture, int textureWidth, int textureHeight,
|
||||
int srcX, int srcY, int srcWidth, int srcHeight, float scale,
|
||||
Component tooltipText, OnPress onPress) {
|
||||
super(0, 0, Math.round(srcWidth * scale), Math.round(srcHeight * scale),
|
||||
Component.empty(), btn -> {
|
||||
}, DEFAULT_NARRATION);
|
||||
this.delegateOnPress = onPress;
|
||||
this.texture = texture;
|
||||
this.textureWidth = textureWidth;
|
||||
this.textureHeight = textureHeight;
|
||||
this.srcX = srcX;
|
||||
this.srcY = srcY;
|
||||
this.srcWidth = srcWidth;
|
||||
this.srcHeight = srcHeight;
|
||||
this.scale = scale;
|
||||
if (tooltipText != null) {
|
||||
this.setTooltip(Tooltip.create(tooltipText));
|
||||
}
|
||||
}
|
||||
|
||||
public void setVisibility(boolean visible) {
|
||||
this.visible = visible;
|
||||
this.active = visible;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPress() {
|
||||
this.delegateOnPress.onPress(this);
|
||||
this.setFocused(false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onClick(double mouseX, double mouseY) {
|
||||
super.onClick(mouseX, mouseY);
|
||||
this.setFocused(false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onRelease(double mouseX, double mouseY) {
|
||||
super.onRelease(mouseX, mouseY);
|
||||
this.setFocused(false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void renderWidget(GuiGraphics guiGraphics, int mouseX, int mouseY, float partialTick) {
|
||||
if (!this.visible) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.width = Math.round(this.srcWidth * this.scale);
|
||||
this.height = Math.round(this.srcHeight * this.scale);
|
||||
|
||||
int yOffset = this.isHovered() ? 1 : 0;
|
||||
|
||||
RenderSystem.disableDepthTest();
|
||||
RenderSystem.enableBlend();
|
||||
|
||||
var pose = guiGraphics.pose();
|
||||
pose.pushPose();
|
||||
pose.translate(getX(), getY() + yOffset, 0.0F);
|
||||
pose.scale(scale, scale, 1.0F);
|
||||
|
||||
Icon.TOOLBAR_BUTTON_BACKGROUND.getBlitter().dest(0, 0).blit(guiGraphics);
|
||||
|
||||
if (!this.active) {
|
||||
RenderSystem.setShaderColor(1.0F, 1.0F, 1.0F, 0.5F);
|
||||
}
|
||||
guiGraphics.blit(this.texture, 0, 0, this.srcX, this.srcY, this.srcWidth, this.srcHeight,
|
||||
this.textureWidth, this.textureHeight);
|
||||
if (!this.active) {
|
||||
RenderSystem.setShaderColor(1.0F, 1.0F, 1.0F, 1.0F);
|
||||
}
|
||||
|
||||
pose.popPose();
|
||||
RenderSystem.enableDepthTest();
|
||||
}
|
||||
}
|
||||
|
|
@ -1,477 +0,0 @@
|
|||
package com.extendedae_plus.client.screen;
|
||||
|
||||
import com.extendedae_plus.ExtendedAEPlus;
|
||||
import com.extendedae_plus.ae.wireless.LabelNetworkRegistry;
|
||||
import com.extendedae_plus.menu.LabeledWirelessTransceiverMenu;
|
||||
import com.extendedae_plus.network.LabelNetworkActionC2SPacket;
|
||||
import com.extendedae_plus.network.LabelNetworkListC2SPacket;
|
||||
import com.extendedae_plus.init.ModNetwork;
|
||||
import com.mojang.blaze3d.systems.RenderSystem;
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.client.gui.GuiGraphics;
|
||||
import net.minecraft.client.gui.components.EditBox;
|
||||
import net.minecraft.client.gui.components.ImageButton;
|
||||
|
||||
import net.minecraft.client.gui.screens.inventory.AbstractContainerScreen;
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.network.chat.Component;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraft.world.entity.player.Inventory;
|
||||
import org.lwjgl.glfw.GLFW;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class LabeledWirelessTransceiverScreen extends AbstractContainerScreen<LabeledWirelessTransceiverMenu> {
|
||||
private static final ResourceLocation TEX = ExtendedAEPlus.id("textures/gui/lable_wireless_transceiver_gui.png");
|
||||
private static final int BTN_U = 2;
|
||||
private static final int BTN_V = 159;
|
||||
private static final int BTN_W = 28;
|
||||
private static final int BTN_H = 16;
|
||||
private static final int TEX_W = 256;
|
||||
private static final int TEX_H = 256;
|
||||
|
||||
private static final int LIST_X = 9;
|
||||
private static final int LIST_Y = 27;
|
||||
private static final int LIST_W = 110; // 118-9+1
|
||||
private static final int LIST_H = 114; // 140-27+1
|
||||
private static final int ROW_H = 11; // 10px text height + 1px 分隔
|
||||
private static final int VISIBLE_ROWS = LIST_H / ROW_H; // 10
|
||||
private static final int SCROLL_X = 123;
|
||||
private static final int SCROLL_Y = 21;
|
||||
private static final int SCROLL_W = 6;
|
||||
private static final int SCROLL_H = 121; // 141-21+1
|
||||
private static final int INFO_MAX_WIDTH = 116; // 信息区实际宽度(249-134+1=116)
|
||||
|
||||
private EditBox searchBox;
|
||||
private ImageButton newBtn;
|
||||
private ImageButton deleteBtn;
|
||||
private ImageButton setBtn;
|
||||
private ImageButton disconnectBtn;
|
||||
|
||||
private final BlockPos bePos;
|
||||
private final List<LabelEntry> entries = new ArrayList<>();
|
||||
private final List<LabelEntry> filtered = new ArrayList<>();
|
||||
private int scrollOffset = 0;
|
||||
private int selectedIndex = -1;
|
||||
private String lastSelectedLabel = "";
|
||||
private String currentLabel = "";
|
||||
private String currentOwner = "";
|
||||
private int onlineCount = 0;
|
||||
private int usedChannels = 0;
|
||||
private int maxChannels = 0;
|
||||
|
||||
public LabeledWirelessTransceiverScreen(LabeledWirelessTransceiverMenu menu, Inventory inv, Component title) {
|
||||
super(menu, inv, title);
|
||||
this.imageWidth = 256;
|
||||
this.imageHeight = 156;
|
||||
this.inventoryLabelY = this.imageHeight; // 不显示玩家物品栏标签
|
||||
this.bePos = menu.getBlockEntityPos();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void init() {
|
||||
super.init();
|
||||
// 搜索框:起点(134,23) 终点(249,31) => 宽116 高9(取整为9)
|
||||
int sx = this.leftPos + 134;
|
||||
int sy = this.topPos + 23;
|
||||
this.searchBox = new EditBox(this.font, sx, sy, 116, 9, Component.empty());
|
||||
this.searchBox.setBordered(false);
|
||||
this.searchBox.setMaxLength(64);
|
||||
this.searchBox.setVisible(true);
|
||||
this.searchBox.setFocused(false);
|
||||
this.searchBox.setResponder(s -> {
|
||||
applyFilter();
|
||||
});
|
||||
this.addRenderableWidget(this.searchBox);
|
||||
|
||||
int startX = this.leftPos + 145;
|
||||
int startY = this.topPos + 101;
|
||||
int hGap = 30;
|
||||
int vGap = 8;
|
||||
int secondColX = startX + BTN_W + hGap;
|
||||
int secondRowY = startY + BTN_H + vGap;
|
||||
|
||||
this.newBtn = new StateImageButton(startX, startY, BTN_W, BTN_H, BTN_U, BTN_V, 2, 177, 2, 195, TEX, TEX_W, TEX_H,
|
||||
b -> sendSet(searchBox.getValue()), Component.translatable("gui.extendedae_plus.labeled_wireless.button.new"));
|
||||
this.deleteBtn = new StateImageButton(secondColX, startY, BTN_W, BTN_H, BTN_U, BTN_V, 2, 177, 2, 195, TEX, TEX_W, TEX_H,
|
||||
b -> sendDelete(), Component.translatable("gui.extendedae_plus.labeled_wireless.button.delete"));
|
||||
this.setBtn = new StateImageButton(startX, secondRowY, BTN_W, BTN_H, BTN_U, BTN_V, 2, 177, 2, 195, TEX, TEX_W, TEX_H,
|
||||
b -> sendSet(getSelectedLabel()), Component.translatable("gui.extendedae_plus.labeled_wireless.button.set"));
|
||||
this.disconnectBtn = new StateImageButton(secondColX, secondRowY, BTN_W, BTN_H, BTN_U, BTN_V, 2, 177, 2, 195, TEX, TEX_W, TEX_H,
|
||||
b -> sendDisconnect(), Component.translatable("gui.extendedae_plus.labeled_wireless.button.refresh"));
|
||||
|
||||
this.addRenderableWidget(this.newBtn);
|
||||
this.addRenderableWidget(this.deleteBtn);
|
||||
this.addRenderableWidget(this.setBtn);
|
||||
this.addRenderableWidget(this.disconnectBtn);
|
||||
|
||||
requestList();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void render(GuiGraphics gfx, int mouseX, int mouseY, float partialTicks) {
|
||||
this.renderBackground(gfx);
|
||||
super.render(gfx, mouseX, mouseY, partialTicks);
|
||||
drawAllButtonText(gfx);
|
||||
this.renderTooltip(gfx, mouseX, mouseY);
|
||||
if (this.searchBox != null) {
|
||||
this.searchBox.render(gfx, mouseX, mouseY, partialTicks);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void renderLabels(GuiGraphics gfx, int mouseX, int mouseY) {
|
||||
float titleScale = getTitleScale();
|
||||
var pose = gfx.pose();
|
||||
pose.pushPose();
|
||||
pose.translate(8, 8, 0);
|
||||
pose.scale(titleScale, titleScale, 1.0f);
|
||||
gfx.drawString(this.font, this.title, 0, 0, 0x404040, false);
|
||||
pose.popPose();
|
||||
|
||||
pose.pushPose();
|
||||
pose.translate(134, 8, 0);
|
||||
pose.scale(titleScale, titleScale, 1.0f);
|
||||
gfx.drawString(this.font, Component.translatable("gui.extendedae_plus.labeled_wireless.info"), 0, 0, 0x404040, false);
|
||||
pose.popPose();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void renderBg(GuiGraphics gfx, float partialTicks, int mouseX, int mouseY) {
|
||||
RenderSystem.setShaderColor(1f, 1f, 1f, 1f);
|
||||
gfx.blit(TEX, this.leftPos, this.topPos, 0, 0, this.imageWidth, this.imageHeight);
|
||||
|
||||
// 占位绘制:列表和信息区内的内容框线
|
||||
// 标签列表区域
|
||||
gfx.fill(this.leftPos + 9, this.topPos + 27, this.leftPos + 118 + 1, this.topPos + 140 + 1, 0x20FFFFFF);
|
||||
// 滚动条区域
|
||||
gfx.fill(this.leftPos + 123, this.topPos + 21, this.leftPos + 128 + 1, this.topPos + 141 + 1, 0x20000000);
|
||||
// 当前收发器信息区域
|
||||
gfx.fill(this.leftPos + 134, this.topPos + 41, this.leftPos + 249 + 1, this.topPos + 92 + 1, 0x10FFFFFF);
|
||||
|
||||
renderList(gfx);
|
||||
renderScrollBar(gfx);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean mouseClicked(double mouseX, double mouseY, int button) {
|
||||
if (this.searchBox != null && this.searchBox.mouseClicked(mouseX, mouseY, button)) {
|
||||
setFocused(this.searchBox);
|
||||
return true;
|
||||
}
|
||||
if (isMouseInList(mouseX, mouseY)) {
|
||||
int localY = (int) mouseY - (this.topPos + LIST_Y);
|
||||
int row = localY / ROW_H;
|
||||
int idx = scrollOffset + row;
|
||||
if (idx >= 0 && idx < filtered.size()) {
|
||||
selectedIndex = idx;
|
||||
lastSelectedLabel = filtered.get(idx).label();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
if (isMouseInScrollbar(mouseX, mouseY)) {
|
||||
updateScrollByMouse((int) mouseY);
|
||||
return true;
|
||||
}
|
||||
return super.mouseClicked(mouseX, mouseY, button);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean keyPressed(int keyCode, int scanCode, int modifiers) {
|
||||
if (this.searchBox != null && this.searchBox.keyPressed(keyCode, scanCode, modifiers)) {
|
||||
return true;
|
||||
}
|
||||
return super.keyPressed(keyCode, scanCode, modifiers);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean mouseScrolled(double mouseX, double mouseY, double delta) {
|
||||
if (isMouseInList(mouseX, mouseY) || isMouseInScrollbar(mouseX, mouseY)) {
|
||||
int maxOffset = Math.max(0, filtered.size() - VISIBLE_ROWS);
|
||||
scrollOffset = Math.max(0, Math.min(maxOffset, scrollOffset - (int) Math.signum(delta)));
|
||||
return true;
|
||||
}
|
||||
return super.mouseScrolled(mouseX, mouseY, delta);
|
||||
}
|
||||
|
||||
private void renderList(GuiGraphics gfx) {
|
||||
int baseX = this.leftPos + LIST_X;
|
||||
int baseY = this.topPos + LIST_Y;
|
||||
for (int row = 0; row < VISIBLE_ROWS; row++) {
|
||||
int idx = scrollOffset + row;
|
||||
if (idx >= filtered.size()) break;
|
||||
int y = baseY + row * ROW_H;
|
||||
if (idx == selectedIndex) {
|
||||
gfx.fill(baseX, y, baseX + LIST_W, y + ROW_H, 0x40FFFFFF);
|
||||
}
|
||||
LabelEntry e = filtered.get(idx);
|
||||
String text = this.font.plainSubstrByWidth(e.label(), LIST_W - 2);
|
||||
int ty = y + (ROW_H - this.font.lineHeight) / 2;
|
||||
gfx.drawString(this.font, text, baseX + 2, ty, 0x404040, false);
|
||||
}
|
||||
|
||||
// 信息显示
|
||||
int infoX = this.leftPos + 134;
|
||||
int infoY = this.topPos + 41;
|
||||
float infoScale = getInfoScale();
|
||||
String labelLine = Component.translatable("gui.extendedae_plus.labeled_wireless.current_label").getString() + ": " + (currentLabel == null || currentLabel.isEmpty() ? "-" : currentLabel);
|
||||
String ownerLine = Component.translatable("gui.extendedae_plus.labeled_wireless.current_owner").getString() + ": " + (currentOwner == null || currentOwner.isEmpty() ? Component.translatable("extendedae_plus.jade.owner.public").getString() : currentOwner);
|
||||
String onlineLine = Component.translatable("gui.extendedae_plus.labeled_wireless.online_count").getString() + ": " + onlineCount;
|
||||
Component channelComp = maxChannels <= 0
|
||||
? Component.translatable("extendedae_plus.jade.channels", usedChannels)
|
||||
: Component.translatable("extendedae_plus.jade.channels_of", usedChannels, maxChannels);
|
||||
drawInfoLine(gfx, labelLine, infoX, infoY, infoScale);
|
||||
drawInfoLine(gfx, ownerLine, infoX, infoY + 12, infoScale);
|
||||
drawInfoLine(gfx, onlineLine, infoX, infoY + 24, infoScale);
|
||||
drawInfoLine(gfx, channelComp.getString(), infoX, infoY + 36, infoScale);
|
||||
}
|
||||
|
||||
private void renderScrollBar(GuiGraphics gfx) {
|
||||
int total = filtered.size();
|
||||
if (total <= VISIBLE_ROWS) {
|
||||
// 画静态条
|
||||
gfx.fill(this.leftPos + SCROLL_X, this.topPos + SCROLL_Y, this.leftPos + SCROLL_X + SCROLL_W, this.topPos + SCROLL_Y + SCROLL_H, 0x20000000);
|
||||
return;
|
||||
}
|
||||
int maxOffset = total - VISIBLE_ROWS;
|
||||
int trackX1 = this.leftPos + SCROLL_X;
|
||||
int trackY1 = this.topPos + SCROLL_Y;
|
||||
int trackY2 = trackY1 + SCROLL_H;
|
||||
gfx.fill(trackX1, trackY1, trackX1 + SCROLL_W, trackY2, 0x20000000);
|
||||
int knobH = Math.max(10, (int) ((double) VISIBLE_ROWS / total * SCROLL_H));
|
||||
int knobY = trackY1 + (int) ((SCROLL_H - knobH) * (scrollOffset / (double) maxOffset));
|
||||
gfx.fill(trackX1, knobY, trackX1 + SCROLL_W, knobY + knobH, 0x80FFFFFF);
|
||||
}
|
||||
|
||||
private boolean isMouseInList(double mouseX, double mouseY) {
|
||||
return mouseX >= this.leftPos + LIST_X && mouseX < this.leftPos + LIST_X + LIST_W
|
||||
&& mouseY >= this.topPos + LIST_Y && mouseY < this.topPos + LIST_Y + LIST_H;
|
||||
}
|
||||
|
||||
private boolean isMouseInScrollbar(double mouseX, double mouseY) {
|
||||
return mouseX >= this.leftPos + SCROLL_X && mouseX < this.leftPos + SCROLL_X + SCROLL_W
|
||||
&& mouseY >= this.topPos + SCROLL_Y && mouseY < this.topPos + SCROLL_Y + SCROLL_H;
|
||||
}
|
||||
|
||||
private void updateScrollByMouse(int mouseY) {
|
||||
int total = filtered.size();
|
||||
if (total <= VISIBLE_ROWS) return;
|
||||
int maxOffset = total - VISIBLE_ROWS;
|
||||
int relativeY = mouseY - (this.topPos + SCROLL_Y);
|
||||
relativeY = Math.max(0, Math.min(SCROLL_H, relativeY));
|
||||
int knobH = Math.max(10, (int) ((double) VISIBLE_ROWS / total * SCROLL_H));
|
||||
double ratio = (relativeY - knobH / 2.0) / (double) (SCROLL_H - knobH);
|
||||
ratio = Math.max(0.0, Math.min(1.0, ratio));
|
||||
scrollOffset = (int) Math.round(ratio * maxOffset);
|
||||
}
|
||||
|
||||
private void applyFilter() {
|
||||
String prevSelected = lastSelectedLabel;
|
||||
String q = searchBox.getValue() == null ? "" : searchBox.getValue().trim();
|
||||
filtered.clear();
|
||||
if (q.isEmpty()) {
|
||||
filtered.addAll(entries);
|
||||
} else {
|
||||
for (LabelEntry e : entries) {
|
||||
if (e.label().contains(q)) {
|
||||
filtered.add(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
scrollOffset = 0;
|
||||
selectedIndex = -1;
|
||||
if (prevSelected != null && !prevSelected.isEmpty()) {
|
||||
for (int i = 0; i < filtered.size(); i++) {
|
||||
if (filtered.get(i).label().equals(prevSelected)) {
|
||||
selectedIndex = i;
|
||||
ensureSelectionVisible();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void requestList() {
|
||||
ModNetwork.CHANNEL.sendToServer(new LabelNetworkListC2SPacket(bePos));
|
||||
}
|
||||
|
||||
private void sendSet(String label) {
|
||||
if (label == null) label = "";
|
||||
ModNetwork.CHANNEL.sendToServer(new LabelNetworkActionC2SPacket(bePos, label, LabelNetworkActionC2SPacket.Action.SET));
|
||||
this.lastSelectedLabel = label;
|
||||
this.searchBox.setValue("");
|
||||
requestList();
|
||||
}
|
||||
|
||||
private void sendDelete() {
|
||||
String label = getSelectedLabel();
|
||||
if (label == null || label.isEmpty()) {
|
||||
label = searchBox.getValue();
|
||||
}
|
||||
if (label == null) label = "";
|
||||
ModNetwork.CHANNEL.sendToServer(new LabelNetworkActionC2SPacket(bePos, label, LabelNetworkActionC2SPacket.Action.DELETE));
|
||||
this.lastSelectedLabel = "";
|
||||
requestList();
|
||||
}
|
||||
|
||||
private void sendDisconnect() {
|
||||
ModNetwork.CHANNEL.sendToServer(new LabelNetworkActionC2SPacket(bePos, "", LabelNetworkActionC2SPacket.Action.DISCONNECT));
|
||||
this.lastSelectedLabel = "";
|
||||
requestList();
|
||||
}
|
||||
|
||||
private String getSelectedLabel() {
|
||||
if (selectedIndex >= 0 && selectedIndex < filtered.size()) {
|
||||
return filtered.get(selectedIndex).label();
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
public void updateList(List<LabelNetworkRegistry.LabelNetworkSnapshot> list, String currentLabel, String ownerName, int usedChannels, int maxChannels, int onlineCount) {
|
||||
String prevSelected = getSelectedLabel();
|
||||
this.entries.clear();
|
||||
for (LabelNetworkRegistry.LabelNetworkSnapshot s : list) {
|
||||
this.entries.add(new LabelEntry(s.label(), s.channel()));
|
||||
}
|
||||
this.currentLabel = currentLabel == null ? "" : currentLabel;
|
||||
this.currentOwner = ownerName == null ? "" : ownerName;
|
||||
this.onlineCount = onlineCount;
|
||||
this.usedChannels = usedChannels;
|
||||
this.maxChannels = maxChannels;
|
||||
|
||||
if (prevSelected != null && !prevSelected.isEmpty()) {
|
||||
this.lastSelectedLabel = prevSelected;
|
||||
} else if (this.currentLabel != null && !this.currentLabel.isEmpty()) {
|
||||
this.lastSelectedLabel = this.currentLabel;
|
||||
} else {
|
||||
this.lastSelectedLabel = "";
|
||||
}
|
||||
applyFilter();
|
||||
}
|
||||
|
||||
public boolean isFor(BlockPos pos) {
|
||||
return this.bePos.equals(pos);
|
||||
}
|
||||
|
||||
private record LabelEntry(String label, long channel) {}
|
||||
|
||||
private void drawAllButtonText(GuiGraphics gfx) {
|
||||
// 按钮文本(24px 内居中,避免溢出)。放在 super.render 之后,确保绘制在按钮纹理之上。
|
||||
int startX = this.leftPos + 145;
|
||||
int startY = this.topPos + 101;
|
||||
int hGap = 30;
|
||||
int vGap = 8;
|
||||
int secondColX = startX + BTN_W + hGap;
|
||||
int secondRowY = startY + BTN_H + vGap;
|
||||
|
||||
drawButtonText(gfx, Component.translatable("gui.extendedae_plus.labeled_wireless.button.new"), startX, startY);
|
||||
drawButtonText(gfx, Component.translatable("gui.extendedae_plus.labeled_wireless.button.delete"), secondColX, startY);
|
||||
drawButtonText(gfx, Component.translatable("gui.extendedae_plus.labeled_wireless.button.set"), startX, secondRowY);
|
||||
drawButtonText(gfx, Component.translatable("gui.extendedae_plus.labeled_wireless.button.refresh"), secondColX, secondRowY);
|
||||
}
|
||||
|
||||
private void drawButtonText(GuiGraphics gfx, Component text, int x, int y) {
|
||||
String s = this.font.plainSubstrByWidth(text.getString(), BTN_W - 4);
|
||||
int tx = x + (BTN_W - this.font.width(s)) / 2;
|
||||
int ty = y + (BTN_H - this.font.lineHeight) / 2 + 1;
|
||||
gfx.drawString(this.font, s, tx, ty, 0xFFFFFF, false);
|
||||
}
|
||||
|
||||
private void ensureSelectionVisible() {
|
||||
if (selectedIndex < 0) return;
|
||||
int maxOffset = Math.max(0, filtered.size() - VISIBLE_ROWS);
|
||||
int targetRow = selectedIndex;
|
||||
if (targetRow < scrollOffset) {
|
||||
scrollOffset = targetRow;
|
||||
} else if (targetRow >= scrollOffset + VISIBLE_ROWS) {
|
||||
scrollOffset = Math.min(maxOffset, targetRow - VISIBLE_ROWS + 1);
|
||||
}
|
||||
}
|
||||
|
||||
private String trimInfo(String text, float scale) {
|
||||
if (text == null) return "";
|
||||
int maxWidth = (int) (INFO_MAX_WIDTH / Math.max(0.0001f, scale));
|
||||
return this.font.plainSubstrByWidth(text, maxWidth);
|
||||
}
|
||||
|
||||
private void drawInfoLine(GuiGraphics gfx, String text, int x, int y, float scale) {
|
||||
String trimmed = trimInfo(text, scale);
|
||||
var pose = gfx.pose();
|
||||
pose.pushPose();
|
||||
pose.translate(x, y, 0);
|
||||
pose.scale(scale, scale, 1.0f);
|
||||
gfx.drawString(this.font, trimmed, 0, 0, 0x404040, false);
|
||||
pose.popPose();
|
||||
}
|
||||
|
||||
private boolean isEnglish() {
|
||||
Minecraft mc = Minecraft.getInstance();
|
||||
var lang = mc.getLanguageManager().getSelected();
|
||||
return lang != null && lang.equalsIgnoreCase("en_us");
|
||||
}
|
||||
|
||||
private float getInfoScale() {
|
||||
return isEnglish() ? 0.75f : 1.0f;
|
||||
}
|
||||
|
||||
private float getTitleScale() {
|
||||
return isEnglish() ? 0.75f : 1.0f;
|
||||
}
|
||||
|
||||
private static class StateImageButton extends ImageButton {
|
||||
private final ResourceLocation tex;
|
||||
private final int texW;
|
||||
private final int texH;
|
||||
private final int baseU;
|
||||
private final int baseV;
|
||||
private final int hoverU;
|
||||
private final int hoverV;
|
||||
private final int pressU;
|
||||
private final int pressV;
|
||||
private boolean pressedVisual = false;
|
||||
|
||||
public StateImageButton(int x, int y, int w, int h, int baseU, int baseV, int hoverU, int hoverV, int pressU, int pressV,
|
||||
ResourceLocation tex, int texW, int texH, OnPress onPress, Component tooltip) {
|
||||
super(x, y, w, h, baseU, baseV, 0, tex, texW, texH, onPress, tooltip);
|
||||
this.tex = tex;
|
||||
this.texW = texW;
|
||||
this.texH = texH;
|
||||
this.baseU = baseU;
|
||||
this.baseV = baseV;
|
||||
this.hoverU = hoverU;
|
||||
this.hoverV = hoverV;
|
||||
this.pressU = pressU;
|
||||
this.pressV = pressV;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void renderWidget(GuiGraphics gfx, int mouseX, int mouseY, float partialTicks) {
|
||||
boolean hovered = this.isMouseOver(mouseX, mouseY);
|
||||
boolean pressed = pressedVisual || (hovered && GLFW.glfwGetMouseButton(Minecraft.getInstance().getWindow().getWindow(), GLFW.GLFW_MOUSE_BUTTON_1) == GLFW.GLFW_PRESS);
|
||||
int u = baseU;
|
||||
int v = baseV;
|
||||
if (pressed) {
|
||||
u = pressU;
|
||||
v = pressV;
|
||||
} else if (hovered) {
|
||||
u = hoverU;
|
||||
v = hoverV;
|
||||
}
|
||||
gfx.blit(tex, this.getX(), this.getY(), u, v, this.width, this.height, texW, texH);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onClick(double mouseX, double mouseY) {
|
||||
this.pressedVisual = true;
|
||||
super.onClick(mouseX, mouseY);
|
||||
this.setFocused(false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onRelease(double mouseX, double mouseY) {
|
||||
this.pressedVisual = false;
|
||||
super.onRelease(mouseX, mouseY);
|
||||
this.setFocused(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -2,19 +2,13 @@ package com.extendedae_plus.client.screen;
|
|||
|
||||
import com.extendedae_plus.init.ModNetwork;
|
||||
import com.extendedae_plus.network.UploadEncodedPatternToProviderC2SPacket;
|
||||
import com.extendedae_plus.network.pattern.CancelPendingPatternC2SPacket;
|
||||
import com.extendedae_plus.util.uploadPattern.RecipeTypeNameConfig;
|
||||
import com.google.gson.*;
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.client.gui.components.Button;
|
||||
import net.minecraft.client.gui.components.EditBox;
|
||||
import net.minecraft.client.gui.screens.Screen;
|
||||
import net.minecraft.network.chat.Component;
|
||||
import net.minecraftforge.fml.loading.FMLPaths;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.*;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
|
@ -44,30 +38,10 @@ public class ProviderSelectScreen extends Screen {
|
|||
private final List<Integer> fTotalSlots = new ArrayList<>();
|
||||
private final List<Integer> fCount = new ArrayList<>();
|
||||
|
||||
// 置顶的供应器名称集合(静态变量,持久化到配置文件)
|
||||
private static final Set<String> pinnedProviders = new HashSet<>();
|
||||
private static final String PINNED_CONFIG_PATH = "extendedae_plus/pinned_providers.json";
|
||||
private static final String AUTO_UPLOAD_UNIQUE_MATCH_KEY = "auto_upload_unique_match";
|
||||
private static final String SHOW_PROCESSING_BUTTONS_KEY = "show_processing_buttons";
|
||||
private static final Gson GSON = new GsonBuilder().setPrettyPrinting().disableHtmlEscaping().create();
|
||||
private static boolean autoUploadUniqueMatchEnabled = true;
|
||||
private static boolean showProcessingButtonsEnabled = true;
|
||||
|
||||
// 静态初始化块:加载置顶配置
|
||||
static {
|
||||
try {
|
||||
loadPinnedProviders();
|
||||
} catch (Throwable t) {
|
||||
// 加载失败时静默处理,不影响界面使用
|
||||
}
|
||||
}
|
||||
|
||||
// 搜索框
|
||||
private EditBox searchBox;
|
||||
// 中文名输入框(用于添加映射)
|
||||
private EditBox cnInput;
|
||||
private Button processingButtonsToggleButton;
|
||||
private Button autoUploadToggleButton;
|
||||
private String query = "";
|
||||
// 翻页按钮
|
||||
private Button prevButton;
|
||||
|
|
@ -84,10 +58,6 @@ public class ProviderSelectScreen extends Screen {
|
|||
// 缓存 Component JSON 解析
|
||||
private static final Map<String, String> componentCache = new HashMap<>();
|
||||
private String lastLanguage = ""; // 当前语言版本
|
||||
private boolean autoUploadRequestedFromPresetSearch = false;
|
||||
private boolean autoUploadAttempted = false;
|
||||
private int lastExactMatchCount = 0;
|
||||
private boolean lastFilterUsedFallback = false;
|
||||
|
||||
public ProviderSelectScreen(Screen parent, List<Long> ids, List<String> names, List<Integer> emptySlots) {
|
||||
super(Component.translatable("extendedae_plus.screen.choose_provider.title"));
|
||||
|
|
@ -95,12 +65,13 @@ public class ProviderSelectScreen extends Screen {
|
|||
this.ids = ids;
|
||||
this.names = names;
|
||||
this.emptySlots = emptySlots;
|
||||
// 如果有来自最近一次写样板流程的预设搜索词,则作为初始查询
|
||||
// 如果有来自 JEI 的最近处理名称,则作为初始查询
|
||||
try {
|
||||
String recent = RecipeTypeNameConfig.consumeLastProviderSearchKey();
|
||||
String recent = RecipeTypeNameConfig.lastProcessingName;
|
||||
if (recent != null && !recent.isBlank()) {
|
||||
this.query = recent;
|
||||
this.autoUploadRequestedFromPresetSearch = true;
|
||||
// 用后即清空,避免污染下次
|
||||
RecipeTypeNameConfig.lastProcessingName = null;
|
||||
}
|
||||
} catch (Throwable ignored) {}
|
||||
buildGroups();
|
||||
|
|
@ -174,55 +145,41 @@ public class ProviderSelectScreen extends Screen {
|
|||
int totalWidth = btnWidth2 + btnGap + inputWidth + btnGap + btnWidth2 * 2 + btnGap + btnWidth2;
|
||||
int startX = centerX - totalWidth / 2;
|
||||
|
||||
int toggleX = startX + btnWidth2 + btnGap + inputWidth + btnGap + btnWidth2 + btnGap;
|
||||
int toggleY = navY + 30;
|
||||
|
||||
this.processingButtonsToggleButton = Button.builder(buildProcessingButtonsToggleLabel(), b -> toggleProcessingButtons())
|
||||
.bounds(toggleX, toggleY, btnWidth2, 20)
|
||||
.build();
|
||||
this.addRenderableWidget(this.processingButtonsToggleButton);
|
||||
|
||||
this.autoUploadToggleButton = Button.builder(buildAutoUploadToggleLabel(), b -> toggleAutoUploadUniqueMatch())
|
||||
.bounds(toggleX + btnWidth2 + btnGap, toggleY, btnWidth2, 20)
|
||||
.build();
|
||||
this.addRenderableWidget(this.autoUploadToggleButton);
|
||||
|
||||
// 重载映射按钮
|
||||
Button reload = Button.builder(Component.translatable("extendedae_plus.screen.reload_mapping"), b -> reloadMapping())
|
||||
.bounds(startX, navY + 55, btnWidth2, 20)
|
||||
.bounds(startX, navY + 30, btnWidth2, 20)
|
||||
.build();
|
||||
this.addRenderableWidget(reload);
|
||||
|
||||
// 中文名输入框(用于新增映射的值)
|
||||
if (cnInput == null) {
|
||||
cnInput = new EditBox(this.font, startX + btnWidth2 + btnGap, navY + 55, inputWidth, 20, Component.translatable("extendedae_plus.screen.upload.name"));
|
||||
cnInput = new EditBox(this.font, startX + btnWidth2 + btnGap, navY + 30, inputWidth, 20, Component.translatable("extendedae_plus.screen.upload.name"));
|
||||
} else {
|
||||
cnInput.setX(startX + btnWidth2 + btnGap);
|
||||
cnInput.setY(navY + 55);
|
||||
cnInput.setY(navY + 30);
|
||||
cnInput.setWidth(inputWidth);
|
||||
}
|
||||
this.addRenderableWidget(cnInput);
|
||||
|
||||
// 关闭按钮
|
||||
Button close = Button.builder(Component.translatable("gui.cancel"), b -> onClose())
|
||||
.bounds(startX + btnWidth2 + btnGap + inputWidth + btnGap, navY + 55, btnWidth2, 20)
|
||||
.bounds(startX + btnWidth2 + btnGap + inputWidth + btnGap, navY + 30, btnWidth2, 20)
|
||||
.build();
|
||||
this.addRenderableWidget(close);
|
||||
|
||||
// 添加映射按钮(使用当前搜索关键字 -> 中文)
|
||||
Button addMap = Button.builder(Component.translatable("extendedae_plus.screen.add_mapping"), b -> addMappingFromUI())
|
||||
.bounds(startX + btnWidth2 + btnGap + inputWidth + btnGap + btnWidth2 + btnGap, navY + 55, btnWidth2, 20)
|
||||
.bounds(startX + btnWidth2 + btnGap + inputWidth + btnGap + btnWidth2 + btnGap, navY + 30, btnWidth2, 20)
|
||||
.build();
|
||||
this.addRenderableWidget(addMap);
|
||||
|
||||
// 删除映射按钮(按中文值精确匹配删除)按钮
|
||||
Button delByCn = Button.builder(Component.translatable("extendedae_plus.screen.delete_mapping"), b -> deleteMappingByCnFromUI())
|
||||
.bounds(startX + btnWidth2 + btnGap + inputWidth + btnGap + btnWidth2 * 2 + btnGap * 2, navY + 55, btnWidth2, 20)
|
||||
.bounds(startX + btnWidth2 + btnGap + inputWidth + btnGap + btnWidth2 * 2 + btnGap * 2, navY + 30, btnWidth2, 20)
|
||||
.build();
|
||||
this.addRenderableWidget(delByCn);
|
||||
|
||||
refreshButtons(); // 初始化完成后刷新按钮状态
|
||||
tryAutoUploadIfUniqueMatch();
|
||||
}
|
||||
|
||||
private void changePage(int delta) {
|
||||
|
|
@ -263,42 +220,6 @@ public class ProviderSelectScreen extends Screen {
|
|||
}
|
||||
}
|
||||
|
||||
private Component buildAutoUploadToggleLabel() {
|
||||
String stateKey = autoUploadUniqueMatchEnabled
|
||||
? "config.extendedae_plus.option.state_on"
|
||||
: "config.extendedae_plus.option.state_off";
|
||||
return Component.translatable("extendedae_plus.screen.auto_upload_unique_match",
|
||||
Component.translatable(stateKey));
|
||||
}
|
||||
|
||||
private Component buildProcessingButtonsToggleLabel() {
|
||||
String stateKey = showProcessingButtonsEnabled
|
||||
? "config.extendedae_plus.option.state_on"
|
||||
: "config.extendedae_plus.option.state_off";
|
||||
return Component.translatable("extendedae_plus.screen.show_processing_buttons",
|
||||
Component.translatable(stateKey));
|
||||
}
|
||||
|
||||
public static boolean isProcessingButtonsEnabled() {
|
||||
return showProcessingButtonsEnabled;
|
||||
}
|
||||
|
||||
private void toggleProcessingButtons() {
|
||||
showProcessingButtonsEnabled = !showProcessingButtonsEnabled;
|
||||
savePinnedProviders();
|
||||
if (this.processingButtonsToggleButton != null) {
|
||||
this.processingButtonsToggleButton.setMessage(buildProcessingButtonsToggleLabel());
|
||||
}
|
||||
}
|
||||
|
||||
private void toggleAutoUploadUniqueMatch() {
|
||||
autoUploadUniqueMatchEnabled = !autoUploadUniqueMatchEnabled;
|
||||
savePinnedProviders();
|
||||
if (this.autoUploadToggleButton != null) {
|
||||
this.autoUploadToggleButton.setMessage(buildAutoUploadToggleLabel());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 将服务器发送的名称(可能是 Component JSON)反序列化为本地化文本
|
||||
*/
|
||||
|
|
@ -324,28 +245,19 @@ public class ProviderSelectScreen extends Screen {
|
|||
int totalSlots = fTotalSlots.get(idx);
|
||||
int count = fCount.get(idx);
|
||||
|
||||
// 如果是置顶条目,在最左侧添加星星标志
|
||||
String prefix = pinnedProviders.contains(name) ? "★ " : "";
|
||||
|
||||
// 不显示具体 id,显示合并统计:名称(总空位)x数量
|
||||
return prefix + name + " (" + totalSlots + ") x" + count;
|
||||
return name + " (" + totalSlots + ") x" + count;
|
||||
}
|
||||
|
||||
private void onChoose(int idx) {
|
||||
onChoose(idx, false);
|
||||
}
|
||||
|
||||
private void onChoose(int idx, boolean showStatusMessage) {
|
||||
if (idx < 0 || idx >= fIds.size()) return;
|
||||
long providerId = fIds.get(idx);
|
||||
String providerName = fNames.get(idx);
|
||||
ModNetwork.CHANNEL.sendToServer(new UploadEncodedPatternToProviderC2SPacket(providerId, showStatusMessage, providerName));
|
||||
ModNetwork.CHANNEL.sendToServer(new UploadEncodedPatternToProviderC2SPacket(providerId));
|
||||
this.onClose();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onClose() {
|
||||
ModNetwork.CHANNEL.sendToServer(CancelPendingPatternC2SPacket.INSTANCE);
|
||||
Minecraft.getInstance().setScreen(parent);
|
||||
}
|
||||
|
||||
|
|
@ -387,8 +299,6 @@ public class ProviderSelectScreen extends Screen {
|
|||
fNames.clear();
|
||||
fTotalSlots.clear();
|
||||
fCount.clear();
|
||||
lastExactMatchCount = 0;
|
||||
lastFilterUsedFallback = false;
|
||||
String q = query == null ? "" : query.trim();
|
||||
String qLower = q.toLowerCase(Locale.ROOT);
|
||||
|
||||
|
|
@ -399,12 +309,10 @@ public class ProviderSelectScreen extends Screen {
|
|||
fNames.add(name);
|
||||
fTotalSlots.add(gTotalSlots.get(i));
|
||||
fCount.add(gCount.get(i));
|
||||
lastExactMatchCount++;
|
||||
}
|
||||
}
|
||||
// 若查询不为空但没有任何匹配,则回退为显示全部,避免“空列表”误导用户
|
||||
if (!q.isEmpty() && fIds.isEmpty()) {
|
||||
lastFilterUsedFallback = true;
|
||||
for (int i = 0; i < gIds.size(); i++) {
|
||||
fIds.add(gIds.get(i));
|
||||
fNames.add(gNames.get(i));
|
||||
|
|
@ -414,22 +322,9 @@ public class ProviderSelectScreen extends Screen {
|
|||
}
|
||||
|
||||
// 对 fNames 进行自然排序,同时同步其它列表
|
||||
// 置顶的条目排在前面,然后按自然排序
|
||||
List<Integer> indices = new ArrayList<>();
|
||||
for (int i = 0; i < fNames.size(); i++) indices.add(i);
|
||||
indices.sort((i1, i2) -> {
|
||||
String name1 = fNames.get(i1);
|
||||
String name2 = fNames.get(i2);
|
||||
boolean pinned1 = pinnedProviders.contains(name1);
|
||||
boolean pinned2 = pinnedProviders.contains(name2);
|
||||
|
||||
// 置顶的排在前面
|
||||
if (pinned1 && !pinned2) return -1;
|
||||
if (!pinned1 && pinned2) return 1;
|
||||
|
||||
// 都置顶或都不置顶,按自然排序
|
||||
return compareNatural(name1, name2);
|
||||
});
|
||||
indices.sort((i1, i2) -> compareNatural(fNames.get(i1), fNames.get(i2)));
|
||||
|
||||
List<Long> sortedIds = new ArrayList<>();
|
||||
List<String> sortedNames = new ArrayList<>();
|
||||
|
|
@ -453,17 +348,6 @@ public class ProviderSelectScreen extends Screen {
|
|||
fCount.addAll(sortedCount);
|
||||
}
|
||||
|
||||
private void tryAutoUploadIfUniqueMatch() {
|
||||
if (!autoUploadUniqueMatchEnabled || !autoUploadRequestedFromPresetSearch || autoUploadAttempted) {
|
||||
return;
|
||||
}
|
||||
autoUploadAttempted = true;
|
||||
if (query == null || query.isBlank() || lastFilterUsedFallback || lastExactMatchCount != 1 || fIds.size() != 1) {
|
||||
return;
|
||||
}
|
||||
onChoose(0, true);
|
||||
}
|
||||
|
||||
// 优先使用 JEC 的拼音匹配,否则回退到大小写不敏感子串匹配
|
||||
private static Boolean JEC_AVAILABLE = null;
|
||||
private static java.lang.reflect.Method JEC_CONTAINS = null;
|
||||
|
|
@ -555,27 +439,6 @@ public class ProviderSelectScreen extends Screen {
|
|||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// 右键点击条目按钮时,切换置顶状态
|
||||
if (button == 1) {
|
||||
for (int i = 0; i < entryButtons.size(); i++) {
|
||||
Button btn = entryButtons.get(i);
|
||||
if (btn.visible && btn.active) {
|
||||
int x = btn.getX();
|
||||
int y = btn.getY();
|
||||
int w = btn.getWidth();
|
||||
int h = btn.getHeight();
|
||||
if (mouseX >= x && mouseX <= x + w && mouseY >= y && mouseY <= y + h) {
|
||||
int actualIdx = buttonIndexMap[i];
|
||||
if (actualIdx >= 0 && actualIdx < fNames.size()) {
|
||||
togglePin(actualIdx);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return super.mouseClicked(mouseX, mouseY, button);
|
||||
}
|
||||
|
||||
|
|
@ -636,7 +499,7 @@ public class ProviderSelectScreen extends Screen {
|
|||
}
|
||||
int removed = RecipeTypeNameConfig.removeMappingsByCnValue(val);
|
||||
if (removed > 0) {
|
||||
sendPlayerMessage(Component.translatable("extendedae_plus.screen.upload.mapping_deleted",val,removed));
|
||||
sendPlayerMessage(Component.translatable("extendedae_plus.screen.upload.mapping_deleted", removed, val));
|
||||
applyFilter();
|
||||
page = 0;
|
||||
refreshButtons();
|
||||
|
|
@ -645,90 +508,6 @@ public class ProviderSelectScreen extends Screen {
|
|||
}
|
||||
}
|
||||
|
||||
// 切换供应器的置顶状态
|
||||
private void togglePin(int idx) {
|
||||
if (idx < 0 || idx >= fNames.size()) return;
|
||||
String name = fNames.get(idx);
|
||||
|
||||
if (pinnedProviders.contains(name)) {
|
||||
pinnedProviders.remove(name);
|
||||
} else {
|
||||
pinnedProviders.add(name);
|
||||
}
|
||||
|
||||
// 保存到配置文件
|
||||
savePinnedProviders();
|
||||
|
||||
// 重新应用过滤和排序
|
||||
applyFilter();
|
||||
refreshButtons();
|
||||
}
|
||||
|
||||
/**
|
||||
* 从配置文件加载置顶的供应器名称列表
|
||||
*/
|
||||
private static synchronized void loadPinnedProviders() {
|
||||
try {
|
||||
Path cfgPath = FMLPaths.CONFIGDIR.get().resolve(PINNED_CONFIG_PATH);
|
||||
if (!Files.exists(cfgPath)) {
|
||||
return; // 文件不存在时不做处理
|
||||
}
|
||||
|
||||
String json = Files.readString(cfgPath);
|
||||
JsonObject obj = GSON.fromJson(json, JsonObject.class);
|
||||
if (obj == null) return;
|
||||
|
||||
JsonElement pinnedElement = obj.get("pinned");
|
||||
if (pinnedElement != null && pinnedElement.isJsonArray()) {
|
||||
JsonArray arr = pinnedElement.getAsJsonArray();
|
||||
pinnedProviders.clear();
|
||||
for (JsonElement elem : arr) {
|
||||
if (elem.isJsonPrimitive()) {
|
||||
String name = elem.getAsString();
|
||||
if (name != null && !name.isBlank()) {
|
||||
pinnedProviders.add(name);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
JsonElement autoUploadElement = obj.get(AUTO_UPLOAD_UNIQUE_MATCH_KEY);
|
||||
if (autoUploadElement != null && autoUploadElement.isJsonPrimitive()) {
|
||||
autoUploadUniqueMatchEnabled = autoUploadElement.getAsBoolean();
|
||||
}
|
||||
|
||||
JsonElement showProcessingButtonsElement = obj.get(SHOW_PROCESSING_BUTTONS_KEY);
|
||||
if (showProcessingButtonsElement != null && showProcessingButtonsElement.isJsonPrimitive()) {
|
||||
showProcessingButtonsEnabled = showProcessingButtonsElement.getAsBoolean();
|
||||
}
|
||||
} catch (IOException | JsonSyntaxException e) {
|
||||
// 加载失败时静默处理
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 保存置顶的供应器名称列表到配置文件
|
||||
*/
|
||||
private static synchronized void savePinnedProviders() {
|
||||
try {
|
||||
Path cfgPath = FMLPaths.CONFIGDIR.get().resolve(PINNED_CONFIG_PATH);
|
||||
Files.createDirectories(cfgPath.getParent());
|
||||
|
||||
JsonObject obj = new JsonObject();
|
||||
JsonArray arr = new JsonArray();
|
||||
for (String name : pinnedProviders) {
|
||||
arr.add(name);
|
||||
}
|
||||
obj.add("pinned", arr);
|
||||
obj.addProperty(AUTO_UPLOAD_UNIQUE_MATCH_KEY, autoUploadUniqueMatchEnabled);
|
||||
obj.addProperty(SHOW_PROCESSING_BUTTONS_KEY, showProcessingButtonsEnabled);
|
||||
|
||||
Files.writeString(cfgPath, GSON.toJson(obj));
|
||||
} catch (IOException e) {
|
||||
// 保存失败时静默处理
|
||||
}
|
||||
}
|
||||
|
||||
private static class Group {
|
||||
long bestId;
|
||||
int bestSlots;
|
||||
|
|
|
|||
|
|
@ -1,12 +0,0 @@
|
|||
package com.extendedae_plus.compat;
|
||||
|
||||
import appeng.api.networking.IGrid;
|
||||
import appeng.api.networking.IManagedGridNode;
|
||||
|
||||
public interface PatternProviderLogicVirtualCompatBridge {
|
||||
boolean eap$compatIsVirtualCraftingEnabled();
|
||||
|
||||
IGrid eap$compatGetGrid();
|
||||
|
||||
IManagedGridNode eap$compatGetMainNode();
|
||||
}
|
||||
|
|
@ -3,30 +3,13 @@ package com.extendedae_plus.compat;
|
|||
import appeng.api.upgrades.IUpgradeInventory;
|
||||
import net.minecraftforge.fml.ModList;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
/**
|
||||
* 升级卡槽兼容性管理类
|
||||
* 统一管理:
|
||||
* 1. 是否由我们自己提供升级槽
|
||||
* 2. appflux存在时是否复用其升级槽
|
||||
* 3. appflux PatternProviderLogic 的反射访问入口
|
||||
* 检测ExtendedAE-appflux模组是否存在,如果存在则使用其升级卡槽功能
|
||||
* 否则使用我们自己的实现
|
||||
*/
|
||||
public final class UpgradeSlotCompat {
|
||||
public class UpgradeSlotCompat {
|
||||
private static final String APPFLUX_MOD_ID = "appflux";
|
||||
private static final int LOCAL_PATTERN_PROVIDER_UPGRADE_SLOTS = 2;
|
||||
private static final int APPFLUX_PATTERN_PROVIDER_UPGRADE_SLOTS = 2;
|
||||
private static final String[] APPFLUX_UPGRADES_FIELD_NAMES = { "af_upgrades", "af_$upgrades" };
|
||||
private static final String[] APPFLUX_UPGRADES_CHANGED_METHOD_NAMES = { "af_onUpgradesChanged", "af_$onUpgradesChanged" };
|
||||
|
||||
private static Field patternProviderAppfluxUpgradesField;
|
||||
private static boolean patternProviderAppfluxUpgradesFieldResolved;
|
||||
private static Method patternProviderAppfluxUpgradesChangedMethod;
|
||||
private static boolean patternProviderAppfluxUpgradesChangedMethodResolved;
|
||||
|
||||
private UpgradeSlotCompat() {
|
||||
}
|
||||
|
||||
/**
|
||||
* 检测Applied Flux模组是否存在
|
||||
|
|
@ -36,33 +19,13 @@ public final class UpgradeSlotCompat {
|
|||
return ModList.get().isLoaded(APPFLUX_MOD_ID);
|
||||
}
|
||||
|
||||
/**
|
||||
* 是否由我们自己提供升级槽实现。
|
||||
*/
|
||||
public static boolean usesDedicatedUpgradeSlots() {
|
||||
return !isAppfluxPresent();
|
||||
}
|
||||
|
||||
/**
|
||||
* 是否应当复用 appflux 注入到 PatternProviderLogic 上的升级槽。
|
||||
*/
|
||||
public static boolean usesAppfluxUpgradeSlots() {
|
||||
return isAppfluxPresent();
|
||||
}
|
||||
|
||||
/**
|
||||
* 检测是否应该启用我们的升级卡槽功能
|
||||
* @return true如果应该启用,false如果检测到appflux模组存在
|
||||
*/
|
||||
public static boolean shouldEnableUpgradeSlots() {
|
||||
return usesDedicatedUpgradeSlots();
|
||||
}
|
||||
|
||||
/**
|
||||
* 是否需要持久化和管理我们本地创建的升级槽。
|
||||
*/
|
||||
public static boolean shouldManageLocalUpgradeInventory() {
|
||||
return usesDedicatedUpgradeSlots();
|
||||
boolean appfluxExists = isAppfluxPresent();
|
||||
return !appfluxExists;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -74,104 +37,18 @@ public final class UpgradeSlotCompat {
|
|||
return true; // 频道卡功能总是启用,因为appflux没有实现这个功能
|
||||
}
|
||||
|
||||
/**
|
||||
* appflux 存在时,我们仍然需要监听其升级槽变化来驱动额外的兼容逻辑。
|
||||
*/
|
||||
public static boolean shouldListenToAppfluxUpgrades() {
|
||||
return usesAppfluxUpgradeSlots();
|
||||
}
|
||||
|
||||
/**
|
||||
* 检测是否应该在Screen中添加升级面板
|
||||
* @return true如果应该添加,false如果检测到appflux模组存在
|
||||
*/
|
||||
public static boolean shouldAddUpgradePanelToScreen() {
|
||||
return usesDedicatedUpgradeSlots();
|
||||
return shouldEnableUpgradeSlots();
|
||||
}
|
||||
|
||||
public static int getPatternProviderLocalUpgradeSlots() {
|
||||
return LOCAL_PATTERN_PROVIDER_UPGRADE_SLOTS;
|
||||
}
|
||||
|
||||
public static int getPatternProviderAppfluxUpgradeSlots() {
|
||||
return APPFLUX_PATTERN_PROVIDER_UPGRADE_SLOTS;
|
||||
}
|
||||
|
||||
public static IUpgradeInventory getPatternProviderAppfluxUpgrades(Object logicInstance) {
|
||||
Field field = resolvePatternProviderAppfluxUpgradesField(logicInstance.getClass());
|
||||
if (field == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
try {
|
||||
Object value = field.get(logicInstance);
|
||||
return value instanceof IUpgradeInventory inventory ? inventory : null;
|
||||
} catch (IllegalAccessException e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public static boolean setPatternProviderAppfluxUpgrades(Object logicInstance, IUpgradeInventory inventory) {
|
||||
Field field = resolvePatternProviderAppfluxUpgradesField(logicInstance.getClass());
|
||||
if (field == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
try {
|
||||
field.set(logicInstance, inventory);
|
||||
return true;
|
||||
} catch (IllegalAccessException e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public static boolean invokePatternProviderAppfluxUpgradesChanged(Object logicInstance) throws ReflectiveOperationException {
|
||||
Method method = resolvePatternProviderAppfluxUpgradesChangedMethod(logicInstance.getClass());
|
||||
if (method == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
method.invoke(logicInstance);
|
||||
return true;
|
||||
}
|
||||
|
||||
private static Field resolvePatternProviderAppfluxUpgradesField(Class<?> logicClass) {
|
||||
if (!patternProviderAppfluxUpgradesFieldResolved) {
|
||||
patternProviderAppfluxUpgradesField = findField(logicClass, APPFLUX_UPGRADES_FIELD_NAMES);
|
||||
patternProviderAppfluxUpgradesFieldResolved = true;
|
||||
}
|
||||
return patternProviderAppfluxUpgradesField;
|
||||
}
|
||||
|
||||
private static Method resolvePatternProviderAppfluxUpgradesChangedMethod(Class<?> logicClass) {
|
||||
if (!patternProviderAppfluxUpgradesChangedMethodResolved) {
|
||||
patternProviderAppfluxUpgradesChangedMethod = findMethod(logicClass, APPFLUX_UPGRADES_CHANGED_METHOD_NAMES);
|
||||
patternProviderAppfluxUpgradesChangedMethodResolved = true;
|
||||
}
|
||||
return patternProviderAppfluxUpgradesChangedMethod;
|
||||
}
|
||||
|
||||
private static Field findField(Class<?> owner, String[] candidates) {
|
||||
for (String candidate : candidates) {
|
||||
try {
|
||||
Field field = owner.getDeclaredField(candidate);
|
||||
field.setAccessible(true);
|
||||
return field;
|
||||
} catch (NoSuchFieldException ignored) {
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private static Method findMethod(Class<?> owner, String[] candidates) {
|
||||
for (String candidate : candidates) {
|
||||
try {
|
||||
Method method = owner.getDeclaredMethod(candidate);
|
||||
method.setAccessible(true);
|
||||
return method;
|
||||
} catch (NoSuchMethodException ignored) {
|
||||
}
|
||||
}
|
||||
return null;
|
||||
/**
|
||||
* 兼容性升级菜单接口
|
||||
*/
|
||||
public interface IUpgradeableMenuCompat {
|
||||
IUpgradeInventory getCompatUpgrades();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -63,15 +63,6 @@ public final class ModConfig {
|
|||
@Configurable.Synchronized
|
||||
public boolean wirelessCrossDimEnable = true;
|
||||
|
||||
@Configurable
|
||||
@Configurable.Comment(value = {
|
||||
"无线收发器待机能耗",
|
||||
"无线收发器的基础待机能耗(AE/t),同时作用于普通与标签无线收发器"
|
||||
})
|
||||
@Configurable.Synchronized
|
||||
@Configurable.DecimalRange(min = 0, max = Double.MAX_VALUE)
|
||||
public double wirelessTransceiverIdlePower = 100.0;
|
||||
|
||||
@Configurable
|
||||
@Configurable.Comment(value = {
|
||||
"智能倍增时是否对样板供应器轮询分配",
|
||||
|
|
@ -120,6 +111,7 @@ public final class ModConfig {
|
|||
"开启后,将优先尝试从磁盘提取FE能量;反之优先消耗AE网络中的能量"
|
||||
})
|
||||
@Configurable.Synchronized
|
||||
@Configurable.ValueUpdateCallback(method = "onPrioritizeDiskEnergyUpdate")
|
||||
public boolean prioritizeDiskEnergy = true;
|
||||
|
||||
@Configurable
|
||||
|
|
@ -140,6 +132,14 @@ public final class ModConfig {
|
|||
@Configurable.ValueUpdateCallback(method = "onEntityTickerMultipliersUpdate")
|
||||
public String[] entityTickerMultipliers = {};
|
||||
|
||||
@Configurable
|
||||
@Configurable.Comment(value = {
|
||||
"限制合成样板自动上传仅进入分子操纵者",
|
||||
"开启后,合成样板将只自动上传到分子操纵者,不再上传至装配矩阵"
|
||||
})
|
||||
@Configurable.Synchronized
|
||||
public boolean restrictCraftingPatternToMolecular = false;
|
||||
|
||||
private static final ScheduledExecutorService EXECUTOR = Executors.newSingleThreadScheduledExecutor();
|
||||
private static ScheduledFuture<?> pendingPowerTask;
|
||||
private static final Object POWER_LOCK = new Object();
|
||||
|
|
|
|||
|
|
@ -1,66 +0,0 @@
|
|||
package com.extendedae_plus.content.ae2;
|
||||
|
||||
import appeng.api.implementations.items.IMemoryCard;
|
||||
import appeng.block.crafting.PatternProviderBlock;
|
||||
import appeng.util.InteractionUtil;
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.network.chat.Component;
|
||||
import net.minecraft.world.InteractionHand;
|
||||
import net.minecraft.world.InteractionResult;
|
||||
import net.minecraft.world.entity.player.Player;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
import net.minecraft.world.level.Level;
|
||||
import net.minecraft.world.phys.BlockHitResult;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
public class MirrorPatternProviderBlock extends PatternProviderBlock {
|
||||
|
||||
public MirrorPatternProviderBlock() {
|
||||
super();
|
||||
}
|
||||
|
||||
@Override
|
||||
public InteractionResult onActivated(Level level, BlockPos pos, Player player, InteractionHand hand,
|
||||
@Nullable ItemStack heldItem, BlockHitResult hit) {
|
||||
var mirror = this.getMirror(level, pos);
|
||||
if (mirror == null) {
|
||||
return InteractionResult.PASS;
|
||||
}
|
||||
|
||||
if (heldItem != null
|
||||
&& (InteractionUtil.canWrenchRotate(heldItem) || heldItem.getItem() instanceof IMemoryCard)) {
|
||||
if (!level.isClientSide) {
|
||||
player.displayClientMessage(
|
||||
Component.translatable("extendedae_plus.message.mirror_pattern_provider.readonly"),
|
||||
true);
|
||||
}
|
||||
return InteractionResult.sidedSuccess(level.isClientSide);
|
||||
}
|
||||
|
||||
if (heldItem != null && !heldItem.isEmpty()) {
|
||||
return InteractionResult.PASS;
|
||||
}
|
||||
|
||||
if (!level.isClientSide) {
|
||||
if (player.isShiftKeyDown()) {
|
||||
if (mirror.tryBindToAdjacentMaster()) {
|
||||
player.displayClientMessage(mirror.createBoundMessage(), true);
|
||||
} else {
|
||||
player.displayClientMessage(
|
||||
Component.translatable("extendedae_plus.message.mirror_pattern_provider.missing_master"),
|
||||
true);
|
||||
}
|
||||
} else {
|
||||
player.displayClientMessage(mirror.getStatusMessage(), true);
|
||||
}
|
||||
}
|
||||
|
||||
return InteractionResult.sidedSuccess(level.isClientSide);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private MirrorPatternProviderBlockEntity getMirror(Level level, BlockPos pos) {
|
||||
var blockEntity = this.getBlockEntity(level, pos);
|
||||
return blockEntity instanceof MirrorPatternProviderBlockEntity mirror ? mirror : null;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,707 +0,0 @@
|
|||
package com.extendedae_plus.content.ae2;
|
||||
|
||||
import appeng.api.config.Settings;
|
||||
import appeng.api.inventories.InternalInventory;
|
||||
import appeng.api.networking.IManagedGridNode;
|
||||
import appeng.block.crafting.PatternProviderBlock;
|
||||
import appeng.block.crafting.PushDirection;
|
||||
import appeng.blockentity.crafting.PatternProviderBlockEntity;
|
||||
import appeng.blockentity.networking.CableBusBlockEntity;
|
||||
import appeng.helpers.patternprovider.PatternProviderLogic;
|
||||
import appeng.helpers.patternprovider.PatternProviderLogicHost;
|
||||
import appeng.parts.AEBasePart;
|
||||
import appeng.util.SettingsFrom;
|
||||
import appeng.util.inv.AppEngInternalInventory;
|
||||
import com.extendedae_plus.api.bridge.PatternProviderLogicSyncBridge;
|
||||
import com.extendedae_plus.config.ModConfig;
|
||||
import com.extendedae_plus.init.ModBlockEntities;
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.core.Direction;
|
||||
import net.minecraft.core.GlobalPos;
|
||||
import net.minecraft.core.registries.Registries;
|
||||
import net.minecraft.nbt.CompoundTag;
|
||||
import net.minecraft.nbt.Tag;
|
||||
import net.minecraft.network.chat.Component;
|
||||
import net.minecraft.network.chat.MutableComponent;
|
||||
import net.minecraft.resources.ResourceKey;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraft.server.level.ServerLevel;
|
||||
import net.minecraft.world.entity.player.Player;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
import net.minecraft.world.level.Level;
|
||||
import net.minecraft.world.level.block.entity.BlockEntity;
|
||||
import net.minecraft.world.level.block.state.BlockState;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
|
||||
public class MirrorPatternProviderBlockEntity extends PatternProviderBlockEntity {
|
||||
private static final String TAG_MASTER = "mirrorMaster";
|
||||
private static final String TAG_MASTER_DIMENSION = "dimension";
|
||||
private static final String TAG_MASTER_POS = "pos";
|
||||
private static final String TAG_MASTER_SIDE = "side";
|
||||
private static final int FAST_SYNC_INTERVAL = 2;
|
||||
private static final int STABLE_SYNC_INTERVAL = 20;
|
||||
private static final int UNLOADED_MASTER_RETRY_INTERVAL = 40;
|
||||
private static final int AE2_PATTERN_SLOTS = 9;
|
||||
private static final int EXTENDED_PATTERN_PROVIDER_BASE_SLOTS = 36;
|
||||
private static final InternalInventory DISABLED_PATTERN_INVENTORY = new AppEngInternalInventory(0);
|
||||
private static final long UNKNOWN_PATTERN_SYNC_VERSION = Long.MIN_VALUE;
|
||||
|
||||
@Nullable
|
||||
private ResourceKey<Level> masterDimension;
|
||||
|
||||
@Nullable
|
||||
private BlockPos masterPos;
|
||||
|
||||
@Nullable
|
||||
private Direction masterSide;
|
||||
|
||||
private long nextSyncTick = Long.MIN_VALUE;
|
||||
private long lastSyncedPatternVersion = UNKNOWN_PATTERN_SYNC_VERSION;
|
||||
private boolean needsUnboundPatternCleanup;
|
||||
|
||||
public record MasterLocation(ResourceKey<Level> dimension, BlockPos pos, @Nullable Direction side) {
|
||||
public MasterLocation {
|
||||
pos = pos.immutable();
|
||||
}
|
||||
|
||||
public GlobalPos asGlobalPos() {
|
||||
return GlobalPos.of(this.dimension, this.pos);
|
||||
}
|
||||
}
|
||||
|
||||
private record ResolvedMaster(MasterLocation location, PatternProviderLogicHost host) {
|
||||
}
|
||||
|
||||
public MirrorPatternProviderBlockEntity(BlockPos pos, BlockState blockState) {
|
||||
super(ModBlockEntities.MIRROR_PATTERN_PROVIDER_BE.get(), pos, blockState);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isVisibleInTerminal() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public InternalInventory getTerminalPatternInventory() {
|
||||
return DISABLED_PATTERN_INVENTORY;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected PatternProviderLogic createLogic() {
|
||||
return new MirrorLogic(this.getMainNode(), this, getMirrorPatternSlotCapacity());
|
||||
}
|
||||
|
||||
public static void serverTick(Level level, BlockPos pos, BlockState state,
|
||||
MirrorPatternProviderBlockEntity blockEntity) {
|
||||
if (level instanceof ServerLevel serverLevel) {
|
||||
blockEntity.serverTick(serverLevel);
|
||||
}
|
||||
}
|
||||
|
||||
private void serverTick(ServerLevel level) {
|
||||
if (level.getGameTime() < this.nextSyncTick) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.nextSyncTick = level.getGameTime() + this.syncBoundMaster();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onReady() {
|
||||
super.onReady();
|
||||
if (this.getLevel() instanceof ServerLevel serverLevel) {
|
||||
this.scheduleImmediateSync();
|
||||
this.nextSyncTick = serverLevel.getGameTime() + this.syncBoundMaster();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void saveAdditional(CompoundTag data) {
|
||||
super.saveAdditional(data);
|
||||
|
||||
if (this.masterDimension != null && this.masterPos != null) {
|
||||
var masterTag = new CompoundTag();
|
||||
masterTag.putString(TAG_MASTER_DIMENSION, this.masterDimension.location().toString());
|
||||
masterTag.putLong(TAG_MASTER_POS, this.masterPos.asLong());
|
||||
if (this.masterSide != null) {
|
||||
masterTag.putString(TAG_MASTER_SIDE, this.masterSide.getSerializedName());
|
||||
}
|
||||
data.put(TAG_MASTER, masterTag);
|
||||
} else {
|
||||
data.remove(TAG_MASTER);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void loadTag(CompoundTag data) {
|
||||
super.loadTag(data);
|
||||
|
||||
this.masterDimension = null;
|
||||
this.masterPos = null;
|
||||
this.masterSide = null;
|
||||
this.scheduleImmediateSync();
|
||||
this.invalidatePatternSyncState();
|
||||
this.needsUnboundPatternCleanup = true;
|
||||
if (data.contains(TAG_MASTER, Tag.TAG_COMPOUND)) {
|
||||
var masterTag = data.getCompound(TAG_MASTER);
|
||||
if (masterTag.contains(TAG_MASTER_DIMENSION, Tag.TAG_STRING)
|
||||
&& masterTag.contains(TAG_MASTER_POS, Tag.TAG_LONG)) {
|
||||
this.masterDimension = ResourceKey.create(Registries.DIMENSION,
|
||||
new ResourceLocation(masterTag.getString(TAG_MASTER_DIMENSION)));
|
||||
this.masterPos = BlockPos.of(masterTag.getLong(TAG_MASTER_POS));
|
||||
if (masterTag.contains(TAG_MASTER_SIDE, Tag.TAG_STRING)) {
|
||||
this.masterSide = Direction.byName(masterTag.getString(TAG_MASTER_SIDE));
|
||||
}
|
||||
this.needsUnboundPatternCleanup = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addAdditionalDrops(Level level, BlockPos pos, List<ItemStack> drops) {
|
||||
var patternInventory = this.getPatternInventory();
|
||||
var snapshot = this.copyInventoryContents(patternInventory);
|
||||
|
||||
this.clearInventory(patternInventory);
|
||||
this.getLogic().updatePatterns();
|
||||
super.addAdditionalDrops(level, pos, drops);
|
||||
|
||||
this.restoreInventoryContents(patternInventory, snapshot);
|
||||
this.getLogic().updatePatterns();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clearContent() {
|
||||
this.getLogic().clearContent();
|
||||
}
|
||||
|
||||
public boolean bindToMaster(GlobalPos master) {
|
||||
return this.bindToMaster(new MasterLocation(master.dimension(), master.pos(), null));
|
||||
}
|
||||
|
||||
public boolean tryBindToAdjacentMaster() {
|
||||
if (!(this.getLevel() instanceof ServerLevel)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
var master = this.findAdjacentMaster();
|
||||
if (master == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
this.syncFromMaster(master);
|
||||
return true;
|
||||
}
|
||||
|
||||
public boolean bindToMaster(MasterLocation master) {
|
||||
if (!(this.getLevel() instanceof ServerLevel serverLevel)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (master.pos().equals(this.getBlockPos()) && master.dimension().equals(serverLevel.dimension())) {
|
||||
return false;
|
||||
}
|
||||
|
||||
var resolvedMaster = this.resolveMaster(serverLevel, master);
|
||||
if (resolvedMaster != null) {
|
||||
this.syncFromMaster(resolvedMaster);
|
||||
return true;
|
||||
}
|
||||
|
||||
var masterLevel = serverLevel.getServer().getLevel(master.dimension());
|
||||
if (masterLevel != null && masterLevel.hasChunkAt(master.pos())) {
|
||||
return false;
|
||||
}
|
||||
|
||||
var changed = this.setBoundMaster(master);
|
||||
if (changed) {
|
||||
this.flushStateChanges();
|
||||
}
|
||||
this.scheduleImmediateSync();
|
||||
return true;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public PatternProviderLogicHost getMaster() {
|
||||
if (!(this.getLevel() instanceof ServerLevel serverLevel)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
var master = this.resolveBoundMaster(serverLevel);
|
||||
return master != null ? master.host() : null;
|
||||
}
|
||||
|
||||
public Component createBoundMessage() {
|
||||
if (this.masterPos != null) {
|
||||
return this.appendMasterSide(Component.translatable(
|
||||
"extendedae_plus.message.mirror_pattern_provider.bound",
|
||||
this.masterPos.getX(),
|
||||
this.masterPos.getY(),
|
||||
this.masterPos.getZ()));
|
||||
}
|
||||
|
||||
return Component.translatable("extendedae_plus.message.mirror_pattern_provider.missing_master");
|
||||
}
|
||||
|
||||
public Component getStatusMessage() {
|
||||
if (this.masterPos != null) {
|
||||
return this.appendMasterSide(Component.translatable(
|
||||
"extendedae_plus.message.mirror_pattern_provider.following",
|
||||
this.masterPos.getX(),
|
||||
this.masterPos.getY(),
|
||||
this.masterPos.getZ()));
|
||||
}
|
||||
|
||||
return Component.translatable("extendedae_plus.message.mirror_pattern_provider.missing_master");
|
||||
}
|
||||
|
||||
public boolean hasMasterBinding() {
|
||||
return this.masterDimension != null && this.masterPos != null;
|
||||
}
|
||||
|
||||
public boolean unbindFromMaster() {
|
||||
if (!(this.getLevel() instanceof ServerLevel)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!this.hasMasterBinding()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
var changed = this.clearMasterBinding(true);
|
||||
if (changed) {
|
||||
this.flushStateChanges();
|
||||
}
|
||||
this.scheduleImmediateSync();
|
||||
return true;
|
||||
}
|
||||
|
||||
public Component createUnboundMessage() {
|
||||
return Component.translatable("extendedae_plus.message.mirror_pattern_provider.unbound");
|
||||
}
|
||||
|
||||
private int syncBoundMaster() {
|
||||
if (this.masterDimension == null || this.masterPos == null) {
|
||||
if (this.needsUnboundPatternCleanup) {
|
||||
this.needsUnboundPatternCleanup = false;
|
||||
if (this.clearMirroredPatterns()) {
|
||||
this.flushStateChanges();
|
||||
}
|
||||
}
|
||||
return UNLOADED_MASTER_RETRY_INTERVAL;
|
||||
}
|
||||
|
||||
if (!(this.getLevel() instanceof ServerLevel serverLevel)) {
|
||||
return UNLOADED_MASTER_RETRY_INTERVAL;
|
||||
}
|
||||
|
||||
var master = this.resolveBoundMaster(serverLevel);
|
||||
if (master != null) {
|
||||
return this.syncFromMaster(master) ? FAST_SYNC_INTERVAL : STABLE_SYNC_INTERVAL;
|
||||
}
|
||||
|
||||
if (this.shouldClearBrokenBinding()) {
|
||||
if (this.clearMasterBinding(true)) {
|
||||
this.flushStateChanges();
|
||||
return FAST_SYNC_INTERVAL;
|
||||
}
|
||||
return STABLE_SYNC_INTERVAL;
|
||||
}
|
||||
|
||||
return UNLOADED_MASTER_RETRY_INTERVAL;
|
||||
}
|
||||
|
||||
private boolean shouldClearBrokenBinding() {
|
||||
if (this.masterDimension == null || this.masterPos == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!(this.getLevel() instanceof ServerLevel serverLevel)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
var target = this.getBoundMasterLocation();
|
||||
if (target == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
var masterLevel = target.dimension() == serverLevel.dimension()
|
||||
? serverLevel
|
||||
: serverLevel.getServer().getLevel(target.dimension());
|
||||
if (masterLevel == null || !masterLevel.hasChunkAt(target.pos())) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return this.resolveMaster(serverLevel, target) == null;
|
||||
}
|
||||
|
||||
private boolean clearMasterBinding(boolean clearMirroredPatterns) {
|
||||
var hadBinding = this.masterDimension != null || this.masterPos != null;
|
||||
|
||||
this.masterDimension = null;
|
||||
this.masterPos = null;
|
||||
this.masterSide = null;
|
||||
this.invalidatePatternSyncState();
|
||||
this.needsUnboundPatternCleanup = false;
|
||||
|
||||
var changed = hadBinding;
|
||||
if (clearMirroredPatterns) {
|
||||
changed |= this.clearMirroredPatterns();
|
||||
changed |= this.resetMirroredSettingsToInitialState();
|
||||
}
|
||||
|
||||
return changed;
|
||||
}
|
||||
|
||||
private boolean setBoundMaster(MasterLocation master) {
|
||||
var changed = !Objects.equals(this.masterDimension, master.dimension())
|
||||
|| !Objects.equals(this.masterPos, master.pos())
|
||||
|| !Objects.equals(this.masterSide, master.side());
|
||||
|
||||
this.masterDimension = master.dimension();
|
||||
this.masterPos = master.pos();
|
||||
this.masterSide = master.side();
|
||||
this.needsUnboundPatternCleanup = false;
|
||||
if (changed) {
|
||||
this.invalidatePatternSyncState();
|
||||
}
|
||||
return changed;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private ResolvedMaster findAdjacentMaster() {
|
||||
var level = this.getLevel();
|
||||
if (level == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
for (var direction : Direction.values()) {
|
||||
var adjacent = level.getBlockEntity(this.getBlockPos().relative(direction));
|
||||
var master = resolveMaster(adjacent, null);
|
||||
if (master == null) {
|
||||
master = resolveMaster(adjacent, direction.getOpposite());
|
||||
}
|
||||
if (master != null) {
|
||||
return master;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private boolean syncFromMaster(ResolvedMaster master) {
|
||||
var bindingChanged = this.setBoundMaster(master.location());
|
||||
var changed = bindingChanged;
|
||||
changed |= this.syncMirroredSettings(master.host());
|
||||
changed |= this.syncMirroredPatterns(master.host(), bindingChanged);
|
||||
|
||||
if (changed) {
|
||||
this.flushStateChanges();
|
||||
}
|
||||
|
||||
return changed;
|
||||
}
|
||||
|
||||
private boolean syncMirroredSettings(PatternProviderLogicHost master) {
|
||||
if (!this.hasDifferentMirroredSettings(master)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
var settingsTag = new CompoundTag();
|
||||
exportMasterSettings(master, settingsTag);
|
||||
super.importSettings(SettingsFrom.MEMORY_CARD, settingsTag, null);
|
||||
this.getLogic().getConfigManager().readFromNBT(settingsTag);
|
||||
|
||||
if (this.getPriority() != master.getPriority()) {
|
||||
this.setPriority(master.getPriority());
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private boolean resetMirroredSettingsToInitialState() {
|
||||
var defaultState = this.getBlockState().getBlock().defaultBlockState();
|
||||
var defaultMirror = new MirrorPatternProviderBlockEntity(this.getBlockPos(), defaultState);
|
||||
return this.syncMirroredSettings(defaultMirror);
|
||||
}
|
||||
|
||||
private boolean hasDifferentMirroredSettings(PatternProviderLogicHost master) {
|
||||
var mirrorSettings = new CompoundTag();
|
||||
var masterSettings = new CompoundTag();
|
||||
|
||||
this.exportSettings(SettingsFrom.MEMORY_CARD, mirrorSettings, null);
|
||||
exportMasterSettings(master, masterSettings);
|
||||
|
||||
return !Objects.equals(this.getCustomName(), getCustomName(master))
|
||||
|| this.getPriority() != master.getPriority()
|
||||
|| !Objects.equals(mirrorSettings, masterSettings)
|
||||
|| supportsPushDirectionState(master)
|
||||
&& this.getBlockState().getValue(PatternProviderBlock.PUSH_DIRECTION) != getPushDirection(master);
|
||||
}
|
||||
|
||||
private boolean syncMirroredPatterns(PatternProviderLogicHost master, boolean forceSync) {
|
||||
var masterPatternVersion = getPatternSyncVersion(master);
|
||||
if (!forceSync && masterPatternVersion != UNKNOWN_PATTERN_SYNC_VERSION
|
||||
&& masterPatternVersion == this.lastSyncedPatternVersion) {
|
||||
return false;
|
||||
}
|
||||
|
||||
var mirrorInventory = this.getPatternInventory();
|
||||
var masterInventory = asPatternInventory(master.getLogic().getPatternInv());
|
||||
var mirrorSize = mirrorInventory.size();
|
||||
var masterSize = masterInventory.size();
|
||||
var changed = false;
|
||||
|
||||
for (int slot = 0; slot < mirrorSize; slot++) {
|
||||
var desiredStack = slot < masterSize ? masterInventory.getStackInSlot(slot) : ItemStack.EMPTY;
|
||||
var currentStack = mirrorInventory.getStackInSlot(slot);
|
||||
if (!sameStack(desiredStack, currentStack)) {
|
||||
mirrorInventory.setItemDirect(slot, desiredStack.isEmpty() ? ItemStack.EMPTY : desiredStack.copy());
|
||||
changed = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (changed) {
|
||||
this.getLogic().updatePatterns();
|
||||
}
|
||||
|
||||
if (masterPatternVersion != UNKNOWN_PATTERN_SYNC_VERSION) {
|
||||
this.lastSyncedPatternVersion = masterPatternVersion;
|
||||
} else if (changed) {
|
||||
this.invalidatePatternSyncState();
|
||||
}
|
||||
|
||||
return changed;
|
||||
}
|
||||
|
||||
private boolean clearMirroredPatterns() {
|
||||
var patternInventory = this.getPatternInventory();
|
||||
var changed = false;
|
||||
|
||||
for (int slot = 0; slot < patternInventory.size(); slot++) {
|
||||
if (!patternInventory.getStackInSlot(slot).isEmpty()) {
|
||||
patternInventory.setItemDirect(slot, ItemStack.EMPTY);
|
||||
changed = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (changed) {
|
||||
this.getLogic().updatePatterns();
|
||||
}
|
||||
|
||||
return changed;
|
||||
}
|
||||
|
||||
private AppEngInternalInventory getPatternInventory() {
|
||||
return ((MirrorLogic) this.getLogic()).getActualPatternInventory();
|
||||
}
|
||||
|
||||
private ItemStack[] copyInventoryContents(AppEngInternalInventory inventory) {
|
||||
var contents = new ItemStack[inventory.size()];
|
||||
for (int slot = 0; slot < inventory.size(); slot++) {
|
||||
contents[slot] = inventory.getStackInSlot(slot).copy();
|
||||
}
|
||||
return contents;
|
||||
}
|
||||
|
||||
private void restoreInventoryContents(AppEngInternalInventory inventory, ItemStack[] contents) {
|
||||
this.clearInventory(inventory);
|
||||
for (int slot = 0; slot < inventory.size(); slot++) {
|
||||
inventory.setItemDirect(slot, contents[slot].copy());
|
||||
}
|
||||
}
|
||||
|
||||
private void clearInventory(AppEngInternalInventory inventory) {
|
||||
for (int slot = 0; slot < inventory.size(); slot++) {
|
||||
inventory.setItemDirect(slot, ItemStack.EMPTY);
|
||||
}
|
||||
}
|
||||
|
||||
private void flushStateChanges() {
|
||||
this.saveChanges();
|
||||
this.markForUpdate();
|
||||
}
|
||||
|
||||
private void scheduleImmediateSync() {
|
||||
this.nextSyncTick = Long.MIN_VALUE;
|
||||
}
|
||||
|
||||
private void invalidatePatternSyncState() {
|
||||
this.lastSyncedPatternVersion = UNKNOWN_PATTERN_SYNC_VERSION;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private MasterLocation getBoundMasterLocation() {
|
||||
if (this.masterDimension == null || this.masterPos == null) {
|
||||
return null;
|
||||
}
|
||||
return new MasterLocation(this.masterDimension, this.masterPos, this.masterSide);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private ResolvedMaster resolveBoundMaster(ServerLevel serverLevel) {
|
||||
var target = this.getBoundMasterLocation();
|
||||
return target != null ? this.resolveMaster(serverLevel, target) : null;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private ResolvedMaster resolveMaster(ServerLevel serverLevel, MasterLocation target) {
|
||||
var masterLevel = target.dimension() == serverLevel.dimension()
|
||||
? serverLevel
|
||||
: serverLevel.getServer().getLevel(target.dimension());
|
||||
if (masterLevel == null || !masterLevel.hasChunkAt(target.pos())) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return resolveMaster(masterLevel.getBlockEntity(target.pos()), target.side());
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private static ResolvedMaster resolveMaster(@Nullable BlockEntity blockEntity, @Nullable Direction partSide) {
|
||||
if (blockEntity == null || blockEntity.isRemoved()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (partSide == null && blockEntity instanceof PatternProviderLogicHost host && isValidMasterHost(host)) {
|
||||
var level = blockEntity.getLevel();
|
||||
if (level == null) {
|
||||
return null;
|
||||
}
|
||||
return new ResolvedMaster(
|
||||
new MasterLocation(level.dimension(), blockEntity.getBlockPos(), null),
|
||||
host);
|
||||
}
|
||||
|
||||
if (partSide != null && blockEntity instanceof CableBusBlockEntity cableBus) {
|
||||
return resolvePartMaster(cableBus, partSide);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private static ResolvedMaster resolvePartMaster(CableBusBlockEntity cableBus, Direction side) {
|
||||
var part = cableBus.getCableBus().getPart(side);
|
||||
if (part instanceof AEBasePart basePart && isValidMasterHost(basePart)) {
|
||||
var level = cableBus.getLevel();
|
||||
if (level == null) {
|
||||
return null;
|
||||
}
|
||||
return new ResolvedMaster(
|
||||
new MasterLocation(level.dimension(), cableBus.getBlockPos(), side),
|
||||
(PatternProviderLogicHost) basePart);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private static void exportMasterSettings(PatternProviderLogicHost master, CompoundTag output) {
|
||||
if (master instanceof PatternProviderBlockEntity blockEntity) {
|
||||
blockEntity.exportSettings(SettingsFrom.MEMORY_CARD, output, null);
|
||||
} else if (master instanceof AEBasePart part) {
|
||||
part.exportSettings(SettingsFrom.MEMORY_CARD, output);
|
||||
}
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private static Component getCustomName(PatternProviderLogicHost host) {
|
||||
if (host instanceof PatternProviderBlockEntity blockEntity) {
|
||||
return blockEntity.getCustomName();
|
||||
}
|
||||
if (host instanceof AEBasePart part) {
|
||||
return part.getCustomName();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private static PushDirection getPushDirection(PatternProviderLogicHost host) {
|
||||
Direction target = null;
|
||||
var targets = host.getTargets();
|
||||
if (targets.size() == 1) {
|
||||
target = targets.iterator().next();
|
||||
}
|
||||
return PushDirection.fromDirection(target);
|
||||
}
|
||||
|
||||
private static boolean supportsPushDirectionState(PatternProviderLogicHost host) {
|
||||
return host instanceof PatternProviderBlockEntity;
|
||||
}
|
||||
|
||||
private Component appendMasterSide(MutableComponent component) {
|
||||
if (this.masterSide != null) {
|
||||
component.append(Component.literal(" [" + this.masterSide.getSerializedName() + "]"));
|
||||
}
|
||||
return component;
|
||||
}
|
||||
|
||||
private static AppEngInternalInventory asPatternInventory(Object inventory) {
|
||||
return (AppEngInternalInventory) inventory;
|
||||
}
|
||||
|
||||
private static int getMirrorPatternSlotCapacity() {
|
||||
int pageMultiplier = 1;
|
||||
if (ModConfig.INSTANCE != null) {
|
||||
pageMultiplier = ModConfig.INSTANCE.pageMultiplier;
|
||||
}
|
||||
|
||||
pageMultiplier = Math.max(1, Math.min(64, pageMultiplier));
|
||||
return Math.max(AE2_PATTERN_SLOTS, EXTENDED_PATTERN_PROVIDER_BASE_SLOTS * pageMultiplier);
|
||||
}
|
||||
|
||||
private static boolean sameStack(ItemStack left, ItemStack right) {
|
||||
if (left.isEmpty() && right.isEmpty()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return ItemStack.isSameItemSameTags(left, right) && left.getCount() == right.getCount();
|
||||
}
|
||||
|
||||
private static long getPatternSyncVersion(PatternProviderLogicHost master) {
|
||||
if (master.getLogic() instanceof PatternProviderLogicSyncBridge bridge) {
|
||||
return bridge.eap$getPatternSyncVersion();
|
||||
}
|
||||
|
||||
return UNKNOWN_PATTERN_SYNC_VERSION;
|
||||
}
|
||||
|
||||
public static boolean isSupportedMaster(@Nullable BlockEntity blockEntity) {
|
||||
return blockEntity instanceof PatternProviderBlockEntity
|
||||
&& !(blockEntity instanceof MirrorPatternProviderBlockEntity)
|
||||
&& !blockEntity.isRemoved();
|
||||
}
|
||||
|
||||
private static boolean isValidMasterHost(Object host) {
|
||||
if (!(host instanceof PatternProviderLogicHost)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (host instanceof MirrorPatternProviderBlockEntity) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (host instanceof BlockEntity blockEntity) {
|
||||
return !blockEntity.isRemoved();
|
||||
}
|
||||
|
||||
return host instanceof AEBasePart part && part.getBlockEntity() != null && !part.getBlockEntity().isRemoved();
|
||||
}
|
||||
|
||||
private static final class MirrorLogic extends PatternProviderLogic {
|
||||
private MirrorLogic(IManagedGridNode mainNode, MirrorPatternProviderBlockEntity mirrorHost,
|
||||
int patternInventorySize) {
|
||||
super(mainNode, mirrorHost, patternInventorySize);
|
||||
}
|
||||
|
||||
@Override
|
||||
public InternalInventory getPatternInv() {
|
||||
return DISABLED_PATTERN_INVENTORY;
|
||||
}
|
||||
|
||||
private AppEngInternalInventory getActualPatternInventory() {
|
||||
return (AppEngInternalInventory) super.getPatternInv();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,41 +0,0 @@
|
|||
package com.extendedae_plus.content.decor;
|
||||
|
||||
import net.minecraft.core.Direction;
|
||||
import net.minecraft.world.item.context.BlockPlaceContext;
|
||||
import net.minecraft.world.level.block.Block;
|
||||
import net.minecraft.world.level.block.Mirror;
|
||||
import net.minecraft.world.level.block.Rotation;
|
||||
import net.minecraft.world.level.block.state.BlockBehaviour;
|
||||
import net.minecraft.world.level.block.state.BlockState;
|
||||
import net.minecraft.world.level.block.state.StateDefinition;
|
||||
import net.minecraft.world.level.block.state.properties.BlockStateProperties;
|
||||
import net.minecraft.world.level.block.state.properties.DirectionProperty;
|
||||
|
||||
public class DollBlock extends Block {
|
||||
public static final DirectionProperty FACING = BlockStateProperties.HORIZONTAL_FACING;
|
||||
|
||||
public DollBlock(BlockBehaviour.Properties properties) {
|
||||
super(properties);
|
||||
this.registerDefaultState(this.stateDefinition.any().setValue(FACING, Direction.NORTH));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void createBlockStateDefinition(StateDefinition.Builder<Block, BlockState> builder) {
|
||||
builder.add(FACING);
|
||||
}
|
||||
|
||||
@Override
|
||||
public BlockState getStateForPlacement(BlockPlaceContext context) {
|
||||
return this.defaultBlockState().setValue(FACING, context.getHorizontalDirection().getOpposite());
|
||||
}
|
||||
|
||||
@Override
|
||||
public BlockState rotate(BlockState state, Rotation rotation) {
|
||||
return state.setValue(FACING, rotation.rotate(state.getValue(FACING)));
|
||||
}
|
||||
|
||||
@Override
|
||||
public BlockState mirror(BlockState state, Mirror mirror) {
|
||||
return state.rotate(mirror.getRotation(state.getValue(FACING)));
|
||||
}
|
||||
}
|
||||
|
|
@ -1,17 +0,0 @@
|
|||
package com.extendedae_plus.content.matrix;
|
||||
|
||||
import com.extendedae_plus.init.ModItems;
|
||||
import com.glodblock.github.extendedae.common.blocks.matrix.BlockAssemblerMatrixBase;
|
||||
import net.minecraft.world.item.Item;
|
||||
|
||||
public class CrafterCorePlusBlock extends BlockAssemblerMatrixBase<CrafterCorePlusBlockEntity> {
|
||||
|
||||
public CrafterCorePlusBlock() {
|
||||
super();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Item getPresentItem() {
|
||||
return ModItems.ASSEMBLER_MATRIX_CRAFTER_PLUS.get();
|
||||
}
|
||||
}
|
||||
|
|
@ -1,87 +0,0 @@
|
|||
package com.extendedae_plus.content.matrix;
|
||||
|
||||
import appeng.api.inventories.InternalInventory;
|
||||
import appeng.api.networking.security.IActionSource;
|
||||
import appeng.util.inv.CombinedInternalInventory;
|
||||
import com.extendedae_plus.init.ModBlockEntities;
|
||||
import com.extendedae_plus.mixin.extendedae.accessor.TileAssemblerMatrixCrafterAccessor;
|
||||
import com.extendedae_plus.mixin.minecraft.accessor.BlockEntityAccessor;
|
||||
import com.glodblock.github.extendedae.common.me.CraftingMatrixThread;
|
||||
import com.glodblock.github.extendedae.common.me.CraftingThread;
|
||||
import com.glodblock.github.extendedae.common.tileentities.matrix.TileAssemblerMatrixCrafter;
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.nbt.CompoundTag;
|
||||
import net.minecraft.world.level.block.entity.BlockEntityType;
|
||||
import net.minecraft.world.level.block.state.BlockState;
|
||||
|
||||
public class CrafterCorePlusBlockEntity extends TileAssemblerMatrixCrafter {
|
||||
|
||||
public static final int MAX_THREAD = 32;
|
||||
|
||||
private int activeThreadMask = 0;
|
||||
|
||||
public CrafterCorePlusBlockEntity(BlockPos pos, BlockState blockState) {
|
||||
super(pos, blockState);
|
||||
|
||||
((BlockEntityAccessor) (Object) this)
|
||||
.extendedae_plus$setType(ModBlockEntities.ASSEMBLER_MATRIX_CRAFTER_PLUS_BE.get());
|
||||
|
||||
var threads = new CraftingThread[MAX_THREAD];
|
||||
var inventories = new InternalInventory[MAX_THREAD];
|
||||
for (int x = 0; x < MAX_THREAD; x++) {
|
||||
final int index = x;
|
||||
threads[index] = new CraftingMatrixThread(this, this::getSrc, signal -> this.changeState(index, signal));
|
||||
inventories[index] = threads[index].getInternalInventory();
|
||||
}
|
||||
|
||||
var accessor = (TileAssemblerMatrixCrafterAccessor) (Object) this;
|
||||
accessor.extendedae_plus$setThreads(threads);
|
||||
accessor.extendedae_plus$setInternalInv(new CombinedInternalInventory(inventories));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void saveAdditional(CompoundTag tag) {
|
||||
super.saveAdditional(tag);
|
||||
|
||||
var threads = ((TileAssemblerMatrixCrafterAccessor) (Object) this).extendedae_plus$getThreads();
|
||||
for (int x = TileAssemblerMatrixCrafter.MAX_THREAD; x < MAX_THREAD; x++) {
|
||||
tag.put("#ct" + x, threads[x].writeNBT());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void loadTag(CompoundTag tag) {
|
||||
super.loadTag(tag);
|
||||
|
||||
var threads = ((TileAssemblerMatrixCrafterAccessor) (Object) this).extendedae_plus$getThreads();
|
||||
for (int x = TileAssemblerMatrixCrafter.MAX_THREAD; x < MAX_THREAD; x++) {
|
||||
if (tag.contains("#ct" + x)) {
|
||||
threads[x].readNBT(tag.getCompound("#ct" + x));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public BlockEntityType<?> getType() {
|
||||
return ModBlockEntities.ASSEMBLER_MATRIX_CRAFTER_PLUS_BE.get();
|
||||
}
|
||||
|
||||
private IActionSource getSrc() {
|
||||
return this.cluster == null ? null : this.cluster.getSrc();
|
||||
}
|
||||
|
||||
private void changeState(int index, boolean state) {
|
||||
int oldMask = this.activeThreadMask;
|
||||
if (state) {
|
||||
this.activeThreadMask |= (1 << index);
|
||||
} else {
|
||||
this.activeThreadMask &= ~(1 << index);
|
||||
}
|
||||
|
||||
if (oldMask == 0 && this.activeThreadMask != 0) {
|
||||
this.getMainNode().ifPresent((grid, node) -> grid.getTickManager().wakeDevice(node));
|
||||
} else if (oldMask != 0 && this.activeThreadMask == 0) {
|
||||
this.getMainNode().ifPresent((grid, node) -> grid.getTickManager().sleepDevice(node));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,18 +0,0 @@
|
|||
package com.extendedae_plus.content.matrix;
|
||||
|
||||
import com.extendedae_plus.init.ModItems;
|
||||
import com.glodblock.github.extendedae.common.blocks.matrix.BlockAssemblerMatrixBase;
|
||||
import com.glodblock.github.extendedae.common.tileentities.matrix.TileAssemblerMatrixPattern;
|
||||
import net.minecraft.world.item.Item;
|
||||
|
||||
public class PatternCorePlusBlock extends BlockAssemblerMatrixBase<PatternCorePlusBlockEntity> {
|
||||
|
||||
public PatternCorePlusBlock() {
|
||||
super();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Item getPresentItem() {
|
||||
return ModItems.ASSEMBLER_MATRIX_PATTERN_PLUS.get();
|
||||
}
|
||||
}
|
||||
|
|
@ -1,78 +0,0 @@
|
|||
package com.extendedae_plus.content.matrix;
|
||||
|
||||
import appeng.api.crafting.PatternDetailsHelper;
|
||||
import appeng.api.implementations.blockentities.PatternContainerGroup;
|
||||
import appeng.api.inventories.InternalInventory;
|
||||
import appeng.api.stacks.AEItemKey;
|
||||
import appeng.blockentity.crafting.IMolecularAssemblerSupportedPattern;
|
||||
import appeng.crafting.pattern.EncodedPatternItem;
|
||||
import appeng.util.inv.AppEngInternalInventory;
|
||||
import appeng.util.inv.FilteredInternalInventory;
|
||||
import appeng.util.inv.filter.AEItemFilters;
|
||||
import appeng.util.inv.filter.IAEItemFilter;
|
||||
import com.extendedae_plus.init.ModBlockEntities;
|
||||
import com.extendedae_plus.init.ModItems;
|
||||
import com.extendedae_plus.mixin.extendedae.accessor.TileAssemblerMatrixPatternAccessor;
|
||||
import com.extendedae_plus.mixin.minecraft.accessor.BlockEntityAccessor;
|
||||
import com.glodblock.github.extendedae.common.tileentities.matrix.TileAssemblerMatrixPattern;
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.network.chat.Component;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
import net.minecraft.world.level.Level;
|
||||
import net.minecraft.world.level.block.entity.BlockEntityType;
|
||||
import net.minecraft.world.level.block.state.BlockState;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
public class PatternCorePlusBlockEntity extends TileAssemblerMatrixPattern {
|
||||
|
||||
public static final int INV_SIZE = 72;
|
||||
|
||||
private final FilteredInternalInventory exposedInventory;
|
||||
|
||||
public PatternCorePlusBlockEntity(BlockPos pos, BlockState blockState) {
|
||||
super(pos, blockState);
|
||||
|
||||
((BlockEntityAccessor) (Object) this)
|
||||
.extendedae_plus$setType(ModBlockEntities.ASSEMBLER_MATRIX_PATTERN_PLUS_BE.get());
|
||||
|
||||
var inventory = new AppEngInternalInventory(this, INV_SIZE, 1);
|
||||
inventory.setFilter(new Filter(this::getLevel));
|
||||
((TileAssemblerMatrixPatternAccessor) (Object) this).extendedae_plus$setPatternInventory(inventory);
|
||||
this.exposedInventory = new FilteredInternalInventory(inventory, AEItemFilters.INSERT_ONLY);
|
||||
}
|
||||
|
||||
@Override
|
||||
public FilteredInternalInventory getExposedInventory() {
|
||||
return this.exposedInventory;
|
||||
}
|
||||
|
||||
@Override
|
||||
public PatternContainerGroup getTerminalGroup() {
|
||||
var icon = AEItemKey.of(ModItems.ASSEMBLER_MATRIX_PATTERN_PLUS.get());
|
||||
var name = this.hasCustomName() ? this.getCustomName() : icon.getDisplayName();
|
||||
return new PatternContainerGroup(
|
||||
icon,
|
||||
name,
|
||||
List.of(Component.translatable("gui.extendedae_plus.assembler_matrix.pattern"))
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
public BlockEntityType<?> getType() {
|
||||
return ModBlockEntities.ASSEMBLER_MATRIX_PATTERN_PLUS_BE.get();
|
||||
}
|
||||
|
||||
public record Filter(Supplier<Level> world) implements IAEItemFilter {
|
||||
|
||||
@Override
|
||||
public boolean allowInsert(InternalInventory inv, int slot, ItemStack stack) {
|
||||
if (stack.getItem() instanceof EncodedPatternItem) {
|
||||
return PatternDetailsHelper.decodePattern(stack, world.get())
|
||||
instanceof IMolecularAssemblerSupportedPattern;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,22 +0,0 @@
|
|||
package com.extendedae_plus.content.matrix;
|
||||
|
||||
import com.extendedae_plus.init.ModItems;
|
||||
import com.glodblock.github.extendedae.common.blocks.matrix.BlockAssemblerMatrixBase;
|
||||
import net.minecraft.world.item.Item;
|
||||
import net.minecraft.world.level.block.state.BlockBehaviour;
|
||||
|
||||
public class SpeedCorePlusBlock extends BlockAssemblerMatrixBase<SpeedCorePlusBlockEntity> {
|
||||
|
||||
public SpeedCorePlusBlock() {
|
||||
super();
|
||||
}
|
||||
|
||||
public SpeedCorePlusBlock(BlockBehaviour.Properties props) {
|
||||
super(props);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Item getPresentItem() {
|
||||
return ModItems.ASSEMBLER_MATRIX_SPEED_PLUS.get();
|
||||
}
|
||||
}
|
||||
|
|
@ -1,24 +0,0 @@
|
|||
package com.extendedae_plus.content.matrix;
|
||||
|
||||
import com.extendedae_plus.init.ModBlockEntities;
|
||||
import com.glodblock.github.extendedae.common.me.matrix.ClusterAssemblerMatrix;
|
||||
import com.glodblock.github.extendedae.common.tileentities.matrix.TileAssemblerMatrixFunction;
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.world.level.block.state.BlockState;
|
||||
|
||||
public class SpeedCorePlusBlockEntity extends TileAssemblerMatrixFunction {
|
||||
|
||||
public SpeedCorePlusBlockEntity(BlockPos pos, BlockState blockState) {
|
||||
super(ModBlockEntities.ASSEMBLER_MATRIX_SPEED_PLUS_BE.get(), pos, blockState);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void add(ClusterAssemblerMatrix cluster) {
|
||||
if (cluster == null) {
|
||||
return;
|
||||
}
|
||||
for (int i = 0; i < 5; i++) {
|
||||
cluster.addSpeedCore();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,89 +0,0 @@
|
|||
package com.extendedae_plus.content.wireless;
|
||||
|
||||
import com.extendedae_plus.init.ModBlockEntities;
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.world.InteractionHand;
|
||||
import net.minecraft.world.InteractionResult;
|
||||
import net.minecraft.world.entity.LivingEntity;
|
||||
import net.minecraft.world.entity.player.Player;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
import net.minecraft.world.level.BlockGetter;
|
||||
import net.minecraft.world.level.Level;
|
||||
import net.minecraft.world.level.block.Block;
|
||||
import net.minecraft.world.level.block.EntityBlock;
|
||||
import net.minecraft.world.level.block.entity.BlockEntity;
|
||||
import net.minecraft.world.level.block.entity.BlockEntityTicker;
|
||||
import net.minecraft.world.level.block.entity.BlockEntityType;
|
||||
import net.minecraft.world.level.block.state.BlockState;
|
||||
import net.minecraft.world.level.block.state.StateDefinition;
|
||||
import net.minecraft.world.level.block.state.properties.BooleanProperty;
|
||||
import net.minecraft.world.phys.BlockHitResult;
|
||||
import net.minecraftforge.network.NetworkHooks;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
/**
|
||||
* 标签无线收发器方块(无交互 UI,占位实现)。
|
||||
* 取消所有徒手/道具调节,只允许右键打开后续 UI(当前仅占位返回 SUCCESS)。
|
||||
*/
|
||||
public class LabeledWirelessTransceiverBlock extends Block implements EntityBlock {
|
||||
public static final BooleanProperty STATE = BooleanProperty.create("state");
|
||||
|
||||
public LabeledWirelessTransceiverBlock(Properties props) {
|
||||
super(props);
|
||||
this.registerDefaultState(this.stateDefinition.any().setValue(STATE, false));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void createBlockStateDefinition(StateDefinition.Builder<Block, BlockState> builder) {
|
||||
super.createBlockStateDefinition(builder);
|
||||
builder.add(STATE);
|
||||
}
|
||||
|
||||
@Override
|
||||
public BlockEntity newBlockEntity(BlockPos pos, BlockState state) {
|
||||
return new LabeledWirelessTransceiverBlockEntity(pos, state);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setPlacedBy(Level level, BlockPos pos, BlockState state, @Nullable LivingEntity placer, ItemStack stack) {
|
||||
super.setPlacedBy(level, pos, state, placer, stack);
|
||||
if (!level.isClientSide && placer instanceof Player player) {
|
||||
BlockEntity be = level.getBlockEntity(pos);
|
||||
if (be instanceof LabeledWirelessTransceiverBlockEntity te) {
|
||||
te.setPlacerId(player.getUUID(), player.getName().getString());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public InteractionResult use(BlockState state, Level level, BlockPos pos, Player player, InteractionHand hand, BlockHitResult hit) {
|
||||
if (level.isClientSide) {
|
||||
return InteractionResult.SUCCESS;
|
||||
}
|
||||
BlockEntity be = level.getBlockEntity(pos);
|
||||
if (be instanceof LabeledWirelessTransceiverBlockEntity te) {
|
||||
NetworkHooks.openScreen((net.minecraft.server.level.ServerPlayer) player, te, buf -> buf.writeBlockPos(pos));
|
||||
return InteractionResult.CONSUME;
|
||||
}
|
||||
return InteractionResult.PASS;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onRemove(BlockState state, Level level, BlockPos pos, BlockState newState, boolean isMoving) {
|
||||
if (!state.is(newState.getBlock())) {
|
||||
BlockEntity be = level.getBlockEntity(pos);
|
||||
if (be instanceof LabeledWirelessTransceiverBlockEntity te) {
|
||||
te.onRemoved();
|
||||
}
|
||||
}
|
||||
super.onRemove(state, level, pos, newState, isMoving);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T extends BlockEntity> BlockEntityTicker<T> getTicker(Level level, BlockState state, BlockEntityType<T> type) {
|
||||
if (level.isClientSide) return null;
|
||||
return type == ModBlockEntities.LABELED_WIRELESS_TRANSCEIVER_BE.get()
|
||||
? (lvl, pos, st, be) -> LabeledWirelessTransceiverBlockEntity.serverTick(lvl, pos, st, (LabeledWirelessTransceiverBlockEntity) be)
|
||||
: null;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,333 +0,0 @@
|
|||
package com.extendedae_plus.content.wireless;
|
||||
|
||||
import appeng.api.networking.GridFlags;
|
||||
import appeng.api.networking.GridHelper;
|
||||
import appeng.api.networking.IGridNode;
|
||||
import appeng.api.networking.IGridNodeListener;
|
||||
import appeng.api.networking.IInWorldGridNodeHost;
|
||||
import appeng.api.networking.IManagedGridNode;
|
||||
import appeng.api.util.AECableType;
|
||||
import appeng.blockentity.AEBaseBlockEntity;
|
||||
import com.extendedae_plus.ae.wireless.IWirelessEndpoint;
|
||||
import com.extendedae_plus.ae.wireless.LabelLink;
|
||||
import com.extendedae_plus.ae.wireless.LabelNetworkRegistry;
|
||||
import com.extendedae_plus.config.ModConfig;
|
||||
import com.extendedae_plus.init.ModBlockEntities;
|
||||
import com.extendedae_plus.init.ModItems;
|
||||
import com.extendedae_plus.menu.LabeledWirelessTransceiverMenu;
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.core.Direction;
|
||||
import net.minecraft.nbt.CompoundTag;
|
||||
import net.minecraft.network.chat.Component;
|
||||
import net.minecraft.server.level.ServerLevel;
|
||||
import net.minecraft.world.MenuProvider;
|
||||
import net.minecraft.world.level.Level;
|
||||
import net.minecraft.world.level.block.state.BlockState;
|
||||
import net.minecraft.world.entity.player.Inventory;
|
||||
import net.minecraft.world.entity.player.Player;
|
||||
import net.minecraft.world.inventory.AbstractContainerMenu;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.EnumSet;
|
||||
import java.util.Objects;
|
||||
import java.util.UUID;
|
||||
|
||||
public class LabeledWirelessTransceiverBlockEntity extends AEBaseBlockEntity implements IWirelessEndpoint, IInWorldGridNodeHost, MenuProvider {
|
||||
|
||||
private IManagedGridNode managedNode;
|
||||
|
||||
private long frequency = 0L;
|
||||
@Nullable
|
||||
private String labelForDisplay;
|
||||
private boolean beingRemoved = false;
|
||||
|
||||
@Nullable
|
||||
private UUID placerId;
|
||||
@Nullable
|
||||
private String placerName;
|
||||
|
||||
private final LabelLink labelLink = new LabelLink(this);
|
||||
|
||||
public LabeledWirelessTransceiverBlockEntity(BlockPos pos, BlockState state) {
|
||||
super(ModBlockEntities.LABELED_WIRELESS_TRANSCEIVER_BE.get(), pos, state);
|
||||
this.managedNode = GridHelper.createManagedNode(this, NodeListener.INSTANCE)
|
||||
.setFlags(GridFlags.DENSE_CAPACITY);
|
||||
this.managedNode.setIdlePowerUsage(ModConfig.INSTANCE.wirelessTransceiverIdlePower);
|
||||
this.managedNode.setTagName("labeled_wireless_node");
|
||||
this.managedNode.setInWorldNode(true);
|
||||
this.managedNode.setExposedOnSides(EnumSet.allOf(Direction.class));
|
||||
this.managedNode.setVisualRepresentation(ModItems.LABELED_WIRELESS_TRANSCEIVER.get().getDefaultInstance());
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable IGridNode getGridNode(Direction dir) {
|
||||
return getGridNode();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ServerLevel getServerLevel() {
|
||||
Level lvl = super.getLevel();
|
||||
return lvl instanceof ServerLevel sl ? sl : null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public BlockPos getBlockPos() {
|
||||
return this.worldPosition;
|
||||
}
|
||||
|
||||
@Override
|
||||
public IGridNode getGridNode() {
|
||||
return managedNode == null ? null : managedNode.getNode();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEndpointRemoved() {
|
||||
return super.isRemoved();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Component getDisplayName() {
|
||||
return Component.translatable("block.extendedae_plus.labeled_wireless_transceiver");
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable AbstractContainerMenu createMenu(int id, Inventory inv, Player player) {
|
||||
return new LabeledWirelessTransceiverMenu(id, inv, this.worldPosition);
|
||||
}
|
||||
|
||||
public void setPlacerId(@Nullable UUID placerId, @Nullable String placerName) {
|
||||
this.placerId = placerId;
|
||||
this.placerName = placerName;
|
||||
setChanged();
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public UUID getPlacerId() {
|
||||
return placerId;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public String getPlacerName() {
|
||||
return placerName;
|
||||
}
|
||||
|
||||
public long getFrequency() {
|
||||
return frequency;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public String getLabelForDisplay() {
|
||||
return labelForDisplay;
|
||||
}
|
||||
|
||||
public void applyLabel(@Nullable String rawLabel) {
|
||||
ServerLevel sl = getServerLevel();
|
||||
if (sl == null) return;
|
||||
|
||||
// 先注销旧网络引用
|
||||
LabelNetworkRegistry.get(sl).unregister(this);
|
||||
|
||||
var network = LabelNetworkRegistry.get(sl).register(sl, rawLabel, placerId, this);
|
||||
if (network == null) {
|
||||
clearLabel();
|
||||
return;
|
||||
}
|
||||
|
||||
this.labelForDisplay = rawLabel;
|
||||
this.frequency = network.channel();
|
||||
this.labelLink.setTarget(network);
|
||||
updateState();
|
||||
setChanged();
|
||||
}
|
||||
|
||||
public void clearLabel() {
|
||||
ServerLevel sl = getServerLevel();
|
||||
if (sl != null) {
|
||||
LabelNetworkRegistry.get(sl).unregister(this);
|
||||
}
|
||||
this.labelForDisplay = null;
|
||||
this.frequency = 0L;
|
||||
this.labelLink.clearTarget();
|
||||
updateState();
|
||||
setChanged();
|
||||
}
|
||||
|
||||
public void refreshLabel(boolean ensureRegister) {
|
||||
ServerLevel sl = getServerLevel();
|
||||
if (sl == null) return;
|
||||
if (labelForDisplay == null || labelForDisplay.isEmpty()) {
|
||||
this.frequency = 0L;
|
||||
this.labelLink.clearTarget();
|
||||
updateState();
|
||||
return;
|
||||
}
|
||||
var registry = LabelNetworkRegistry.get(sl);
|
||||
var network = registry.getNetwork(sl, labelForDisplay, placerId);
|
||||
if (network == null && ensureRegister) {
|
||||
network = registry.register(sl, labelForDisplay, placerId, this);
|
||||
}
|
||||
if (network == null) {
|
||||
this.frequency = 0L;
|
||||
this.labelLink.clearTarget();
|
||||
} else {
|
||||
// 确保虚拟节点重建(网络从存档恢复时 managedNode 为空)
|
||||
network.ensureVirtualNode(sl);
|
||||
this.frequency = network.channel();
|
||||
this.labelLink.setTarget(network);
|
||||
}
|
||||
updateState();
|
||||
setChanged();
|
||||
}
|
||||
|
||||
public void refreshLabel() {
|
||||
refreshLabel(false);
|
||||
}
|
||||
|
||||
public void onRemoved() {
|
||||
cleanupForRemoval();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onChunkUnloaded() {
|
||||
cleanupForRemoval();
|
||||
super.onChunkUnloaded();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setRemoved() {
|
||||
cleanupForRemoval();
|
||||
super.setRemoved();
|
||||
}
|
||||
|
||||
private void cleanupForRemoval() {
|
||||
if (this.beingRemoved) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.beingRemoved = true;
|
||||
this.labelLink.onUnloadOrRemove();
|
||||
ServerLevel sl = getServerLevel();
|
||||
if (sl != null) {
|
||||
LabelNetworkRegistry.get(sl).unregister(this);
|
||||
}
|
||||
if (this.managedNode != null) {
|
||||
this.managedNode.destroy();
|
||||
}
|
||||
}
|
||||
|
||||
public static void serverTick(Level level, BlockPos pos, BlockState state, LabeledWirelessTransceiverBlockEntity be) {
|
||||
if (!(level instanceof ServerLevel)) return;
|
||||
be.labelLink.updateStatus();
|
||||
be.updateState();
|
||||
}
|
||||
|
||||
private void updateState() {
|
||||
if (this.level == null || this.level.isClientSide) return;
|
||||
if (this.beingRemoved || this.isRemoved()) return;
|
||||
BlockState currentState = this.getBlockState();
|
||||
if (!(currentState.getBlock() instanceof LabeledWirelessTransceiverBlock)) {
|
||||
return;
|
||||
}
|
||||
|
||||
IGridNode node = this.getGridNode();
|
||||
boolean online = false;
|
||||
if (node != null && node.isActive()) {
|
||||
try {
|
||||
var grid = node.getGrid();
|
||||
online = grid != null && grid.getEnergyService().isNetworkPowered();
|
||||
} catch (Throwable ignored) {
|
||||
online = false;
|
||||
}
|
||||
}
|
||||
|
||||
if (currentState.getValue(LabeledWirelessTransceiverBlock.STATE) != online) {
|
||||
this.level.setBlock(this.worldPosition, currentState.setValue(LabeledWirelessTransceiverBlock.STATE, online), 3);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public AECableType getCableConnectionType(Direction dir) {
|
||||
if (this.level == null) return AECableType.GLASS;
|
||||
var adjacentPos = this.worldPosition.relative(dir);
|
||||
if (!Objects.requireNonNull(this.getLevel()).hasChunkAt(adjacentPos)) return AECableType.GLASS;
|
||||
var adjacentHost = GridHelper.getNodeHost(this.getLevel(), adjacentPos);
|
||||
if (adjacentHost != null) {
|
||||
var t = adjacentHost.getCableConnectionType(dir.getOpposite());
|
||||
if (t != null) return t;
|
||||
}
|
||||
return AECableType.GLASS;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onLoad() {
|
||||
super.onLoad();
|
||||
ServerLevel sl = getServerLevel();
|
||||
if (sl == null) return;
|
||||
GridHelper.onFirstTick(this, be -> {
|
||||
be.managedNode.create(be.getLevel(), be.getBlockPos());
|
||||
be.refreshLabel(true);
|
||||
be.labelLink.updateStatus();
|
||||
be.updateState();
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void saveAdditional(CompoundTag tag) {
|
||||
super.saveAdditional(tag);
|
||||
tag.putLong("frequency", frequency);
|
||||
if (labelForDisplay != null) {
|
||||
tag.putString("label", labelForDisplay);
|
||||
}
|
||||
if (placerId != null) {
|
||||
tag.putUUID("placerId", placerId);
|
||||
}
|
||||
if (placerName != null) {
|
||||
tag.putString("placerName", placerName);
|
||||
}
|
||||
if (managedNode != null) {
|
||||
managedNode.saveToNBT(tag);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void loadTag(CompoundTag tag) {
|
||||
super.loadTag(tag);
|
||||
this.frequency = tag.getLong("frequency");
|
||||
if (tag.contains("label")) {
|
||||
this.labelForDisplay = tag.getString("label");
|
||||
} else {
|
||||
this.labelForDisplay = null;
|
||||
}
|
||||
if (tag.hasUUID("placerId")) {
|
||||
this.placerId = tag.getUUID("placerId");
|
||||
}
|
||||
if (tag.contains("placerName")) {
|
||||
this.placerName = tag.getString("placerName");
|
||||
}
|
||||
if (managedNode != null) {
|
||||
managedNode.loadFromNBT(tag);
|
||||
}
|
||||
}
|
||||
|
||||
enum NodeListener implements IGridNodeListener<LabeledWirelessTransceiverBlockEntity> {
|
||||
INSTANCE;
|
||||
@Override
|
||||
public void onSaveChanges(LabeledWirelessTransceiverBlockEntity host, IGridNode node) {
|
||||
host.setChanged();
|
||||
}
|
||||
@Override
|
||||
public void onStateChanged(LabeledWirelessTransceiverBlockEntity host, IGridNode node, State state) {
|
||||
host.updateState();
|
||||
}
|
||||
@Override
|
||||
public void onInWorldConnectionChanged(LabeledWirelessTransceiverBlockEntity host, IGridNode node) {
|
||||
host.updateState();
|
||||
}
|
||||
@Override
|
||||
public void onGridChanged(LabeledWirelessTransceiverBlockEntity host, IGridNode node) {
|
||||
host.updateState();
|
||||
}
|
||||
@Override
|
||||
public void onOwnerChanged(LabeledWirelessTransceiverBlockEntity host, IGridNode node) {}
|
||||
}
|
||||
}
|
||||
|
|
@ -19,25 +19,14 @@ import net.minecraft.world.level.block.entity.BlockEntity;
|
|||
import net.minecraft.world.level.block.entity.BlockEntityTicker;
|
||||
import net.minecraft.world.level.block.entity.BlockEntityType;
|
||||
import net.minecraft.world.level.block.state.BlockState;
|
||||
import net.minecraft.world.level.block.state.StateDefinition;
|
||||
import net.minecraft.world.level.block.state.properties.IntegerProperty;
|
||||
import net.minecraft.world.phys.BlockHitResult;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
public class WirelessTransceiverBlock extends Block implements EntityBlock {
|
||||
public static final IntegerProperty STATE = IntegerProperty.create("state", 0, 5);
|
||||
|
||||
public WirelessTransceiverBlock(Properties props) {
|
||||
super(props);
|
||||
this.registerDefaultState(this.stateDefinition.any().setValue(STATE, 5));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void createBlockStateDefinition(StateDefinition.Builder<Block, BlockState> builder) {
|
||||
super.createBlockStateDefinition(builder);
|
||||
builder.add(STATE);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
|||
|
|
@ -6,7 +6,6 @@ import appeng.blockentity.AEBaseBlockEntity;
|
|||
import com.extendedae_plus.ae.wireless.IWirelessEndpoint;
|
||||
import com.extendedae_plus.ae.wireless.WirelessMasterLink;
|
||||
import com.extendedae_plus.ae.wireless.WirelessSlaveLink;
|
||||
import com.extendedae_plus.config.ModConfig;
|
||||
import com.extendedae_plus.init.ModBlockEntities;
|
||||
import com.extendedae_plus.init.ModItems;
|
||||
import net.minecraft.core.BlockPos;
|
||||
|
|
@ -36,10 +35,6 @@ public class WirelessTransceiverBlockEntity extends AEBaseBlockEntity implements
|
|||
private long frequency = 1L;
|
||||
private boolean masterMode = false;
|
||||
private boolean locked = false;
|
||||
/**
|
||||
* 标记该方块实体是否正在被移除,用于避免在移除流程中再次 setBlock 导致方块“复活”。
|
||||
*/
|
||||
private boolean beingRemoved = false;
|
||||
|
||||
@Nullable
|
||||
private UUID placerId; // 放置者UUID,用于队伍隔离
|
||||
|
|
@ -54,7 +49,7 @@ public class WirelessTransceiverBlockEntity extends AEBaseBlockEntity implements
|
|||
// 创建 AE2 管理节点
|
||||
this.managedNode = GridHelper.createManagedNode(this, NodeListener.INSTANCE)
|
||||
.setFlags(GridFlags.DENSE_CAPACITY);
|
||||
this.managedNode.setIdlePowerUsage(ModConfig.INSTANCE.wirelessTransceiverIdlePower); // 可配置基础待机功耗
|
||||
this.managedNode.setIdlePowerUsage(1.0); // 可按需调整基础待机功耗
|
||||
this.managedNode.setTagName("wireless_node");
|
||||
this.managedNode.setInWorldNode(true);
|
||||
this.managedNode.setExposedOnSides(EnumSet.allOf(Direction.class));
|
||||
|
|
@ -66,7 +61,7 @@ public class WirelessTransceiverBlockEntity extends AEBaseBlockEntity implements
|
|||
}
|
||||
|
||||
@Override
|
||||
public AECableType getCableConnectionType(Direction dir) {
|
||||
public appeng.api.util.AECableType getCableConnectionType(Direction dir) {
|
||||
// 根据相邻方块的实际连接类型渲染(优先采用相邻主机返回的类型),回退为 GLASS。
|
||||
if (this.level == null) return AECableType.GLASS;
|
||||
var adjacentPos = this.worldPosition.relative(dir);
|
||||
|
|
@ -195,8 +190,6 @@ public class WirelessTransceiverBlockEntity extends AEBaseBlockEntity implements
|
|||
}
|
||||
|
||||
public void onRemoved() {
|
||||
// 标记正在被移除,避免在 AE2 回调期间触发状态刷新把方块重新放回世界中
|
||||
this.beingRemoved = true;
|
||||
if (this.masterMode) {
|
||||
masterLink.onUnloadOrRemove();
|
||||
} else {
|
||||
|
|
@ -214,57 +207,6 @@ public class WirelessTransceiverBlockEntity extends AEBaseBlockEntity implements
|
|||
// 从端需要周期检查与维护连接
|
||||
be.slaveLink.updateStatus();
|
||||
}
|
||||
// 更新状态
|
||||
be.updateState();
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据连接状态和频道数更新方块状态
|
||||
*/
|
||||
private void updateState() {
|
||||
if (this.level == null || this.level.isClientSide) return;
|
||||
// 如果方块实体已经被标记为移除,或已经从世界中移除,不再尝试刷新状态
|
||||
if (this.beingRemoved || this.isRemoved()) return;
|
||||
// 确保当前位置仍然是本方块,防止在方块被替换/破坏时错误地重新放置方块
|
||||
BlockState currentState = this.getBlockState();
|
||||
if (!(currentState.getBlock() instanceof WirelessTransceiverBlock)) {
|
||||
return;
|
||||
}
|
||||
|
||||
IGridNode node = this.getGridNode();
|
||||
int newState = 5; // 默认状态:无连接
|
||||
|
||||
if (node != null && node.isActive()) {
|
||||
// 获取该节点使用的频道数(与 jade 中获取频道使用量的方式一致)
|
||||
int usedChannels = 0;
|
||||
for (var connection : node.getConnections()) {
|
||||
usedChannels = Math.max(connection.getUsedChannels(), usedChannels);
|
||||
}
|
||||
|
||||
// 根据频道数计算状态:
|
||||
// 有连接但频道数 < 8:状态0(创建连接时)
|
||||
// 频道数 >= 8 且 < 16:状态1
|
||||
// 频道数 >= 16 且 < 24:状态2
|
||||
// 频道数 >= 24 且 < 32:状态3
|
||||
// 频道数 >= 32:状态4
|
||||
if (usedChannels >= 32) {
|
||||
newState = 4;
|
||||
} else if (usedChannels >= 24) {
|
||||
newState = 3;
|
||||
} else if (usedChannels >= 16) {
|
||||
newState = 2;
|
||||
} else if (usedChannels >= 8) {
|
||||
newState = 1;
|
||||
} else if (usedChannels >= 0) {
|
||||
newState = 0; // 有连接但频道数 < 8
|
||||
}
|
||||
// 如果 usedChannels == 0,保持 newState = 5(无连接状态)
|
||||
}
|
||||
|
||||
// 更新方块状态
|
||||
if (currentState.getValue(WirelessTransceiverBlock.STATE) != newState) {
|
||||
this.level.setBlock(this.worldPosition, currentState.setValue(WirelessTransceiverBlock.STATE, newState), 3);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
@ -342,18 +284,11 @@ public class WirelessTransceiverBlockEntity extends AEBaseBlockEntity implements
|
|||
@Override
|
||||
public void onStateChanged(WirelessTransceiverBlockEntity host, IGridNode node, State state) {
|
||||
// 可在此响应 POWER/CHANNEL 等变化,刷新显示等
|
||||
host.updateState();
|
||||
}
|
||||
@Override
|
||||
public void onInWorldConnectionChanged(WirelessTransceiverBlockEntity host, IGridNode node) {
|
||||
// 连接变化时更新状态
|
||||
host.updateState();
|
||||
}
|
||||
public void onInWorldConnectionChanged(WirelessTransceiverBlockEntity host, IGridNode node) {}
|
||||
@Override
|
||||
public void onGridChanged(WirelessTransceiverBlockEntity host, IGridNode node) {
|
||||
// 网格变化时更新状态
|
||||
host.updateState();
|
||||
}
|
||||
public void onGridChanged(WirelessTransceiverBlockEntity host, IGridNode node) {}
|
||||
@Override
|
||||
public void onOwnerChanged(WirelessTransceiverBlockEntity host, IGridNode node) {}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -72,7 +72,7 @@ public class CraftingRecipes extends RecipeProvider {
|
|||
.save(consumer);
|
||||
|
||||
// 实体加速器
|
||||
/* NBTShapedRecipeBuilder.shaped(RecipeCategory.MISC, ModItems.ENTITY_TICKER_PART_ITEM.get())
|
||||
NBTShapedRecipeBuilder.shaped(RecipeCategory.MISC, ModItems.ENTITY_TICKER_PART_ITEM.get())
|
||||
.pattern("SZS")
|
||||
.pattern("QXQ")
|
||||
.pattern("SIS")
|
||||
|
|
@ -83,7 +83,7 @@ public class CraftingRecipes extends RecipeProvider {
|
|||
.define('I', EPPItemAndBlock.EX_IO_PORT)
|
||||
.unlockedBy("has_entity_speed_card_x2", has(EntitySpeedCardItem.withMultiplier(2).getItem()))
|
||||
.unlockedBy("has_singularity", has(AEItems.SINGULARITY))
|
||||
.save(consumer);*/
|
||||
.save(consumer);
|
||||
|
||||
// 上传核心
|
||||
ShapelessRecipeBuilder.shapeless(RecipeCategory.MISC, ModItems.ASSEMBLER_MATRIX_UPLOAD_CORE.get())
|
||||
|
|
@ -92,60 +92,6 @@ public class CraftingRecipes extends RecipeProvider {
|
|||
.unlockedBy("has_assembler_matrix_wall", has(EPPItemAndBlock.ASSEMBLER_MATRIX_WALL))
|
||||
.save(consumer);
|
||||
|
||||
//超级装配矩阵速度核心
|
||||
ShapedRecipeBuilder.shaped(RecipeCategory.MISC, ModItems.ASSEMBLER_MATRIX_SPEED_PLUS.get())
|
||||
.pattern("BRB")
|
||||
.pattern("RLR")
|
||||
.pattern("BRB")
|
||||
.define('R', EPPItemAndBlock.ASSEMBLER_MATRIX_SPEED)
|
||||
.define('L', Items.NETHER_STAR)
|
||||
.define('B', EPPItemAndBlock.ASSEMBLER_MATRIX_WALL)
|
||||
.unlockedBy("has_quantum_ring", has(AEBlocks.QUANTUM_RING))
|
||||
.save(consumer);
|
||||
|
||||
//超级装配矩阵合成核心
|
||||
ShapedRecipeBuilder.shaped(RecipeCategory.MISC, ModItems.ASSEMBLER_MATRIX_CRAFTER_PLUS.get())
|
||||
.pattern("BRB")
|
||||
.pattern("RLR")
|
||||
.pattern("BRB")
|
||||
.define('R', EPPItemAndBlock.ASSEMBLER_MATRIX_CRAFTER)
|
||||
.define('L', Items.NETHER_STAR)
|
||||
.define('B', EPPItemAndBlock.ASSEMBLER_MATRIX_WALL)
|
||||
.unlockedBy("has_quantum_ring", has(AEBlocks.QUANTUM_RING))
|
||||
.save(consumer);
|
||||
|
||||
//超级装配矩阵样板核心
|
||||
ShapedRecipeBuilder.shaped(RecipeCategory.MISC, ModItems.ASSEMBLER_MATRIX_PATTERN_PLUS.get())
|
||||
.pattern("BRB")
|
||||
.pattern("RLR")
|
||||
.pattern("BRB")
|
||||
.define('R', EPPItemAndBlock.ASSEMBLER_MATRIX_PATTERN)
|
||||
.define('L', Items.NETHER_STAR)
|
||||
.define('B', EPPItemAndBlock.ASSEMBLER_MATRIX_WALL)
|
||||
.unlockedBy("has_quantum_ring", has(AEBlocks.QUANTUM_RING))
|
||||
.save(consumer);
|
||||
|
||||
//镜像样板供应器
|
||||
ShapedRecipeBuilder.shaped(RecipeCategory.MISC, ModItems.MIRROR_PATTERN_PROVIDER.get())
|
||||
.pattern("AAA")
|
||||
.pattern("ABA")
|
||||
.pattern("AAA")
|
||||
.unlockedBy("has_mirror_pattern_provider",has(ModItems.MIRROR_PATTERN_PROVIDER.get()))
|
||||
.define('A',Items.GLASS)
|
||||
.define('B',AEBlocks.PATTERN_PROVIDER)
|
||||
.save(consumer);
|
||||
|
||||
//镜像样板绑定工具
|
||||
ShapedRecipeBuilder.shaped(RecipeCategory.MISC, ModItems.MIRROR_PATTERN_BINDING_TOOL.get())
|
||||
.pattern(" A")
|
||||
.pattern("BCD")
|
||||
.pattern("BBB")
|
||||
.unlockedBy("has_mirror_pattern_binding_tool",has(ModItems.MIRROR_PATTERN_BINDING_TOOL.get()))
|
||||
.define('A', AEItems.WIRELESS_RECEIVER)
|
||||
.define('B', Items.IRON_INGOT)
|
||||
.define('C', Items.REDSTONE)
|
||||
.define('D', AEItems.CALCULATION_PROCESSOR)
|
||||
.save(consumer);
|
||||
}
|
||||
|
||||
private void addCraftingAccelerators(Consumer<FinishedRecipe> consumer) {
|
||||
|
|
@ -181,17 +127,6 @@ public class CraftingRecipes extends RecipeProvider {
|
|||
.define('C', ModItems.CRAFTING_ACCELERATOR_256x.get())
|
||||
.unlockedBy("has_256x", has(ModItems.CRAFTING_ACCELERATOR_256x.get()))
|
||||
.save(consumer, ExtendedAEPlus.id("network/crafting/" + ModItems.CRAFTING_ACCELERATOR_1024x.get().toString().toLowerCase()));
|
||||
|
||||
ShapedRecipeBuilder.shaped(RecipeCategory.MISC,ModItems.LABELED_WIRELESS_TRANSCEIVER.get())
|
||||
.pattern("CAC")
|
||||
.pattern("ABA")
|
||||
.pattern("CAC")
|
||||
.define('A', Items.PAPER)
|
||||
.define('B', ModItems.WIRELESS_TRANSCEIVER.get())
|
||||
.define('C',Items.EMERALD)
|
||||
.unlockedBy("has_wireless_transceiver", has(ModItems.WIRELESS_TRANSCEIVER.get()))
|
||||
.save( consumer)
|
||||
;
|
||||
}
|
||||
|
||||
private void addCardRecipes(Consumer<FinishedRecipe> consumer) {
|
||||
|
|
@ -201,13 +136,6 @@ public class CraftingRecipes extends RecipeProvider {
|
|||
.unlockedBy("has_advanced_card", has(AEItems.ADVANCED_CARD))
|
||||
.save(consumer);
|
||||
|
||||
ShapelessRecipeBuilder.shapeless(RecipeCategory.MISC, ModItems.VIRTUAL_CRAFTING_CARD.get())
|
||||
.requires(AEItems.ADVANCED_CARD)
|
||||
.requires(Items.CRAFTING_TABLE)
|
||||
.requires(Items.LEVER)
|
||||
.unlockedBy("has_virtual_crafting_card_ingredients", has(AEItems.ADVANCED_CARD))
|
||||
.save(consumer);
|
||||
|
||||
// 2x Entity Speed Card
|
||||
NBTShapedRecipeBuilder.shaped(RecipeCategory.MISC, EntitySpeedCardItem.withMultiplier(2))
|
||||
.pattern("SBS")
|
||||
|
|
|
|||
|
|
@ -1,16 +1,10 @@
|
|||
package com.extendedae_plus.init;
|
||||
|
||||
import com.extendedae_plus.ExtendedAEPlus;
|
||||
import com.extendedae_plus.content.ae2.MirrorPatternProviderBlockEntity;
|
||||
import com.extendedae_plus.content.matrix.CrafterCorePlusBlockEntity;
|
||||
import com.extendedae_plus.content.matrix.PatternCorePlusBlockEntity;
|
||||
import com.extendedae_plus.content.matrix.SpeedCorePlusBlockEntity;
|
||||
import com.extendedae_plus.content.wireless.LabeledWirelessTransceiverBlockEntity;
|
||||
import com.extendedae_plus.content.wireless.WirelessTransceiverBlockEntity;
|
||||
import com.extendedae_plus.content.matrix.UploadCoreBlockEntity;
|
||||
import com.extendedae_plus.content.controller.NetworkPatternControllerBlockEntity;
|
||||
import net.minecraft.world.level.block.entity.BlockEntityType;
|
||||
import net.minecraftforge.fml.common.Mod;
|
||||
import net.minecraftforge.registries.DeferredRegister;
|
||||
import net.minecraftforge.registries.ForgeRegistries;
|
||||
import net.minecraftforge.registries.RegistryObject;
|
||||
|
|
@ -26,42 +20,14 @@ public final class ModBlockEntities {
|
|||
() -> BlockEntityType.Builder.of(WirelessTransceiverBlockEntity::new,
|
||||
ModBlocks.WIRELESS_TRANSCEIVER.get()).build(null));
|
||||
|
||||
public static final RegistryObject<BlockEntityType<LabeledWirelessTransceiverBlockEntity>> LABELED_WIRELESS_TRANSCEIVER_BE =
|
||||
BLOCK_ENTITY_TYPES.register("labeled_wireless_transceiver",
|
||||
() -> BlockEntityType.Builder.of(LabeledWirelessTransceiverBlockEntity::new,
|
||||
ModBlocks.LABELED_WIRELESS_TRANSCEIVER.get()).build(null));
|
||||
|
||||
public static final RegistryObject<BlockEntityType<NetworkPatternControllerBlockEntity>> NETWORK_PATTERN_CONTROLLER_BE =
|
||||
BLOCK_ENTITY_TYPES.register("network_pattern_controller",
|
||||
() -> BlockEntityType.Builder.of(NetworkPatternControllerBlockEntity::new,
|
||||
ModBlocks.NETWORK_PATTERN_CONTROLLER.get()).build(null));
|
||||
|
||||
//超级装配矩阵速度核心
|
||||
public static final RegistryObject<BlockEntityType<SpeedCorePlusBlockEntity>> ASSEMBLER_MATRIX_SPEED_PLUS_BE =
|
||||
BLOCK_ENTITY_TYPES.register("assembler_matrix_speed_plus",
|
||||
()-> BlockEntityType.Builder.of(SpeedCorePlusBlockEntity::new,
|
||||
ModBlocks.ASSEMBLER_MATRIX_SPEED_PLUS.get()).build(null));
|
||||
|
||||
//超级装配矩阵合成核心
|
||||
public static final RegistryObject<BlockEntityType<CrafterCorePlusBlockEntity>> ASSEMBLER_MATRIX_CRAFTER_PLUS_BE =
|
||||
BLOCK_ENTITY_TYPES.register("assembler_matrix_crafter_plus",
|
||||
()-> BlockEntityType.Builder.of(CrafterCorePlusBlockEntity::new,
|
||||
ModBlocks.ASSEMBLER_MATRIX_CRAFTER_PLUS.get()).build(null));
|
||||
|
||||
//超级装配矩阵样板核心
|
||||
public static final RegistryObject<BlockEntityType<PatternCorePlusBlockEntity>> ASSEMBLER_MATRIX_PATTERN_PLUS_BE =
|
||||
BLOCK_ENTITY_TYPES.register("assmbler_matrix_pattern_plus",
|
||||
()-> BlockEntityType.Builder.of(PatternCorePlusBlockEntity::new,
|
||||
ModBlocks.ASSEMBLER_MATRIX_PATTERN_PLUS.get()).build(null));
|
||||
|
||||
// 装配矩阵上传核心
|
||||
public static final RegistryObject<BlockEntityType<UploadCoreBlockEntity>> UPLOAD_CORE_BE =
|
||||
BLOCK_ENTITY_TYPES.register("assembler_matrix_upload_core",
|
||||
() -> BlockEntityType.Builder.of(UploadCoreBlockEntity::new,
|
||||
ModBlocks.ASSEMBLER_MATRIX_UPLOAD_CORE.get()).build(null));
|
||||
|
||||
public static final RegistryObject<BlockEntityType<MirrorPatternProviderBlockEntity>> MIRROR_PATTERN_PROVIDER_BE =
|
||||
BLOCK_ENTITY_TYPES.register("mirror_pattern_provider",
|
||||
() -> BlockEntityType.Builder.of(MirrorPatternProviderBlockEntity::new,
|
||||
ModBlocks.MIRROR_PATTERN_PROVIDER.get()).build(null));
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,17 +4,10 @@ import appeng.block.crafting.CraftingUnitBlock;
|
|||
import appeng.blockentity.crafting.CraftingBlockEntity;
|
||||
import appeng.core.definitions.AEBlockEntities;
|
||||
import com.extendedae_plus.ExtendedAEPlus;
|
||||
import com.extendedae_plus.content.ae2.MirrorPatternProviderBlock;
|
||||
import com.extendedae_plus.content.crafting.EPlusCraftingUnitType;
|
||||
import com.extendedae_plus.content.decor.DollBlock;
|
||||
import com.extendedae_plus.content.matrix.CrafterCorePlusBlock;
|
||||
import com.extendedae_plus.content.matrix.PatternCorePlusBlock;
|
||||
import com.extendedae_plus.content.matrix.SpeedCorePlusBlock;
|
||||
import com.extendedae_plus.content.matrix.UploadCoreBlock;
|
||||
import com.extendedae_plus.content.wireless.LabeledWirelessTransceiverBlock;
|
||||
import com.extendedae_plus.content.wireless.WirelessTransceiverBlock;
|
||||
import net.minecraft.world.level.block.Block;
|
||||
import net.minecraft.world.level.block.Blocks;
|
||||
import net.minecraft.world.level.block.state.BlockBehaviour;
|
||||
import net.minecraft.world.level.material.MapColor;
|
||||
import net.minecraftforge.registries.DeferredRegister;
|
||||
|
|
@ -36,16 +29,6 @@ public final class ModBlocks {
|
|||
)
|
||||
);
|
||||
|
||||
public static final RegistryObject<Block> LABELED_WIRELESS_TRANSCEIVER = BLOCKS.register(
|
||||
"labeled_wireless_transceiver",
|
||||
() -> new LabeledWirelessTransceiverBlock(
|
||||
BlockBehaviour.Properties.of()
|
||||
.mapColor(MapColor.METAL)
|
||||
.strength(2F, 6.0F)
|
||||
.requiresCorrectToolForDrops()
|
||||
)
|
||||
);
|
||||
|
||||
// AE2 网络模式控制器方块
|
||||
public static final RegistryObject<Block> NETWORK_PATTERN_CONTROLLER = BLOCKS.register(
|
||||
"network_pattern_controller",
|
||||
|
|
@ -67,23 +50,6 @@ public final class ModBlocks {
|
|||
}
|
||||
);
|
||||
|
||||
// 超级装配矩阵速度核心
|
||||
public static final RegistryObject<SpeedCorePlusBlock> ASSEMBLER_MATRIX_SPEED_PLUS = BLOCKS.register(
|
||||
"assembler_matrix_speed_plus",
|
||||
SpeedCorePlusBlock::new
|
||||
);
|
||||
|
||||
//超级装配矩阵合成核心
|
||||
public static final RegistryObject<CrafterCorePlusBlock> ASSEMBLER_MATRIX_CRAFTER_PLUS = BLOCKS.register(
|
||||
"assembler_matrix_crafter_plus",
|
||||
CrafterCorePlusBlock::new
|
||||
);
|
||||
|
||||
//超级装配矩阵样板核心
|
||||
public static final RegistryObject<PatternCorePlusBlock> ASSEMBLER_MATRIX_PATTERN_PLUS = BLOCKS.register(
|
||||
"assembler_matrix_pattern_plus",
|
||||
PatternCorePlusBlock::new
|
||||
);
|
||||
// Crafting Accelerators (reuse MAE2 textures/models)
|
||||
public static final RegistryObject<CraftingUnitBlock> CRAFTING_ACCELERATOR_4x = BLOCKS.register(
|
||||
"4x_crafting_accelerator",
|
||||
|
|
@ -129,21 +95,4 @@ public final class ModBlocks {
|
|||
return b;
|
||||
}
|
||||
);
|
||||
|
||||
public static final RegistryObject<MirrorPatternProviderBlock> MIRROR_PATTERN_PROVIDER = BLOCKS.register(
|
||||
"mirror_pattern_provider",
|
||||
MirrorPatternProviderBlock::new
|
||||
);
|
||||
|
||||
public static final RegistryObject<Block> C_H716 = registerDollBlock("c-h716");
|
||||
public static final RegistryObject<Block> FISH_DAN = registerDollBlock("fish_dan_");
|
||||
public static final RegistryObject<Block> _FENG = registerDollBlock("_feng");
|
||||
public static final RegistryObject<Block> XBAI = registerDollBlock("xbai");
|
||||
|
||||
private static RegistryObject<Block> registerDollBlock(String name) {
|
||||
return BLOCKS.register(
|
||||
name,
|
||||
() -> new DollBlock(BlockBehaviour.Properties.copy(Blocks.WHITE_WOOL).noOcclusion())
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -17,25 +17,9 @@ public final class ModCreativeTabs {
|
|||
.displayItems((params, output) -> {
|
||||
// 将本模组物品加入创造物品栏
|
||||
output.accept(ModItems.WIRELESS_TRANSCEIVER.get());
|
||||
output.accept(ModItems.LABELED_WIRELESS_TRANSCEIVER.get());
|
||||
output.accept(ModItems.NETWORK_PATTERN_CONTROLLER.get());
|
||||
// 装配矩阵上传核心
|
||||
output.accept(ModItems.ASSEMBLER_MATRIX_UPLOAD_CORE.get());
|
||||
//超级装配矩阵速度核心
|
||||
output.accept(ModItems.ASSEMBLER_MATRIX_SPEED_PLUS.get());
|
||||
//超级装配矩阵合成核心
|
||||
output.accept(ModItems.ASSEMBLER_MATRIX_CRAFTER_PLUS.get());
|
||||
//超级装配矩阵样板核心
|
||||
output.accept(ModItems.ASSEMBLER_MATRIX_PATTERN_PLUS.get());
|
||||
output.accept(ModItems.MIRROR_PATTERN_PROVIDER.get());
|
||||
output.accept(ModItems.C_H716.get());
|
||||
output.accept(ModItems.FISH_DAN.get());
|
||||
output.accept(ModItems._FENG.get());
|
||||
output.accept(ModItems.XBAI.get());
|
||||
output.accept(ModItems.MIRROR_PATTERN_BINDING_TOOL.get());
|
||||
|
||||
|
||||
//实体加速器&加速卡
|
||||
output.accept(ModItems.CRAFTING_ACCELERATOR_4x.get());
|
||||
output.accept(ModItems.CRAFTING_ACCELERATOR_16x.get());
|
||||
output.accept(ModItems.CRAFTING_ACCELERATOR_64x.get());
|
||||
|
|
@ -50,7 +34,6 @@ public final class ModCreativeTabs {
|
|||
output.accept(ModItems.createEntitySpeedCardStack(16));
|
||||
// 频道卡
|
||||
output.accept(ModItems.CHANNEL_CARD.get());
|
||||
output.accept(ModItems.VIRTUAL_CRAFTING_CARD.get());
|
||||
|
||||
output.accept(ModItems.OBLIVION_SINGULARITY.get());
|
||||
output.accept(ModItems.BASIC_CORE.get());
|
||||
|
|
|
|||
|
|
@ -9,8 +9,6 @@ import com.extendedae_plus.items.EntitySpeedTickerPartItem;
|
|||
import com.extendedae_plus.items.InfinityBigIntegerCellItem;
|
||||
import com.extendedae_plus.items.materials.ChannelCardItem;
|
||||
import com.extendedae_plus.items.materials.EntitySpeedCardItem;
|
||||
import com.extendedae_plus.items.materials.VirtualCraftingCardItem;
|
||||
import com.extendedae_plus.items.tools.MirrorPatternBindingToolItem;
|
||||
import com.extendedae_plus.util.ModCheckUtils;
|
||||
import net.minecraft.world.item.BlockItem;
|
||||
import net.minecraft.world.item.Item;
|
||||
|
|
@ -27,11 +25,6 @@ public final class ModItems {
|
|||
() -> new BlockItem(ModBlocks.WIRELESS_TRANSCEIVER.get(), new Item.Properties())
|
||||
);
|
||||
|
||||
public static final RegistryObject<Item> LABELED_WIRELESS_TRANSCEIVER = ITEMS.register(
|
||||
"labeled_wireless_transceiver",
|
||||
() -> new BlockItem(ModBlocks.LABELED_WIRELESS_TRANSCEIVER.get(), new Item.Properties())
|
||||
);
|
||||
|
||||
public static final RegistryObject<Item> NETWORK_PATTERN_CONTROLLER = ITEMS.register(
|
||||
"network_pattern_controller",
|
||||
() -> new BlockItem(ModBlocks.NETWORK_PATTERN_CONTROLLER.get(), new Item.Properties())
|
||||
|
|
@ -43,23 +36,6 @@ public final class ModItems {
|
|||
() -> new BlockItem(ModBlocks.ASSEMBLER_MATRIX_UPLOAD_CORE.get(), new Item.Properties())
|
||||
);
|
||||
|
||||
//超级装配矩阵速度核心
|
||||
public static final RegistryObject<Item> ASSEMBLER_MATRIX_SPEED_PLUS = ITEMS.register(
|
||||
"assembler_matrix_speed_plus",
|
||||
()-> new BlockItem(ModBlocks.ASSEMBLER_MATRIX_SPEED_PLUS.get(), new Item.Properties())
|
||||
);
|
||||
|
||||
//超级装配矩阵合成核心
|
||||
public static final RegistryObject<Item> ASSEMBLER_MATRIX_CRAFTER_PLUS = ITEMS.register(
|
||||
"assembler_matrix_crafter_plus",
|
||||
()-> new BlockItem(ModBlocks.ASSEMBLER_MATRIX_CRAFTER_PLUS.get(), new Item.Properties())
|
||||
);
|
||||
|
||||
//超级装配矩阵样板核心
|
||||
public static final RegistryObject<Item> ASSEMBLER_MATRIX_PATTERN_PLUS = ITEMS.register(
|
||||
"assembler_matrix_pattern_plus",
|
||||
()-> new BlockItem(ModBlocks.ASSEMBLER_MATRIX_PATTERN_PLUS.get(), new Item.Properties())
|
||||
);
|
||||
// Crafting Accelerators
|
||||
public static final RegistryObject<Item> CRAFTING_ACCELERATOR_4x = ITEMS.register(
|
||||
"4x_crafting_accelerator",
|
||||
|
|
@ -86,36 +62,6 @@ public final class ModItems {
|
|||
() -> new BlockItem(ModBlocks.CRAFTING_ACCELERATOR_1024x.get(), new Item.Properties())
|
||||
);
|
||||
|
||||
public static final RegistryObject<Item> MIRROR_PATTERN_PROVIDER = ITEMS.register(
|
||||
"mirror_pattern_provider",
|
||||
() -> new BlockItem(ModBlocks.MIRROR_PATTERN_PROVIDER.get(), new Item.Properties())
|
||||
);
|
||||
|
||||
public static final RegistryObject<Item> C_H716 = ITEMS.register(
|
||||
"c-h716",
|
||||
() -> new BlockItem(ModBlocks.C_H716.get(), new Item.Properties())
|
||||
);
|
||||
|
||||
public static final RegistryObject<Item> FISH_DAN = ITEMS.register(
|
||||
"fish_dan_",
|
||||
() -> new BlockItem(ModBlocks.FISH_DAN.get(), new Item.Properties())
|
||||
);
|
||||
|
||||
public static final RegistryObject<Item> _FENG = ITEMS.register(
|
||||
"_feng",
|
||||
() -> new BlockItem(ModBlocks._FENG.get(), new Item.Properties())
|
||||
);
|
||||
|
||||
public static final RegistryObject<Item> XBAI = ITEMS.register(
|
||||
"xbai",
|
||||
() -> new BlockItem(ModBlocks.XBAI.get(), new Item.Properties())
|
||||
);
|
||||
|
||||
public static final RegistryObject<MirrorPatternBindingToolItem> MIRROR_PATTERN_BINDING_TOOL = ITEMS.register(
|
||||
"mirror_pattern_binding_tool",
|
||||
() -> new MirrorPatternBindingToolItem(new Item.Properties())
|
||||
);
|
||||
|
||||
public static final RegistryObject<EntitySpeedTickerPartItem> ENTITY_TICKER_PART_ITEM = ITEMS.register(
|
||||
"entity_speed_ticker",
|
||||
() -> new EntitySpeedTickerPartItem(new Item.Properties())
|
||||
|
|
@ -138,12 +84,6 @@ public final class ModItems {
|
|||
() -> new ChannelCardItem(new Item.Properties())
|
||||
);
|
||||
|
||||
// 虚拟合成卡
|
||||
public static final RegistryObject<VirtualCraftingCardItem> VIRTUAL_CRAFTING_CARD = ITEMS.register(
|
||||
"virtual_crafting_card",
|
||||
() -> new VirtualCraftingCardItem(new Item.Properties())
|
||||
);
|
||||
|
||||
public static final RegistryObject<BasicCoreItem> BASIC_CORE = ITEMS.register(
|
||||
"basic_core",
|
||||
() -> new BasicCoreItem(new Item.Properties())
|
||||
|
|
|
|||
|
|
@ -4,7 +4,6 @@ import appeng.menu.implementations.MenuTypeBuilder;
|
|||
import com.extendedae_plus.ExtendedAEPlus;
|
||||
import com.extendedae_plus.ae.menu.EntitySpeedTickerMenu;
|
||||
import com.extendedae_plus.ae.parts.EntitySpeedTickerPart;
|
||||
import com.extendedae_plus.menu.LabeledWirelessTransceiverMenu;
|
||||
import com.extendedae_plus.menu.NetworkPatternControllerMenu;
|
||||
import net.minecraft.world.inventory.MenuType;
|
||||
import net.minecraftforge.common.extensions.IForgeMenuType;
|
||||
|
|
@ -23,10 +22,6 @@ public final class ModMenuTypes {
|
|||
MENUS.register("network_pattern_controller",
|
||||
() -> IForgeMenuType.create(NetworkPatternControllerMenu::new));
|
||||
|
||||
public static final RegistryObject<MenuType<LabeledWirelessTransceiverMenu>> LABELED_WIRELESS_TRANSCEIVER =
|
||||
MENUS.register("labeled_wireless_transceiver",
|
||||
() -> IForgeMenuType.create(LabeledWirelessTransceiverMenu::new));
|
||||
|
||||
public static final RegistryObject<MenuType<EntitySpeedTickerMenu>> ENTITY_TICKER_MENU =
|
||||
MENUS.register("entity_speed_ticker",
|
||||
() -> MenuTypeBuilder
|
||||
|
|
|
|||
|
|
@ -5,13 +5,8 @@ import com.extendedae_plus.network.*;
|
|||
import com.extendedae_plus.network.crafting.CraftingMonitorJumpC2SPacket;
|
||||
import com.extendedae_plus.network.crafting.CraftingMonitorOpenProviderC2SPacket;
|
||||
import com.extendedae_plus.network.crafting.OpenCraftFromJeiC2SPacket;
|
||||
import com.extendedae_plus.network.crafting.SetSearchTextS2CPacket;
|
||||
import com.extendedae_plus.network.meInterface.InterfaceAdjustConfigAmountC2SPacket;
|
||||
import com.extendedae_plus.network.pattern.CancelPendingPatternC2SPacket;
|
||||
import com.extendedae_plus.network.pattern.CreateCtrlQPatternC2SPacket;
|
||||
import com.extendedae_plus.network.pattern.CreateAndUploadPatternC2SPacket;
|
||||
import com.extendedae_plus.network.provider.*;
|
||||
import com.extendedae_plus.network.upload.EncodeWithShiftFlagC2SPacket;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraftforge.network.NetworkDirection;
|
||||
import net.minecraftforge.network.NetworkRegistry;
|
||||
|
|
@ -47,30 +42,12 @@ public final class ModNetwork {
|
|||
.consumerNetworkThread(OpenCraftFromJeiC2SPacket::handle)
|
||||
.add();
|
||||
|
||||
CHANNEL.messageBuilder(SetSearchTextS2CPacket.class, nextId(), NetworkDirection.PLAY_TO_CLIENT)
|
||||
.encoder(SetSearchTextS2CPacket::encode)
|
||||
.decoder(SetSearchTextS2CPacket::decode)
|
||||
.consumerNetworkThread(SetSearchTextS2CPacket::handle)
|
||||
.add();
|
||||
|
||||
CHANNEL.messageBuilder(PullFromJeiOrCraftC2SPacket.class, nextId(), NetworkDirection.PLAY_TO_SERVER)
|
||||
.encoder(PullFromJeiOrCraftC2SPacket::encode)
|
||||
.decoder(PullFromJeiOrCraftC2SPacket::decode)
|
||||
.consumerNetworkThread(PullFromJeiOrCraftC2SPacket::handle)
|
||||
.add();
|
||||
|
||||
CHANNEL.messageBuilder(EncodeWithShiftFlagC2SPacket.class, nextId(), NetworkDirection.PLAY_TO_SERVER)
|
||||
.encoder(EncodeWithShiftFlagC2SPacket::encode)
|
||||
.decoder(EncodeWithShiftFlagC2SPacket::decode)
|
||||
.consumerNetworkThread(EncodeWithShiftFlagC2SPacket::handle)
|
||||
.add();
|
||||
|
||||
CHANNEL.messageBuilder(ScaleEncodingPatternC2SPacket.class, nextId(), NetworkDirection.PLAY_TO_SERVER)
|
||||
.encoder(ScaleEncodingPatternC2SPacket::encode)
|
||||
.decoder(ScaleEncodingPatternC2SPacket::decode)
|
||||
.consumerNetworkThread(ScaleEncodingPatternC2SPacket::handle)
|
||||
.add();
|
||||
|
||||
CHANNEL.messageBuilder(UploadEncodedPatternToProviderC2SPacket.class, nextId(), NetworkDirection.PLAY_TO_SERVER)
|
||||
.encoder(UploadEncodedPatternToProviderC2SPacket::encode)
|
||||
.decoder(UploadEncodedPatternToProviderC2SPacket::decode)
|
||||
|
|
@ -166,42 +143,6 @@ public final class ModNetwork {
|
|||
.decoder(SetWirelessFrequencyC2SPacket::decode)
|
||||
.consumerNetworkThread(SetWirelessFrequencyC2SPacket::handle)
|
||||
.add();
|
||||
|
||||
CHANNEL.messageBuilder(LabelNetworkActionC2SPacket.class, nextId(), NetworkDirection.PLAY_TO_SERVER)
|
||||
.encoder(LabelNetworkActionC2SPacket::encode)
|
||||
.decoder(LabelNetworkActionC2SPacket::decode)
|
||||
.consumerNetworkThread(LabelNetworkActionC2SPacket::handle)
|
||||
.add();
|
||||
|
||||
CHANNEL.messageBuilder(LabelNetworkListC2SPacket.class, nextId(), NetworkDirection.PLAY_TO_SERVER)
|
||||
.encoder(LabelNetworkListC2SPacket::encode)
|
||||
.decoder(LabelNetworkListC2SPacket::decode)
|
||||
.consumerNetworkThread(LabelNetworkListC2SPacket::handle)
|
||||
.add();
|
||||
|
||||
CHANNEL.messageBuilder(LabelNetworkListS2CPacket.class, nextId(), NetworkDirection.PLAY_TO_CLIENT)
|
||||
.encoder(LabelNetworkListS2CPacket::encode)
|
||||
.decoder(LabelNetworkListS2CPacket::decode)
|
||||
.consumerNetworkThread(LabelNetworkListS2CPacket::handle)
|
||||
.add();
|
||||
|
||||
CHANNEL.messageBuilder(CreateCtrlQPatternC2SPacket.class, nextId(), NetworkDirection.PLAY_TO_SERVER)
|
||||
.encoder(CreateCtrlQPatternC2SPacket::encode)
|
||||
.decoder(CreateCtrlQPatternC2SPacket::decode)
|
||||
.consumerNetworkThread(CreateCtrlQPatternC2SPacket::handle)
|
||||
.add();
|
||||
|
||||
CHANNEL.messageBuilder(CreateAndUploadPatternC2SPacket.class, nextId(), NetworkDirection.PLAY_TO_SERVER)
|
||||
.encoder(CreateAndUploadPatternC2SPacket::encode)
|
||||
.decoder(CreateAndUploadPatternC2SPacket::decode)
|
||||
.consumerNetworkThread(CreateAndUploadPatternC2SPacket::handle)
|
||||
.add();
|
||||
|
||||
CHANNEL.messageBuilder(CancelPendingPatternC2SPacket.class,nextId(), NetworkDirection.PLAY_TO_SERVER)
|
||||
.encoder(CancelPendingPatternC2SPacket::encode)
|
||||
.decoder(CancelPendingPatternC2SPacket::decode)
|
||||
.consumerNetworkThread(CancelPendingPatternC2SPacket::handle)
|
||||
.add();
|
||||
}
|
||||
|
||||
private static int nextId() { return id++; }
|
||||
|
|
|
|||
|
|
@ -15,29 +15,24 @@ import static com.glodblock.github.extendedae.common.EPPItemAndBlock.*;
|
|||
public final class UpgradeCards {
|
||||
public UpgradeCards(final FMLCommonSetupEvent event) {
|
||||
event.enqueueWork(() -> {
|
||||
// 现有:把 Entity Ticker 的部件注册为处理 SPEED/ENERGY/CHANNEL 卡的宿主
|
||||
// 现有:把 Entity Ticker 的部件注册为处理 SPEED/ENERGY 卡的宿主
|
||||
Upgrades.add(AEItems.ENERGY_CARD, ModItems.ENTITY_TICKER_PART_ITEM.get(), 8, "group.entity_ticker.name");
|
||||
// 使用单一的 UpgradeCard Item 作为注册键,总共允许安装 4 张(不同等级由 ItemStack NBT 区分)
|
||||
Upgrades.add(ModItems.ENTITY_SPEED_CARD.get(), ModItems.ENTITY_TICKER_PART_ITEM.get(), 4, "group.entity_ticker.name");
|
||||
Upgrades.add(ModItems.CHANNEL_CARD.get(), ModItems.ENTITY_TICKER_PART_ITEM.get(), 1, "group.entity_ticker.name");
|
||||
|
||||
// 新增:频道卡仅允许安装在 ME 接口(方块与部件)上,每台最多 1 张
|
||||
String interfaceGroup = GuiText.Interface.getTranslationKey();
|
||||
Upgrades.add(ModItems.CHANNEL_CARD.get(), AEBlocks.INTERFACE, 1, interfaceGroup);
|
||||
Upgrades.add(ModItems.CHANNEL_CARD.get(), AEParts.INTERFACE, 1, interfaceGroup);
|
||||
|
||||
// 新增:样板供应器(方块与部件)支持频道卡、虚拟合成卡,每台最多 1 张
|
||||
// 新增:样板供应器(方块与部件)支持频道卡,每台最多 1 张
|
||||
String patternProviderGroup = "group.pattern_provider.name";
|
||||
Upgrades.add(ModItems.CHANNEL_CARD.get(), AEBlocks.PATTERN_PROVIDER, 1, patternProviderGroup);
|
||||
Upgrades.add(ModItems.CHANNEL_CARD.get(), AEParts.PATTERN_PROVIDER, 1, patternProviderGroup);
|
||||
Upgrades.add(ModItems.VIRTUAL_CRAFTING_CARD.get(), AEBlocks.PATTERN_PROVIDER, 1, patternProviderGroup);
|
||||
Upgrades.add(ModItems.VIRTUAL_CRAFTING_CARD.get(), AEParts.PATTERN_PROVIDER, 1, patternProviderGroup);
|
||||
|
||||
// ExtendedAE 的扩展样板供应器(方块与部件)
|
||||
Upgrades.add(ModItems.CHANNEL_CARD.get(),EX_PATTERN_PROVIDER, 1, patternProviderGroup);
|
||||
Upgrades.add(ModItems.CHANNEL_CARD.get(),EX_PATTERN_PROVIDER_PART, 1, patternProviderGroup);
|
||||
Upgrades.add(ModItems.VIRTUAL_CRAFTING_CARD.get(),EX_PATTERN_PROVIDER, 1, patternProviderGroup);
|
||||
Upgrades.add(ModItems.VIRTUAL_CRAFTING_CARD.get(),EX_PATTERN_PROVIDER_PART, 1, patternProviderGroup);
|
||||
|
||||
//EAE 的扩展接口与超大接口(方块与部件)支持频道卡
|
||||
Upgrades.add(ModItems.CHANNEL_CARD.get(), EX_INTERFACE, 1, interfaceGroup);
|
||||
|
|
@ -52,10 +47,6 @@ public final class UpgradeCards {
|
|||
Upgrades.add(ModItems.CHANNEL_CARD.get(), AEParts.EXPORT_BUS, 1, ioBusGroup);
|
||||
Upgrades.add(ModItems.CHANNEL_CARD.get(), AEParts.STORAGE_BUS, 1, storageGroup);
|
||||
|
||||
String storageCellGroup = GuiText.StorageCells.getTranslationKey();
|
||||
Upgrades.add(AEItems.FUZZY_CARD, ModItems.INFINITY_BIGINTEGER_CELL.get(), 1, storageCellGroup);
|
||||
Upgrades.add(AEItems.INVERTER_CARD, ModItems.INFINITY_BIGINTEGER_CELL.get(), 1, storageCellGroup);
|
||||
|
||||
//EAE 的扩展输入/输出总线支持频道卡(部件)
|
||||
Upgrades.add(ModItems.CHANNEL_CARD.get(), EX_IMPORT_BUS, 1, ioBusGroup);
|
||||
Upgrades.add(ModItems.CHANNEL_CARD.get(), EX_EXPORT_BUS, 1, ioBusGroup);
|
||||
|
|
|
|||
|
|
@ -1,65 +0,0 @@
|
|||
package com.extendedae_plus.integration.jade;
|
||||
|
||||
import net.minecraft.network.chat.Component;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import snownee.jade.api.BlockAccessor;
|
||||
import snownee.jade.api.IBlockComponentProvider;
|
||||
import snownee.jade.api.ITooltip;
|
||||
import snownee.jade.api.config.IPluginConfig;
|
||||
|
||||
public enum LabeledWirelessTransceiverComponents implements IBlockComponentProvider {
|
||||
LABEL_AND_CHANNEL("labeled_wireless_component") {
|
||||
@Override
|
||||
protected void add(BlockAccessor accessor, ITooltip tooltip, IPluginConfig config, net.minecraft.nbt.CompoundTag data) {
|
||||
String label = data.contains("label") ? data.getString("label") : "";
|
||||
tooltip.add(Component.translatable("extendedae_plus.jade.label", label.isEmpty() ? "-" : label));
|
||||
|
||||
// 所有者
|
||||
if (data.contains("ownerName")) {
|
||||
tooltip.add(Component.translatable("extendedae_plus.jade.owner", data.getString("ownerName")));
|
||||
} else if (data.contains("placerId")) {
|
||||
java.util.UUID placerId = data.getUUID("placerId");
|
||||
tooltip.add(Component.translatable("extendedae_plus.jade.owner", placerId.toString().substring(0, 8) + "..."));
|
||||
} else {
|
||||
tooltip.add(Component.translatable("extendedae_plus.jade.owner.public"));
|
||||
}
|
||||
|
||||
// 频道占用
|
||||
if (data.contains("usedChannels") && data.contains("maxChannels")) {
|
||||
int used = data.getInt("usedChannels");
|
||||
int max = data.getInt("maxChannels");
|
||||
if (max <= 0) {
|
||||
tooltip.add(Component.translatable("extendedae_plus.jade.channels", used));
|
||||
} else {
|
||||
tooltip.add(Component.translatable("extendedae_plus.jade.channels_of", used, max));
|
||||
}
|
||||
}
|
||||
|
||||
// 网络在线
|
||||
if (data.contains("networkUsable")) {
|
||||
boolean online = data.getBoolean("networkUsable");
|
||||
tooltip.add(Component.translatable(online ? "extendedae_plus.jade.online" : "extendedae_plus.jade.offline"));
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
private final ResourceLocation uid;
|
||||
|
||||
LabeledWirelessTransceiverComponents(String name) {
|
||||
this.uid = new ResourceLocation("extendedae_plus", name);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ResourceLocation getUid() {
|
||||
return uid;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void appendTooltip(ITooltip tooltip, BlockAccessor accessor, IPluginConfig config) {
|
||||
if (accessor.getServerData() != null) {
|
||||
add(accessor, tooltip, config, accessor.getServerData());
|
||||
}
|
||||
}
|
||||
|
||||
protected abstract void add(BlockAccessor accessor, ITooltip tooltip, IPluginConfig config, net.minecraft.nbt.CompoundTag data);
|
||||
}
|
||||
|
|
@ -1,68 +0,0 @@
|
|||
package com.extendedae_plus.integration.jade;
|
||||
|
||||
import appeng.api.networking.IGrid;
|
||||
import appeng.api.networking.IGridNode;
|
||||
import com.extendedae_plus.content.wireless.LabeledWirelessTransceiverBlockEntity;
|
||||
import com.extendedae_plus.util.wireless.WirelessTeamUtil;
|
||||
import net.minecraft.nbt.CompoundTag;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import snownee.jade.api.BlockAccessor;
|
||||
import snownee.jade.api.IServerDataProvider;
|
||||
|
||||
/**
|
||||
* 标签无线收发器:服务端数据同步(无主从)。
|
||||
*/
|
||||
public enum LabeledWirelessTransceiverProvider implements IServerDataProvider<BlockAccessor> {
|
||||
INSTANCE;
|
||||
|
||||
private static final ResourceLocation UID = new ResourceLocation("extendedae_plus", "labeled_wireless_info");
|
||||
|
||||
@Override
|
||||
public ResourceLocation getUid() {
|
||||
return UID;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void appendServerData(CompoundTag data, BlockAccessor accessor) {
|
||||
if (!(accessor.getBlockEntity() instanceof LabeledWirelessTransceiverBlockEntity be)) return;
|
||||
String label = be.getLabelForDisplay();
|
||||
if (label != null) {
|
||||
data.putString("label", label);
|
||||
}
|
||||
// 所有者
|
||||
var placerId = be.getPlacerId();
|
||||
if (placerId != null && be.getServerLevel() != null) {
|
||||
data.putUUID("placerId", placerId);
|
||||
data.putString("ownerName", WirelessTeamUtil.getNetworkOwnerName(be.getServerLevel(), placerId).getString());
|
||||
}
|
||||
|
||||
IGridNode node = be.getGridNode();
|
||||
IGrid grid = node == null ? null : node.getGrid();
|
||||
boolean networkUsable = false;
|
||||
int usedChannels = 0;
|
||||
int maxChannels = 0;
|
||||
if (node != null && node.isActive()) {
|
||||
for (var connection : node.getConnections()) {
|
||||
usedChannels = Math.max(connection.getUsedChannels(), usedChannels);
|
||||
}
|
||||
if (node instanceof appeng.me.GridNode gridNode) {
|
||||
var channelMode = gridNode.getGrid().getPathingService().getChannelMode();
|
||||
if (channelMode == appeng.api.networking.pathing.ChannelMode.INFINITE) {
|
||||
maxChannels = -1;
|
||||
} else {
|
||||
maxChannels = gridNode.getMaxChannels();
|
||||
}
|
||||
}
|
||||
}
|
||||
if (grid != null) {
|
||||
try {
|
||||
networkUsable = grid.getEnergyService().isNetworkPowered();
|
||||
} catch (Throwable ignored) {
|
||||
networkUsable = false;
|
||||
}
|
||||
}
|
||||
data.putInt("usedChannels", usedChannels);
|
||||
data.putInt("maxChannels", maxChannels);
|
||||
data.putBoolean("networkUsable", networkUsable);
|
||||
}
|
||||
}
|
||||
|
|
@ -2,8 +2,6 @@ package com.extendedae_plus.integration.jade;
|
|||
|
||||
import com.extendedae_plus.content.wireless.WirelessTransceiverBlock;
|
||||
import com.extendedae_plus.content.wireless.WirelessTransceiverBlockEntity;
|
||||
import com.extendedae_plus.content.wireless.LabeledWirelessTransceiverBlock;
|
||||
import com.extendedae_plus.content.wireless.LabeledWirelessTransceiverBlockEntity;
|
||||
import snownee.jade.api.IWailaClientRegistration;
|
||||
import snownee.jade.api.IWailaCommonRegistration;
|
||||
import snownee.jade.api.IWailaPlugin;
|
||||
|
|
@ -16,7 +14,6 @@ public class WirelessTransceiverJadePlugin implements IWailaPlugin {
|
|||
public void register(IWailaCommonRegistration registration) {
|
||||
// 注册服务端数据提供者(用于同步数据)
|
||||
registration.registerBlockDataProvider(WirelessTransceiverProvider.INSTANCE, WirelessTransceiverBlockEntity.class);
|
||||
registration.registerBlockDataProvider(com.extendedae_plus.integration.jade.LabeledWirelessTransceiverProvider.INSTANCE, LabeledWirelessTransceiverBlockEntity.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
@ -25,6 +22,5 @@ public class WirelessTransceiverJadePlugin implements IWailaPlugin {
|
|||
for (var component : WirelessTransceiverJadePluginComponents.values()) {
|
||||
registration.registerBlockComponent(component, WirelessTransceiverBlock.class);
|
||||
}
|
||||
registration.registerBlockComponent(com.extendedae_plus.integration.jade.LabeledWirelessTransceiverComponents.LABEL_AND_CHANNEL, LabeledWirelessTransceiverBlock.class);
|
||||
}
|
||||
}
|
||||
|
|
@ -9,7 +9,6 @@ import mezz.jei.api.runtime.IBookmarkOverlay;
|
|||
import mezz.jei.api.runtime.IJeiRuntime;
|
||||
import mezz.jei.gui.bookmarks.BookmarkList;
|
||||
import mezz.jei.gui.bookmarks.IngredientBookmark;
|
||||
import mezz.jei.gui.input.MouseUtil;
|
||||
import mezz.jei.gui.overlay.elements.IElement;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
import net.minecraftforge.fluids.FluidStack;
|
||||
|
|
@ -19,7 +18,6 @@ import org.spongepowered.asm.mixin.Pseudo;
|
|||
import javax.annotation.Nullable;
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
|
@ -57,49 +55,6 @@ public final class JeiBookmarkBridge {
|
|||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
public static Optional<ITypedIngredient<?>> getIngredientUnderMouse() {
|
||||
return getIngredientUnderMouse(MouseUtil.getX(), MouseUtil.getY());
|
||||
}
|
||||
|
||||
public static Optional<ITypedIngredient<?>> getIngredientUnderMouse(double mouseX, double mouseY) {
|
||||
IJeiRuntime rt = getRuntime();
|
||||
if (rt == null) {
|
||||
return Optional.empty();
|
||||
}
|
||||
|
||||
Optional<ITypedIngredient<?>> ingredient = getTypedIngredientFromClickableSource(rt.getIngredientListOverlay(), mouseX, mouseY);
|
||||
if (ingredient.isPresent()) {
|
||||
return ingredient;
|
||||
}
|
||||
|
||||
IBookmarkOverlay bookmarkOverlay = rt.getBookmarkOverlay();
|
||||
ingredient = getTypedIngredientFromClickableSource(bookmarkOverlay, mouseX, mouseY);
|
||||
if (ingredient.isPresent()) {
|
||||
return ingredient;
|
||||
}
|
||||
|
||||
if (rt.getIngredientListOverlay() != null) {
|
||||
var hovered = rt.getIngredientListOverlay().getIngredientUnderMouse();
|
||||
if (hovered.isPresent()) {
|
||||
return hovered.map(i -> (ITypedIngredient<?>) i);
|
||||
}
|
||||
}
|
||||
|
||||
if (bookmarkOverlay != null) {
|
||||
var hovered = bookmarkOverlay.getIngredientUnderMouse();
|
||||
if (hovered.isPresent()) {
|
||||
return hovered.map(i -> (ITypedIngredient<?>) i);
|
||||
}
|
||||
}
|
||||
|
||||
Optional<Object> bookmark = getBookmarkUnderMouse(bookmarkOverlay, mouseX, mouseY);
|
||||
if (bookmark.isPresent()) {
|
||||
return getTypedIngredientFromBookmark(bookmark.get());
|
||||
}
|
||||
|
||||
return Optional.empty();
|
||||
}
|
||||
|
||||
public static void addBookmark(ItemStack stack) {
|
||||
IJeiRuntime rt = getRuntime();
|
||||
if (rt == null) return;
|
||||
|
|
@ -205,27 +160,6 @@ public final class JeiBookmarkBridge {
|
|||
return typeField;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取鼠标下的配方书签(如果存在)
|
||||
*
|
||||
* @return 配方书签对象(RecipeBookmark<?, ?>),如果不是配方书签则返回空
|
||||
*/
|
||||
public static Optional<?> getRecipeBookmarkUnderMouse() {
|
||||
Optional<?> bookmark = getBookmarkUnderMouse();
|
||||
if (bookmark.isPresent() && isRecipeBookmark(bookmark.get())) {
|
||||
return bookmark;
|
||||
}
|
||||
return Optional.empty();
|
||||
}
|
||||
|
||||
public static Optional<?> getBookmarkUnderMouse() {
|
||||
IJeiRuntime rt = getRuntime();
|
||||
if (rt == null) {
|
||||
return Optional.empty();
|
||||
}
|
||||
return getBookmarkUnderMouse(rt.getBookmarkOverlay(), MouseUtil.getX(), MouseUtil.getY());
|
||||
}
|
||||
|
||||
/**
|
||||
* 从 JEI 书签移除物品
|
||||
*/
|
||||
|
|
@ -244,190 +178,4 @@ public final class JeiBookmarkBridge {
|
|||
});
|
||||
}
|
||||
}
|
||||
|
||||
private static Optional<Object> getClickableIngredientUnderMouse(Object owner, double mouseX, double mouseY) {
|
||||
if (owner == null) {
|
||||
return Optional.empty();
|
||||
}
|
||||
try {
|
||||
Object streamObj = owner.getClass()
|
||||
.getMethod("getIngredientUnderMouse", double.class, double.class)
|
||||
.invoke(owner, mouseX, mouseY);
|
||||
if (streamObj instanceof java.util.stream.Stream<?> stream) {
|
||||
return Optional.ofNullable(stream.findFirst().orElse(null));
|
||||
}
|
||||
} catch (Throwable ignored) {
|
||||
}
|
||||
return Optional.empty();
|
||||
}
|
||||
|
||||
private static Optional<ITypedIngredient<?>> getTypedIngredientFromClickableSource(Object owner, double mouseX, double mouseY) {
|
||||
return getClickableIngredientUnderMouse(owner, mouseX, mouseY)
|
||||
.flatMap(JeiBookmarkBridge::getElementFromClickable)
|
||||
.flatMap(JeiBookmarkBridge::getTypedIngredientFromElement);
|
||||
}
|
||||
|
||||
private static Optional<Object> getBookmarkUnderMouse(IBookmarkOverlay overlay, double mouseX, double mouseY) {
|
||||
if (overlay == null) {
|
||||
return Optional.empty();
|
||||
}
|
||||
|
||||
Optional<Object> bookmark = getClickableIngredientUnderMouse(overlay, mouseX, mouseY)
|
||||
.flatMap(JeiBookmarkBridge::getElementFromClickable)
|
||||
.flatMap(JeiBookmarkBridge::getBookmarkFromElement);
|
||||
if (bookmark.isPresent()) {
|
||||
return bookmark;
|
||||
}
|
||||
|
||||
Optional<ITypedIngredient<?>> hoveredIngredient = getTypedIngredientFromClickableSource(overlay, mouseX, mouseY);
|
||||
if (hoveredIngredient.isEmpty()) {
|
||||
hoveredIngredient = overlay.getIngredientUnderMouse();
|
||||
}
|
||||
if (hoveredIngredient.isEmpty()) {
|
||||
return Optional.empty();
|
||||
}
|
||||
|
||||
return findBookmarkByTypedIngredient(overlay, hoveredIngredient.get());
|
||||
}
|
||||
|
||||
private static Optional<Object> getElementFromClickable(Object clickable) {
|
||||
if (clickable == null) {
|
||||
return Optional.empty();
|
||||
}
|
||||
try {
|
||||
return Optional.ofNullable(clickable.getClass().getMethod("getElement").invoke(clickable));
|
||||
} catch (Throwable ignored) {
|
||||
return Optional.empty();
|
||||
}
|
||||
}
|
||||
|
||||
private static Optional<Object> getBookmarkFromElement(Object element) {
|
||||
if (element == null) {
|
||||
return Optional.empty();
|
||||
}
|
||||
try {
|
||||
Object bookmarkOpt = element.getClass().getMethod("getBookmark").invoke(element);
|
||||
if (bookmarkOpt instanceof Optional<?> optional && optional.isPresent()) {
|
||||
return Optional.ofNullable(optional.get());
|
||||
}
|
||||
} catch (Throwable ignored) {
|
||||
}
|
||||
return Optional.empty();
|
||||
}
|
||||
|
||||
private static Optional<ITypedIngredient<?>> getTypedIngredientFromElement(Object element) {
|
||||
if (element == null) {
|
||||
return Optional.empty();
|
||||
}
|
||||
try {
|
||||
Object typed = element.getClass().getMethod("getTypedIngredient").invoke(element);
|
||||
if (typed instanceof ITypedIngredient<?> typedIngredient) {
|
||||
return Optional.of(typedIngredient);
|
||||
}
|
||||
} catch (Throwable ignored) {
|
||||
}
|
||||
return Optional.empty();
|
||||
}
|
||||
|
||||
private static Optional<ITypedIngredient<?>> getTypedIngredientFromBookmark(Object bookmark) {
|
||||
if (bookmark == null) {
|
||||
return Optional.empty();
|
||||
}
|
||||
try {
|
||||
Object typed = bookmark.getClass().getMethod("getDisplayIngredient").invoke(bookmark);
|
||||
if (typed instanceof ITypedIngredient<?> typedIngredient) {
|
||||
return Optional.of(typedIngredient);
|
||||
}
|
||||
} catch (Throwable ignored) {
|
||||
}
|
||||
try {
|
||||
Object typed = bookmark.getClass().getMethod("getIngredient").invoke(bookmark);
|
||||
if (typed instanceof ITypedIngredient<?> typedIngredient) {
|
||||
return Optional.of(typedIngredient);
|
||||
}
|
||||
} catch (Throwable ignored) {
|
||||
}
|
||||
try {
|
||||
Object element = bookmark.getClass().getMethod("getElement").invoke(bookmark);
|
||||
return getTypedIngredientFromElement(element);
|
||||
} catch (Throwable ignored) {
|
||||
return Optional.empty();
|
||||
}
|
||||
}
|
||||
|
||||
private static Optional<Object> findBookmarkByTypedIngredient(IBookmarkOverlay overlay, ITypedIngredient<?> hoveredIngredient) {
|
||||
if (overlay == null || hoveredIngredient == null) {
|
||||
return Optional.empty();
|
||||
}
|
||||
Object firstMatch = null;
|
||||
for (Object bookmark : getBookmarksFromOverlay(overlay)) {
|
||||
Optional<ITypedIngredient<?>> typedIngredient = getTypedIngredientFromBookmark(bookmark);
|
||||
if (typedIngredient.isPresent() && hoveredIngredient.equals(typedIngredient.get())) {
|
||||
if (isRecipeBookmark(bookmark)) {
|
||||
return Optional.of(bookmark);
|
||||
}
|
||||
if (firstMatch == null) {
|
||||
firstMatch = bookmark;
|
||||
}
|
||||
}
|
||||
}
|
||||
return Optional.ofNullable(firstMatch);
|
||||
}
|
||||
|
||||
private static List<Object> getBookmarksFromOverlay(IBookmarkOverlay overlay) {
|
||||
if (overlay == null) {
|
||||
return List.of();
|
||||
}
|
||||
|
||||
List<Object> bookmarks = new ArrayList<>();
|
||||
Object bookmarkList = readFieldValue(overlay, "bookmarkList");
|
||||
Object bookmarkValues = readFieldValue(bookmarkList, "bookmarksList");
|
||||
addBookmarks(bookmarks, bookmarkValues);
|
||||
|
||||
Object lookupHistoryOverlay = readFieldValue(overlay, "lookupHistoryOverlay");
|
||||
Object lookupHistory = readFieldValue(lookupHistoryOverlay, "lookupHistory");
|
||||
Object historyValues = readFieldValue(lookupHistory, "elements");
|
||||
addBookmarks(bookmarks, historyValues);
|
||||
return bookmarks;
|
||||
}
|
||||
|
||||
private static void addBookmarks(List<Object> target, Object source) {
|
||||
if (!(source instanceof List<?> list)) {
|
||||
return;
|
||||
}
|
||||
for (Object value : list) {
|
||||
if (value != null) {
|
||||
target.add(value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static Object readFieldValue(Object owner, String fieldName) {
|
||||
if (owner == null) {
|
||||
return null;
|
||||
}
|
||||
try {
|
||||
Field field = owner.getClass().getDeclaredField(fieldName);
|
||||
field.setAccessible(true);
|
||||
return field.get(owner);
|
||||
} catch (Throwable ignored) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean isRecipeBookmark(Object bookmark) {
|
||||
if (bookmark == null) {
|
||||
return false;
|
||||
}
|
||||
if ("RecipeBookmark".equals(bookmark.getClass().getSimpleName())) {
|
||||
return true;
|
||||
}
|
||||
try {
|
||||
bookmark.getClass().getMethod("getRecipeCategory");
|
||||
bookmark.getClass().getMethod("getRecipe");
|
||||
return true;
|
||||
} catch (Throwable ignored) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -35,14 +35,6 @@ public final class JeiRuntimeProxy {
|
|||
}
|
||||
|
||||
public static Optional<ITypedIngredient<?>> getIngredientUnderMouse() {
|
||||
try {
|
||||
Class<?> mouseUtil = Class.forName("mezz.jei.gui.input.MouseUtil");
|
||||
double mouseX = ((Number) mouseUtil.getMethod("getX").invoke(null)).doubleValue();
|
||||
double mouseY = ((Number) mouseUtil.getMethod("getY").invoke(null)).doubleValue();
|
||||
return getIngredientUnderMouse(mouseX, mouseY);
|
||||
} catch (Throwable ignored) {
|
||||
}
|
||||
|
||||
IJeiRuntime rt = RUNTIME;
|
||||
if (rt == null) return Optional.empty();
|
||||
|
||||
|
|
@ -63,17 +55,6 @@ public final class JeiRuntimeProxy {
|
|||
* 在 JEI 配方界面区域内,基于屏幕坐标查询鼠标下的配料(优先物品,其次流体)。
|
||||
*/
|
||||
public static Optional<ITypedIngredient<?>> getIngredientUnderMouse(double mouseX, double mouseY) {
|
||||
try {
|
||||
Class<?> bridge = Class.forName("com.extendedae_plus.integration.jei.JeiBookmarkBridge");
|
||||
var method = bridge.getMethod("getIngredientUnderMouse", double.class, double.class);
|
||||
@SuppressWarnings("unchecked")
|
||||
Optional<ITypedIngredient<?>> result = (Optional<ITypedIngredient<?>>) method.invoke(null, mouseX, mouseY);
|
||||
if (result != null && result.isPresent()) {
|
||||
return result;
|
||||
}
|
||||
} catch (Throwable ignored) {
|
||||
}
|
||||
|
||||
IJeiRuntime rt = RUNTIME;
|
||||
if (rt == null || rt.getRecipesGui() == null) return Optional.empty();
|
||||
|
||||
|
|
@ -186,35 +167,6 @@ public final class JeiRuntimeProxy {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取鼠标下的配方书签(如果存在)
|
||||
*
|
||||
* @return 配方书签对象(RecipeBookmark<?, ?>),如果不是配方书签则返回空
|
||||
*/
|
||||
public static Optional<?> getRecipeBookmarkUnderMouse() {
|
||||
try {
|
||||
Class<?> bridge = Class.forName("com.extendedae_plus.integration.jei.JeiBookmarkBridge");
|
||||
var m = bridge.getMethod("getRecipeBookmarkUnderMouse");
|
||||
@SuppressWarnings("unchecked")
|
||||
Optional<?> result = (Optional<?>) m.invoke(null);
|
||||
return result == null ? Optional.empty() : result;
|
||||
} catch (Throwable ignored) {
|
||||
return Optional.empty();
|
||||
}
|
||||
}
|
||||
|
||||
public static Optional<?> getBookmarkUnderMouse() {
|
||||
try {
|
||||
Class<?> bridge = Class.forName("com.extendedae_plus.integration.jei.JeiBookmarkBridge");
|
||||
var m = bridge.getMethod("getBookmarkUnderMouse");
|
||||
@SuppressWarnings("unchecked")
|
||||
Optional<?> result = (Optional<?>) m.invoke(null);
|
||||
return result == null ? Optional.empty() : result;
|
||||
} catch (Throwable ignored) {
|
||||
return Optional.empty();
|
||||
}
|
||||
}
|
||||
|
||||
// Note: helper methods moved to bridge to avoid referencing JEI GUI at class load time.
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -2,10 +2,6 @@ package com.extendedae_plus.items;
|
|||
|
||||
import appeng.api.config.FuzzyMode;
|
||||
import appeng.api.storage.cells.ICellWorkbenchItem;
|
||||
import appeng.api.upgrades.IUpgradeInventory;
|
||||
import appeng.api.upgrades.UpgradeInventories;
|
||||
import appeng.items.contents.CellConfig;
|
||||
import appeng.util.ConfigInventory;
|
||||
import com.extendedae_plus.api.storage.InfinityBigIntegerCellInventory;
|
||||
import com.extendedae_plus.util.storage.InfinityConstants;
|
||||
import com.google.common.base.Preconditions;
|
||||
|
|
@ -98,31 +94,12 @@ public class InfinityBigIntegerCellItem extends Item implements ICellWorkbenchIt
|
|||
return stack;
|
||||
}
|
||||
|
||||
@Override
|
||||
public IUpgradeInventory getUpgrades(ItemStack itemStack) {
|
||||
return UpgradeInventories.forItem(itemStack, 4);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ConfigInventory getConfigInventory(ItemStack itemStack) {
|
||||
return CellConfig.create(itemStack);
|
||||
}
|
||||
|
||||
@Override
|
||||
public FuzzyMode getFuzzyMode(ItemStack itemStack) {
|
||||
final String fz = itemStack.getOrCreateTag().getString("FuzzyMode");
|
||||
if (fz.isEmpty()) {
|
||||
return FuzzyMode.IGNORE_ALL;
|
||||
}
|
||||
try {
|
||||
return FuzzyMode.valueOf(fz);
|
||||
} catch (Throwable t) {
|
||||
return FuzzyMode.IGNORE_ALL;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setFuzzyMode(ItemStack itemStack, FuzzyMode fuzzyMode) {
|
||||
itemStack.getOrCreateTag().putString("FuzzyMode", fuzzyMode.name());
|
||||
}
|
||||
}
|
||||
|
|
@ -1,31 +0,0 @@
|
|||
package com.extendedae_plus.items.materials;
|
||||
|
||||
import appeng.items.materials.UpgradeCardItem;
|
||||
import net.minecraft.network.chat.Component;
|
||||
import net.minecraft.world.item.Item;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
import net.minecraft.world.item.TooltipFlag;
|
||||
import net.minecraft.world.level.Level;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 虚拟合成卡:安装在样板供应器中以触发自动完成逻辑。
|
||||
*/
|
||||
public class VirtualCraftingCardItem extends UpgradeCardItem {
|
||||
|
||||
public VirtualCraftingCardItem(Item.Properties properties) {
|
||||
super(properties);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void appendHoverText(ItemStack stack,
|
||||
@Nullable Level level,
|
||||
List<Component> lines,
|
||||
TooltipFlag flag) {
|
||||
super.appendHoverText(stack, level, lines, flag);
|
||||
lines.add(Component.translatable("item.extendedae_plus.virtual_crafting_card.tooltip_main"));
|
||||
lines.add(Component.translatable("item.extendedae_plus.virtual_crafting_card.tooltip_detail"));
|
||||
}
|
||||
}
|
||||
|
|
@ -1,382 +0,0 @@
|
|||
package com.extendedae_plus.items.tools;
|
||||
|
||||
import appeng.blockentity.networking.CableBusBlockEntity;
|
||||
import appeng.helpers.patternprovider.PatternProviderLogicHost;
|
||||
import appeng.parts.AEBasePart;
|
||||
import com.extendedae_plus.content.ae2.MirrorPatternProviderBlockEntity;
|
||||
import com.extendedae_plus.content.ae2.MirrorPatternProviderBlockEntity.MasterLocation;
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.core.Direction;
|
||||
import net.minecraft.core.GlobalPos;
|
||||
import net.minecraft.core.registries.Registries;
|
||||
import net.minecraft.nbt.CompoundTag;
|
||||
import net.minecraft.nbt.Tag;
|
||||
import net.minecraft.network.chat.Component;
|
||||
import net.minecraft.network.chat.MutableComponent;
|
||||
import net.minecraft.resources.ResourceKey;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraft.world.InteractionHand;
|
||||
import net.minecraft.world.InteractionResult;
|
||||
import net.minecraft.world.InteractionResultHolder;
|
||||
import net.minecraft.world.entity.player.Player;
|
||||
import net.minecraft.world.item.Item;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
import net.minecraft.world.item.TooltipFlag;
|
||||
import net.minecraft.world.item.context.UseOnContext;
|
||||
import net.minecraft.world.level.Level;
|
||||
import net.minecraft.world.phys.Vec3;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class MirrorPatternBindingToolItem extends Item {
|
||||
private static final String TAG_SELECTED_MASTER = "selectedMaster";
|
||||
private static final String TAG_SELECTED_RANGE_START = "selectedRangeStart";
|
||||
private static final String TAG_DIMENSION = "dimension";
|
||||
private static final String TAG_POS = "pos";
|
||||
private static final String TAG_SIDE = "side";
|
||||
|
||||
public MirrorPatternBindingToolItem(Properties properties) {
|
||||
super(properties.stacksTo(1));
|
||||
}
|
||||
|
||||
@Override
|
||||
public InteractionResult onItemUseFirst(ItemStack stack, UseOnContext context) {
|
||||
return this.handleBlockUse(context, stack);
|
||||
}
|
||||
|
||||
@Override
|
||||
public InteractionResult useOn(UseOnContext context) {
|
||||
return this.handleBlockUse(context, context.getItemInHand());
|
||||
}
|
||||
|
||||
private InteractionResult handleBlockUse(UseOnContext context, ItemStack stack) {
|
||||
var level = context.getLevel();
|
||||
var player = context.getPlayer();
|
||||
var blockEntity = level.getBlockEntity(context.getClickedPos());
|
||||
|
||||
var clickedMaster = getClickedMaster(level, context);
|
||||
if (clickedMaster != null) {
|
||||
if (player != null && player.isShiftKeyDown()) {
|
||||
if (!level.isClientSide) {
|
||||
setSelectedMaster(stack, clickedMaster);
|
||||
clearSelectedRangeStart(stack);
|
||||
player.displayClientMessage(createSelectedMessage(clickedMaster), true);
|
||||
}
|
||||
return InteractionResult.sidedSuccess(level.isClientSide);
|
||||
}
|
||||
|
||||
return InteractionResult.PASS;
|
||||
}
|
||||
|
||||
if (blockEntity instanceof MirrorPatternProviderBlockEntity mirror) {
|
||||
if (player != null && player.isShiftKeyDown()) {
|
||||
if (!level.isClientSide) {
|
||||
this.handleRangeBinding(level, context.getClickedPos(), stack, player);
|
||||
}
|
||||
return InteractionResult.sidedSuccess(level.isClientSide);
|
||||
}
|
||||
|
||||
if (!level.isClientSide) {
|
||||
if (mirror.hasMasterBinding()) {
|
||||
if (player != null && mirror.unbindFromMaster()) {
|
||||
player.displayClientMessage(mirror.createUnboundMessage(), true);
|
||||
}
|
||||
return InteractionResult.SUCCESS;
|
||||
}
|
||||
|
||||
var selectedMaster = getSelectedMaster(stack);
|
||||
if (selectedMaster == null) {
|
||||
if (player != null) {
|
||||
player.displayClientMessage(
|
||||
Component.translatable("extendedae_plus.message.mirror_binding_tool.no_selection"),
|
||||
true);
|
||||
}
|
||||
return InteractionResult.SUCCESS;
|
||||
}
|
||||
|
||||
if (mirror.bindToMaster(selectedMaster)) {
|
||||
if (player != null) {
|
||||
player.displayClientMessage(mirror.createBoundMessage(), true);
|
||||
}
|
||||
} else if (player != null) {
|
||||
player.displayClientMessage(
|
||||
Component.translatable("extendedae_plus.message.mirror_binding_tool.bind_failed"),
|
||||
true);
|
||||
}
|
||||
}
|
||||
|
||||
return InteractionResult.sidedSuccess(level.isClientSide);
|
||||
}
|
||||
|
||||
return InteractionResult.PASS;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void appendHoverText(ItemStack stack, @Nullable Level level, List<Component> tooltipComponents,
|
||||
TooltipFlag tooltipFlag) {
|
||||
super.appendHoverText(stack, level, tooltipComponents, tooltipFlag);
|
||||
|
||||
tooltipComponents.add(Component.translatable("item.extendedae_plus.mirror_pattern_binding_tool.tip.select"));
|
||||
tooltipComponents.add(Component.translatable("item.extendedae_plus.mirror_pattern_binding_tool.tip.bind"));
|
||||
tooltipComponents.add(Component.translatable("item.extendedae_plus.mirror_pattern_binding_tool.tip.unbind"));
|
||||
tooltipComponents.add(Component.translatable("item.extendedae_plus.mirror_pattern_binding_tool.tip.range"));
|
||||
|
||||
var selectedMaster = getSelectedMaster(stack);
|
||||
if (selectedMaster != null) {
|
||||
var pos = selectedMaster.pos();
|
||||
tooltipComponents.add(Component.translatable(
|
||||
"item.extendedae_plus.mirror_pattern_binding_tool.selected",
|
||||
pos.getX(),
|
||||
pos.getY(),
|
||||
pos.getZ()));
|
||||
tooltipComponents.add(Component.translatable(
|
||||
"item.extendedae_plus.mirror_pattern_binding_tool.dimension",
|
||||
selectedMaster.dimension().location().toString()));
|
||||
if (selectedMaster.side() != null) {
|
||||
tooltipComponents.add(Component.literal("方向: " + selectedMaster.side().getSerializedName()));
|
||||
}
|
||||
} else {
|
||||
tooltipComponents.add(Component.translatable("item.extendedae_plus.mirror_pattern_binding_tool.unset"));
|
||||
}
|
||||
|
||||
var selectedRangeStart = getSelectedRangeStart(stack);
|
||||
if (selectedRangeStart != null) {
|
||||
var pos = selectedRangeStart.pos();
|
||||
tooltipComponents.add(Component.translatable(
|
||||
"item.extendedae_plus.mirror_pattern_binding_tool.range_start",
|
||||
pos.getX(),
|
||||
pos.getY(),
|
||||
pos.getZ()));
|
||||
}
|
||||
}
|
||||
|
||||
private static void setSelectedMaster(ItemStack stack, MasterLocation master) {
|
||||
var tag = stack.getOrCreateTag();
|
||||
tag.put(TAG_SELECTED_MASTER, createMasterTag(master));
|
||||
}
|
||||
|
||||
private static void clearSelectedMaster(ItemStack stack) {
|
||||
var tag = stack.getTag();
|
||||
if (tag != null) {
|
||||
tag.remove(TAG_SELECTED_MASTER);
|
||||
}
|
||||
}
|
||||
|
||||
private static void setSelectedRangeStart(ItemStack stack, GlobalPos start) {
|
||||
var tag = stack.getOrCreateTag();
|
||||
tag.put(TAG_SELECTED_RANGE_START, createGlobalPosTag(start));
|
||||
}
|
||||
|
||||
private static void clearSelectedRangeStart(ItemStack stack) {
|
||||
var tag = stack.getTag();
|
||||
if (tag != null) {
|
||||
tag.remove(TAG_SELECTED_RANGE_START);
|
||||
}
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private static MasterLocation getSelectedMaster(ItemStack stack) {
|
||||
return getStoredMasterLocation(stack, TAG_SELECTED_MASTER);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private static GlobalPos getSelectedRangeStart(ItemStack stack) {
|
||||
return getStoredGlobalPos(stack, TAG_SELECTED_RANGE_START);
|
||||
}
|
||||
|
||||
private static CompoundTag createMasterTag(MasterLocation master) {
|
||||
var selectedTag = createGlobalPosTag(master.asGlobalPos());
|
||||
if (master.side() != null) {
|
||||
selectedTag.putString(TAG_SIDE, master.side().getSerializedName());
|
||||
}
|
||||
return selectedTag;
|
||||
}
|
||||
|
||||
private static CompoundTag createGlobalPosTag(GlobalPos globalPos) {
|
||||
var selectedTag = new CompoundTag();
|
||||
selectedTag.putString(TAG_DIMENSION, globalPos.dimension().location().toString());
|
||||
selectedTag.putLong(TAG_POS, globalPos.pos().asLong());
|
||||
return selectedTag;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private static MasterLocation getStoredMasterLocation(ItemStack stack, String tagKey) {
|
||||
var globalPos = getStoredGlobalPos(stack, tagKey);
|
||||
if (globalPos == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
var tag = stack.getTag();
|
||||
if (tag == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
var selectedTag = tag.getCompound(tagKey);
|
||||
Direction side = null;
|
||||
if (selectedTag.contains(TAG_SIDE, Tag.TAG_STRING)) {
|
||||
side = Direction.byName(selectedTag.getString(TAG_SIDE));
|
||||
}
|
||||
|
||||
return new MasterLocation(globalPos.dimension(), globalPos.pos(), side);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private static GlobalPos getStoredGlobalPos(ItemStack stack, String tagKey) {
|
||||
var tag = stack.getTag();
|
||||
if (tag == null || !tag.contains(tagKey, Tag.TAG_COMPOUND)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
var selectedTag = tag.getCompound(tagKey);
|
||||
if (!selectedTag.contains(TAG_DIMENSION, Tag.TAG_STRING) || !selectedTag.contains(TAG_POS, Tag.TAG_LONG)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return GlobalPos.of(
|
||||
ResourceKey.create(Registries.DIMENSION, new ResourceLocation(selectedTag.getString(TAG_DIMENSION))),
|
||||
BlockPos.of(selectedTag.getLong(TAG_POS)));
|
||||
}
|
||||
|
||||
private void handleRangeBinding(Level level, BlockPos clickedPos, ItemStack stack, Player player) {
|
||||
var selectedMaster = getSelectedMaster(stack);
|
||||
if (selectedMaster == null) {
|
||||
player.displayClientMessage(
|
||||
Component.translatable("extendedae_plus.message.mirror_binding_tool.no_selection"),
|
||||
true);
|
||||
return;
|
||||
}
|
||||
|
||||
var rangeStart = getSelectedRangeStart(stack);
|
||||
if (rangeStart == null || !rangeStart.dimension().equals(level.dimension())) {
|
||||
setSelectedRangeStart(stack, GlobalPos.of(level.dimension(), clickedPos));
|
||||
player.displayClientMessage(
|
||||
Component.translatable(
|
||||
"extendedae_plus.message.mirror_binding_tool.range_start_selected",
|
||||
clickedPos.getX(),
|
||||
clickedPos.getY(),
|
||||
clickedPos.getZ()),
|
||||
true);
|
||||
return;
|
||||
}
|
||||
|
||||
var rangeEnd = clickedPos.immutable();
|
||||
var bindResult = bindMirrorsInRange(level, rangeStart.pos(), rangeEnd, selectedMaster);
|
||||
clearSelectedRangeStart(stack);
|
||||
|
||||
if (bindResult.totalMirrors() == 0) {
|
||||
player.displayClientMessage(
|
||||
Component.translatable("extendedae_plus.message.mirror_binding_tool.range_no_mirror"),
|
||||
true);
|
||||
return;
|
||||
}
|
||||
|
||||
player.displayClientMessage(
|
||||
Component.translatable(
|
||||
"extendedae_plus.message.mirror_binding_tool.range_bound",
|
||||
rangeStart.pos().getX(),
|
||||
rangeStart.pos().getY(),
|
||||
rangeStart.pos().getZ(),
|
||||
rangeEnd.getX(),
|
||||
rangeEnd.getY(),
|
||||
rangeEnd.getZ(),
|
||||
bindResult.totalMirrors(),
|
||||
bindResult.boundMirrors(),
|
||||
bindResult.failedMirrors()),
|
||||
true);
|
||||
}
|
||||
|
||||
private static RangeBindResult bindMirrorsInRange(Level level, BlockPos start, BlockPos end, MasterLocation selectedMaster) {
|
||||
int totalMirrors = 0;
|
||||
int boundMirrors = 0;
|
||||
|
||||
var minX = Math.min(start.getX(), end.getX());
|
||||
var minY = Math.min(start.getY(), end.getY());
|
||||
var minZ = Math.min(start.getZ(), end.getZ());
|
||||
var maxX = Math.max(start.getX(), end.getX());
|
||||
var maxY = Math.max(start.getY(), end.getY());
|
||||
var maxZ = Math.max(start.getZ(), end.getZ());
|
||||
|
||||
for (var pos : BlockPos.betweenClosed(minX, minY, minZ, maxX, maxY, maxZ)) {
|
||||
var blockEntity = level.getBlockEntity(pos);
|
||||
if (blockEntity instanceof MirrorPatternProviderBlockEntity mirror) {
|
||||
totalMirrors++;
|
||||
if (mirror.bindToMaster(selectedMaster)) {
|
||||
boundMirrors++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return new RangeBindResult(totalMirrors, boundMirrors, totalMirrors - boundMirrors);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private static MasterLocation getClickedMaster(Level level, UseOnContext context) {
|
||||
var pos = context.getClickedPos();
|
||||
var blockEntity = level.getBlockEntity(pos);
|
||||
if (blockEntity == null || blockEntity instanceof MirrorPatternProviderBlockEntity) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (blockEntity instanceof PatternProviderLogicHost) {
|
||||
return new MasterLocation(level.dimension(), pos, null);
|
||||
}
|
||||
|
||||
if (blockEntity instanceof CableBusBlockEntity cableBus) {
|
||||
Vec3 hitVec = context.getClickLocation();
|
||||
Vec3 hitInBlock = new Vec3(hitVec.x - pos.getX(), hitVec.y - pos.getY(), hitVec.z - pos.getZ());
|
||||
var selectedPart = cableBus.getCableBus().selectPartLocal(hitInBlock).part;
|
||||
if (selectedPart instanceof AEBasePart basePart
|
||||
&& selectedPart instanceof PatternProviderLogicHost) {
|
||||
return new MasterLocation(level.dimension(), pos, basePart.getSide());
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private static Component createSelectedMessage(MasterLocation master) {
|
||||
return appendSide(
|
||||
Component.translatable(
|
||||
"extendedae_plus.message.mirror_binding_tool.selected",
|
||||
master.pos().getX(),
|
||||
master.pos().getY(),
|
||||
master.pos().getZ()),
|
||||
master.side());
|
||||
}
|
||||
|
||||
private static Component appendSide(MutableComponent message, @Nullable Direction side) {
|
||||
if (side != null) {
|
||||
message.append(Component.literal(" [" + side.getSerializedName() + "]"));
|
||||
}
|
||||
return message;
|
||||
}
|
||||
|
||||
@Override
|
||||
public InteractionResultHolder<ItemStack> use(Level level, Player player, InteractionHand usedHand) {
|
||||
var stack = player.getItemInHand(usedHand);
|
||||
|
||||
if (!player.isShiftKeyDown()) {
|
||||
return InteractionResultHolder.pass(stack);
|
||||
}
|
||||
|
||||
if (!level.isClientSide) {
|
||||
if (getSelectedMaster(stack) != null || getSelectedRangeStart(stack) != null) {
|
||||
clearSelectedMaster(stack);
|
||||
clearSelectedRangeStart(stack);
|
||||
player.displayClientMessage(
|
||||
Component.translatable("extendedae_plus.message.mirror_binding_tool.cleared"),
|
||||
true);
|
||||
} else {
|
||||
player.displayClientMessage(
|
||||
Component.translatable("extendedae_plus.message.mirror_binding_tool.no_selection"),
|
||||
true);
|
||||
}
|
||||
}
|
||||
|
||||
return InteractionResultHolder.sidedSuccess(stack, level.isClientSide);
|
||||
}
|
||||
|
||||
private record RangeBindResult(int totalMirrors, int boundMirrors, int failedMirrors) {
|
||||
}
|
||||
}
|
||||
|
|
@ -1,41 +0,0 @@
|
|||
package com.extendedae_plus.menu;
|
||||
|
||||
import com.extendedae_plus.init.ModMenuTypes;
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.network.FriendlyByteBuf;
|
||||
import net.minecraft.world.entity.player.Inventory;
|
||||
import net.minecraft.world.entity.player.Player;
|
||||
import net.minecraft.world.inventory.AbstractContainerMenu;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
|
||||
/**
|
||||
* 标签无线收发器菜单(暂无线槽,仅用于打开客户端界面)。
|
||||
*/
|
||||
public class LabeledWirelessTransceiverMenu extends AbstractContainerMenu {
|
||||
private final BlockPos bePos;
|
||||
|
||||
public LabeledWirelessTransceiverMenu(int id, Inventory inv, BlockPos bePos) {
|
||||
super(ModMenuTypes.LABELED_WIRELESS_TRANSCEIVER.get(), id);
|
||||
this.bePos = bePos;
|
||||
}
|
||||
|
||||
public LabeledWirelessTransceiverMenu(int id, Inventory inv, FriendlyByteBuf buf) {
|
||||
this(id, inv, buf.readBlockPos());
|
||||
}
|
||||
|
||||
public BlockPos getBlockEntityPos() {
|
||||
return bePos;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean stillValid(Player player) {
|
||||
return player.level() != null
|
||||
&& player.level().getBlockEntity(bePos) != null
|
||||
&& player.distanceToSqr(bePos.getX() + 0.5, bePos.getY() + 0.5, bePos.getZ() + 0.5) <= 64;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ItemStack quickMoveStack(Player player, int index) {
|
||||
return ItemStack.EMPTY;
|
||||
}
|
||||
}
|
||||
|
|
@ -36,25 +36,11 @@ public class MixinConditions implements IMixinConfigPlugin {
|
|||
return ModCheckUtils.isLoaded(ModCheckUtils.MODID_AAE);
|
||||
}
|
||||
|
||||
// === AppFlux 兼容 ===
|
||||
if (mixinClassName.startsWith("com.extendedae_plus.mixin.appflux")) {
|
||||
return ModCheckUtils.isLoaded(ModCheckUtils.MODID_APPFLUX);
|
||||
}
|
||||
|
||||
// === JEI 兼容 ===
|
||||
if (mixinClassName.startsWith("com.extendedae_plus.mixin.jei.")) {
|
||||
return ModCheckUtils.isLoaded("jei");
|
||||
}
|
||||
|
||||
// === GuideME 版本兼容 ===
|
||||
if (mixinClassName.startsWith("com.extendedae_plus.mixin.guideme.")) {
|
||||
return ModCheckUtils.isLoadedAndLowerThan(ModCheckUtils.MODID_GUIDEME, "20.1.14");
|
||||
}
|
||||
|
||||
if (mixinClassName.contains("CraftConfirmScreen")) {
|
||||
return !ModCheckUtils.isLoaded(ModCheckUtils.MODID_EPA);
|
||||
}
|
||||
|
||||
return true;
|
||||
} catch (Exception e) {
|
||||
System.err.println("[ExtendedAE_Plus] 检查 Mixin 条件时出错: " + e.getMessage());
|
||||
|
|
|
|||
|
|
@ -1,17 +0,0 @@
|
|||
package com.extendedae_plus.mixin.advancedae.accessor;
|
||||
|
||||
import net.pedroksl.advanced_ae.common.logic.AdvCraftingCPULogic;
|
||||
import net.pedroksl.advanced_ae.common.logic.ExecutingCraftingJob;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.gen.Accessor;
|
||||
import org.spongepowered.asm.mixin.gen.Invoker;
|
||||
|
||||
@Mixin(value = AdvCraftingCPULogic.class, remap = false)
|
||||
public interface AdvCraftingCPULogicAccessor {
|
||||
|
||||
@Accessor("job")
|
||||
ExecutingCraftingJob eap$getAdvJob();
|
||||
|
||||
@Invoker("finishJob")
|
||||
void eap$invokeAdvFinishJob(boolean success);
|
||||
}
|
||||
|
|
@ -1,15 +0,0 @@
|
|||
package com.extendedae_plus.mixin.advancedae.accessor;
|
||||
|
||||
import appeng.api.crafting.IPatternDetails;
|
||||
import net.pedroksl.advanced_ae.common.logic.ExecutingCraftingJob;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.gen.Accessor;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
@Mixin(value = ExecutingCraftingJob.class, remap = false)
|
||||
public interface AdvExecutingCraftingJobAccessor {
|
||||
|
||||
@Accessor("tasks")
|
||||
Map<IPatternDetails, Object> eap$getAdvTasks();
|
||||
}
|
||||
|
|
@ -1,11 +0,0 @@
|
|||
package com.extendedae_plus.mixin.advancedae.accessor;
|
||||
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.gen.Accessor;
|
||||
|
||||
@Mixin(targets = "net.pedroksl.advanced_ae.common.logic.ExecutingCraftingJob$TaskProgress", remap = false)
|
||||
public interface AdvExecutingCraftingJobTaskProgressAccessor {
|
||||
|
||||
@Accessor("value")
|
||||
long eap$getAdvValue();
|
||||
}
|
||||
|
|
@ -1,112 +0,0 @@
|
|||
package com.extendedae_plus.mixin.advancedae.compat;
|
||||
|
||||
import appeng.api.networking.crafting.ICraftingCPU;
|
||||
import appeng.api.networking.crafting.ICraftingService;
|
||||
import appeng.api.crafting.IPatternDetails;
|
||||
import appeng.api.stacks.KeyCounter;
|
||||
import appeng.helpers.patternprovider.PatternProviderLogic;
|
||||
import com.extendedae_plus.compat.PatternProviderLogicVirtualCompatBridge;
|
||||
import com.extendedae_plus.mixin.advancedae.accessor.AdvCraftingCPULogicAccessor;
|
||||
import com.extendedae_plus.mixin.advancedae.accessor.AdvExecutingCraftingJobAccessor;
|
||||
import com.extendedae_plus.mixin.advancedae.accessor.AdvExecutingCraftingJobTaskProgressAccessor;
|
||||
import net.pedroksl.advanced_ae.common.cluster.AdvCraftingCPU;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.Unique;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Inject;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
@Mixin(value = PatternProviderLogic.class, priority = 450, remap = false)
|
||||
public abstract class PatternProviderLogicVirtualCompletionMixin {
|
||||
|
||||
@Inject(method = "pushPattern", at = @At("RETURN"))
|
||||
private void eap$advancedaeVirtualCompletion(IPatternDetails patternDetails, KeyCounter[] inputHolder,
|
||||
CallbackInfoReturnable<Boolean> cir) {
|
||||
if (!cir.getReturnValueZ()) {
|
||||
return;
|
||||
}
|
||||
if (!(this instanceof PatternProviderLogicVirtualCompatBridge bridge)) {
|
||||
return;
|
||||
}
|
||||
if (!bridge.eap$compatIsVirtualCraftingEnabled()) {
|
||||
return;
|
||||
}
|
||||
|
||||
var grid = bridge.eap$compatGetGrid();
|
||||
if (grid == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
ICraftingService craftingService = grid.getCraftingService();
|
||||
if (craftingService == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (ICraftingCPU cpu : craftingService.getCpus()) {
|
||||
if (!cpu.isBusy()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (cpu instanceof AdvCraftingCPU advCpu) {
|
||||
var logic = advCpu.craftingLogic;
|
||||
if (logic instanceof AdvCraftingCPULogicAccessor advLogicAccessor) {
|
||||
var job = advLogicAccessor.eap$getAdvJob();
|
||||
if (job != null && job instanceof AdvExecutingCraftingJobAccessor advJobAccessor) {
|
||||
var tasks = advJobAccessor.eap$getAdvTasks();
|
||||
var progress = tasks.get(patternDetails);
|
||||
if (progress instanceof AdvExecutingCraftingJobTaskProgressAccessor advProgressAccessor) {
|
||||
if (eap$advancedaeShouldFinishWholeJob(tasks, advProgressAccessor)) {
|
||||
boolean finished = false;
|
||||
try {
|
||||
advCpu.updateOutput(null);
|
||||
} catch (Throwable ignored) {
|
||||
}
|
||||
try {
|
||||
advLogicAccessor.eap$invokeAdvFinishJob(true);
|
||||
finished = true;
|
||||
} catch (Throwable ignored) {
|
||||
}
|
||||
if (!finished) {
|
||||
try {
|
||||
advCpu.cancelJob();
|
||||
} catch (Throwable ignored) {
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Unique
|
||||
private boolean eap$advancedaeShouldFinishWholeJob(
|
||||
Map<IPatternDetails, Object> tasks,
|
||||
AdvExecutingCraftingJobTaskProgressAccessor matchedProgress) {
|
||||
if (matchedProgress.eap$getAdvValue() > 1) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for (var entry : tasks.entrySet()) {
|
||||
var taskProgress = entry.getValue();
|
||||
if (!(taskProgress instanceof AdvExecutingCraftingJobTaskProgressAccessor advTaskProgress)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
long remaining = advTaskProgress.eap$getAdvValue();
|
||||
if (taskProgress == matchedProgress) {
|
||||
remaining -= 1;
|
||||
}
|
||||
|
||||
if (remaining > 0) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,17 +0,0 @@
|
|||
package com.extendedae_plus.mixin.ae2.accessor;
|
||||
|
||||
import appeng.crafting.execution.CraftingCpuLogic;
|
||||
import appeng.crafting.execution.ExecutingCraftingJob;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.gen.Accessor;
|
||||
import org.spongepowered.asm.mixin.gen.Invoker;
|
||||
|
||||
@Mixin(value = CraftingCpuLogic.class, remap = false)
|
||||
public interface CraftingCpuLogicAccessor {
|
||||
|
||||
@Accessor("job")
|
||||
ExecutingCraftingJob extendedae_plus$getJob();
|
||||
|
||||
@Invoker("finishJob")
|
||||
void extendedae_plus$invokeFinishJob(boolean success);
|
||||
}
|
||||
|
|
@ -1,29 +0,0 @@
|
|||
package com.extendedae_plus.mixin.ae2.accessor;
|
||||
|
||||
import appeng.api.crafting.IPatternDetails;
|
||||
import appeng.api.stacks.GenericStack;
|
||||
import appeng.crafting.CraftingLink;
|
||||
import appeng.crafting.execution.ExecutingCraftingJob;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.gen.Accessor;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
@Mixin(value = ExecutingCraftingJob.class, remap = false)
|
||||
public interface ExecutingCraftingJobAccessor {
|
||||
|
||||
@Accessor("tasks")
|
||||
Map<IPatternDetails, ExecutingCraftingJobTaskProgressAccessor> extendedae_plus$getTasks();
|
||||
|
||||
@Accessor("playerId")
|
||||
Integer extendedae_plus$getPlayerId();
|
||||
|
||||
@Accessor("finalOutput")
|
||||
GenericStack extendedae_plus$getFinalOutput();
|
||||
|
||||
@Accessor("remainingAmount")
|
||||
long extendedae_plus$getRemainingAmount();
|
||||
|
||||
@Accessor("link")
|
||||
CraftingLink extendedae_plus$getLink();
|
||||
}
|
||||
|
|
@ -1,11 +0,0 @@
|
|||
package com.extendedae_plus.mixin.ae2.accessor;
|
||||
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.gen.Accessor;
|
||||
|
||||
@Mixin(targets = "appeng.crafting.execution.ExecutingCraftingJob$TaskProgress", remap = false)
|
||||
public interface ExecutingCraftingJobTaskProgressAccessor {
|
||||
|
||||
@Accessor("value")
|
||||
long extendedae_plus$getValue();
|
||||
}
|
||||
|
|
@ -2,7 +2,6 @@ package com.extendedae_plus.mixin.ae2.accessor;
|
|||
|
||||
import appeng.menu.me.items.PatternEncodingTermMenu;
|
||||
import appeng.menu.slot.RestrictedInputSlot;
|
||||
import appeng.util.ConfigInventory;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.gen.Accessor;
|
||||
|
||||
|
|
@ -13,10 +12,4 @@ public interface PatternEncodingTermMenuAccessor {
|
|||
|
||||
@Accessor(value = "blankPatternSlot",remap = false)
|
||||
RestrictedInputSlot eap$getBlankPatternSlot();
|
||||
|
||||
@Accessor(value = "encodedInputsInv", remap = false)
|
||||
ConfigInventory eap$getEncodedInputsInv();
|
||||
|
||||
@Accessor(value = "encodedOutputsInv", remap = false)
|
||||
ConfigInventory eap$getEncodedOutputsInv();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,7 +1,6 @@
|
|||
package com.extendedae_plus.mixin.ae2.autopattern;
|
||||
|
||||
import appeng.api.crafting.IPatternDetails;
|
||||
import appeng.api.networking.crafting.ICraftingProvider;
|
||||
import appeng.crafting.CraftingCalculation;
|
||||
import appeng.crafting.CraftingPlan;
|
||||
import appeng.crafting.inv.CraftingSimulationState;
|
||||
|
|
@ -12,12 +11,12 @@ import com.extendedae_plus.api.smartDoubling.ICraftingCalculationExt;
|
|||
import com.extendedae_plus.api.smartDoubling.ISmartDoublingAwarePattern;
|
||||
import com.extendedae_plus.config.ModConfig;
|
||||
import com.extendedae_plus.util.smartDoubling.PatternScaler;
|
||||
import com.google.common.collect.Iterables;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Inject;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
|
||||
|
|
@ -35,7 +34,6 @@ public abstract class CraftingSimulationStateMixin {
|
|||
Map<IPatternDetails, Long> crafts = accessor.getCrafts();
|
||||
// 存放最终分配后的 crafts
|
||||
Map<IPatternDetails, Long> finalCrafts = new LinkedHashMap<>();
|
||||
Map<IPatternDetails, Integer> providerCountCache = new HashMap<>();
|
||||
|
||||
for (Map.Entry<IPatternDetails, Long> entry : crafts.entrySet()) {
|
||||
IPatternDetails details = entry.getKey();
|
||||
|
|
@ -67,11 +65,7 @@ public abstract class CraftingSimulationStateMixin {
|
|||
// 检查是否开启 provider 轮询分配功能
|
||||
if (ModConfig.INSTANCE.providerRoundRobinEnable) {
|
||||
CraftingService craftingService = (CraftingService) ((ICraftingCalculationExt) calculation).getGrid().getCraftingService();
|
||||
int providerCount = Math.max(
|
||||
providerCountCache.computeIfAbsent(
|
||||
getProviderCacheKey(processingPattern),
|
||||
key -> countProvidersUpTo(craftingService.getProviders(key), totalAmount)),
|
||||
1);
|
||||
int providerCount = Math.max(Iterables.size(craftingService.getProviders(processingPattern)), 1);
|
||||
|
||||
// totalAmount < providerCount → 只激活 totalAmount 台 provider
|
||||
if (totalAmount < providerCount) {
|
||||
|
|
@ -117,27 +111,4 @@ public abstract class CraftingSimulationStateMixin {
|
|||
crafts.clear();
|
||||
crafts.putAll(finalCrafts);
|
||||
}
|
||||
|
||||
private static IPatternDetails getProviderCacheKey(IPatternDetails pattern) {
|
||||
if (pattern instanceof ScaledProcessingPattern scaled) {
|
||||
return scaled.getOriginal();
|
||||
}
|
||||
return pattern;
|
||||
}
|
||||
|
||||
private static int countProvidersUpTo(Iterable<ICraftingProvider> providers, long maxNeeded) {
|
||||
int count = 0;
|
||||
int limit = maxNeeded <= 0
|
||||
? Integer.MAX_VALUE
|
||||
: maxNeeded >= Integer.MAX_VALUE ? Integer.MAX_VALUE : (int) maxNeeded;
|
||||
|
||||
for (var ignored : providers) {
|
||||
count++;
|
||||
if (count >= limit) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,105 @@
|
|||
package com.extendedae_plus.mixin.ae2.autopattern.gtceu;
|
||||
|
||||
import appeng.api.crafting.IPatternDetails;
|
||||
import appeng.crafting.pattern.AEProcessingPattern;
|
||||
import com.extendedae_plus.api.crafting.ScaledProcessingPattern;
|
||||
import com.extendedae_plus.api.smartDoubling.ISmartDoublingAwarePattern;
|
||||
import com.google.common.collect.BiMap;
|
||||
import org.gtlcore.gtlcore.common.machine.multiblock.part.ae.MEPatternBufferPartMachine;
|
||||
import org.spongepowered.asm.mixin.Final;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.Pseudo;
|
||||
import org.spongepowered.asm.mixin.Shadow;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Inject;
|
||||
import org.spongepowered.asm.mixin.injection.Redirect;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
|
||||
@Pseudo
|
||||
@Mixin(value = MEPatternBufferPartMachine.class, remap = false)
|
||||
public class GTLCoreMEPatternBufferPartMachineMixin {
|
||||
|
||||
@Final
|
||||
@Shadow
|
||||
private BiMap<IPatternDetails, Integer> patternSlotMap;
|
||||
|
||||
// 设置样板总成是否翻倍
|
||||
@Inject(method = "getAvailablePatterns", at = @At(value = "INVOKE", target = "Ljava/util/stream/Stream;toList()Ljava/util/List;", shift = At.Shift.BEFORE))
|
||||
private void beforeToList(CallbackInfoReturnable<List<IPatternDetails>> cir) {
|
||||
if (patternSlotMap == null) return;
|
||||
for (Map.Entry<IPatternDetails, Integer> entry : patternSlotMap.entrySet()) {
|
||||
IPatternDetails key = entry.getKey();
|
||||
if (key instanceof AEProcessingPattern proc && proc instanceof ISmartDoublingAwarePattern aware) {
|
||||
aware.eap$setAllowScaling(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 重定向containsKey检查
|
||||
@Redirect(method = "pushPattern(Lappeng/api/crafting/IPatternDetails;[Lappeng/api/stacks/KeyCounter;)Z",
|
||||
at = @At(value = "INVOKE", target = "Lcom/google/common/collect/BiMap;containsKey(Ljava/lang/Object;)Z"))
|
||||
private boolean redirectContainsKey(BiMap<IPatternDetails, ?> detailsSlotMap, Object key) {
|
||||
try {
|
||||
// 如果key是ScaledProcessingPattern类型,尝试用其原始pattern进行判断
|
||||
if (key instanceof ScaledProcessingPattern scaled) {
|
||||
IPatternDetails base = scaled.getOriginal();
|
||||
if (base != null) {
|
||||
// 避免递归重定向,直接遍历keySet判断
|
||||
for (IPatternDetails d : detailsSlotMap.keySet()) {
|
||||
if (Objects.equals(d, base)) return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
// 常规判断,遍历keySet
|
||||
for (IPatternDetails d : detailsSlotMap.keySet()) {
|
||||
if (Objects.equals(d, key)) return true;
|
||||
}
|
||||
return false;
|
||||
} catch (Throwable t) {
|
||||
// 出现异常时,回退到常规判断
|
||||
for (IPatternDetails d : detailsSlotMap.keySet()) {
|
||||
if (Objects.equals(d, key)) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@Redirect(method = "pushPattern(Lappeng/api/crafting/IPatternDetails;[Lappeng/api/stacks/KeyCounter;)Z",
|
||||
at = @At(value = "INVOKE", target = "Lcom/google/common/collect/BiMap;get(Ljava/lang/Object;)Ljava/lang/Object;"))
|
||||
private Object redirectGet(BiMap<IPatternDetails, ?> detailsSlotMap, Object key) {
|
||||
try {
|
||||
// 如果是 ScaledProcessingPattern,优先尝试其原始 pattern 对应的值
|
||||
if (key instanceof ScaledProcessingPattern scaled) {
|
||||
IPatternDetails base = scaled.getOriginal();
|
||||
if (base != null) {
|
||||
for (Map.Entry<IPatternDetails, ?> e : detailsSlotMap.entrySet()) {
|
||||
if (Objects.equals(e.getKey(), base)) {
|
||||
return e.getValue();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 常规查找:遍历 entrySet 避免再次调用 BiMap.get 导致递归重定向
|
||||
for (Map.Entry<IPatternDetails, ?> e : detailsSlotMap.entrySet()) {
|
||||
if (Objects.equals(e.getKey(), key)) {
|
||||
return e.getValue();
|
||||
}
|
||||
}
|
||||
return null;
|
||||
} catch (Throwable t) {
|
||||
for (Map.Entry<IPatternDetails, ?> e : detailsSlotMap.entrySet()) {
|
||||
if (Objects.equals(e.getKey(), key)) {
|
||||
return e.getValue();
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -0,0 +1,105 @@
|
|||
package com.extendedae_plus.mixin.ae2.autopattern.gtceu;
|
||||
|
||||
import appeng.api.crafting.IPatternDetails;
|
||||
import appeng.crafting.pattern.AEProcessingPattern;
|
||||
import com.extendedae_plus.api.crafting.ScaledProcessingPattern;
|
||||
import com.extendedae_plus.api.smartDoubling.ISmartDoublingAwarePattern;
|
||||
import com.google.common.collect.BiMap;
|
||||
import com.gregtechceu.gtceu.integration.ae2.machine.MEPatternBufferPartMachine;
|
||||
import org.spongepowered.asm.mixin.Final;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.Pseudo;
|
||||
import org.spongepowered.asm.mixin.Shadow;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Inject;
|
||||
import org.spongepowered.asm.mixin.injection.Redirect;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
|
||||
@Pseudo
|
||||
@Mixin(value = MEPatternBufferPartMachine.class, remap = false)
|
||||
public class MEPatternBufferPartMachineMixin {
|
||||
|
||||
@Shadow
|
||||
@Final
|
||||
private BiMap<IPatternDetails, Integer> detailsSlotMap;
|
||||
|
||||
// 设置样板总成是否翻倍
|
||||
@Inject(method = "getAvailablePatterns", at = @At(value = "INVOKE", target = "Ljava/util/stream/Stream;toList()Ljava/util/List;", shift = At.Shift.BEFORE))
|
||||
private void beforeToList(CallbackInfoReturnable<List<IPatternDetails>> cir) {
|
||||
if (detailsSlotMap == null) return;
|
||||
for (Map.Entry<IPatternDetails, Integer> entry : detailsSlotMap.entrySet()) {
|
||||
IPatternDetails key = entry.getKey();
|
||||
if (key instanceof AEProcessingPattern proc && proc instanceof ISmartDoublingAwarePattern aware) {
|
||||
aware.eap$setAllowScaling(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 重定向containsKey检查
|
||||
@Redirect(method = "pushPattern(Lappeng/api/crafting/IPatternDetails;[Lappeng/api/stacks/KeyCounter;)Z",
|
||||
at = @At(value = "INVOKE", target = "Lcom/google/common/collect/BiMap;containsKey(Ljava/lang/Object;)Z"))
|
||||
private boolean redirectContainsKey(BiMap<IPatternDetails, ?> detailsSlotMap, Object key) {
|
||||
try {
|
||||
// 如果key是ScaledProcessingPattern类型,尝试用其原始pattern进行判断
|
||||
if (key instanceof ScaledProcessingPattern scaled) {
|
||||
IPatternDetails base = scaled.getOriginal();
|
||||
if (base != null) {
|
||||
// 避免递归重定向,直接遍历keySet判断
|
||||
for (IPatternDetails d : detailsSlotMap.keySet()) {
|
||||
if (Objects.equals(d, base)) return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
// 常规判断,遍历keySet
|
||||
for (IPatternDetails d : detailsSlotMap.keySet()) {
|
||||
if (Objects.equals(d, key)) return true;
|
||||
}
|
||||
return false;
|
||||
} catch (Throwable t) {
|
||||
// 出现异常时,回退到常规判断
|
||||
for (IPatternDetails d : detailsSlotMap.keySet()) {
|
||||
if (Objects.equals(d, key)) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@Redirect(method = "pushPattern(Lappeng/api/crafting/IPatternDetails;[Lappeng/api/stacks/KeyCounter;)Z",
|
||||
at = @At(value = "INVOKE", target = "Lcom/google/common/collect/BiMap;get(Ljava/lang/Object;)Ljava/lang/Object;"))
|
||||
private Object redirectGet(BiMap<IPatternDetails, ?> detailsSlotMap, Object key) {
|
||||
try {
|
||||
// 如果是 ScaledProcessingPattern,优先尝试其原始 pattern 对应的值
|
||||
if (key instanceof ScaledProcessingPattern scaled) {
|
||||
IPatternDetails base = scaled.getOriginal();
|
||||
if (base != null) {
|
||||
for (Map.Entry<IPatternDetails, ?> e : detailsSlotMap.entrySet()) {
|
||||
if (Objects.equals(e.getKey(), base)) {
|
||||
return e.getValue();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 常规查找:遍历 entrySet 避免再次调用 BiMap.get 导致递归重定向
|
||||
for (Map.Entry<IPatternDetails, ?> e : detailsSlotMap.entrySet()) {
|
||||
if (Objects.equals(e.getKey(), key)) {
|
||||
return e.getValue();
|
||||
}
|
||||
}
|
||||
return null;
|
||||
} catch (Throwable t) {
|
||||
for (Map.Entry<IPatternDetails, ?> e : detailsSlotMap.entrySet()) {
|
||||
if (Objects.equals(e.getKey(), key)) {
|
||||
return e.getValue();
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -19,18 +19,14 @@ import com.extendedae_plus.mixin.minecraft.accessor.AbstractContainerScreenAcces
|
|||
import com.extendedae_plus.mixin.minecraft.accessor.ScreenAccessor;
|
||||
import com.extendedae_plus.network.crafting.CraftingMonitorJumpC2SPacket;
|
||||
import com.extendedae_plus.network.crafting.CraftingMonitorOpenProviderC2SPacket;
|
||||
import com.extendedae_plus.api.upload.IGuiExPatternTerminalUploadAccessor;
|
||||
import com.extendedae_plus.util.GuiUtil;
|
||||
import com.glodblock.github.extendedae.client.gui.GuiExPatternProvider;
|
||||
import com.glodblock.github.extendedae.client.gui.GuiExPatternTerminal;
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.client.gui.Font;
|
||||
import net.minecraft.client.gui.GuiGraphics;
|
||||
import net.minecraft.client.gui.screens.Screen;
|
||||
import net.minecraft.client.renderer.Rect2i;
|
||||
import net.minecraft.network.chat.Component;
|
||||
import net.minecraft.world.inventory.Slot;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
import net.minecraft.network.chat.contents.TranslatableContents;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
|
|
@ -82,57 +78,6 @@ public abstract class AEBaseScreenMixin {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 在 AEBaseScreen 的 mouseClicked 入口拦截 GuiExPatternTerminal 的 Shift+左键快速上传样板功能
|
||||
*/
|
||||
@Inject(method = "mouseClicked", at = @At("HEAD"), cancellable = true)
|
||||
private void eap$exPatternTerminalShiftClick(double mouseX, double mouseY, int button, CallbackInfoReturnable<Boolean> cir) {
|
||||
// 仅处理 GuiExPatternTerminal 实例
|
||||
Object self = this;
|
||||
if (!(self instanceof GuiExPatternTerminal<?> terminal) || !(self instanceof IGuiExPatternTerminalUploadAccessor accessor)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// 检查是否是左键点击 + Shift键
|
||||
if (button != 0 || !Screen.hasShiftDown()) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
// 获取点击的槽位(通过accessor访问hoveredSlot字段)
|
||||
if (!(self instanceof AbstractContainerScreenAccessor<?> screenAccessor)) {
|
||||
return;
|
||||
}
|
||||
Slot hoveredSlot = screenAccessor.eap$getHoveredSlot();
|
||||
if (hoveredSlot == null || hoveredSlot.container != Minecraft.getInstance().player.getInventory()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// 点击的是玩家背包槽位
|
||||
ItemStack clickedItem = hoveredSlot.getItem();
|
||||
|
||||
// 检查是否是有效的编码样板
|
||||
if (clickedItem.isEmpty() || !PatternDetailsHelper.isEncodedPattern(clickedItem)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// 检查是否选择了样板供应器
|
||||
if (accessor.eap$getCurrentlyChoicePatternProvider() != -1) {
|
||||
// 执行快速上传
|
||||
accessor.eap$quickUploadPattern(hoveredSlot.getSlotIndex());
|
||||
// 取消默认的点击行为
|
||||
cir.setReturnValue(true);
|
||||
} else {
|
||||
// 显示提示消息:请先选择一个样板供应器
|
||||
if (Minecraft.getInstance().player != null) {
|
||||
Minecraft.getInstance().player.displayClientMessage(
|
||||
Component.translatable("extendedae_plus.screen.upload.select_provider_first"), false);
|
||||
}
|
||||
}
|
||||
} catch (Throwable ignored) {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 为所有可见的样板槽位添加数量显示
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -1,29 +1,24 @@
|
|||
package com.extendedae_plus.mixin.ae2.client.gui;
|
||||
|
||||
import appeng.api.config.ActionItems;
|
||||
import appeng.client.gui.Icon;
|
||||
import appeng.client.gui.me.items.PatternEncodingTermScreen;
|
||||
import appeng.client.gui.style.ScreenStyle;
|
||||
import appeng.client.gui.style.WidgetStyle;
|
||||
import appeng.client.gui.widgets.ActionButton;
|
||||
import appeng.client.gui.widgets.IconButton;
|
||||
import com.extendedae_plus.init.ModNetwork;
|
||||
import com.extendedae_plus.mixin.ae2.accessor.AEBaseScreenAccessor;
|
||||
import com.extendedae_plus.mixin.minecraft.accessor.AbstractContainerScreenAccessor;
|
||||
import com.extendedae_plus.mixin.minecraft.accessor.ScreenAccessor;
|
||||
import com.extendedae_plus.network.provider.RequestProvidersListC2SPacket;
|
||||
import com.extendedae_plus.network.upload.EncodeWithShiftFlagC2SPacket;
|
||||
import com.mojang.blaze3d.systems.RenderSystem;
|
||||
import net.minecraft.client.gui.GuiGraphics;
|
||||
import net.minecraft.client.gui.components.Tooltip;
|
||||
import net.minecraft.client.gui.screens.Screen;
|
||||
import net.minecraft.client.renderer.Rect2i;
|
||||
import net.minecraft.network.chat.Component;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.Unique;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Inject;
|
||||
import org.spongepowered.asm.mixin.injection.ModifyVariable;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
||||
|
||||
/**
|
||||
|
|
@ -37,15 +32,6 @@ public abstract class PatternEncodingTermScreenMixin {
|
|||
|
||||
@Unique private IconButton eap$uploadBtn;
|
||||
|
||||
@ModifyVariable(method = "<init>", at = @At(value = "STORE"), name = "encodeBtn")
|
||||
private ActionButton eap$wrapEncodeButton(ActionButton original) {
|
||||
return new ActionButton(ActionItems.ENCODE, act -> {
|
||||
ModNetwork.CHANNEL.sendToServer(new EncodeWithShiftFlagC2SPacket(Screen.hasShiftDown()));
|
||||
var screen = (PatternEncodingTermScreen<?>) (Object) this;
|
||||
screen.getMenu().encode();
|
||||
});
|
||||
}
|
||||
|
||||
// 只创建按钮
|
||||
@Inject(method = "<init>", at = @At("TAIL"))
|
||||
private void eap$createUploadButton(CallbackInfo ci) {
|
||||
|
|
|
|||
|
|
@ -1,222 +0,0 @@
|
|||
package com.extendedae_plus.mixin.ae2.client.gui;
|
||||
|
||||
import appeng.client.gui.WidgetContainer;
|
||||
import appeng.client.gui.me.items.EncodingModePanel;
|
||||
import appeng.client.gui.me.items.PatternEncodingTermScreen;
|
||||
import appeng.client.gui.me.items.ProcessingEncodingPanel;
|
||||
import appeng.client.gui.widgets.ActionButton;
|
||||
import com.extendedae_plus.ExtendedAEPlus;
|
||||
import com.extendedae_plus.client.screen.ProviderSelectScreen;
|
||||
import com.extendedae_plus.client.gui.widgets.ScaledTextureButton;
|
||||
import com.extendedae_plus.init.ModNetwork;
|
||||
import com.extendedae_plus.mixin.minecraft.accessor.AbstractContainerScreenAccessor;
|
||||
import com.extendedae_plus.mixin.minecraft.accessor.ScreenAccessor;
|
||||
import com.extendedae_plus.network.ScaleEncodingPatternC2SPacket;
|
||||
import net.minecraft.network.chat.Component;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import org.spongepowered.asm.mixin.Final;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.Shadow;
|
||||
import org.spongepowered.asm.mixin.Unique;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Inject;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
||||
|
||||
@SuppressWarnings({ "AddedMixinMembersNamePattern" })
|
||||
@Mixin(value = ProcessingEncodingPanel.class, remap = false)
|
||||
public abstract class ProcessingEncodingPanelMixin extends EncodingModePanel {
|
||||
@Unique
|
||||
private static final ResourceLocation EAP$SCALE_BUTTON_TEXTURE =
|
||||
new ResourceLocation(ExtendedAEPlus.MODID, "textures/gui/beizeng.png");
|
||||
@Unique
|
||||
private static final ResourceLocation EAP$SWAP_OUTPUT_TEXTURE =
|
||||
new ResourceLocation(ExtendedAEPlus.MODID, "textures/gui/zhu_fu_qie_huan.png");
|
||||
@Unique
|
||||
private static final ResourceLocation EAP$RESTORE_RATIO_TEXTURE =
|
||||
new ResourceLocation(ExtendedAEPlus.MODID, "textures/gui/huanyuan.png");
|
||||
|
||||
@Unique
|
||||
private static final int EAP$SWAP_OUTPUT_LEFT = 126;
|
||||
@Unique
|
||||
private static final int EAP$SWAP_OUTPUT_BOTTOM = 159;
|
||||
@Unique
|
||||
private static final int EAP$RESTORE_RATIO_LEFT = 100;
|
||||
@Unique
|
||||
private static final int EAP$RESTORE_RATIO_BOTTOM = 159;
|
||||
@Unique
|
||||
private static final int EAP$MUL2_LEFT = 126;
|
||||
@Unique
|
||||
private static final int EAP$MUL2_BOTTOM = 149;
|
||||
@Unique
|
||||
private static final int EAP$MUL3_LEFT = 126;
|
||||
@Unique
|
||||
private static final int EAP$MUL3_BOTTOM = 138;
|
||||
@Unique
|
||||
private static final int EAP$MUL5_LEFT = 126;
|
||||
@Unique
|
||||
private static final int EAP$MUL5_BOTTOM = 127;
|
||||
@Unique
|
||||
private static final int EAP$DIV2_LEFT = 100;
|
||||
@Unique
|
||||
private static final int EAP$DIV2_BOTTOM = 149;
|
||||
@Unique
|
||||
private static final int EAP$DIV3_LEFT = 100;
|
||||
@Unique
|
||||
private static final int EAP$DIV3_BOTTOM = 138;
|
||||
@Unique
|
||||
private static final int EAP$DIV5_LEFT = 100;
|
||||
@Unique
|
||||
private static final int EAP$DIV5_BOTTOM = 127;
|
||||
|
||||
@Shadow
|
||||
@Final
|
||||
private ActionButton cycleOutputBtn;
|
||||
|
||||
@Unique private ScaledTextureButton eap$mul2Button;
|
||||
@Unique private ScaledTextureButton eap$mul3Button;
|
||||
@Unique private ScaledTextureButton eap$mul5Button;
|
||||
@Unique private ScaledTextureButton eap$div2Button;
|
||||
@Unique private ScaledTextureButton eap$div3Button;
|
||||
@Unique private ScaledTextureButton eap$div5Button;
|
||||
@Unique private ScaledTextureButton eap$swapOutputsButton;
|
||||
@Unique private ScaledTextureButton eap$restoreRatioButton;
|
||||
|
||||
protected ProcessingEncodingPanelMixin(PatternEncodingTermScreen<?> screen, WidgetContainer widgets) {
|
||||
super(screen, widgets);
|
||||
}
|
||||
|
||||
@Inject(method = "<init>", at = @At("TAIL"), remap = false)
|
||||
private void eap$initButtons(PatternEncodingTermScreen<?> screen, WidgetContainer widgets, CallbackInfo ci) {
|
||||
if (this.eap$mul2Button != null) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.eap$mul2Button = eap$createScaleButton(0, 0, "x2", ScaleEncodingPatternC2SPacket.Operation.MUL2);
|
||||
this.eap$mul3Button = eap$createScaleButton(16, 0, "x3", ScaleEncodingPatternC2SPacket.Operation.MUL3);
|
||||
this.eap$mul5Button = eap$createScaleButton(32, 0, "x5", ScaleEncodingPatternC2SPacket.Operation.MUL5);
|
||||
this.eap$div2Button = eap$createScaleButton(0, 16, "/2", ScaleEncodingPatternC2SPacket.Operation.DIV2);
|
||||
this.eap$div3Button = eap$createScaleButton(16, 16, "/3", ScaleEncodingPatternC2SPacket.Operation.DIV3);
|
||||
this.eap$div5Button = eap$createScaleButton(32, 16, "/5", ScaleEncodingPatternC2SPacket.Operation.DIV5);
|
||||
this.eap$swapOutputsButton = eap$createStandaloneButton(
|
||||
EAP$SWAP_OUTPUT_TEXTURE,
|
||||
Component.translatable("extendedae_plus.tooltip.swap_processing_outputs"),
|
||||
ScaleEncodingPatternC2SPacket.Operation.SWAP_OUTPUTS);
|
||||
this.eap$restoreRatioButton = eap$createStandaloneButton(
|
||||
EAP$RESTORE_RATIO_TEXTURE,
|
||||
Component.translatable("extendedae_plus.tooltip.restore_processing_ratio"),
|
||||
ScaleEncodingPatternC2SPacket.Operation.RESTORE_RATIO);
|
||||
|
||||
this.eap$mul2Button.setVisibility(false);
|
||||
this.eap$mul3Button.setVisibility(false);
|
||||
this.eap$mul5Button.setVisibility(false);
|
||||
this.eap$div2Button.setVisibility(false);
|
||||
this.eap$div3Button.setVisibility(false);
|
||||
this.eap$div5Button.setVisibility(false);
|
||||
this.eap$swapOutputsButton.setVisibility(false);
|
||||
this.eap$restoreRatioButton.setVisibility(false);
|
||||
|
||||
eap$ensureAdded(this.eap$mul2Button);
|
||||
eap$ensureAdded(this.eap$mul3Button);
|
||||
eap$ensureAdded(this.eap$mul5Button);
|
||||
eap$ensureAdded(this.eap$div2Button);
|
||||
eap$ensureAdded(this.eap$div3Button);
|
||||
eap$ensureAdded(this.eap$div5Button);
|
||||
eap$ensureAdded(this.eap$swapOutputsButton);
|
||||
eap$ensureAdded(this.eap$restoreRatioButton);
|
||||
}
|
||||
|
||||
@Inject(method = "setVisible", at = @At("TAIL"), remap = false)
|
||||
private void eap$updateInjectedButtons(boolean visible, CallbackInfo ci) {
|
||||
boolean showInjectedButtons = visible && ProviderSelectScreen.isProcessingButtonsEnabled();
|
||||
this.cycleOutputBtn.setVisibility(visible && !showInjectedButtons);
|
||||
|
||||
if (this.eap$mul2Button == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
eap$ensureAdded(this.eap$mul2Button);
|
||||
eap$ensureAdded(this.eap$mul3Button);
|
||||
eap$ensureAdded(this.eap$mul5Button);
|
||||
eap$ensureAdded(this.eap$div2Button);
|
||||
eap$ensureAdded(this.eap$div3Button);
|
||||
eap$ensureAdded(this.eap$div5Button);
|
||||
eap$ensureAdded(this.eap$swapOutputsButton);
|
||||
eap$ensureAdded(this.eap$restoreRatioButton);
|
||||
|
||||
this.eap$mul2Button.setVisibility(showInjectedButtons);
|
||||
this.eap$mul3Button.setVisibility(showInjectedButtons);
|
||||
this.eap$mul5Button.setVisibility(showInjectedButtons);
|
||||
this.eap$div2Button.setVisibility(showInjectedButtons);
|
||||
this.eap$div3Button.setVisibility(showInjectedButtons);
|
||||
this.eap$div5Button.setVisibility(showInjectedButtons);
|
||||
this.eap$swapOutputsButton.setVisibility(showInjectedButtons);
|
||||
this.eap$restoreRatioButton.setVisibility(showInjectedButtons);
|
||||
|
||||
if (!showInjectedButtons) {
|
||||
return;
|
||||
}
|
||||
|
||||
eap$placeButton(this.eap$swapOutputsButton, EAP$SWAP_OUTPUT_LEFT, EAP$SWAP_OUTPUT_BOTTOM);
|
||||
eap$placeButton(this.eap$restoreRatioButton, EAP$RESTORE_RATIO_LEFT, EAP$RESTORE_RATIO_BOTTOM);
|
||||
eap$placeButton(this.eap$mul2Button, EAP$MUL2_LEFT, EAP$MUL2_BOTTOM);
|
||||
eap$placeButton(this.eap$mul3Button, EAP$MUL3_LEFT, EAP$MUL3_BOTTOM);
|
||||
eap$placeButton(this.eap$mul5Button, EAP$MUL5_LEFT, EAP$MUL5_BOTTOM);
|
||||
eap$placeButton(this.eap$div2Button, EAP$DIV2_LEFT, EAP$DIV2_BOTTOM);
|
||||
eap$placeButton(this.eap$div3Button, EAP$DIV3_LEFT, EAP$DIV3_BOTTOM);
|
||||
eap$placeButton(this.eap$div5Button, EAP$DIV5_LEFT, EAP$DIV5_BOTTOM);
|
||||
}
|
||||
|
||||
@Unique
|
||||
private ScaledTextureButton eap$createScaleButton(int srcX, int srcY, String tooltipText,
|
||||
ScaleEncodingPatternC2SPacket.Operation op) {
|
||||
return new ScaledTextureButton(
|
||||
EAP$SCALE_BUTTON_TEXTURE,
|
||||
48,
|
||||
32,
|
||||
srcX,
|
||||
srcY,
|
||||
16,
|
||||
16,
|
||||
0.5f,
|
||||
Component.literal(tooltipText),
|
||||
btn -> ModNetwork.CHANNEL.sendToServer(new ScaleEncodingPatternC2SPacket(op)));
|
||||
}
|
||||
|
||||
@Unique
|
||||
private ScaledTextureButton eap$createStandaloneButton(ResourceLocation texture, Component tooltipText,
|
||||
ScaleEncodingPatternC2SPacket.Operation op) {
|
||||
return new ScaledTextureButton(
|
||||
texture,
|
||||
16,
|
||||
16,
|
||||
0,
|
||||
0,
|
||||
16,
|
||||
16,
|
||||
0.5f,
|
||||
tooltipText,
|
||||
btn -> ModNetwork.CHANNEL.sendToServer(new ScaleEncodingPatternC2SPacket(op)));
|
||||
}
|
||||
|
||||
@Unique
|
||||
private void eap$ensureAdded(ScaledTextureButton button) {
|
||||
var accessor = (ScreenAccessor) (Object) this.screen;
|
||||
var renderables = accessor.eap$getRenderables();
|
||||
var children = accessor.eap$getChildren();
|
||||
if (!renderables.contains(button)) {
|
||||
renderables.add(button);
|
||||
}
|
||||
if (!children.contains(button)) {
|
||||
children.add(button);
|
||||
}
|
||||
}
|
||||
|
||||
@Unique
|
||||
private void eap$placeButton(ScaledTextureButton button, int left, int bottom) {
|
||||
int leftPos = ((AbstractContainerScreenAccessor<?>) (Object) this.screen).eap$getLeftPos();
|
||||
int topPos = ((AbstractContainerScreenAccessor<?>) (Object) this.screen).eap$getTopPos();
|
||||
int imageHeight = ((AbstractContainerScreenAccessor<?>) (Object) this.screen).eap$getImageHeight();
|
||||
button.setX(leftPos + left + 1);
|
||||
button.setY(topPos + imageHeight - bottom);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,48 +1,22 @@
|
|||
package com.extendedae_plus.mixin.ae2.client.gui.patternProvider;
|
||||
|
||||
import appeng.api.upgrades.IUpgradeableObject;
|
||||
import appeng.api.upgrades.Upgrades;
|
||||
import appeng.client.gui.AEBaseScreen;
|
||||
import appeng.client.gui.implementations.PatternProviderScreen;
|
||||
import appeng.client.gui.style.ScreenStyle;
|
||||
import appeng.client.gui.widgets.UpgradesPanel;
|
||||
import appeng.core.localization.GuiText;
|
||||
import appeng.menu.SlotSemantics;
|
||||
import appeng.menu.implementations.PatternProviderMenu;
|
||||
import com.extendedae_plus.compat.UpgradeSlotCompat;
|
||||
import com.extendedae_plus.mixin.ae2.accessor.PatternProviderMenuAccessor;
|
||||
import net.minecraft.network.chat.Component;
|
||||
import net.minecraft.world.entity.player.Inventory;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.Unique;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Inject;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
@Mixin(PatternProviderScreen.class)
|
||||
public abstract class PatternProviderScreenMixin<C extends PatternProviderMenu> extends AEBaseScreen<C> {
|
||||
public PatternProviderScreenMixin(C menu, Inventory playerInventory, Component title, ScreenStyle style) {
|
||||
super(menu, playerInventory, title, style);
|
||||
}
|
||||
|
||||
@Inject(method = "<init>", at = @At("TAIL"))
|
||||
private void eap$initCompatUpgrades(PatternProviderMenu menu, Inventory playerInventory, Component title, ScreenStyle style, CallbackInfo ci) {
|
||||
if (!UpgradeSlotCompat.shouldAddUpgradePanelToScreen()) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
this.widgets.add("upgrades", new UpgradesPanel(
|
||||
menu.getSlots(SlotSemantics.UPGRADE),
|
||||
this::eap$getCompatibleUpgrades));
|
||||
} catch (Exception e) {
|
||||
com.extendedae_plus.util.Logger.EAP$LOGGER.error("PatternProviderScreen兼容性升级面板初始化失败", e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 显示样板供应器的customName
|
||||
*/
|
||||
|
|
@ -53,23 +27,4 @@ public abstract class PatternProviderScreenMixin<C extends PatternProviderMenu>
|
|||
this.setTextContent(AEBaseScreen.TEXT_ID_DIALOG_TITLE, t);
|
||||
}
|
||||
}
|
||||
|
||||
@Unique
|
||||
private List<Component> eap$getCompatibleUpgrades() {
|
||||
var list = new ArrayList<Component>();
|
||||
list.add(GuiText.CompatibleUpgrades.text());
|
||||
|
||||
try {
|
||||
if (((PatternProviderMenuAccessor) this.menu).eap$logic() instanceof IUpgradeableObject upgradeableLogic) {
|
||||
var upgrades = upgradeableLogic.getUpgrades();
|
||||
if (upgrades != null) {
|
||||
list.addAll(Upgrades.getTooltipLinesForMachine(upgrades.getUpgradableItem()));
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
com.extendedae_plus.util.Logger.EAP$LOGGER.error("获取兼容升级列表失败", e);
|
||||
}
|
||||
|
||||
return list;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@ import com.extendedae_plus.compat.UpgradeSlotCompat;
|
|||
import net.minecraft.world.entity.player.Inventory;
|
||||
import net.minecraft.world.inventory.MenuType;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.Unique;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Inject;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
||||
|
|
@ -20,7 +21,9 @@ import static com.extendedae_plus.util.Logger.EAP$LOGGER;
|
|||
* 优先级设置为500,低于appflux的默认优先级,避免冲突
|
||||
*/
|
||||
@Mixin(value = PatternProviderMenu.class, priority = 500, remap = false)
|
||||
public abstract class PatternProviderCompatMixin extends AEBaseMenu {
|
||||
public abstract class PatternProviderCompatMixin extends AEBaseMenu implements UpgradeSlotCompat.IUpgradeableMenuCompat {
|
||||
@Unique
|
||||
private IUpgradeInventory eap$compatUpgrades;
|
||||
|
||||
public PatternProviderCompatMixin(MenuType<?> menuType, int id, Inventory playerInventory, Object host) {
|
||||
super(menuType, id, playerInventory, host);
|
||||
|
|
@ -30,10 +33,12 @@ public abstract class PatternProviderCompatMixin extends AEBaseMenu {
|
|||
at = @At("TAIL"))
|
||||
private void eap$initCompatUpgrades(MenuType<?> menuType, int id, Inventory playerInventory, PatternProviderLogicHost host, CallbackInfo ci) {
|
||||
try {
|
||||
if (UpgradeSlotCompat.shouldManageLocalUpgradeInventory()) {
|
||||
if (host.getLogic() instanceof IUpgradeableObject upgradeableLogic) {
|
||||
IUpgradeInventory upgrades = upgradeableLogic.getUpgrades();
|
||||
this.setupUpgrades(upgrades);
|
||||
// 检测是否应该启用升级卡槽功能
|
||||
if (UpgradeSlotCompat.shouldEnableUpgradeSlots()) {
|
||||
// 直接初始化升级功能
|
||||
if (host instanceof IUpgradeableObject upgradeableHost) {
|
||||
this.eap$compatUpgrades = upgradeableHost.getUpgrades();
|
||||
this.setupUpgrades(this.eap$compatUpgrades);
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
|
|
@ -41,4 +46,9 @@ public abstract class PatternProviderCompatMixin extends AEBaseMenu {
|
|||
EAP$LOGGER.error("PatternProviderMenu兼容性升级初始化失败", e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public IUpgradeInventory getCompatUpgrades() {
|
||||
return this.eap$compatUpgrades;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,28 +1,19 @@
|
|||
package com.extendedae_plus.mixin.ae2.compat;
|
||||
|
||||
import appeng.api.crafting.IPatternDetails;
|
||||
import appeng.api.networking.IGrid;
|
||||
import appeng.api.networking.IManagedGridNode;
|
||||
import appeng.api.networking.crafting.ICraftingCPU;
|
||||
import appeng.api.networking.security.IActionSource;
|
||||
import appeng.api.stacks.KeyCounter;
|
||||
import appeng.api.upgrades.IUpgradeInventory;
|
||||
import appeng.api.upgrades.IUpgradeableObject;
|
||||
import appeng.api.upgrades.UpgradeInventories;
|
||||
import appeng.blockentity.AEBaseBlockEntity;
|
||||
import appeng.helpers.patternprovider.PatternProviderLogic;
|
||||
import appeng.helpers.patternprovider.PatternProviderLogicHost;
|
||||
import appeng.me.cluster.implementations.CraftingCPUCluster;
|
||||
import com.extendedae_plus.ae.wireless.WirelessSlaveLink;
|
||||
import com.extendedae_plus.ae.wireless.endpoint.GenericNodeEndpointImpl;
|
||||
import com.extendedae_plus.api.bridge.IInterfaceWirelessLinkBridge;
|
||||
import com.extendedae_plus.compat.PatternProviderLogicVirtualCompatBridge;
|
||||
import com.extendedae_plus.compat.UpgradeSlotCompat;
|
||||
import com.extendedae_plus.init.ModItems;
|
||||
import com.extendedae_plus.mixin.ae2.accessor.CraftingCpuLogicAccessor;
|
||||
import com.extendedae_plus.mixin.ae2.accessor.ExecutingCraftingJobAccessor;
|
||||
import com.extendedae_plus.items.materials.ChannelCardItem;
|
||||
import com.extendedae_plus.util.Logger;
|
||||
import com.extendedae_plus.util.wireless.ChannelCardLinkHelper;
|
||||
import net.minecraft.nbt.CompoundTag;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
import org.spongepowered.asm.mixin.Final;
|
||||
|
|
@ -32,19 +23,16 @@ import org.spongepowered.asm.mixin.Unique;
|
|||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Inject;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* PatternProviderLogic的兼容性Mixin
|
||||
* 优先级设置为500,在appflux之前应用
|
||||
* 优先级设置为1500,在appflux之后应用
|
||||
* 根据appflux是否存在来决定是否实现IUpgradeableObject接口
|
||||
*/
|
||||
@Mixin(value = PatternProviderLogic.class, priority = 500, remap = false)
|
||||
public abstract class PatternProviderLogicCompatMixin implements IUpgradeableObject, IInterfaceWirelessLinkBridge, PatternProviderLogicVirtualCompatBridge {
|
||||
public abstract class PatternProviderLogicCompatMixin implements IUpgradeableObject, IInterfaceWirelessLinkBridge {
|
||||
|
||||
@Unique
|
||||
private IUpgradeInventory eap$compatUpgrades = UpgradeInventories.empty();
|
||||
|
|
@ -55,9 +43,6 @@ public abstract class PatternProviderLogicCompatMixin implements IUpgradeableObj
|
|||
@Unique
|
||||
private long eap$compatLastChannel = -1;
|
||||
|
||||
@Unique
|
||||
private UUID eap$compatLastOwner;
|
||||
|
||||
@Unique
|
||||
private boolean eap$compatClientConnected = false;
|
||||
|
||||
|
|
@ -79,20 +64,14 @@ public abstract class PatternProviderLogicCompatMixin implements IUpgradeableObj
|
|||
@Shadow
|
||||
private IActionSource actionSource;
|
||||
|
||||
@Unique
|
||||
private boolean eap$compatVirtualCraftingEnabled = false;
|
||||
|
||||
@Shadow
|
||||
public abstract IGrid getGrid();
|
||||
|
||||
@Unique
|
||||
private void eap$compatOnUpgradesChanged() {
|
||||
try {
|
||||
this.eap$compatNotifyHostChanged();
|
||||
eap$compatSyncVirtualCraftingState();
|
||||
this.host.saveChanges();
|
||||
// 频道卡功能独立于升级槽功能,总是处理
|
||||
if (UpgradeSlotCompat.shouldEnableChannelCard()) {
|
||||
// 升级变更,重置并尝试初始化频道卡
|
||||
eap$compatLastChannel = -1;
|
||||
eap$compatLastOwner = null;
|
||||
eap$compatHasInitialized = false;
|
||||
eap$compatInitializeChannelLink();
|
||||
}
|
||||
|
|
@ -101,74 +80,13 @@ public abstract class PatternProviderLogicCompatMixin implements IUpgradeableObj
|
|||
}
|
||||
}
|
||||
|
||||
@Unique
|
||||
private void eap$compatSyncVirtualCraftingState() {
|
||||
boolean hasCard = false;
|
||||
var inventory = eap$compatGetEffectiveUpgradeInventory();
|
||||
if (inventory != null) {
|
||||
for (ItemStack stack : inventory) {
|
||||
if (!stack.isEmpty() && stack.getItem() == ModItems.VIRTUAL_CRAFTING_CARD.get()) {
|
||||
hasCard = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
eap$compatVirtualCraftingEnabled = hasCard;
|
||||
}
|
||||
|
||||
@Unique
|
||||
private void eap$compatTryVirtualCompletion(IPatternDetails patternDetails) {
|
||||
if (!eap$compatVirtualCraftingEnabled) {
|
||||
return;
|
||||
}
|
||||
|
||||
var be = this.host.getBlockEntity();
|
||||
if (be == null || be.getLevel() == null || be.getLevel().isClientSide) {
|
||||
return;
|
||||
}
|
||||
|
||||
var grid = getGrid();
|
||||
if (grid == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
var craftingService = grid.getCraftingService();
|
||||
if (craftingService == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (ICraftingCPU cpu : craftingService.getCpus()) {
|
||||
if (!cpu.isBusy()) {
|
||||
continue;
|
||||
}
|
||||
if (cpu instanceof CraftingCPUCluster cluster) {
|
||||
if (cluster.craftingLogic instanceof CraftingCpuLogicAccessor logicAccessor) {
|
||||
var job = logicAccessor.extendedae_plus$getJob();
|
||||
if (job instanceof ExecutingCraftingJobAccessor accessor) {
|
||||
var tasks = accessor.extendedae_plus$getTasks();
|
||||
var progress = tasks.get(patternDetails);
|
||||
if (eap$compatShouldFinishWholeJob(tasks, progress)) {
|
||||
cluster.updateOutput(null);
|
||||
// 监听appflux的升级变化 - 通过注入到appflux的af_$onUpgradesChanged方法
|
||||
@Inject(method = "af_$onUpgradesChanged", at = @At("TAIL"), remap = false, require = 0)
|
||||
private void eap$onAppfluxUpgradesChanged(CallbackInfo ci) {
|
||||
try {
|
||||
logicAccessor.extendedae_plus$invokeFinishJob(true);
|
||||
} catch (Throwable ignored) {
|
||||
cluster.cancelJob();
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Unique
|
||||
private void eap$compatOnExternalUpgradesChanged() {
|
||||
try {
|
||||
eap$compatSyncVirtualCraftingState();
|
||||
if (UpgradeSlotCompat.shouldEnableChannelCard()) {
|
||||
// 升级变更,重置并尝试初始化频道卡
|
||||
eap$compatLastChannel = -1;
|
||||
eap$compatLastOwner = null;
|
||||
eap$compatHasInitialized = false;
|
||||
eap$compatInitializeChannelLink();
|
||||
}
|
||||
|
|
@ -177,34 +95,25 @@ public abstract class PatternProviderLogicCompatMixin implements IUpgradeableObj
|
|||
}
|
||||
}
|
||||
|
||||
// 兼容较新的 appflux 升级变化回调命名
|
||||
@Inject(method = "af_onUpgradesChanged", at = @At("TAIL"), remap = false, require = 0)
|
||||
private void eap$onAppfluxUpgradesChanged(CallbackInfo ci) {
|
||||
eap$compatOnExternalUpgradesChanged();
|
||||
}
|
||||
|
||||
// 兼容旧命名,避免不同 appflux 版本导致注入失效
|
||||
@Inject(method = "af_$onUpgradesChanged", at = @At("TAIL"), remap = false, require = 0)
|
||||
private void eap$onLegacyAppfluxUpgradesChanged(CallbackInfo ci) {
|
||||
eap$compatOnExternalUpgradesChanged();
|
||||
}
|
||||
|
||||
@Inject(method = "<init>(Lappeng/api/networking/IManagedGridNode;Lappeng/helpers/patternprovider/PatternProviderLogicHost;I)V",
|
||||
at = @At("TAIL"))
|
||||
private void eap$compatInitUpgrades(IManagedGridNode mainNode, PatternProviderLogicHost host, int patternInventorySize, CallbackInfo ci) {
|
||||
try {
|
||||
|
||||
boolean upgradeSlots = UpgradeSlotCompat.shouldManageLocalUpgradeInventory();
|
||||
boolean upgradeSlots = UpgradeSlotCompat.shouldEnableUpgradeSlots();
|
||||
boolean channelCard = UpgradeSlotCompat.shouldEnableChannelCard();
|
||||
|
||||
|
||||
if (upgradeSlots) {
|
||||
// 只有在升级槽功能启用时才创建升级槽
|
||||
this.eap$compatUpgrades = UpgradeInventories.forMachine(
|
||||
host.getTerminalIcon().getItem(),
|
||||
UpgradeSlotCompat.getPatternProviderLocalUpgradeSlots(),
|
||||
1,
|
||||
this::eap$compatOnUpgradesChanged
|
||||
);
|
||||
} else if (!channelCard) {
|
||||
this.eap$compatUpgrades = UpgradeInventories.empty();
|
||||
} else if (channelCard) {
|
||||
// 如果装了appflux,我们不创建自己的升级槽,而是监听appflux的升级槽
|
||||
} else {
|
||||
}
|
||||
} catch (Exception e) {
|
||||
Logger.EAP$LOGGER.error("兼容性升级初始化失败", e);
|
||||
|
|
@ -214,7 +123,7 @@ public abstract class PatternProviderLogicCompatMixin implements IUpgradeableObj
|
|||
@Inject(method = "writeToNBT", at = @At("TAIL"))
|
||||
private void eap$compatSaveUpgrades(CompoundTag tag, CallbackInfo ci) {
|
||||
try {
|
||||
if (UpgradeSlotCompat.shouldManageLocalUpgradeInventory()) {
|
||||
if (UpgradeSlotCompat.shouldEnableUpgradeSlots() || UpgradeSlotCompat.shouldEnableChannelCard()) {
|
||||
this.eap$compatUpgrades.writeToNBT(tag, "compat_upgrades");
|
||||
}
|
||||
} catch (Exception e) {
|
||||
|
|
@ -225,17 +134,15 @@ public abstract class PatternProviderLogicCompatMixin implements IUpgradeableObj
|
|||
@Inject(method = "readFromNBT", at = @At("TAIL"))
|
||||
private void eap$compatLoadUpgrades(CompoundTag tag, CallbackInfo ci) {
|
||||
try {
|
||||
if (UpgradeSlotCompat.shouldManageLocalUpgradeInventory()) {
|
||||
if (UpgradeSlotCompat.shouldEnableUpgradeSlots() || UpgradeSlotCompat.shouldEnableChannelCard()) {
|
||||
this.eap$compatUpgrades.readFromNBT(tag, "compat_upgrades");
|
||||
}
|
||||
|
||||
// 从 NBT 加载后,重置并尝试初始化频道卡
|
||||
if (UpgradeSlotCompat.shouldEnableChannelCard()) {
|
||||
eap$compatLastChannel = -1;
|
||||
eap$compatLastOwner = null;
|
||||
eap$compatHasInitialized = false;
|
||||
eap$compatInitializeChannelLink();
|
||||
}
|
||||
eap$compatSyncVirtualCraftingState();
|
||||
}
|
||||
} catch (Exception e) {
|
||||
Logger.EAP$LOGGER.error("兼容性升级加载失败", e);
|
||||
}
|
||||
|
|
@ -244,7 +151,7 @@ public abstract class PatternProviderLogicCompatMixin implements IUpgradeableObj
|
|||
@Inject(method = "addDrops", at = @At("TAIL"))
|
||||
private void eap$compatDropUpgrades(List<ItemStack> drops, CallbackInfo ci) {
|
||||
try {
|
||||
if (UpgradeSlotCompat.shouldManageLocalUpgradeInventory()) {
|
||||
if (UpgradeSlotCompat.shouldEnableUpgradeSlots() || UpgradeSlotCompat.shouldEnableChannelCard()) {
|
||||
for (var stack : this.eap$compatUpgrades) {
|
||||
if (!stack.isEmpty()) {
|
||||
drops.add(stack);
|
||||
|
|
@ -259,12 +166,9 @@ public abstract class PatternProviderLogicCompatMixin implements IUpgradeableObj
|
|||
@Inject(method = "clearContent", at = @At("TAIL"))
|
||||
private void eap$compatClearUpgrades(CallbackInfo ci) {
|
||||
try {
|
||||
if (UpgradeSlotCompat.shouldManageLocalUpgradeInventory()) {
|
||||
if (UpgradeSlotCompat.shouldEnableUpgradeSlots() || UpgradeSlotCompat.shouldEnableChannelCard()) {
|
||||
this.eap$compatUpgrades.clear();
|
||||
}
|
||||
if (UpgradeSlotCompat.shouldEnableChannelCard()) {
|
||||
eap$compatVirtualCraftingEnabled = false;
|
||||
}
|
||||
} catch (Exception e) {
|
||||
Logger.EAP$LOGGER.error("兼容性升级清理失败", e);
|
||||
}
|
||||
|
|
@ -272,60 +176,16 @@ public abstract class PatternProviderLogicCompatMixin implements IUpgradeableObj
|
|||
|
||||
@Override
|
||||
public IUpgradeInventory getUpgrades() {
|
||||
if (UpgradeSlotCompat.shouldManageLocalUpgradeInventory()) {
|
||||
if (UpgradeSlotCompat.shouldEnableUpgradeSlots()) {
|
||||
// 不装appflux时,返回我们自己的升级槽
|
||||
return this.eap$compatUpgrades != null ? this.eap$compatUpgrades : UpgradeInventories.empty();
|
||||
} else {
|
||||
return eap$compatGetEffectiveUpgradeInventory();
|
||||
// 装了appflux时,这个方法不应该被调用,因为appflux的Mixin会覆盖它
|
||||
// 但是为了安全起见,返回空的升级槽
|
||||
return UpgradeInventories.empty();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean eap$compatIsVirtualCraftingEnabled() {
|
||||
return this.eap$compatVirtualCraftingEnabled;
|
||||
}
|
||||
|
||||
@Override
|
||||
public IGrid eap$compatGetGrid() {
|
||||
return this.getGrid();
|
||||
}
|
||||
|
||||
@Override
|
||||
public IManagedGridNode eap$compatGetMainNode() {
|
||||
return this.mainNode;
|
||||
}
|
||||
|
||||
@Unique
|
||||
private boolean eap$compatShouldFinishWholeJob(
|
||||
Map<IPatternDetails, com.extendedae_plus.mixin.ae2.accessor.ExecutingCraftingJobTaskProgressAccessor> tasks,
|
||||
com.extendedae_plus.mixin.ae2.accessor.ExecutingCraftingJobTaskProgressAccessor matchedProgress) {
|
||||
if (matchedProgress == null || matchedProgress.extendedae_plus$getValue() > 1) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for (var entry : tasks.entrySet()) {
|
||||
var taskProgress = entry.getValue();
|
||||
if (taskProgress == null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
long remaining = taskProgress.extendedae_plus$getValue();
|
||||
if (taskProgress == matchedProgress) {
|
||||
remaining -= 1;
|
||||
}
|
||||
|
||||
if (remaining > 0) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@Inject(method = "pushPattern", at = @At("HEAD"))
|
||||
private void eap$compatOnPushPattern(IPatternDetails patternDetails, KeyCounter[] inputHolder, CallbackInfoReturnable<Boolean> cir) {
|
||||
eap$compatTryVirtualCompletion(patternDetails);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void eap$updateWirelessLink() {
|
||||
if (!UpgradeSlotCompat.shouldEnableChannelCard()) {
|
||||
|
|
@ -334,11 +194,7 @@ public abstract class PatternProviderLogicCompatMixin implements IUpgradeableObj
|
|||
|
||||
try {
|
||||
if (eap$compatLink != null) {
|
||||
boolean wasConnected = eap$compatLink.isConnected();
|
||||
eap$compatLink.updateStatus();
|
||||
if (wasConnected != eap$compatLink.isConnected()) {
|
||||
this.eap$compatNotifyHostChanged();
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
Logger.EAP$LOGGER.error("兼容性无线链接更新失败", e);
|
||||
|
|
@ -346,7 +202,7 @@ public abstract class PatternProviderLogicCompatMixin implements IUpgradeableObj
|
|||
}
|
||||
|
||||
@Unique
|
||||
private void eap$compatInitializeChannelLink() {
|
||||
public void eap$compatInitializeChannelLink() {
|
||||
if (!UpgradeSlotCompat.shouldEnableChannelCard()) {
|
||||
return;
|
||||
}
|
||||
|
|
@ -375,33 +231,46 @@ public abstract class PatternProviderLogicCompatMixin implements IUpgradeableObj
|
|||
|
||||
long channel = 0L;
|
||||
boolean found = false;
|
||||
UUID ownerUUID = null;
|
||||
|
||||
// 获取升级槽 - 如果装了appflux则从appflux获取,否则从我们自己的获取
|
||||
IUpgradeInventory upgrades = eap$compatGetEffectiveUpgradeInventory();
|
||||
IUpgradeInventory upgrades = null;
|
||||
if (UpgradeSlotCompat.shouldEnableUpgradeSlots()) {
|
||||
// 不装appflux时使用我们自己的升级槽
|
||||
upgrades = this.eap$compatUpgrades;
|
||||
} else if (UpgradeSlotCompat.shouldEnableChannelCard()) {
|
||||
// 装了appflux时,尝试从PatternProviderLogic获取升级槽
|
||||
try {
|
||||
if (this instanceof IUpgradeableObject) {
|
||||
IUpgradeableObject upgradeableThis = (IUpgradeableObject) this;
|
||||
upgrades = upgradeableThis.getUpgrades();
|
||||
}
|
||||
} catch (Exception e) {
|
||||
Logger.EAP$LOGGER.error("获取appflux升级槽失败", e);
|
||||
}
|
||||
}
|
||||
|
||||
var boundChannel = ChannelCardLinkHelper.findBoundChannel(upgrades, this::eap$getFallbackOwner);
|
||||
if (boundChannel != null) {
|
||||
channel = boundChannel.channel();
|
||||
ownerUUID = boundChannel.owner();
|
||||
if (upgrades != null) {
|
||||
for (ItemStack stack : upgrades) {
|
||||
if (!stack.isEmpty() && stack.getItem() == ModItems.CHANNEL_CARD.get()) {
|
||||
channel = ChannelCardItem.getChannel(stack);
|
||||
java.util.UUID ownerUUID = ChannelCardItem.getOwnerUUID(stack);
|
||||
if (ownerUUID != null) {
|
||||
// 保存ownerUUID到局部变量,后面设置到link
|
||||
channel |= ((long) ownerUUID.hashCode() << 32); // 临时存储
|
||||
}
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!found) {
|
||||
// 无频道卡:断开并视为初始化完成
|
||||
ChannelCardLinkHelper.disconnect(eap$compatLink);
|
||||
eap$compatHasInitialized = true;
|
||||
eap$compatLastChannel = 0L;
|
||||
eap$compatLastOwner = null;
|
||||
this.eap$compatNotifyHostChanged();
|
||||
return;
|
||||
if (eap$compatLink != null) {
|
||||
eap$compatLink.setFrequency(0L);
|
||||
eap$compatLink.updateStatus();
|
||||
}
|
||||
|
||||
if (eap$compatLink != null
|
||||
&& ChannelCardLinkHelper.sameTarget(eap$compatLastChannel, eap$compatLastOwner, boundChannel)) {
|
||||
if (eap$compatLink.isConnected()) {
|
||||
eap$compatHasInitialized = true;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -410,12 +279,20 @@ public abstract class PatternProviderLogicCompatMixin implements IUpgradeableObj
|
|||
eap$compatLink = new WirelessSlaveLink(endpoint);
|
||||
}
|
||||
|
||||
eap$compatLink.setPlacerId(ownerUUID);
|
||||
// 从频道卡重新读取ownerUUID并设置
|
||||
java.util.UUID cardOwner = null;
|
||||
if (upgrades != null) {
|
||||
for (ItemStack stack : upgrades) {
|
||||
if (!stack.isEmpty() && stack.getItem() == ModItems.CHANNEL_CARD.get()) {
|
||||
cardOwner = ChannelCardItem.getOwnerUUID(stack);
|
||||
channel = ChannelCardItem.getChannel(stack); // 重新读取正确的频率
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
eap$compatLink.setPlacerId(cardOwner);
|
||||
eap$compatLink.setFrequency(channel);
|
||||
eap$compatLink.updateStatus();
|
||||
eap$compatLastChannel = channel;
|
||||
eap$compatLastOwner = ownerUUID;
|
||||
this.eap$compatNotifyHostChanged();
|
||||
|
||||
if (eap$compatLink.isConnected()) {
|
||||
eap$compatHasInitialized = true;
|
||||
|
|
@ -433,44 +310,6 @@ public abstract class PatternProviderLogicCompatMixin implements IUpgradeableObj
|
|||
}
|
||||
}
|
||||
|
||||
@Unique
|
||||
private IUpgradeInventory eap$compatGetEffectiveUpgradeInventory() {
|
||||
if (UpgradeSlotCompat.shouldManageLocalUpgradeInventory()) {
|
||||
return this.eap$compatUpgrades;
|
||||
}
|
||||
|
||||
if (!UpgradeSlotCompat.shouldListenToAppfluxUpgrades()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
IUpgradeInventory inventory = UpgradeSlotCompat.getPatternProviderAppfluxUpgrades(this);
|
||||
if (inventory != null) {
|
||||
return inventory;
|
||||
}
|
||||
|
||||
return UpgradeInventories.empty();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean eap$shouldKeepTicking() {
|
||||
if (!UpgradeSlotCompat.shouldEnableChannelCard()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
try {
|
||||
if (host.getBlockEntity() == null || host.getBlockEntity().getLevel() == null
|
||||
|| host.getBlockEntity().getLevel().isClientSide) {
|
||||
return false;
|
||||
}
|
||||
return ChannelCardLinkHelper.shouldKeepTicking(
|
||||
eap$compatGetEffectiveUpgradeInventory(),
|
||||
eap$compatLink,
|
||||
eap$compatHasInitialized);
|
||||
} catch (Throwable ignored) {
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void eap$setClientWirelessState(boolean connected) {
|
||||
if (UpgradeSlotCompat.shouldEnableChannelCard()) {
|
||||
|
|
@ -537,7 +376,6 @@ public abstract class PatternProviderLogicCompatMixin implements IUpgradeableObj
|
|||
}
|
||||
} else {
|
||||
eap$compatInitializeChannelLink();
|
||||
eap$compatSyncVirtualCraftingState();
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
|
|
@ -553,7 +391,6 @@ public abstract class PatternProviderLogicCompatMixin implements IUpgradeableObj
|
|||
|
||||
try {
|
||||
eap$compatLastChannel = -1;
|
||||
eap$compatLastOwner = null;
|
||||
eap$compatHasInitialized = false;
|
||||
eap$compatDelayedInitTicks = 10;
|
||||
try {
|
||||
|
|
@ -565,27 +402,4 @@ public abstract class PatternProviderLogicCompatMixin implements IUpgradeableObj
|
|||
Logger.EAP$LOGGER.error("兼容性主节点状态变更处理失败", e);
|
||||
}
|
||||
}
|
||||
|
||||
@Unique
|
||||
private UUID eap$getFallbackOwner() {
|
||||
if (this.mainNode != null && this.mainNode.getNode() != null) {
|
||||
return this.mainNode.getNode().getOwningPlayerProfileId();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Unique
|
||||
private void eap$compatNotifyHostChanged() {
|
||||
try {
|
||||
this.host.saveChanges();
|
||||
} catch (Throwable ignored) {
|
||||
}
|
||||
|
||||
try {
|
||||
if (this.host.getBlockEntity() instanceof AEBaseBlockEntity blockEntity) {
|
||||
blockEntity.markForUpdate();
|
||||
}
|
||||
} catch (Throwable ignored) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,27 @@
|
|||
package com.extendedae_plus.mixin.ae2.compat;
|
||||
|
||||
import appeng.api.upgrades.IUpgradeInventory;
|
||||
import appeng.api.upgrades.IUpgradeableObject;
|
||||
import appeng.api.upgrades.UpgradeInventories;
|
||||
import appeng.helpers.patternprovider.PatternProviderLogic;
|
||||
import appeng.helpers.patternprovider.PatternProviderLogicHost;
|
||||
import com.extendedae_plus.compat.UpgradeSlotCompat;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.Shadow;
|
||||
|
||||
/**
|
||||
* PatternProviderLogicHost的兼容性Mixin
|
||||
* 优先级设置为500,避免与appflux冲突
|
||||
*/
|
||||
@Mixin(value = PatternProviderLogicHost.class, priority = 500, remap = false)
|
||||
public interface PatternProviderLogicHostCompatMixin extends IUpgradeableObject {
|
||||
@Shadow PatternProviderLogic getLogic();
|
||||
|
||||
@Override
|
||||
default IUpgradeInventory getUpgrades() {
|
||||
if (!UpgradeSlotCompat.shouldEnableUpgradeSlots() && !UpgradeSlotCompat.shouldEnableChannelCard()) {
|
||||
return UpgradeInventories.empty();
|
||||
}
|
||||
return ((IUpgradeableObject) this.getLogic()).getUpgrades();
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,78 @@
|
|||
package com.extendedae_plus.mixin.ae2.compat;
|
||||
|
||||
import appeng.api.upgrades.Upgrades;
|
||||
import appeng.client.gui.AEBaseScreen;
|
||||
import appeng.client.gui.implementations.PatternProviderScreen;
|
||||
import appeng.client.gui.style.ScreenStyle;
|
||||
import appeng.client.gui.widgets.UpgradesPanel;
|
||||
import appeng.core.localization.GuiText;
|
||||
import appeng.menu.SlotSemantics;
|
||||
import appeng.menu.implementations.PatternProviderMenu;
|
||||
import com.extendedae_plus.compat.UpgradeSlotCompat;
|
||||
import com.extendedae_plus.util.Logger;
|
||||
import net.minecraft.network.chat.Component;
|
||||
import net.minecraft.world.entity.player.Inventory;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.Unique;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Inject;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* PatternProviderScreen的兼容性Mixin
|
||||
* 优先级设置为500,避免与appflux冲突
|
||||
*/
|
||||
@Mixin(value = PatternProviderScreen.class, priority = 500, remap = false)
|
||||
public abstract class PatternProviderScreenCompatMixin<C extends PatternProviderMenu> extends AEBaseScreen<C> {
|
||||
public PatternProviderScreenCompatMixin(C menu, Inventory playerInventory, Component title, ScreenStyle style) {
|
||||
super(menu, playerInventory, title, style);
|
||||
}
|
||||
|
||||
@Inject(method = "<init>", at = @At("TAIL"))
|
||||
private void eap$initCompatUpgrades(PatternProviderMenu menu, Inventory playerInventory, Component title, ScreenStyle style, CallbackInfo ci) {
|
||||
try {
|
||||
// 检测是否应该添加升级面板
|
||||
if (UpgradeSlotCompat.shouldAddUpgradePanelToScreen()) {
|
||||
// 直接添加升级面板,不使用复杂的反射
|
||||
this.eap$addUpgradePanelDirect(menu, style);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
// 静默处理异常,确保不会因为升级功能导致崩溃
|
||||
Logger.EAP$LOGGER.error("PatternProviderScreen兼容性升级面板初始化失败", e);
|
||||
}
|
||||
}
|
||||
|
||||
@Unique
|
||||
private void eap$addUpgradePanelDirect(PatternProviderMenu menu, ScreenStyle style) {
|
||||
try {
|
||||
// 直接添加升级面板
|
||||
this.widgets.add("upgrades", new UpgradesPanel(
|
||||
menu.getSlots(SlotSemantics.UPGRADE),
|
||||
this::eap$getCompatibleUpgrades));
|
||||
} catch (Exception e) {
|
||||
Logger.EAP$LOGGER.error("直接添加升级面板失败", e);
|
||||
}
|
||||
}
|
||||
|
||||
@Unique
|
||||
private List<Component> eap$getCompatibleUpgrades() {
|
||||
var list = new ArrayList<Component>();
|
||||
list.add(GuiText.CompatibleUpgrades.text());
|
||||
|
||||
try {
|
||||
if (menu instanceof UpgradeSlotCompat.IUpgradeableMenuCompat compatMenu) {
|
||||
var upgrades = compatMenu.getCompatUpgrades();
|
||||
if (upgrades != null) {
|
||||
list.addAll(Upgrades.getTooltipLinesForMachine(upgrades.getUpgradableItem()));
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
Logger.EAP$LOGGER.error("获取兼容升级列表失败", e);
|
||||
}
|
||||
|
||||
return list;
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user