├── .github └── workflows │ └── buildingMenu.yml ├── README.md ├── buildingMenu.py └── memo ├── fabric └── loom │ └── LoomUpdateMapping.md ├── forge ├── Forge下反射.md ├── block │ └── EntityBlock看不到.md ├── capability │ └── ItemStack的Capability同步.md ├── container │ └── quickMoveStack的简要分析.md ├── forgedocs.md ├── forgegradle │ ├── FGUpdateMapping.md │ ├── FG加依赖.md │ ├── UnsupportedFG.md │ ├── genRuns.md │ ├── setDecW没了.md │ └── 找不到依赖项.md ├── glm │ ├── AddLootTableModifier.md │ ├── GLM是什么.md │ └── 使用数据生成器快速生成GLM.md ├── item │ └── 经典锤制合成.md ├── json │ └── 从json读取数据.md ├── ml+8u321.md ├── netty报错.md ├── parchment.md ├── render │ └── IModelData.md ├── serialize │ └── 通用的序列化和反序列化方式.md └── text │ └── 自定义文字颜色.md ├── gradle └── datagen去重.md ├── java ├── Java入门.md ├── PKIXPath.md └── PKIXPath长版.md ├── list.md ├── meme ├── 3T定律.md ├── 943指数.md ├── Fledge指数.md ├── LG法则.md └── ZekerZhayard指数.md ├── mixin └── MixinJavadocs.md ├── tool └── paste.md └── vanilla ├── Player分类.md ├── block └── BlockState.md ├── f3+t.md ├── model └── ModelResourceLocation.md ├── render ├── OverlayTexture.md ├── RenderChunk&Cache.md ├── block │ ├── BlockJsonMdoelStructure.md │ ├── Colouring.md │ ├── MultipartModel.md │ └── VariantsModel.md ├── item │ ├── BlockEntityWithoutLevelRenderer.md │ ├── Colouring.md │ ├── ItemModel.md │ └── ItemPropertyOverride.md └── renderType.md └── vertex.md /.github/workflows/buildingMenu.yml: -------------------------------------------------------------------------------- 1 | name: Building the Menu List 2 | 3 | on: 4 | push: 5 | workflow_dispatch: 6 | concurrency: 7 | group: indexing 8 | cancel-in-progress: true 9 | jobs: 10 | build: 11 | name: Build 12 | runs-on: ubuntu-18.04 13 | steps: 14 | - uses: actions/checkout@v1 15 | - name: Set up Python 16 | uses: actions/setup-python@master 17 | with: 18 | python-version: 3.8 19 | 20 | - name: Download 21 | run: | 22 | python buildingMenu.py 23 | env: 24 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 25 | 26 | - name: Commit 27 | run: | 28 | git add . 29 | if ! git diff-index --quiet HEAD; then 30 | git config --global user.name 'github-actions[bot]' 31 | git config --global user.email 'actions@users.noreply.github.com' 32 | git commit -m "Building the Menu List" -a 33 | fi 34 | 35 | - name: Push changes 36 | uses: ad-m/github-push-action@master 37 | with: 38 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 39 | branch: "bleeding" 40 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # 《模组开发手记》(*Manuscript of Modding*) 2 | 3 | 《模组开发手记》(_Manuscript of Modding_)是一套模仿 [Fabric Discord 使用的 Tag 系统](https://github.com/FabricMC/community)设计的「碎片化 Mod 开发指南」,体裁上接近推特或者微博。 4 | 5 | 如果你使用过 Fabric Discord 的 Tag,或者 Forge Discord 对等的 K9 trick 系统,你会立即明白这个项目的性质和这二者非常相似,只有一点例外:本项目只收集数据,不负责数据的展示方式。未来我们会有包括网页、聊天机器人等方式来实际展示这些内容,你也可以直接通过本 GitHub 仓库浏览。 6 | 7 | 本项目目前仍处于早期建设阶段。欢迎你的贡献! 8 | 9 | ## 覆盖范围 10 | 11 | 目前,本项目的覆盖范围为「一切在 Minecraft Mod 开发中可能会遇到的概念、问题、经验和其他信息」。 12 | 如果你还是拿不准,你可以浏览一下本仓库现有的内容来感受一下。 13 | 14 | ## 项目结构 15 | 16 | 本项目收录的条目都放置于 `memo` 目录下,该目录下又细分为若干子目录,便于浏览与查询。 17 | 18 | 有鉴于本项目目前仍处于早期建设阶段,目录结构的约定(convention)可能需要在很久以后才能形成并记录下来,所以目前请尽量跟随现有的结构对条目进行分类。 19 | 20 | ## 许可 21 | 22 | 本项目内所有内容均在公有领域(Public Domain)下发布。 23 | 24 | 向本仓库贡献新内容即代表你同意将你贡献的内容在公有领域(Public Domain)下发布。 25 | -------------------------------------------------------------------------------- /buildingMenu.py: -------------------------------------------------------------------------------- 1 | import os 2 | 3 | oldFile = open("memo/list.md", "r", encoding="utf-8") 4 | oldText = oldFile.read() 5 | oldFile.close() 6 | 7 | markPos = oldText.find("") 8 | 9 | newText = oldText[0:markPos] + "" 10 | 11 | def addLine(name, level, isDir): 12 | global newText 13 | pre = "\n"+ " " * level + "- " 14 | middle = "`" + name + "`" if isDir else name 15 | newText += pre + middle 16 | 17 | def readFolder(loc, level): 18 | files = [] 19 | dirs = [] 20 | for file in os.listdir(loc): 21 | if(file[0] == "."): 22 | continue 23 | if os.path.isfile(os.path.join(loc, file)): 24 | if file.endswith(".md"): 25 | files.append(file) 26 | else: 27 | dirs.append(file) 28 | for file in files: 29 | addLine(file, level, False) 30 | for dir in dirs: 31 | addLine(dir, level, True) 32 | readFolder(loc + "/" + dir, level + 1) 33 | 34 | readFolder("./memo/", 0) 35 | 36 | print(newText) 37 | 38 | oldFile = open("memo/list.md", "w", encoding="utf-8") 39 | oldFile.write(newText) 40 | oldFile.close() 41 | -------------------------------------------------------------------------------- /memo/fabric/loom/LoomUpdateMapping.md: -------------------------------------------------------------------------------- 1 | [https://fabricmc.net/wiki/tutorial:migratemappings](https://fabricmc.net/wiki/tutorial:migratemappings) 2 | 3 | ```bash 4 | ./gradlew migrateMappings --mappings "目标映射表版本号" 5 | ``` 6 | 7 | 转换结果在 `remappedSrc` 目录。请在运行完后再修改 `build.gradle` 里的映射表版本! -------------------------------------------------------------------------------- /memo/forge/Forge下反射.md: -------------------------------------------------------------------------------- 1 | Forge 提供了 `ObfuscationReflectionHelper` 可以帮助你搞定在不同混淆环境下的反射需求。 2 | 3 | 使用 `ObfuscationReflectionHelper` 时,请一定要使用目标方法/字段的 SRG 名! 4 | 5 | 你可以在众多 Minecraft Mod 开发相关聊天室中(比如 Discord,比如开黑啦,比如……)找到自助查询这个的机器人。 -------------------------------------------------------------------------------- /memo/forge/block/EntityBlock看不到.md: -------------------------------------------------------------------------------- 1 | `BaseEntityBlock` 默认覆写了 2 | ```java 3 | public RenderShape getRenderShape(BlockState pState) { 4 | return RenderShape.INVISIBLE; 5 | } 6 | ``` 7 | 在自己继承的类改为 8 | ```java 9 | @Override 10 | public RenderShape getRenderShape(BlockState pState) { 11 | return RenderShape.MODEL; 12 | } 13 | ``` 14 | 即可 -------------------------------------------------------------------------------- /memo/forge/capability/ItemStack的Capability同步.md: -------------------------------------------------------------------------------- 1 | 由于`Capability`的`Tag`是独立于`ItemStack`本身的`Tag`的,所以我们需要手动进行同步。 2 | 3 | Forge 提供了一组方法方便同步`Capability`。 4 | 5 | ```java 6 | /** 7 | * 用来获取待同步的`Tag`,默认实现为获取`ItemStack`本身的 tag。 8 | * 注意!如果这里覆写方法没有调用 super,可能会导致`ItemStack`本身的同步失效。 9 | */ 10 | @Nullable 11 | @Override 12 | public CompoundTag getShareTag(ItemStack stack) { 13 | // 由于默认实现是 getTag,这里就不判空了,直接使用 stack.getOrCreateTag() 14 | var result = stack.getOrCreateTag(); 15 | // 获取 capability 的值存入 result,这里拿 ENERGY 举例,因为我的上下文确定这里用的一定是 EnergyStorage 所以直接强转了,可以使用 instaceof INBTSerializable serializable 来判断是否可以直接 serializeNBT 16 | stack.getCapability(CapabilityEnergy.ENERGY).ifPresent(cap -> { 17 | result.put("energy", ((EnergyStorage) cap).serializeNBT()); 18 | }); 19 | return result; 20 | } 21 | 22 | /** 用来在`Tag`同步后读取的方法。 */ 23 | @Override 24 | public void readShareTag(ItemStack stack, @Nullable CompoundTagnbt) { 25 | super.readShareTag(stack, nbt); 26 | // 他不太可能是 null,但判空保平安,省得爆了再去找原因 27 | if (nbt == null) { 28 | return; 29 | } 30 | // 同样,获取 capability 的值。 31 | stack.getCapability(CapabilityEnergy.ENERGY).ifPresent(cap -> { 32 | ((EnergyStorage) cap).deserializeNBT(nbt.get("energy")); 33 | }); 34 | } 35 | ``` -------------------------------------------------------------------------------- /memo/forge/container/quickMoveStack的简要分析.md: -------------------------------------------------------------------------------- 1 | ```java 2 | @NotNull 3 | @Override 4 | public ItemStack quickMoveStack(Player playerIn, int index) 5 | { 6 | //自定义容器的物品槽数量,这里以13为例子 7 | int customContainerSlotNum = 13; 8 | ItemStack itemstack = ItemStack.EMPTY; 9 | Slot slot = this.slots.get(index); 10 | if(slot != null && slot.hasItem()) 11 | { 12 | ItemStack stack = slot.getItem(); 13 | itemstack = stack.copy(); 14 | //在这个范围内,都是自定义物品槽,所以要尝试把他们移动到玩家栏 15 | if(index < customContainerSlotNum) 16 | { 17 | //尝试移动到玩家栏,失败则返回空值 18 | 19 | //moveItemStackTo四个参数为: 20 | //要移动的itemStack 21 | //移动槽位的起始ID(包含) 22 | //移动槽位的结束ID(不包含) 23 | //是否优先移动到ID较大的槽位 24 | if(!this.moveItemStackTo(stack, customContainerSlotNum, customContainerSlotNum +36, true)) 25 | { 26 | return ItemStack.EMPTY; 27 | } 28 | slot.onQuickCraft(stack, itemstack); 29 | } 30 | //在这个范围内,都是玩家栏,所以要尝试把他们移动到自定义物品槽 31 | else 32 | { 33 | //尝试移动到自定义物品槽,失败则尝试在快捷栏和背包之间互相移动 34 | if(!this.moveItemStackTo(stack, 0, customContainerSlotNum, false)) 35 | { 36 | //在这个范围内,都是玩家背包,所以要尝试把他们移动到快捷栏 37 | if(index < customContainerSlotNum + 27) 38 | { 39 | //尝试移动到快捷栏,失败则返回空值 40 | if(!this.moveItemStackTo(stack, customContainerSlotNum + 27, customContainerSlotNum + 36, false)) 41 | { 42 | return ItemStack.EMPTY; 43 | } 44 | } 45 | //尝试移动到玩家背包,失败则返回空值 46 | else if(index < customContainerSlotNum + 36 && !this.moveItemStackTo(stack, customContainerSlotNum, customContainerSlotNum + 27, false)) 47 | { 48 | return ItemStack.EMPTY; 49 | } 50 | } 51 | } 52 | 53 | if(stack.isEmpty()) 54 | { 55 | slot.set(ItemStack.EMPTY); 56 | } 57 | else 58 | { 59 | slot.setChanged(); 60 | } 61 | 62 | if(stack.getCount() == itemstack.getCount()) 63 | { 64 | return ItemStack.EMPTY; 65 | } 66 | 67 | slot.onTake(playerIn, stack); 68 | } 69 | 70 | return itemstack; 71 | } 72 | ``` 73 | -------------------------------------------------------------------------------- /memo/forge/forgedocs.md: -------------------------------------------------------------------------------- 1 | Forge 自己的文档位于 https://forgedocs.readthedocs.io/。 2 | 3 | 另有一份不属于 Forge,由社区成员另起炉灶维护的文档,Forge Community Wiki(FCW):https://forge.gemwire.uk 4 | 5 | 因为历史原因,Forge 一直没有维护过 Forge 本身的 Javadocs。 6 | 此外,Minecraft 本体的 Javadocs 实质为映射表数据的一部分,且目前为止无任何有广泛应用的映射表项目可以提供完整的 Javadocs。 -------------------------------------------------------------------------------- /memo/forge/forgegradle/FGUpdateMapping.md: -------------------------------------------------------------------------------- 1 | [https://gist.github.com/JDLogic/bf16deed3bcf99bd9e1a22eb21148389](https://gist.github.com/JDLogic/bf16deed3bcf99bd9e1a22eb21148389) 2 | 3 | 记得先备份! 4 | 5 | ```bash 6 | ./gradlew -PUPDATE_MAPPINGS="这里写目标映射表版本号" -PUPDATE_MAPPINGS_CHANNEL="这里写目标映射表种类" updateMappings 7 | ``` 8 | 9 | 请在运行完后再修改 `build.gradle` 里的映射表版本! -------------------------------------------------------------------------------- /memo/forge/forgegradle/FG加依赖.md: -------------------------------------------------------------------------------- 1 | ```groovy 2 | buildscript { 3 | // 这里面不要碰! 4 | } 5 | 6 | respositories { 7 | // 在这里加依赖所在的仓库,请根据实际情况填写! 8 | maven { 9 | url 'https://fake.repo.invalid/' 10 | } 11 | } 12 | 13 | dependencies { 14 | // 如果你的依赖是一个正常的 Mod,这样就够了 15 | // 不要再把你的 Mod 丢进 run/mods 目录下了!丢进 run/mods 没用的! 16 | implementation fg.deobf('这里换成那个依赖项的 maven coordiante,那个项目肯定会给你这个') 17 | } 18 | 19 | // 但如果你的依赖并不是 Mod,而是一般的 jar,会比较麻烦,你需要下面两个东西: 20 | configurations { 21 | library 22 | implementation.extendsFrom library 23 | } 24 | 25 | minecraft.runs.all { 26 | lazyToken('minecraft_classpath') { 27 | configurations.library.copyRecursive().resolve().collect { it.absolutePath }.join(File.pathSeparator) 28 | } 29 | } 30 | 31 | dependencies { 32 | // 然后这样就可以正常在开发环境使用不是 Mod 的 jar 包了 33 | library '这里换成那个依赖项的 maven coordiante' 34 | } 35 | 36 | publish { 37 | // 这里面不要碰! 38 | } 39 | ``` -------------------------------------------------------------------------------- /memo/forge/forgegradle/UnsupportedFG.md: -------------------------------------------------------------------------------- 1 | 如果你看到了这样的提示: 2 | 3 | ``` 4 | WARNING: You are using an unsupported version of ForgeGradle. 5 | Please consider upgrading to ForgeGradle 5 and helping in the efforts to get old versions working on the modern toolchain. 6 | See https://gist.github.com/TheCurle/fe7ad3ede188cbdd15c235cc75d52d4a for more info on contributing. 7 | ``` 8 | 9 | 这不是错误,这只是一个「你在使用旧版 ForgeGradle」的温馨提示。 10 | 你可以继续使用旧版,只是你在遇到真正的问题时需要自己解决。 11 | 12 | 这个提示还建议你帮助 Forge 将旧版 MDK 所使用的数据迁移到新版上供 ForgeGradle 使用(即 RetroGradle 计划),并给了一个内有更详细说明的链接。但如果你觉得能力不够的话,还是暂时无视这个建议吧。 -------------------------------------------------------------------------------- /memo/forge/forgegradle/genRuns.md: -------------------------------------------------------------------------------- 1 | 每当你改完你 `build.gradle` 中 `minecraft {}` 块里的内容后,都应该重新跑一遍 `genIntellijRuns` 以生成新的 Run Profile,否则你的改动很可能不会生效! 2 | 3 | Eclipse 用户可使用 `genEclipseRuns`。 4 | VSCode 用户可使用 `genVSCodeRuns`。 -------------------------------------------------------------------------------- /memo/forge/forgegradle/setDecW没了.md: -------------------------------------------------------------------------------- 1 | 如果你配置开发环境时被提示 2 | 3 | ``` 4 | Task 'setupDecompWorkspace' not found in root project 5 | ``` 6 | 7 | 说明你在使用 ForgeGradle 3(FG3)及以上版本,`setupDecompWorkspace` 已经不复存在,再执行也没用。 8 | 9 | 此时的正确做法是直接导入 IDEA 即可。 10 | Eclipse 用户可能需要先执行 `./gradlew eclipse`,然后再导入 Eclipse。 -------------------------------------------------------------------------------- /memo/forge/forgegradle/找不到依赖项.md: -------------------------------------------------------------------------------- 1 | 如果你配置开发环境的时候有类似这样的提示,特征是版本号后面跟着一长串长得像 `_mapped_xxxx_yyyy` 的东西: 2 | 3 | ``` 4 | Could not find net.minecraftforge:forge:1.16.5-36.2.20_mapped_official_1.16.5 5 | Searched in the following locations: 6 | ``` 7 | 8 | 首先,这个版本号里有 `_mapped_xxxx_yyyy` 的依赖项是 ForgeGradle 在本地生成的,为的是让网上下载到的 jar 包使用的映射表和你项目配置的映射表一致。 9 | 其次,因为这个依赖项是在本地生成的,你看到的这个错误不是原因,而是结果,是其他地方出错导致 ForgeGradle 没有继续执行以生成这些 jar,进而导致出错。 10 | 11 | 如果你在使用命令行,请往上翻以寻找真正出错的原因。 12 | 如果你在使用 IDEA,你看到的应该是这样的界面: 13 | 14 | ![我是图][#] 15 | 16 | 请点击左侧窗口中最上面一行,也就是有 `Failed`/`已失败` 字样,且最左侧有倒三角箭头的那一行,这样你才能看到完整日志。 -------------------------------------------------------------------------------- /memo/forge/glm/AddLootTableModifier.md: -------------------------------------------------------------------------------- 1 | # AddLootTableModifier——用额外的战利品表作为GLM的参数 2 | 基于Minecraft Forge 1.18.2 40.0.44。**低版本内容可能会有所变化。** 3 | ***** 4 | ## `AddLootTableModifier`的起源与发展 5 |   这个类是先由 Commoble 编写,后被 vectorwing 用于农夫乐事的奖励箱修改。具体的出处目前暂不得知,有可能是 Commoble 直接为 vectorwing 编写了这个类也说不定。目前能确定的首次使用是农夫乐事。因为代码简短清晰、可拓展性强、易于使用等优点集于一身,它是GLM的优秀实例之一。本文提供的代码则是由作者一定优化过后的版本。 6 | ***** 7 | ## `AddLootTableModifier`代码 8 | 完整代码[^完整代码]如下。 9 | ```java 10 | import com.google.gson.JsonObject; 11 | import net.minecraft.resources.ResourceLocation; 12 | import net.minecraft.util.GsonHelper; 13 | import net.minecraft.world.item.ItemStack; 14 | import net.minecraft.world.level.storage.loot.LootContext; 15 | import net.minecraft.world.level.storage.loot.LootTable; 16 | import net.minecraft.world.level.storage.loot.predicates.LootItemCondition; 17 | import net.minecraftforge.common.loot.GlobalLootModifierSerializer; 18 | import net.minecraftforge.common.loot.LootModifier; 19 | import javax.annotation.Nonnull; 20 | import java.util.List; 21 | 22 | public class AddLootTableModifier extends LootModifier { 23 | private final ResourceLocation lootTable; 24 | 25 | public AddLootTableModifier(LootItemCondition[] conditionsIn, ResourceLocation lootTable) { 26 | super(conditionsIn); 27 | this.lootTable = lootTable; 28 | } 29 | 30 | public boolean canApplyModifier() { 31 | return true; 32 | } 33 | 34 | @Nonnull 35 | @Override 36 | protected List doApply(List generatedLoot, LootContext context) { 37 | if(this.canApplyModifier()) { 38 | LootTable extraTable = context.getLootTable(this.lootTable); 39 | extraTable.getRandomItemsRaw(context, LootTable.createStackSplitter(generatedLoot::add)); 40 | } 41 | return generatedLoot; 42 | } 43 | 44 | public static class Serializer extends GlobalLootModifierSerializer { 45 | @Override 46 | public AddLootTableModifier read(ResourceLocation location, JsonObject object, LootItemCondition[] conditions) { 47 | ResourceLocation lootTable = new ResourceLocation(GsonHelper.getAsString(object, "lootTable")); 48 | return new AddLootTableModifier(conditions, lootTable); 49 | } 50 | 51 | @Override 52 | public JsonObject write(AddLootTableModifier instance) { 53 | JsonObject object = this.makeConditions(instance.conditions); 54 | object.addProperty("lootTable", instance.lootTable.toString()); 55 | return object; 56 | } 57 | } 58 | } 59 | ``` 60 |   即便无法看懂以上内容**也没有关系**。这个代码是可以直接复制并使用的。这个类也适用于需要数据包生成器的场合,因为我们的序列化器允许我们这样做。 61 | 62 | [^完整代码]: 不包括package语句 63 | -------------------------------------------------------------------------------- /memo/forge/glm/GLM是什么.md: -------------------------------------------------------------------------------- 1 | # 全局战利品表修改器[^全局战利品表修改器]概述 2 | ***** 3 | ## 什么是全局战利品表修改器(GLM) 4 |   全局战利品表修改器,简称GLM,是 Forge 为方便模组开发者修改战利品表而推出的“数据驱动”型战利品表修改工具。通过不同的战利品表修改器[^战利品表修改器]与不同的战利品表条件[^战利品表条件]组合来对所有满足特定条件的战利品表进行修改。 5 |   这里需要明确一点:GLM 是“全局”和“数据驱动”的。这意味着GLM可以利用数据包对所有满足特定条件的战利品表进行修改。所以战利品表条件是起决定性作用的,你需要考虑“我应该在什么场合下修改怎么样的战利品表?”。 6 | *** 7 | ## 如何使用现成的全局战利品表修改器 8 |   使用GLM ,你需要在你的数据包内添加以下一些文件: 9 | > 1. "data/forge/loot_modifiers/global_loot_modifiers.json",用于选取数据包内需要加载的修改器,路径不可变。 10 | > 2. 一些已经序列化的修改器JSON文件,存储在"data/命名空间[^命名空间]/loot_modifiers"当中。 11 | 12 |   "global_loot_modifiers.json"是用于选取数据包内修改器的**唯一方式**。它的JSON格式类似于标签。我们用一个示例文件为例来讲解它的格式。 13 | ```json 14 | { 15 | "replace": false, // 必要项 16 | "entries": [ 17 | // 按以下目录选取修改器:'data/examplemod/loot_modifiers/example_glm.json' 18 | "examplemod:example_glm", 19 | "examplemod:example_glm2" 20 | // 此处可以添加更多的修改器... 21 | ] 22 | } 23 | ``` 24 |   `replace`的取值代表本数据包是否会覆盖所有的GLM,如果为`true`则其他所有GLM都将**直接无效**。模组开发者建议最好不要选`true`以保证模组兼容性;整合包作者如有必要可以改为`true`来确保整合包稳定性。 25 |   `entries`是该数据包需要加载的修改器的**有序表**,所有的修改器按照行数先后顺序加载。Forge会处理不同数据包里的冲突问题。 26 | 27 | 一个序列化过的修改器JSON文件的格式如下。 28 | ```json 29 | { 30 | "type": "examplemod:example_loot_modifier", 31 | "conditions": [ 32 | // 这里填写各种战利品表条件 33 | // ... 34 | ], 35 | "prop1": "val1", 36 | "prop2": 10, 37 | "prop3": "minecraft:dirt" 38 | } 39 | ``` 40 |   其中`prop1`之类的类型是修改器具体的“参数”,它们并非必要项,而且根据该修改器的序列化器也会有所变化。比如有可能填写某个物品,或者填写一个代表特定战利品表的`ID`[^ID]。 41 |   `type`是该战利品修改器的**序列器**的`ID`,用于决定这个修改器按照哪一种修改器类型对战利品表进行修改。不同的修改器允许使用同一种序列器。 42 |   `conditions`是该修改器起效的条件。所有条件在**大多数情况下**默认为“与”连接,也就是所有条件必须同时满足。你可以利用原版的条件完成“与或非”连接。 43 | *** 44 | ## 对于模组开发者而言没有现成! 45 |   很遗憾,Forge 虽然推出了 GLM 这个强大的工具,但是没有一个现成的修改器以供开发者们使用。这也是 GLM 饱受诟病的原因之一:大多数开发者**并不知道如何正确使用GLM。** 对于任何一个开发者而言,除非你有一个提供了现成实现的前置之外,**只能自己从头开始写一个修改器。** 值得庆幸的是,Forge 提供了一个修改器的抽象类:`LootModifier`类。 46 | 47 | 48 | [^全局战利品表修改器]: 暂译名,即Global Loot Modifier。 49 | [^战利品表修改器]: 暂译名,即Loot Modifier。 50 | [^战利品表条件]: 即Loot Condition,译名出自Minecraft中文维基[战利品表](https://minecraft.fandom.com/zh/wiki/%E6%88%98%E5%88%A9%E5%93%81%E8%A1%A8)。 51 | [^命名空间]: 对于模组来说,命名空间一定等于modid。 52 | [^ID]: 全称为命名空间ID,又称为资源路径(Resource location)。它是一个按照"命名空间:名称"格式的字符串。**每一个ID只能唯一对应一种资源!** 53 | -------------------------------------------------------------------------------- /memo/forge/glm/使用数据生成器快速生成GLM.md: -------------------------------------------------------------------------------- 1 | # 使用数据生成器快速生成GLM 2 | *** 3 | ## `GlobalLootModifierProvider`类 4 |   数据生成器需要一个“供应商”——`Provider`,所以我们来写一个GLM的“供应商”。 5 | ```java 6 | public class GLMProvider extends GlobalLootModifierProvider { 7 | public GLMProvider(DataGenerator gen, String modid) { 8 | super(gen, modid); 9 | } 10 | 11 | @Override 12 | protected void start() { 13 | } 14 | } 15 | ``` 16 |   这就是一个最简单的“供应商”了。要想添加一个新的需要生成数据的修改器,只需要在`start`方法中调用`add`方法就可以了。Forge 提供的示例代码如下。 17 | ```java 18 | this.add("example_modifier", EXAMPLE_MODIFIER_SERIALIZER, new ExampleModifier( 19 | new LootItemCondition[] { 20 | WeatherCheck.weather().setRaining(true).build() // 雨天生效 21 | }, 22 | "val1", // "prop1=val1" 23 | 10, // "prop2=10" 24 | Items.DIRT // "prop3=minecraft:dirt" 25 | )); 26 | ``` 27 |   从上面的代码中我们可以看出`add`方法总共有三个参数:第一个字符串参数代表最终生成结果的ID的“path”;第二个则是该修改器序列化器的实例,如果你注册规范的话直接获取实例就可以使用了;第三个则是一个该修改器的实例,直接创建新实例就可以了。 28 | *** 29 | ## 战利品表条件数组 30 |   战利品表条件是有序加载的。 31 | >   如果你要表示**同时满足所有条件**完成与连接,直接添加一个数组元素就可以了。通常来说你的修改器继承的都是`LootModifier`类,所以他们一定是并列关系。 32 | >   如果你要表示**满足任一条件**完成或连接,则在你的元素中调用`or`方法嵌套一个条件,生成器会自动转换。 33 | >   条件可以组合使用**与或连接**。 34 | >   如果你的情况比较特殊,则根据你的实际情况进行处理。 35 | -------------------------------------------------------------------------------- /memo/forge/item/经典锤制合成.md: -------------------------------------------------------------------------------- 1 | ```java 2 | /** 因为不止合成时会调用,必须要 copy 才不会让原物品受到损伤 */ 3 | @Override 4 | public ItemStack getContainerItem(ItemStack itemStack) { 5 | var result = itemStack.copy(); 6 | result.hurt(1, new Random(), null); 7 | return result; 8 | } 9 | 10 | /** 必须要覆写,他返回值是 true 才会让 getContainerItem 生效,也用来让物品在该爆掉的时候爆掉。 */ 11 | @Override 12 | public boolean hasContainerItem(ItemStack stack) { 13 | return stack.getDamageValue() != stack.getMaxDamage(); 14 | } 15 | ``` -------------------------------------------------------------------------------- /memo/forge/json/从json读取数据.md: -------------------------------------------------------------------------------- 1 | ## Ingredient 2 | ```java 3 | JsonElement JsonElement = ...; 4 | //... 5 | Ingredient anIngredient = Ingredient.fromJson(JsonElement); 6 | ``` 7 | JSON格式参考:https://mcforge.readthedocs.io/en/1.18.x/resources/server/recipes/ingredients 8 | 9 | ## ItemStack 10 | ```java 11 | JsonElement JsonElement = ...; 12 | //... 13 | //第二个boolean参数决定是否读取nbt数据 14 | ItemStack anItemStack = CraftingHelper.getItemStack(JsonElement, true); 15 | ``` 16 | JSON格式参考: 17 | ```json 18 | { 19 | "item": "minecraft:furnace", 20 | "count": 1, 21 | "nbt": { 22 | "...": "..." 23 | } 24 | } 25 | ``` 26 | 27 | ## NBT 28 | ```java 29 | JsonElement JsonElement = ...; 30 | //... 31 | CompoundTag aCompoundTag = CraftingHelper.getNBT(JsonElement); 32 | ``` 33 | 34 | ## BlockState 35 | ```java 36 | JsonElement JsonElement = ...; 37 | //... 38 | CompoundTag aCompoundTag = CraftingHelper.getNBT(JsonElement); 39 | BlockState aBlockState = NBTUtils.readBlockState(aCompoundTag); 40 | ``` 41 | JSON格式参考: 42 | ```json 43 | { 44 | "Name": "minecraft:stone_slab", 45 | "Properties": 46 | { 47 | "type": "top" 48 | } 49 | } 50 | ``` 51 | -------------------------------------------------------------------------------- /memo/forge/ml+8u321.md: -------------------------------------------------------------------------------- 1 | 不论你在哪启动游戏,如果你看到了类似这样的报错: 2 | 3 | ``` 4 | java.lang.NoSuchMethodError: sun.security.util.ManifestEntryVerifier.(Ljava/util/jar/Manifest;)V 5 | ``` 6 | 7 | 请更新 Forge 到至少 36.2.26。 8 | 如果你因为条件受限不能更新 Forge,请不要使用 Java 8u321/11.0.14/15.0.6/17.0.2 或更高版本 9 | 10 | 延伸阅读:[McModLauncher/modlauncher#91](https://github.com/McModLauncher/modlauncher/issues/91) -------------------------------------------------------------------------------- /memo/forge/netty报错.md: -------------------------------------------------------------------------------- 1 | 如果你翻日志的时候翻到类似如下和 Netty 有关的内容: 2 | 3 | ``` 4 | java.lang.UnsupportedOperationException: Reflective setAccessible(true) disabled 5 | at io.netty.util.intenral.ReflectionUtil.trySetAccessible(ReflectionUtil.java:31) ~[netty-all-4.1.68.Final.jar%2332!/:4.1.68.Final] 6 | [以下省略] 7 | ``` 8 | 9 | 这是正常现象,和你遇到的任何其他问题都没有关系。请直接无视这一大段。 10 | 11 | 刷这个栈帧是因为 JEP 396 等 JEP 交付以来 JDK 对其内部实现类的保护越来越严格,表现之一就是限制反射。 -------------------------------------------------------------------------------- /memo/forge/parchment.md: -------------------------------------------------------------------------------- 1 | 在Mod的开发过程中我们往往需要阅读大量的Minecraft代码,但是mojang放出的**offcial mappings**并没有给出参数名的映射,于是在阅读代码时就会遇见如下面这样的代码: 2 | 3 | `private void addMessage(Component p_93791_, int p_93792_, int p_93793_, boolean p_93794_)` 4 | 5 | 这样的参数名字是不可读的,而**Parchment**正是这样一个用来反混淆参数名的项目 6 | 你只需依照[这里](https://github.com/ParchmentMC/Librarian/blob/dev/docs/FORGEGRADLE.md)的做法更改你的**build.gradle**,然后reload你的gradle项目就可以看见你的参数拥有了可读的参数命名 7 | 不过仍然要注意的是**Parchment**的反混淆是人工进行,并不能保证每个地方的参数命名都被反混淆了 -------------------------------------------------------------------------------- /memo/forge/render/IModelData.md: -------------------------------------------------------------------------------- 1 | # IModelData 2 | 3 | `forge`对于原版的扩充,基本可以理解为一个`Map,T>` 4 | 原版未使用的地方,`forge`大多进行了`wrapper`,并传入一个`EmptyModelData.INSTANCE` 5 | ```java 6 | public interface IModelData 7 | { 8 | /** 9 | * Check if this data has a property, even if the value is {@code null}. Can be 10 | * used by code that intends to fill in data for a render pipeline, such as the 11 | * forge animation system. 12 | *

13 | * IMPORTANT: {@link #getData(ModelProperty)} can return {@code null} 14 | * even if this method returns {@code true}. 15 | * 16 | * @param prop The property to check for inclusion in this model data 17 | * @return {@code true} if this data has the given property, even if no value is present 18 | */ 19 | boolean hasProperty(ModelProperty prop); 20 | 21 | @Nullable 22 | T getData(ModelProperty prop); 23 | 24 | @Nullable 25 | T setData(ModelProperty prop, T data); 26 | } 27 | ``` 28 | `CTM`这种链接纹理,依靠的就是这个机制 29 | 30 | 和他一样很重要的是`ModelDataManager`,可以理解为带拥有缓存,刷新等机制的`Map` 31 | `forge`通过`IForgeBlockEntity`为`BlockEntiy`添加了 32 | `requestModelDataUpdate()` 33 | `default @Nonnull IModelData getModelData()` -------------------------------------------------------------------------------- /memo/forge/serialize/通用的序列化和反序列化方式.md: -------------------------------------------------------------------------------- 1 | 对于游戏中的一些对象,都已有一个通用的`save(CompoundTag)`方法将数据存入一个`CompoundTag`对象中用以传输,并拥有一个`load(CompoundTag)`方法从对象中读取数据并返回。 2 | 3 | 以下是部分含有这个通用方法的类: 4 | 5 | - MobEffectInstance 6 | - ItemStack 7 | - Entity 8 | -------------------------------------------------------------------------------- /memo/forge/text/自定义文字颜色.md: -------------------------------------------------------------------------------- 1 | `Minecraft`原版中默认提供了16种颜色的格式化代码,但是在某些情况下(例:文字与各种药水效果的颜色同步)这可能并不够用。 2 | 3 | 以常用的`TranslatableComponent`组件为例: 4 | 5 | ```java 6 | public static TranslatableComponent colorfulText(TranslatableComponent text, int color) 7 | { 8 | return (TranslatableComponent)text.withStyle( 9 | style -> style.withColor(TextColor.fromRgb(color))); 10 | } 11 | ``` 12 | ```java 13 | TranslatableComponent result = colorfulText( 14 | new TranslatableComponent("tooltip.examplemod.example") 15 | 0xfffbf0); 16 | ``` 17 | 18 | 以上的两段代码会获取`tooltip.examplemod.example`对应的本地化字符串,并将之染成`0xfffbf0`的颜色,之后可以通过`result`变量使用这段文字。 19 | -------------------------------------------------------------------------------- /memo/gradle/datagen去重.md: -------------------------------------------------------------------------------- 1 | 将可能自带的一句`sourceSets.main.resources { srcDir 'src/generated/resources' }`换为如下内容: 2 | 3 | ```groovy 4 | task mergeResources(type: Copy) { 5 | def generated = files("src/generated/resources") 6 | def resources = files("src/main/resources") 7 | 8 | from generated 9 | exclude(str -> { 10 | def file = file("src/main/resources/" + str.relativePath) 11 | return file.isFile() && !resources.contains(file) 12 | }) 13 | into "$buildDir/resources/main" 14 | } 15 | 16 | compileJava.dependsOn mergeResources 17 | ``` 18 | 19 | 效果是将包含在 generated 但不包含在 main 中的 resources 复制到 build/resources/main 中。 -------------------------------------------------------------------------------- /memo/java/Java入门.md: -------------------------------------------------------------------------------- 1 | # Java 入门 2 | 3 | 当你阅读这段文字时,你可能是从什么地方得到了这段笔记的链接,也可能是有一个聊天机器人给你了这段话。 4 | 无论如何,首先,欢迎来到 Minecraft Mod 开发的世界! 5 | 6 | 自然,十多年过去了我们有诸如 MCreator 这样的工具,但如果你想实现未曾设想的道路,你终究还是绕不开「坚实的 Java 编程基础」这个必要条件。 7 | 8 | 我们不建议一边写 Mod 一边学习 Java:根据我们过去数年积累的经验,这样做一般来说会浪费很多时间。天赋异禀者有,但是少数。 9 | 我们这里也准备了一些有用的免费资源: 10 | 11 | - [廖雪峰的 Java 教程(适用于 Java 18)](https://www.liaoxuefeng.com/wiki/1252599548343744/) 12 | 13 | ## 我学到什么地方才能开始写 Mod? 14 | 15 | 以下是「写出一个新方块和新物品」可能会需要的 Java 知识点: 16 | 17 | - Java 的数据类型 18 | - 面向对象、创建新对象 19 | - 创建新的类、字段、方法 20 | - 静态和非静态的差别 21 | - 注解(Annotation) 22 | - 匿名函数/Lambda 表达式、方法引用 23 | 24 | ## Java 入门之后 25 | 26 | 未完待续 -------------------------------------------------------------------------------- /memo/java/PKIXPath.md: -------------------------------------------------------------------------------- 1 | 不论你在干什么,如果你看到了这个错误提示 2 | 3 | ``` 4 | javax.net.ssl.SSLHandshakeException: sun.security.validator.ValidatorException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target 5 | ``` 6 | 7 | 或者任何有类似 `PKIX path building failed` 字样的错误或者日志,请更新你的 JDK。 8 | 9 | 如果你电脑上有不止一个 JDK,请确认你没选错 JDK。 10 | 11 | 查阅「PKIXPath长版」以了解这个错误背后的故事。 -------------------------------------------------------------------------------- /memo/java/PKIXPath长版.md: -------------------------------------------------------------------------------- 1 | 不论你在干什么,如果你看到了这个错误提示 2 | 3 | ``` 4 | javax.net.ssl.SSLHandshakeException: sun.security.validator.ValidatorException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target 5 | ``` 6 | 7 | 或者任何有类似 `PKIX path building failed` 字样的错误或者日志,这说明你现在在跑的程序无法验证从某个地方发来的 SSL/TLS 证书,并因此抛出异常。 8 | 9 | 这个错误很容易出现在部署 Mod 开发环境的时候,因为这个过程经常涉及到从一些网站上下载文件,而这些网站中有很多都在使用近年颁发的根证书所签发的 SSL/TLS 证书来启用 HTTPS。 10 | 11 | 解决方法很简单:更新你的 JDK,因为新版 JDK 发布时往往也会更新其自带的根证书信息。 12 | 13 | 如果你电脑上有不止一个 JDK,那基本可以确认你选用了一个旧版的 JDK。请确认你没选错 JDK。 14 | -------------------------------------------------------------------------------- /memo/list.md: -------------------------------------------------------------------------------- 1 | # 总表 2 | 3 | 这是一个所谓的 meta-memo:它用来放置当前 LKSJ-mom 的所有收录条目的总列表。 4 | 5 | 下文是总列表。 6 | 7 | ## 条目列表 8 | 9 | 10 | 11 | 12 | - list.md 13 | - `java` 14 | - PKIXPath长版.md 15 | - PKIXPath.md 16 | - Java入门.md 17 | - `mixin` 18 | - MixinJavadocs.md 19 | - `forge` 20 | - Forge下反射.md 21 | - parchment.md 22 | - netty报错.md 23 | - ml+8u321.md 24 | - forgedocs.md 25 | - `json` 26 | - 从json读取数据.md 27 | - `serialize` 28 | - 通用的序列化和反序列化方式.md 29 | - `container` 30 | - quickMoveStack的简要分析.md 31 | - `text` 32 | - 自定义文字颜色.md 33 | - `item` 34 | - 经典锤制合成.md 35 | - `render` 36 | - IModelData.md 37 | - `block` 38 | - EntityBlock看不到.md 39 | - `glm` 40 | - 使用数据生成器快速生成GLM.md 41 | - AddLootTableModifier.md 42 | - GLM是什么.md 43 | - `forgegradle` 44 | - genRuns.md 45 | - 找不到依赖项.md 46 | - FG加依赖.md 47 | - FGUpdateMapping.md 48 | - setDecW没了.md 49 | - UnsupportedFG.md 50 | - `capability` 51 | - ItemStack的Capability同步.md 52 | - `tool` 53 | - paste.md 54 | - `fabric` 55 | - `loom` 56 | - LoomUpdateMapping.md 57 | - `meme` 58 | - LG法则.md 59 | - Fledge指数.md 60 | - 3T定律.md 61 | - 943指数.md 62 | - ZekerZhayard指数.md 63 | - `vanilla` 64 | - vertex.md 65 | - Player分类.md 66 | - f3+t.md 67 | - `model` 68 | - ModelResourceLocation.md 69 | - `render` 70 | - renderType.md 71 | - RenderChunk&Cache.md 72 | - OverlayTexture.md 73 | - `item` 74 | - BlockEntityWithoutLevelRenderer.md 75 | - ItemPropertyOverride.md 76 | - ItemModel.md 77 | - Colouring.md 78 | - `block` 79 | - MultipartModel.md 80 | - VariantsModel.md 81 | - Colouring.md 82 | - BlockJsonMdoelStructure.md 83 | - `block` 84 | - BlockState.md 85 | - `gradle` 86 | - datagen去重.md -------------------------------------------------------------------------------- /memo/meme/3T定律.md: -------------------------------------------------------------------------------- 1 | # 3TUSK 定律 2 | 3 | 一个经验规律,可表述为: 4 | 5 | > 没有编程开发经验或基础的用户,在遇到游戏崩溃等问题寻求帮助时,所提交的崩溃报告等信息往往不完整,且仅有的信息是和真正的错误完全无关的信息。 6 | 7 | 因一张来自开黑啦的 3TUSK 的聊天记录截图而得名,原文如下: 8 | 9 | > 你们似乎很擅长截图崩溃报告中最不重要的部分 10 | 11 | 虽然这个截图在此之前已经小范围流传了一段时间,但「3T 定律」这个名字直到 2022 年 5 月才首次有人使用。 12 | -------------------------------------------------------------------------------- /memo/meme/943指数.md: -------------------------------------------------------------------------------- 1 | # 酒石酸指数 2 | 3 | 经验常数,定义如下: 4 | 5 | > 一个人从自我感觉 100% 有耐心到完全没有耐心之前,所能回答的新人提出的 Mod 开发相关问题的最大数量。 6 | 7 | 约形成于 2021 年中叶。因「我是酒石酸,不是你妈妈」的 meme 图而得名「酒石酸指数」。 8 | 9 | 一度有「道家深湖指数」的别称,但道家深湖本人出于方便传播的考虑希望统一使用「酒石酸指数」这个名字。 -------------------------------------------------------------------------------- /memo/meme/Fledge指数.md: -------------------------------------------------------------------------------- 1 | # Fledge 指数 2 | 3 | 设社区中有 X 份基于 Forge 或 Fabric 的 Mod 开发教程,再设其中有 0 <= Y <= X 份教程讲述了如何编写方块实体(Block Entity,旧称 Tile Entity),那么 Fledge 指数定义为 Y / X。 4 | 5 | 通常会乘以 100% 后再使用。 6 | 7 | 该指数的值会因计算者所选取的样本变化而变化。 8 | 9 | 最早提出时间可能为 2020 年。 10 | -------------------------------------------------------------------------------- /memo/meme/LG法则.md: -------------------------------------------------------------------------------- 1 | # LG 法则 2 | 3 | 一种 Mod 开发时的设计思想,可表述为 4 | 5 | > 在编写自动化设备等 TileEntity 时,总假定你的 Mod 会被 LG 装在 Origind 服务器上,并且 LG 会放置 800 万台你编写的机器。 6 | 7 | 此法则通过夸张的表述强调了在编写游戏对象的工作逻辑时,代码整体的时间复杂度的重要性。 8 | 9 | 因 LasmGratal(LG)所在的 Origind 服务器的大规模自动化产生的极端 use case 而得名。 -------------------------------------------------------------------------------- /memo/meme/ZekerZhayard指数.md: -------------------------------------------------------------------------------- 1 | # ZekerZhayard 指数 2 | 3 | 设社区中有 X 份 Mod 开发教程,再设其中有 0 <= Y <= X 份教程给出了 Mod 开发相关 QQ 群的群号,那么 ZekerZhayard 指数定义为 Y / X。 4 | 5 | 形成于 2020 或 2021 年。可能是因 ZekerZhayard 喜欢到处加教程的反馈群而得名。 6 | 7 | 和 Fledge 指数一样,通常会乘以 100% 后再使用。 -------------------------------------------------------------------------------- /memo/mixin/MixinJavadocs.md: -------------------------------------------------------------------------------- 1 | [https://jenkins.liteloader.com/job/Mixin/javadoc/overview-summary.html](https://jenkins.liteloader.com/job/Mixin/javadoc/overview-summary.html) -------------------------------------------------------------------------------- /memo/tool/paste.md: -------------------------------------------------------------------------------- 1 | # 粘贴箱(Paste bin) 2 | 3 | 粘贴箱是一种专用来分享软件运行日志、代码、大段文本等的网站。 4 | 5 | 类比一下图床:你可以用图床分享、外链图片,类似地你可以用粘贴箱分享、外链大段文本和代码。 6 | 7 | 你应该使用粘贴箱的理由: 8 | - 在论坛、聊天软件中有效避免刷屏 9 | - 方便手机等移动端用户临时查阅 10 | - 对代码来说,可以提供语法高亮 11 | - 对于文字内容来说,可以视作网盘的替代品 12 | 13 | 这里推荐几个常用的粘贴箱网站,有的需要先注册,有的需要*切换网络环境*方能访问: 14 | 15 | - https://jishiben.me 16 | - https://paste.centos.org 17 | - https://paste.mozilla.org 18 | - https://paste.ubuntu.com 19 | - https://paste.ee 20 | - https://hastebin.com 21 | - https://pastebin.com 22 | - https://gist.github.com -------------------------------------------------------------------------------- /memo/vanilla/Player分类.md: -------------------------------------------------------------------------------- 1 | - `ServerPlayer` 在逻辑服务器上,说话算数 2 | - `LocalPlayer` 是 `ServerPlayer` 的提线木偶 3 | - `RemotePlayer` 是你看到的服务器上的其他玩家 4 | - `FakePlayer` 是假的,是特技,由 Forge 提供,继承自 `ServerPlayer` 5 | - `MockPlayer` 是只在 gametest 的时候存在的训练用稻草人 6 | - `AbstractClientPlayer` 是 `LocalPlayer` 和 `RemotePlayer` 的共同基类 7 | - `Player` 是上面这一大坨的共同基类,上面还有 `LivingEntity` 和 `Entity` -------------------------------------------------------------------------------- /memo/vanilla/block/BlockState.md: -------------------------------------------------------------------------------- 1 | # BlockState 2 | 3 | 打开游戏,按下f3,可以看到右侧的`Targeted Block`和`Targeted Fluid`下,除了显示所指向的方块名称 4 | 在以`#`为标识的`Tag`之上,有的方块/流体还显示了一些别的信息,这就是`BlockState` 5 | 6 | 在Minecraft内创建世界的时候,有一种隐藏的世界类型称之为`debug mode`,见[wiki](https://minecraft.fandom.com/wiki/Debug_mode) 7 | 里面枚举所有方块/流体的BlockState 8 | 9 | 想要为你的方块添加`BlockState`,你需要复写`protected void createBlockStateDefinition(StateDefinition.Builder pBuilder)` 10 | 原版已定义的在类`BlockStateProperties`内,可以直接引用 11 | 12 | 若你想创建自己的BlockState,可以选择实现抽象类`net.minecraft.world.level.block.state.properties.Property>` 13 | 当然,原版已经有特化实现`BooleanProperty`,`DirectionProperty`,`EnumProperty`,`IntegerProperty` 14 | 15 | > 方块所持有的`BlockState`会在加载模型时候,穷举其所有排列组合,即笛卡尔积 16 | > 请确保其枚举总可能结果数量在一个合理的范围内 17 | 18 | `BlockState`的切换需要你手动设置,可以覆写诸如 19 | `getStateForPlacement`在放置时设置 20 | `neighborChanged`毗邻方块更新时设置 21 | `updateIndirectNeighbourShapes`,`updateShape`等 -------------------------------------------------------------------------------- /memo/vanilla/f3+t.md: -------------------------------------------------------------------------------- 1 | 原版快捷键:按 F3+T 可立即重载当前资源包。 2 | 3 | 如果你是从 IDE 中启动的游戏(尤其是 IntelliJ IDEA),你需要先构建一遍项目(菜单栏 Build -> Build Project),然后 F3+T 才会有效果。 -------------------------------------------------------------------------------- /memo/vanilla/model/ModelResourceLocation.md: -------------------------------------------------------------------------------- 1 | ## ModelResourceLocation 2 | 3 | `ModelResourceLocation`,继承自`ResourceLocation`,与之相比多了一个名为`variant`的字段 4 | 与`ResourceLocation`一样,同样拥有`namespace`和`path`字段 5 | 在这里,前者表示模型所处的`命名空间`,即`modID`,而后者则对应所属物品/方块的`registryName` 6 | 而`variant`对于物品,则为`inventory` 7 | 对于方块,则描述了其`BlockState`,若没有BlockState,则为空字符串 8 | `toString`方法为`:#` 9 | 10 | 想要拿到`BlockState`对应的`ModelResourceLocation`可以通过`BlockModelShaper#stateToModelLocation` 11 | 物品则可通过`ModelResourceLocation(.registryName, "inventory")` -------------------------------------------------------------------------------- /memo/vanilla/render/OverlayTexture.md: -------------------------------------------------------------------------------- 1 | # OverlayTexture 2 | 3 | 这张材质由代码生成,大致长这样 4 | ![overlayTexture](https://zomb-676.github.io/CobaltDocs/svg/overlayTexture.svg) 5 | 6 | 在实体死亡/受伤即TNT点燃后的闪烁中有使用 7 | 详见[这里](https://zomb-676.github.io/CobaltDocs/#/render/overlayTexture) 8 | 9 | 案例效果: 10 | ![效果](https://zomb-676.github.io/CobaltDocs/picture/overlayTexture/tnt.gif) -------------------------------------------------------------------------------- /memo/vanilla/render/RenderChunk&Cache.md: -------------------------------------------------------------------------------- 1 | # RenderChunk的缓存问题 2 | 3 | `ChunkRenderDispatcher.RenderChunk`存在一个缓存机制,仅在内部变量`dirty`被设置后才会更新 4 | 如果不依赖`BlockState`变化想要使用`BlockColor`会遇到无法即使更新的情况 5 | 对单个方块进行`dirty`标记的方法为`LevelRender#setBlockDirty`如下 6 | 7 | ```java 8 | public void setBlockDirty(BlockPos pPos, BlockState pOldState, BlockState pNewState) { 9 | if (this.minecraft.getModelManager().requiresRender(pOldState, pNewState)) { 10 | this.setBlocksDirty(pPos.getX(), pPos.getY(), pPos.getZ(), pPos.getX(), pPos.getY(), pPos.getZ()); 11 | } 12 | } 13 | ``` 14 | 可以看到判断新旧`BlockState`所需的模型是否相等,在这里我们按照它的调用方式调用 15 | `setBlocksDirty(int pMinX, int pMinY, int pMinZ, int pMaxX, int pMaxY, int pMaxZ)` 16 | 即可标记`dirty` 17 | 18 | 同理的还有`dirty` `section`的方法 -------------------------------------------------------------------------------- /memo/vanilla/render/block/BlockJsonMdoelStructure.md: -------------------------------------------------------------------------------- 1 | # Block JSON Model Structure 2 | 3 | 首先是`models/block/block.json`,定义了方块在不同`视角/TransformType`对应的变换参数 4 | 5 |

6 | block.json 7 | 8 | ```json 9 | { 10 | "gui_light": "side", 11 | "display": { 12 | "gui": { 13 | "rotation": [ 30, 225, 0 ], 14 | "translation": [ 0, 0, 0], 15 | "scale":[ 0.625, 0.625, 0.625 ] 16 | }, 17 | "ground": { 18 | "rotation": [ 0, 0, 0 ], 19 | "translation": [ 0, 3, 0], 20 | "scale":[ 0.25, 0.25, 0.25 ] 21 | }, 22 | "fixed": { 23 | "rotation": [ 0, 0, 0 ], 24 | "translation": [ 0, 0, 0], 25 | "scale":[ 0.5, 0.5, 0.5 ] 26 | }, 27 | "thirdperson_righthand": { 28 | "rotation": [ 75, 45, 0 ], 29 | "translation": [ 0, 2.5, 0], 30 | "scale": [ 0.375, 0.375, 0.375 ] 31 | }, 32 | "firstperson_righthand": { 33 | "rotation": [ 0, 45, 0 ], 34 | "translation": [ 0, 0, 0 ], 35 | "scale": [ 0.40, 0.40, 0.40 ] 36 | }, 37 | "firstperson_lefthand": { 38 | "rotation": [ 0, 225, 0 ], 39 | "translation": [ 0, 0, 0 ], 40 | "scale": [ 0.40, 0.40, 0.40 ] 41 | } 42 | } 43 | } 44 | 45 | ``` 46 | 47 |
48 | 49 | 然后对于普通的六面方块来说,都来直接或间接来自`models/block/cube.json` 50 | 51 | ```json 52 | { 53 | "parent": "block/block", 54 | "elements": [ 55 | { "from": [ 0, 0, 0 ], 56 | "to": [ 16, 16, 16 ], 57 | "faces": { 58 | "down": { "texture": "#down", "cullface": "down" }, 59 | "up": { "texture": "#up", "cullface": "up" }, 60 | "north": { "texture": "#north", "cullface": "north" }, 61 | "south": { "texture": "#south", "cullface": "south" }, 62 | "west": { "texture": "#west", "cullface": "west" }, 63 | "east": { "texture": "#east", "cullface": "east" } 64 | } 65 | } 66 | ] 67 | } 68 | ``` 69 | 70 | 这里比较特殊的是`texture`后面的`#down` `#up`等,将会查找自将继承此模型的模型,比如`models/block/cube_all.json` 71 | 72 | ```json 73 | { 74 | "parent": "block/cube", 75 | "textures": { 76 | "particle": "#all", 77 | "down": "#all", 78 | "up": "#all", 79 | "north": "#all", 80 | "east": "#all", 81 | "south": "#all", 82 | "west": "#all" 83 | } 84 | } 85 | ``` 86 | 87 | 其他非普通六面的模型,则定义其`elements`,如台阶 88 | 89 |
90 | stair.json 91 | 92 | ```json 93 | { "parent": "block/block", 94 | "display": { 95 | "gui": { 96 | "rotation": [ 30, 135, 0 ], 97 | "translation": [ 0, 0, 0], 98 | "scale":[ 0.625, 0.625, 0.625 ] 99 | }, 100 | "head": { 101 | "rotation": [ 0, -90, 0 ], 102 | "translation": [ 0, 0, 0 ], 103 | "scale": [ 1, 1, 1 ] 104 | }, 105 | "thirdperson_lefthand": { 106 | "rotation": [ 75, -135, 0 ], 107 | "translation": [ 0, 2.5, 0], 108 | "scale": [ 0.375, 0.375, 0.375 ] 109 | } 110 | }, 111 | "textures": { 112 | "particle": "#side" 113 | }, 114 | "elements": [ 115 | { "from": [ 0, 0, 0 ], 116 | "to": [ 16, 8, 16 ], 117 | "faces": { 118 | "down": { "uv": [ 0, 0, 16, 16 ], "texture": "#bottom", "cullface": "down" }, 119 | "up": { "uv": [ 0, 0, 16, 16 ], "texture": "#top" }, 120 | "north": { "uv": [ 0, 8, 16, 16 ], "texture": "#side", "cullface": "north" }, 121 | "south": { "uv": [ 0, 8, 16, 16 ], "texture": "#side", "cullface": "south" }, 122 | "west": { "uv": [ 0, 8, 16, 16 ], "texture": "#side", "cullface": "west" }, 123 | "east": { "uv": [ 0, 8, 16, 16 ], "texture": "#side", "cullface": "east" } 124 | } 125 | }, 126 | { "from": [ 8, 8, 0 ], 127 | "to": [ 16, 16, 16 ], 128 | "faces": { 129 | "up": { "uv": [ 8, 0, 16, 16 ], "texture": "#top", "cullface": "up" }, 130 | "north": { "uv": [ 0, 0, 8, 8 ], "texture": "#side", "cullface": "north" }, 131 | "south": { "uv": [ 8, 0, 16, 8 ], "texture": "#side", "cullface": "south" }, 132 | "west": { "uv": [ 0, 0, 16, 8 ], "texture": "#side" }, 133 | "east": { "uv": [ 0, 0, 16, 8 ], "texture": "#side", "cullface": "east" } 134 | } 135 | } 136 | ] 137 | } 138 | 139 | ``` 140 | 141 |
142 | 143 | 可以观察出,一个`element`,由`from`和`to`定义其在16个体素范围内的位置,由`face`定义其每个面的材质 -------------------------------------------------------------------------------- /memo/vanilla/render/block/Colouring.md: -------------------------------------------------------------------------------- 1 | # Colouring 2 | 3 | 与物品一样,方块也可以染色,原理也一致,只不过接口变了而已 4 | ```java 5 | @OnlyIn(Dist.CLIENT) 6 | public interface BlockColor { 7 | int getColor(BlockState pState, @Nullable BlockAndTintGetter pLevel, @Nullable BlockPos pPos, int pTintIndex); 8 | } 9 | ``` 10 | 11 | 但是方块与物品不同,不存在所谓的`BlockStack`,`BlockState`也无法支持任意颜色的方块存在 12 | 因此,我们需要另外的载体,来存储方块所需的数据 13 | 14 | > 一定要使得模型的`BakedQuad`的**_tintindex_**不为默认值-1 15 | 16 | > 如有需要请调用`LevelRender#setBlocksDirty` 17 | > 否则方块的数据不会**_刷新_** 18 | > 会被阻拦在`LevelRender#compileChunks`内的`ChunkRenderDispatcher.RenderChunk#isDirty` 19 | > 详见[RenderChunk的Cache问题](../render/RenderChunk&Cache.md) 20 | 21 | 案例及出处可以参见[这里](https://zomb-676.github.io/CobaltDocs/#/render/blockModel?id=coloring) 22 | 23 | 示例效果: 24 | ![效果](https://zomb-676.github.io/CobaltDocs/picture/blockModel/colorfulBlock.gif) -------------------------------------------------------------------------------- /memo/vanilla/render/block/MultipartModel.md: -------------------------------------------------------------------------------- 1 | # Multipart Model 2 | 3 | 原版拥有两种以`BlockState`描述模型的方式,在[wiki](https://minecraft.fandom.com/wiki/Model#Block_states)都有描述 4 | 5 | `Multipart Model`,与`Variants`不同,这种方式可以视为模型在一系列条件下的叠加 6 | 以原版的栅栏为例 7 | 8 | ```json 9 | { 10 | "multipart": [ 11 | { 12 | "apply": {"model": "minecraft:block/acacia_fence_post"} 13 | }, 14 | { 15 | "when": {"north": "true"}, 16 | "apply": {"model": "minecraft:block/acacia_fence_side","uvlock": true} 17 | }, 18 | { 19 | "when": {"east": "true"}, 20 | "apply": {"model": "minecraft:block/acacia_fence_side","y": 90,"uvlock": true} 21 | }, 22 | { 23 | "when": {"south": "true"}, 24 | "apply": {"model": "minecraft:block/acacia_fence_side","y": 180,"uvlock": true} 25 | }, 26 | { 27 | "when": {"west": "true"}, 28 | "apply": {"model": "minecraft:block/acacia_fence_side","y": 270,"uvlock": true} 29 | } 30 | ] 31 | } 32 | ``` 33 | 34 | 其渲染流程可视为,对一系列`when`进行判断,如果成立,则`叠加/应用/apply`所指定的模型 35 | 当然这这一系列操作并不会发生在渲染时,在模型加载阶段就已经完成 36 | 37 | 在这里我们给出一个管道的[例子](https://github.com/MalayPrime/rotarism-decorations/blob/master/src/generated/resources/assets/blockstates/normal_pipe.json) 38 | 39 | 40 | 示例效果: 41 | ![效果](https://zomb-676.github.io/CobaltDocs/picture/blockModel/exampleForMultiPart.png) 42 | -------------------------------------------------------------------------------- /memo/vanilla/render/block/VariantsModel.md: -------------------------------------------------------------------------------- 1 | # Variants Model 2 | 3 | 原版拥有两种以`BlockState`描述模型的方式,在[wiki](https://minecraft.fandom.com/wiki/Model#Block_states)都有描述 4 | 5 | `Variants Block Model`其思路为排列组合枚举所有的`BlockState`,并要求逐一给出对应的模型 6 | 这里以草方块为例,其拥有一个布尔类型的名为snow的BlockState 7 | 8 | 首先是`blockState`的文件,blockstates/grass_block.json 9 | 10 |
11 | blockstates/grass_block.json 12 | 13 | ```json 14 | { 15 | "variants": { 16 | "snowy=false": [ 17 | { 18 | "model": "minecraft:block/grass_block" 19 | }, 20 | { 21 | "model": "minecraft:block/grass_block", 22 | "y": 90 23 | }, 24 | { 25 | "model": "minecraft:block/grass_block", 26 | "y": 180 27 | }, 28 | { 29 | "model": "minecraft:block/grass_block", 30 | "y": 270 31 | } 32 | ], 33 | "snowy=true": { 34 | "model": "minecraft:block/grass_block_snow" 35 | } 36 | } 37 | } 38 | ``` 39 | 40 |
41 | 42 | 然后是模型文件 43 | 44 | #### **block/grass_block.json** 45 | 46 |
47 | models/block/grass_block 48 | 49 | ```json 50 | { "parent": "block/block", 51 | "textures": { 52 | "particle": "block/dirt", 53 | "bottom": "block/dirt", 54 | "top": "block/grass_block_top", 55 | "side": "block/grass_block_side", 56 | "overlay": "block/grass_block_side_overlay" 57 | }, 58 | "elements": [ 59 | { "from": [ 0, 0, 0 ], 60 | "to": [ 16, 16, 16 ], 61 | "faces": { 62 | "down": { "uv": [ 0, 0, 16, 16 ], "texture": "#bottom", "cullface": "down" }, 63 | "up": { "uv": [ 0, 0, 16, 16 ], "texture": "#top", "cullface": "up", "tintindex": 0 }, 64 | "north": { "uv": [ 0, 0, 16, 16 ], "texture": "#side", "cullface": "north" }, 65 | "south": { "uv": [ 0, 0, 16, 16 ], "texture": "#side", "cullface": "south" }, 66 | "west": { "uv": [ 0, 0, 16, 16 ], "texture": "#side", "cullface": "west" }, 67 | "east": { "uv": [ 0, 0, 16, 16 ], "texture": "#side", "cullface": "east" } 68 | } 69 | }, 70 | { "from": [ 0, 0, 0 ], 71 | "to": [ 16, 16, 16 ], 72 | "faces": { 73 | "north": { "uv": [ 0, 0, 16, 16 ], "texture": "#overlay", "tintindex": 0, "cullface": "north" }, 74 | "south": { "uv": [ 0, 0, 16, 16 ], "texture": "#overlay", "tintindex": 0, "cullface": "south" }, 75 | "west": { "uv": [ 0, 0, 16, 16 ], "texture": "#overlay", "tintindex": 0, "cullface": "west" }, 76 | "east": { "uv": [ 0, 0, 16, 16 ], "texture": "#overlay", "tintindex": 0, "cullface": "east" } 77 | } 78 | } 79 | ] 80 | } 81 | 82 | ``` 83 | 84 |
85 | 86 | #### **block/grass_block_snow** 87 | 88 | ```json 89 | { 90 | "parent": "minecraft:block/cube_bottom_top", 91 | "textures": { 92 | "top": "minecraft:block/grass_block_top", 93 | "bottom": "minecraft:block/dirt", 94 | "side": "minecraft:block/grass_block_snow", 95 | "particle": "minecraft:block/dirt" 96 | } 97 | } 98 | ``` 99 | 100 | #### **item/grass_block** 101 | ```json 102 | { 103 | "parent": "minecraft:block/grass_block" 104 | } 105 | ``` 106 | 107 | 这里我们给出一个多阶段作物的[例子](https://github.com/MalayPrime/rotarism-decorations/blob/master/src/generated/resources/assets/blockstates/canola.json) 108 | 109 | 示例效果 110 | ![效果](https://zomb-676.github.io/CobaltDocs/picture/blockModel/exampleForVariant.png) -------------------------------------------------------------------------------- /memo/vanilla/render/item/BlockEntityWithoutLevelRenderer.md: -------------------------------------------------------------------------------- 1 | # BlockEntityWithoutLevelRenderer 2 | 3 | 如果你需要更加动态的渲染物品或者物品的渲染需要和使用了`BlockEntityRender`的方块渲染效果一致 4 | 那么你需要的是名为`BlockEntityWithoutLevelRenderer`,曾叫做`ItemStackTileEntityRenderer` 5 | 以代码的方式进行渲染,做到你想要的一切 6 | 7 | 首先要让MC知道你的物品模型需要`BlockEntityWithoutLevelRenderer`,这需要你的`BakedModel.isCustomRenderer`返回`true` 8 | 9 | 因为在`ItemRender#render`内,会以此作为判断 10 | 11 | 而要实现这一目标,给出两种办法 12 | 13 | 一种是让你的`json`模型,直接或间接继承自`builtin/entity`,原因如下 14 | 15 | 在`ModelBakery#loadBlockModel`中,如果你的物品模型,继承自`builtin/entity` 16 | 你的模型就会被读取为一个名为`BLOCK_ENTITY_MARKER`的`BlockModel/UnbakedModel` 17 | 在`BlockModel#bakeVanilla`,模型就会被`bake`为`BuiltInModel`,它的`isCustomRender()`返回就为`true` 18 | 19 | 另一种就是在`ModelBakeEvent`中进行替换,同上文替换`overrides`一致 20 | 21 | 当然你也可以和上文一样,直接定义一个`IModelLoader`走一个模型加载的全套流程 22 | 23 | 这样,只要给你的物品复写`public void initializeClient(Consumer consumer)` 24 | 给传入的`consumer`传入一个复写了`BlockEntityWithoutLevelRenderer getItemStackRenderer()`的`IItemRenderProperties`即可 25 | 不然则会默认返回`BlockEntityWithoutLevelRenderer(blockEntityRenderDispatcher,/*EntityModelSet*/ entityModels)` 26 | 27 | 通过以上操作,物品在渲染时候就会调用你传入的`BlockEntityWithoutLevelRender#renderByItem` 28 | -------------------------------------------------------------------------------- /memo/vanilla/render/item/Colouring.md: -------------------------------------------------------------------------------- 1 | # Colouring 2 | 3 | Minecraft为物品提供了一种染色的方式 4 | ```java 5 | @OnlyIn(Dist.CLIENT) 6 | public interface ItemColor { 7 | int getColor(ItemStack pStack, int pTintIndex); 8 | } 9 | ``` 10 | 利用此接口,返回值为`rgb`,`pTintIndex`为`json`模型内参数 11 | 12 | 注册通过`ItemColors.register(ItemColor pItemColor, ItemLike... pItems)` 13 | 14 | 具体原理为:在提交渲染的最小抽象单位`BakedQuad`前,mc会对`TintIndex`判断,默认情况下,若是不为默认值`-1`则会调用物品对应的`ItemColor#getColor` 15 | 16 | 所以我们可以通过将颜色信息存储在`ItemStack`中,被调用`getColor`时,从中获得颜色信息并返回 17 | 18 | 案例及详细介绍和出处可以参见[这里](https://zomb-676.github.io/CobaltDocs/#/render/itemModel?id=colouring) 19 | 20 | 案例效果 21 | ![效果](https://zomb-676.github.io/CobaltDocs/picture/itemModel/colorfulChalk.gif) -------------------------------------------------------------------------------- /memo/vanilla/render/item/ItemModel.md: -------------------------------------------------------------------------------- 1 | # ItemModel 2 | 3 | minecraft[wiki](https://minecraft.fandom.com/zh/wiki/%E6%A8%A1%E5%9E%8B#.E7.89.A9.E5.93.81.E6.A8.A1.E5.9E.8B)描述 4 | 5 | ### Use Block Model 6 | 7 | **_方块对应的物品默认是没有材质的_** 8 | 如果你的物品想要使用方块的模型,例如`BlockItem`的物品模型 9 | 可以直接让`parent`指向方块对应的模型,格式:`:block/`,`<>`内为根据实际填写的字段 10 | 11 | ### Use 3D Model 12 | 13 | 如果你想让你的物品使用`BlockBench`生成的模型 14 | 可以直接将`BlockBench`导出的文件命名为`` 15 | 或者将`parent`设置为`:item/` 16 | 17 | ### Layer Model 18 | 19 | mc自带的一种生成模型的方式,一多层的`Layer`叠加,为物品生成模型 20 | 可以查看`forge`对原版的扩展,在`ItemLayerModel`内,扩展了原版仅支持4个`layer`至无限 21 | 22 | 案例及详细介绍和出处可以参见[这里](https://zomb-676.github.io/CobaltDocs/#/render/itemModel?id=_3d-json-model) 23 | 24 | 示例效果: 25 | ![效果](https://zomb-676.github.io/CobaltDocs/picture/itemModel/empty.png) -------------------------------------------------------------------------------- /memo/vanilla/render/item/ItemPropertyOverride.md: -------------------------------------------------------------------------------- 1 | # Item Property Override 2 | 3 | 原版提供了一种名为`overrides`的机制,可以通过一定的上下文,从有限数目的模型中指定一个进行渲染 4 | 5 | 调用`ItemProperties.register(Item pItem, ResourceLocation pName, ItemPropertyFunction pProperty)` 6 | 第一个参数`pItem`即需要绑定的物品 7 | 第二个参数`pName`指的是`overrides`的名称,原版的有[这些](https://minecraft.fandom.com/zh/wiki/%E6%A8%A1%E5%9E%8B#.E7.89.A9.E5.93.81.E6.A0.87.E7.AD.BE.E8.B0.93.E8.AF.8D) 8 | 第三个参数就是给定上下文,返回模型的地方了 9 | 10 | ```java 11 | @Deprecated 12 | @OnlyIn(Dist.CLIENT) 13 | public interface ItemPropertyFunction { 14 | float call(ItemStack pStack, @Nullable ClientLevel pLevel, @Nullable LivingEntity pEntity, int pSeed); 15 | } 16 | 17 | @OnlyIn(Dist.CLIENT) 18 | public interface ClampedItemPropertyFunction extends ItemPropertyFunction { 19 | /** @deprecated */ 20 | @Deprecated 21 | default float call(ItemStack pStack, @Nullable ClientLevel pLevel, @Nullable LivingEntity pEntity, int pSeed) { 22 | return Mth.clamp(this.unclampedCall(pStack, pLevel, pEntity, pSeed), 0.0F, 1.0F); 23 | } 24 | 25 | float unclampedCall(ItemStack pStack, @Nullable ClientLevel pLevel, @Nullable LivingEntity pEntity, int pSeed); 26 | } 27 | ``` 28 | 29 | 我们应该使用下面那个函数式接口 30 | 31 | 第三个参数pSeed,部分传入为`0`,部分为`ItemEntity的ID` 32 | 理论上也可以自己随意使用 33 | 34 | 案例及详细介绍和出处可以参见[这里](https://zomb-676.github.io/CobaltDocs/#/render/itemModel?id=overrides) 35 | 36 | 案例效果 37 | ![example](https://zomb-676.github.io/CobaltDocs/picture/itemModel/weather_indicator.gif) -------------------------------------------------------------------------------- /memo/vanilla/render/renderType.md: -------------------------------------------------------------------------------- 1 | # RenderType 2 | 3 | `RenderType`封装了一系列`opengl上下文的操作` 4 | 配合`BufferBuilder`以做到`batch render`加速渲染 5 | 6 | 案例及详细介绍和出处可以参见[这里](https://zomb-676.github.io/CobaltDocs/#/render/renderType) -------------------------------------------------------------------------------- /memo/vanilla/vertex.md: -------------------------------------------------------------------------------- 1 | # Vertex 2 | 3 | mojang对整个过程进行了封装,顶点的容器被封装为`BufferBuilder` 4 | 一个函数若同时拥有float/double和int类型的参数的重载,则表明其值范围不同 5 | float/double表明参数需要`标准化/归一化`必须处于0-1,而int一般表明其值位于0-255 6 | 接口`VertexConsumer`还继承自`IForgeVertexConsumer`,里面也是`balk`版本的函数 7 | 8 | 那么我们该如何获取我们想要的`VertexBuilder`呢? 9 | 10 | 借助`RenderType`我们可以通过`MultiBufferSource#getBuffer` 11 | 前者若上下文无法获取,可以通过`Minecraft.getInstance().renderBuffers().bufferSource()` 12 | 13 | 也可以直接通过`Tesselator.getInstance().getBuilder()`或者直接使用`BufferBuilder`的构造函数 14 | 在上传数据时候,前者通过`Tesselator#end`,后者需要`BufferBuilder#end`和`BufferUploader.end(buffer)` 15 | 16 | 17 | 18 | 这里给出一个提交数据的例子 19 | ```kotlin 20 | fun dataFill(event: RenderLevelLastEvent, buffer: VertexConsumer,block:Block) { 21 | val stack = event.poseStack 22 | val cameraPos = Minecraft.getInstance().gameRenderer.mainCamera.position 23 | stack.translate(-cameraPos.x, -cameraPos.y, -cameraPos.z) 24 | val playerPos = Minecraft.getInstance().player!!.blockPosition() 25 | val x = playerPos.x 26 | val y = playerPos.y 27 | val z = playerPos.z 28 | val pos = BlockPos.MutableBlockPos() 29 | for (dx in (x - 15)..(x + 15)) { 30 | pos.x = dx 31 | for (dy in (y - 15)..(y + 15)) { 32 | pos.y = dy 33 | for (dz in (z - 15)..(z + 15)) { 34 | pos.z = dz 35 | val blockState = Minecraft.getInstance().level!!.getBlockState(pos) 36 | if (blockState.block == block) { 37 | stack.pushPose() 38 | stack.translate(pos.x.toDouble(), pos.y.toDouble(), pos.z.toDouble()) 39 | val lastPose = stack.last().pose() 40 | 41 | buffer.vertex(lastPose, 0f, 0f, 0f).color(1f, 0f, 0f, 0.75f).endVertex() 42 | buffer.vertex(lastPose, 0f, 1f, 0f).color(0f, 1f, 0f, 0.75f).endVertex() 43 | buffer.vertex(lastPose, 1f, 1f, 0f).color(1f, 1f, 1f, 0.75f).endVertex() 44 | buffer.vertex(lastPose, 1f, 0f, 0f).color(1f, 1f, 1f, 0.75f).endVertex() 45 | 46 | // buffer.vertex(lastPose.pose(),1f,0f,0f).color(1f,1f,1f,1f).endVertex() 47 | // buffer.vertex(lastPose.pose(),1f,1f,0f).color(1f,1f,1f,1f).endVertex() 48 | // buffer.vertex(lastPose.pose(),0f,1f,0f).color(1f,1f,1f,1f).endVertex() 49 | // buffer.vertex(lastPose.pose(),0f,0f,0f).color(1f,0f,0f,1f).endVertex() 50 | stack.popPose() 51 | } 52 | } 53 | } 54 | } 55 | } 56 | ``` 57 | 58 | 可以看到下方的注释块于上方仅有顺序上的差别 59 | 原因在于顶点提交的数据决定了面的朝向,有时你需要改变递交的顺序来达到你想要的效果 60 | 61 | 详细介绍和出处可以参见[这里](https://zomb-676.github.io/CobaltDocs/#/render/vertexLife) --------------------------------------------------------------------------------