<?xml version="1.0"?>
<feed xmlns="http://www.w3.org/2005/Atom">
    <id>https://turou.fun</id>
    <title>兔肉的随笔</title>
    <link href="https://turou.fun" />
    <updated>2026-03-17T06:56:52.000Z</updated>
    <category term="比赛作品" />
    <category term="腾讯会议" />
    <category term="Minecraft" />
    <category term="模组开发" />
    <category term="Galgame" />
    <category term="杂谈" />
    <category term="osu!" />
    <category term="ArchitecturyAPI" />
    <category term="CustomStuff3" />
    <entry>
        <id>https://turou.fun/rambling/ome-stream/</id>
        <title>基于 Oven Media Engine 低成本部署亚秒级直播平台</title>
        <link rel="alternate" href="https://turou.fun/rambling/ome-stream/"/>
        <content type="html">&lt;h1 id=&#34;基于-oven-media-engine-低成本部署亚秒级直播平台&#34;&gt;&lt;a class=&#34;anchor&#34; href=&#34;#基于-oven-media-engine-低成本部署亚秒级直播平台&#34;&gt;#&lt;/a&gt; 基于 Oven Media Engine 低成本部署亚秒级直播平台&lt;/h1&gt;
&lt;p&gt;OvenMediaEngine (OME) 是一款亚秒级延迟、支持大规模高清直播的服务器。借助 OME，您可以创建平台 / 服务 / 系统，以亚秒级延迟向数十万观众传输高清视频，并可根据并发观众数量进行扩展。&lt;/p&gt;
&lt;p&gt;这里笔者将带领你从选购 VPS 开始，使用 OME 快速搭建一个亚秒级直播平台，适合与朋友共同观影使用，或者在小范围内进行直播。&lt;/p&gt;
&lt;h2 id=&#34;0-准备工作&#34;&gt;&lt;a class=&#34;anchor&#34; href=&#34;#0-准备工作&#34;&gt;#&lt;/a&gt; 0. 准备工作&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;良好的网络环境，同时&lt;a href=&#34;https://github.com/nelvko/clash-for-linux-install&#34;&gt;让 VPS 也拥有良好的网络环境&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;我们建议以全新的 VPS 实例来部署 OME，如果你想在已有的 VPS 上部署，请注意安全组相关设置，不要随意关闭防火墙&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id=&#34;1-选购-vps&#34;&gt;&lt;a class=&#34;anchor&#34; href=&#34;#1-选购-vps&#34;&gt;#&lt;/a&gt; 1. 选购 VPS&lt;/h2&gt;
&lt;p&gt;首先，为了承载一人推流，多人拉流的高带宽需求，建议选择带宽较大的 VPS，这里笔者比较推荐的是阿里云 200M 峰值带宽的轻量应用服务器。虽然在周末等业务繁忙的时间段可能会有一定的网络波动，但对于个人使用来说已经足够了。&lt;/p&gt;
&lt;p&gt;如果是学生可以领取 300 元云工开物的优惠券，可以免费白嫖半年。也可以准点抢购 38 元一年同配置的服务器，性价比非常高，如果抢不到可以选择 68 的，实际上是与 38 的一样的。&lt;/p&gt;
&lt;p&gt;&lt;img loading=&#34;lazy&#34; data-src=&#34;https://files.seeusercontent.com/2026/03/17/kaI6/11ffc839-d14c-4f82-8058-868b547f.png&#34; alt=&#34;11ffc839-d14c-4f82-8058-868b547ff222.png&#34; /&gt;&lt;/p&gt;
&lt;p&gt;这里我们选择 Debian 12 的系统实例，地区建议选择离你较近的，或者是与你和观众所处地理位置构成的较小的三角形的地区，这样可以减少网络延迟。提交后等待实例创建。&lt;/p&gt;
&lt;h2 id=&#34;2-设置-vps&#34;&gt;&lt;a class=&#34;anchor&#34; href=&#34;#2-设置-vps&#34;&gt;#&lt;/a&gt; 2. 设置 VPS&lt;/h2&gt;
&lt;h3 id=&#34;ip-地址-密码&#34;&gt;&lt;a class=&#34;anchor&#34; href=&#34;#ip-地址-密码&#34;&gt;#&lt;/a&gt; IP 地址 &amp;amp; 密码&lt;/h3&gt;
&lt;p&gt;实例创建完成后，进入实例详情页，点击重置密码:&lt;/p&gt;
&lt;p&gt;&lt;img loading=&#34;lazy&#34; data-src=&#34;https://files.seeusercontent.com/2026/03/17/8oXc/Snipaste_2026-03-17_12-14-56.png&#34; alt=&#34;Snipaste_2026-03-17_12-14-56.png&#34; /&gt;&lt;/p&gt;
&lt;p&gt;在红框处可以找到公网 IP 地址，后续我们将使用这个地址来连接 VPS 和访问 OME 服务，记录下来&lt;/p&gt;
&lt;p&gt;密码设置方面，我们非常推荐&lt;a href=&#34;https://1password.com/zh-cn/password-generator&#34;&gt;随机生成一个密码&lt;/a&gt;，我们选择 20 位随机，设置并记录下来&lt;/p&gt;
&lt;h3 id=&#34;关闭安全组&#34;&gt;&lt;a class=&#34;anchor&#34; href=&#34;#关闭安全组&#34;&gt;#&lt;/a&gt; 关闭安全组&lt;/h3&gt;
&lt;p&gt;如果仅使用 OME 搭建直播站，安全组的配置必要性不大，反而可能会因为配置不当导致无法访问 VPS，所以这里我们直接关闭安全组：&lt;/p&gt;
&lt;p&gt;点击防火墙 - 添加规则 - 应用类型选择全部 TCP + UDP - 点击确认添加：&lt;/p&gt;
&lt;p&gt;&lt;img loading=&#34;lazy&#34; data-src=&#34;https://files.seeusercontent.com/2026/03/17/t7Mh/pasted-image-1773721571838.webp&#34; alt=&#34;pasted-image-1773721571838.webp&#34; /&gt;&lt;/p&gt;
&lt;h3 id=&#34;连接主机&#34;&gt;&lt;a class=&#34;anchor&#34; href=&#34;#连接主机&#34;&gt;#&lt;/a&gt; 连接主机&lt;/h3&gt;
&lt;p&gt;如果你还不知道如何登录 Linux 主机，这里推荐安装 &lt;a href=&#34;https://termius.com/&#34;&gt;Termius&lt;/a&gt; 工具。&lt;/p&gt;
&lt;p&gt;在 Termius 中添加主机，输入记录的公网 IP 地址、密码，用户名 root，这样我们就可以通过 Termius 连接到 VPS 上了，连接后，我们先安装一些必要的工具：&lt;/p&gt;
&lt;figure class=&#34;highlight bash&#34;&gt;&lt;figcaption data-lang=&#34;bash&#34;&gt;&lt;/figcaption&gt;&lt;table&gt;&lt;tr&gt;&lt;td data-num=&#34;1&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token function&#34;&gt;apt&lt;/span&gt; update &lt;span class=&#34;token operator&#34;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&#34;token function&#34;&gt;apt&lt;/span&gt; &lt;span class=&#34;token function&#34;&gt;install&lt;/span&gt; &lt;span class=&#34;token parameter variable&#34;&gt;-y&lt;/span&gt; &lt;span class=&#34;token function&#34;&gt;curl&lt;/span&gt; &lt;span class=&#34;token function&#34;&gt;wget&lt;/span&gt; &lt;span class=&#34;token function&#34;&gt;git&lt;/span&gt; &lt;span class=&#34;token function&#34;&gt;unzip&lt;/span&gt; ca-certificates&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;2&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token function&#34;&gt;curl&lt;/span&gt; &lt;span class=&#34;token parameter variable&#34;&gt;-fsSL&lt;/span&gt; https://get.docker.com &lt;span class=&#34;token parameter variable&#34;&gt;-o&lt;/span&gt; get-docker.sh&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;3&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token function&#34;&gt;sh&lt;/span&gt; get-docker.sh&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;&lt;h2 id=&#34;3-安装-ome&#34;&gt;&lt;a class=&#34;anchor&#34; href=&#34;#3-安装-ome&#34;&gt;#&lt;/a&gt; 3. 安装 OME&lt;/h2&gt;
&lt;p&gt;下面的内容参考 &lt;a href=&#34;https://docs.ovenmediaengine.com/getting-started/getting-started-with-ome-docker-launcher&#34;&gt;OME 官方文档&lt;/a&gt;，我们将使用 OME Docker Launcher 来部署。&lt;/p&gt;
&lt;p&gt;连接至主机后，执行以下命令来安装 OME Docker Launcher，如果等待时间过长，请检查 准备工作 中提到的网络环境问题（建议使用 TUN 模式）：&lt;/p&gt;
&lt;figure class=&#34;highlight bash&#34;&gt;&lt;figcaption data-lang=&#34;bash&#34;&gt;&lt;/figcaption&gt;&lt;table&gt;&lt;tr&gt;&lt;td data-num=&#34;1&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token function&#34;&gt;curl&lt;/span&gt; &lt;span class=&#34;token parameter variable&#34;&gt;-OL&lt;/span&gt; &lt;span class=&#34;token string&#34;&gt;&#39;https://raw.githubusercontent.com/AirenSoft/OvenMediaEngine/v0.20.0/misc/ome_docker_launcher.sh&#39;&lt;/span&gt; &lt;span class=&#34;token operator&#34;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&#34;token function&#34;&gt;chmod&lt;/span&gt; +x ome_docker_launcher.sh&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;2&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;./ome_docker_launcher.sh setup&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;&lt;p&gt;当在命令行中看到  &lt;code&gt;OvenMediaEngine is ready to start!&lt;/code&gt;  的提示时，说明 OME 已经安装完成了，接下来我们可以启动 OME 服务：&lt;/p&gt;
&lt;figure class=&#34;highlight bash&#34;&gt;&lt;figcaption data-lang=&#34;bash&#34;&gt;&lt;/figcaption&gt;&lt;table&gt;&lt;tr&gt;&lt;td data-num=&#34;1&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token assign-left variable&#34;&gt;OME_HOST_IP&lt;/span&gt;&lt;span class=&#34;token operator&#34;&gt;=&lt;/span&gt;YOUR_IP ./ome_docker_launcher.sh start&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;&lt;p&gt;&lt;img loading=&#34;lazy&#34; data-src=&#34;https://files.seeusercontent.com/2026/03/17/Nmk7/pasted-image-1773725836508.webp&#34; alt=&#34;pasted-image-1773725836508.webp&#34; /&gt;&lt;/p&gt;
&lt;p&gt;看到对应端口即可说明 OME 已经成功启动了，接下来我们就可以使用 OBS 进行推流了。&lt;/p&gt;
&lt;h2 id=&#34;4-推流设置&#34;&gt;&lt;a class=&#34;anchor&#34; href=&#34;#4-推流设置&#34;&gt;#&lt;/a&gt; 4. 推流设置&lt;/h2&gt;
&lt;p&gt;我们推荐下面几个推流方案，推流几乎不影响直播延迟：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;RTMP：最常见的协议，兼容性好，几乎所有直播软件都支持。&lt;/li&gt;
&lt;li&gt;SRT：说是适用于网络不稳定的环境，提供优化，并且支持 HEVC 编码。&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;这里笔者测试以后，二者的延迟没有明显差距，但是 SRT 支持 HEVC 编码，保证画质的情况下可以适当降低码率，这里笔者比较推荐。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;使用 RTMP 推流（推荐使用 OBS）： &lt;code&gt;rtmp://8.137.114.514:1935/app/stream&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;使用 SRT 推流（推荐使用 OBS）： &lt;code&gt;srt://8.137.114.514:9999?streamid=vhost/app/stream&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;可以参考的设置：&lt;/p&gt;
&lt;p&gt;&lt;img loading=&#34;lazy&#34; data-src=&#34;https://files.seeusercontent.com/2026/03/17/9Sfu/pasted-image-1773726820761.webp&#34; alt=&#34;pasted-image-1773726820761.webp&#34; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img loading=&#34;lazy&#34; data-src=&#34;https://files.seeusercontent.com/2026/03/17/a6Yd/pasted-image-1773726719654.webp&#34; alt=&#34;pasted-image-1773726719654.webp&#34; /&gt;&lt;/p&gt;
&lt;h2 id=&#34;5-拉流设置&#34;&gt;&lt;a class=&#34;anchor&#34; href=&#34;#5-拉流设置&#34;&gt;#&lt;/a&gt; 5. 拉流设置&lt;/h2&gt;
&lt;p&gt;拉流对延迟的影响很大，为了实现亚秒级的直播延迟，这里只推荐使用 WebRTC 协议进行拉流。&lt;/p&gt;
&lt;p&gt;WebRTC 可以实现 1 秒以下的直播延迟，HLS 的延迟通常在 2-5 秒左右，SRT 不支持直接在浏览器播放，这里笔者没有进行测试。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;使用 WebRTC 拉流（推荐使用 OvenPlayer）： &lt;code&gt;ws://8.137.114.514:3333/app/stream&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;使用 HLS 拉流（推荐使用 OvenPlayer）： &lt;code&gt;http://8.137.114.514:3333/app/stream/llhls.m3u8&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;这里推荐使用 OvenPlayer 来播放，OvenPlayer 是 OME 官方推出的播放器，支持多协议播放，兼容性好，性能优越，这里我们可以先测试一下我们的部署：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;使用 OBS 推流到 OME：使用 SRT 推流  &lt;code&gt;srt://8.137.114.514:9999?streamid=vhost/app/stream&lt;/code&gt; ，IP 地址替换为你的 VPS 公网 IP 地址。&lt;/li&gt;
&lt;li&gt;使用 OvenPlayer 拉流：在浏览器中打开  &lt;code&gt;http://demo.ovenplayer.com/&lt;/code&gt; ，在源处输入  &lt;code&gt;ws://8.137.114.514:3333/app/stream&lt;/code&gt; ，点击播放，如果能够看到视频画面，说明部署成功了。&lt;/li&gt;
&lt;/ol&gt;
&lt;blockquote&gt;
&lt;p&gt;注意：如果无法播放，请注意 &lt;a href=&#34;http://demo.ovenplayer.com&#34;&gt;demo.ovenplayer.com&lt;/a&gt; 的前缀是 HTTP 而不是 HTTPS，因为我们的 WebRTC 没有使用安全连接，如果你在 HTTPS 的页面中使用 HTTP 的地址，浏览器会阻止加载不安全的内容。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id=&#34;6-更好的体验&#34;&gt;&lt;a class=&#34;anchor&#34; href=&#34;#6-更好的体验&#34;&gt;#&lt;/a&gt; 6. 更好的体验&lt;/h2&gt;
&lt;p&gt;到这里，我们已经成功搭建了一个亚秒级直播平台，但是每次都需要用户输入 IP 地址和流名称来推流和拉流，体验并不好，我们可以通过一些简单的前端页面来优化这个体验。&lt;/p&gt;
&lt;p&gt;为了创建一个简单的网页展示给朋友们，我们可以先安装 Nginx 来托管我们的前端页面：&lt;/p&gt;
&lt;figure class=&#34;highlight bash&#34;&gt;&lt;figcaption data-lang=&#34;bash&#34;&gt;&lt;/figcaption&gt;&lt;table&gt;&lt;tr&gt;&lt;td data-num=&#34;1&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token function&#34;&gt;apt&lt;/span&gt; &lt;span class=&#34;token function&#34;&gt;install&lt;/span&gt; &lt;span class=&#34;token parameter variable&#34;&gt;-y&lt;/span&gt; nginx &lt;span class=&#34;token operator&#34;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&#34;token builtin class-name&#34;&gt;cd&lt;/span&gt; /var/www/html &lt;span class=&#34;token operator&#34;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&#34;token function&#34;&gt;rm&lt;/span&gt; &lt;span class=&#34;token parameter variable&#34;&gt;-rf&lt;/span&gt; * &lt;span class=&#34;token operator&#34;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&#34;token function&#34;&gt;wget&lt;/span&gt; https://raw.githubusercontent.com/TrueRou/PureOvenPlayer/refs/heads/main/index.html&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;2&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token function&#34;&gt;nano&lt;/span&gt; /var/www/html/index.html&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;&lt;p&gt;在  &lt;code&gt;index.html&lt;/code&gt;  的前半部分可以找到配置项，我们将其中的  &lt;code&gt;demo.ovenplayer.com&lt;/code&gt;  替换为我们的 VPS 公网 IP 地址：&lt;/p&gt;
&lt;p&gt;&lt;img loading=&#34;lazy&#34; data-src=&#34;https://files.seeusercontent.com/2026/03/17/3hJc/pasted-image-1773730635894.webp&#34; alt=&#34;pasted-image-1773730635894.webp&#34; /&gt;&lt;/p&gt;
&lt;p&gt;修改完成后保存，然后重新载入 Nginx 即可：&lt;/p&gt;
&lt;figure class=&#34;highlight bash&#34;&gt;&lt;figcaption data-lang=&#34;bash&#34;&gt;&lt;/figcaption&gt;&lt;table&gt;&lt;tr&gt;&lt;td data-num=&#34;1&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;nginx &lt;span class=&#34;token parameter variable&#34;&gt;-s&lt;/span&gt; reload&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;&lt;p&gt;现在我们就可以通过访问公网地址  &lt;code&gt;http://8.137.114.514/&lt;/code&gt;  来观看直播了，当然你也可以将这个地址分享给你的朋友们，让他们也能观看直播。&lt;/p&gt;
</content>
        <category term="杂谈" />
        <updated>2026-03-17T06:56:52.000Z</updated>
    </entry>
    <entry>
        <id>https://turou.fun/rambling/niijima-2026/</id>
        <title>给最喜欢的你，也是最讨厌的你 —— 2026年的视点看新岛夕</title>
        <link rel="alternate" href="https://turou.fun/rambling/niijima-2026/"/>
        <content type="html">&lt;p&gt;&lt;strong&gt;本文参与《于 Comic Market 50 周年所思，视觉小说的从今往后》高校联合刊物投稿&lt;/strong&gt;&lt;/p&gt;
&lt;h2 id=&#34;正文&#34;&gt;&lt;a class=&#34;anchor&#34; href=&#34;#正文&#34;&gt;#&lt;/a&gt; 正文&lt;/h2&gt;
&lt;p&gt;喜欢一部作品可以有很多理由，但讨厌一部作品只需要一个理由就够了。而一个理由就可以让玩家退出游戏。近年来，视觉小说企划逐渐转向以市场为主导的模式，更多倾向于呈现玩家喜欢的内容，而不一定是剧本家想写的内容，剧本家的个人表达受限，更倾向于写出稳定优秀的剧本。在 2026 年回看新岛夕的作品，我们也能略微窥探业界的奥秘。&lt;/p&gt;
&lt;p&gt;新岛夕在 Saga Planets 时期是一个足够自信的剧本家，他极具个人风格的剧本体现在对于破碎美的执念，对于自己想法的传达的执念。简单来说就是新岛夕敢于筛选读者，他的剧本倾向于展示自己的长处，如果你不喜欢，大可断然离开。拿《夏梦渚》来说，最出彩的是羊线反转和整体夏梦结构和节奏的设计（梦境与现实交织、部分叙诡伏笔安插）。在非奇幻背景下写好这样的故事结构，已经是非常不容易的事情。但如果揭这作的短，拿 TE 前后来说，揭开这场夏日是永无止境的梦以后，故事达到高潮，这意味着新岛夕最擅长的故事性开始发力，但此时文笔方面的不足太过严重，步的 TE 情感爆发甚至不如个人线的羊，玩家在情感爆发时找不到落点，玩家只能依靠理性理解，而缺少一触即发的感动，这会造成非常大的落差。非常可喜的是，2010 年前后的业界非常欢迎偏科生，首先存在一批非常喜欢破碎美结局的玩家，我们愿意赞扬你 100 分的优点，理性分析你 60 分的缺点，并以此津津乐道。&lt;/p&gt;
&lt;p&gt;在自由身初期，新岛夕更加贯彻其理想主义者的风格，交出了《魔女恋爱日记》的答卷，实际上，这部作品的题材和主题比较筛选读者，这侧面规避了冗长的前期铺垫和氛围渲染可能会直接劝退不少读者的风险，但与此同时，本作在内核上更进一步，可以说是个人风格的纵向延续。这作在企划上属于母公司的试验品，站在阵容和后续 FD 情况上来看，商业上仍然算是比较成功的作品。愿意静下心来完整倾听剧本家的故事，仍然是新岛夕的市场能够形成的最底层的逻辑。&lt;/p&gt;
&lt;p&gt;在魔恋日发售之后的几年中，业界和市场发生了更大的变化，新岛夕参与了《Summer Pockets》的制作，但仅作为脚本负责鸥线和白羽主线。在 Key 社的主场中，他作为脚本展现的能力不弱，是难得的在个人表达部分受限的环境下仍能保有其不可妥协的鲜明风格的剧本家。在久岛鸥线的故事进行中，可以看到很多来自 SAGA 时期的影子，结局也颇有夏梦渚男主存在的真相和玉树樱线 TE 的味道。站在整个作品的角度而言，鸥线 “不完美的结局” 作为个人线的非典型表达，出色地完成了使命。新岛夕对于其浪漫与美学的执念作为参与该作品的所有剧本家中的非典型表达，不再是筛选读者，而是变相拓展了读者。新岛夕已然不再是曾经那个锋利而自恋的少年，我们最喜欢的他，或许永远停留在了过去，但未来的他可以被更多人喜欢。&lt;/p&gt;
&lt;p&gt;在 2026 年的视角来看，启用新岛夕进行完全企划是颇具风险的选择，简单来说，他的完全企划更像是粉丝向作品，而近年的游戏公司常常难以承担受众萎缩的风险，更希望选择稳定优秀受众广的制作主题和制作团队。另一方面，新岛夕在 “打工战士” 生涯中展现出了更强的均衡性和兼容性，我们仍然可以期待他与其他会社碰撞出更多火花。&lt;/p&gt;
</content>
        <category term="Galgame" />
        <category term="杂谈" />
        <updated>2026-01-23T05:18:52.000Z</updated>
    </entry>
    <entry>
        <id>https://turou.fun/osu/pp-system/</id>
        <title>PP究竟是如何算出来的? 剖析osu!的PP算法!</title>
        <link rel="alternate" href="https://turou.fun/osu/pp-system/"/>
        <content type="html">&lt;p&gt;本文是笔者研究 osu! 戳泡泡模式 PP 算法的一个记录文章，旨在帮助其他对 PP 算法感兴趣的玩家快速建立对 PP 算法的认知。由于大部分内容是笔者的个人理解和经验谈，可能存在错误或者偏差，欢迎读者提出改进意见和补充内容.&lt;/p&gt;
&lt;p&gt;本文的代码片段来源于 rosu-pp, rosu-pp 是 osu!lazer 全模式算法的 Rust 实现，也是目前最流行的 PP 计算库。本文面向略有编程经验的读者，读者不必深入了解 Rust, 只需要具有本科级别的编程知识和数学知识即可理解本文内容.&lt;/p&gt;
&lt;h1 id=&#34;初探pp算法&#34;&gt;&lt;a class=&#34;anchor&#34; href=&#34;#初探pp算法&#34;&gt;#&lt;/a&gt; 初探 PP 算法&lt;/h1&gt;
&lt;h2 id=&#34;两步式计算&#34;&gt;&lt;a class=&#34;anchor&#34; href=&#34;#两步式计算&#34;&gt;#&lt;/a&gt; 两步式计算&lt;/h2&gt;
&lt;p&gt;抛开复杂的算法不谈，我们先来谈论一些表象。在表面来看，PP 系统会综合评估&lt;strong&gt;谱面难不难&lt;/strong&gt;和&lt;strong&gt;打的好不好&lt;/strong&gt;两个概念，最终计算出一个 PP 数值.&lt;/p&gt;
&lt;p&gt;广泛存在的一个误区是 PP 系统会根据 Replay 来评判这两个概念，这是一种错误的认知。实际上，PP 计算是&lt;strong&gt;两步式&lt;/strong&gt;进行的，第一步计算谱面难度 (这一步通常自变量是所有物件和速度倍率), 第二步是根据第一步算出的&lt;strong&gt;谱面难度&lt;/strong&gt;和以及一些&lt;strong&gt;其他变量&lt;/strong&gt; (例如谱面的三维，玩家 Acc, Miss 数，最大 Combo) 共同计算出最终 PP 数值.&lt;/p&gt;
&lt;p&gt;不难想象，第一步计算的时候我们主要在考虑物件之间相互影响产生的基值，而第二步计算主要是根据玩家的表现在基值上进行倍率的调整。这里我们举一个简单的例子，曲奇在 FDFD 中取得了 939PP 的成绩，而 mrekk 取得了 926PP 的成绩，Accolibed 取得了 640PP 的成绩，他们三人在第一步计算中算出的基值是&lt;strong&gt;相同&lt;/strong&gt;的，只是在第二步计算中产生了不同的倍率。曲奇的准确度高于 mrekk, 固取得了更高的 acc_bonus. Accolibed 掉了一个 Miss, 固得到了 miss_penalize. 最终形形色色的 bonus 和 penalize 在第二步作用在基值上，算出了最终的数值.&lt;/p&gt;
&lt;p&gt;也正因如此，在利用各种 PP 计算器来计算大量成绩的时候，先按照谱面归类将大幅提高计算效率，因为在没有开启调整速度的 Mod 的情况下，第一步计算 (这也是决速步骤) 只用进行一次就足够了.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;上文提到的误区实际上源自于玩家对算法的过度期待: Miss 了简单的部分，SS 了困难的部分，PP 算法是无法分辨出这两个场景的。这些场景只能被视为一个整体，表现于上文提到的&lt;strong&gt;其他变量&lt;/strong&gt;中，影响第二步计算的各种倍率。这也是各个 PP 计算器不需要提供 Replay, 只需要提供最终结果，就能计算出准确 PP 的原理所在.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;至此，我们已经将 PP 计算的过程分为了两部分，通常来说，第一步被称为 Difficulty 计算，第二步被称为 Performance 计算.&lt;/p&gt;
&lt;p&gt;&lt;img loading=&#34;lazy&#34; data-src=&#34;https://s2.loli.net/2024/07/01/JE8c1DUvZdnLy7g.png&#34; alt=&#34;Snipaste_2024-07-01_11-34-12.png&#34; /&gt;&lt;/p&gt;
&lt;h2 id=&#34;difficulty-与-skills&#34;&gt;&lt;a class=&#34;anchor&#34; href=&#34;#difficulty-与-skills&#34;&gt;#&lt;/a&gt; Difficulty 与 Skills&lt;/h2&gt;
&lt;p&gt;经过上文的探究，我们明白了 Difficulty 的计算是重中之重，是区分&lt;strong&gt;谱面难不难&lt;/strong&gt;的关键.&lt;/p&gt;
&lt;p&gt;osu! 的设计者们有意识地从多个维度来评估谱面的难度，在多年的逐步迭代中明确了 Skills 这个概念。当然，针对不同的模式，Skill 分别有不同维度的定义.&lt;/p&gt;
&lt;p&gt;对于戳泡泡玩家而言，戳泡泡模式的 Skills 应该至少包含了 Aim 和 Speed 这两个老生常谈的维度，当然事实也是这样的，可以参考下面的表格.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;osu!standard: Aim, Speed, Flashlight&lt;/li&gt;
&lt;li&gt;osu!mania: Strain&lt;/li&gt;
&lt;li&gt;osu!catch: Movement&lt;/li&gt;
&lt;li&gt;osu!taiko: Color, Rhythm, Stamina&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;这里由于笔者对其他模式不甚了解，这里我们主要将目光聚焦于 osu!standard. 本文主要也将谈论 Aim, Speed 两个 Skill.&lt;/p&gt;
&lt;h2 id=&#34;skill-与-微积分&#34;&gt;&lt;a class=&#34;anchor&#34; href=&#34;#skill-与-微积分&#34;&gt;#&lt;/a&gt; Skill 与 微积分？&lt;/h2&gt;
&lt;p&gt;相信读者读到这里已经对 Aim, Speed 的具体计算非常感兴趣了，但在正式开始之前，我们先笼统地认识一下 Skill 的计算方式.&lt;/p&gt;
&lt;p&gt;读者不妨设想一下，对于一张时长几分钟的谱面，我们应该如何计算他的难度呢？实际上这个问题是困难的，物件以时间为轴堆叠仅仅是一堆表示位置的数组，是很难衡量困难与否的。如果在本科期间修读过高等数学的读者可能对这个例子感到熟悉：一个光滑的曲面本身可能是不规则的，想要计算面积是很困难的，但是如果将曲面无限细分，将最小单位看作是一个正方体，问题就很好解决了。即整体的求解是困难的，但是一个切片往往是容易求解的.&lt;/p&gt;
&lt;p&gt;Skill 的计算遵循了类似先&lt;em&gt;微分&lt;/em&gt;后&lt;em&gt;积分&lt;/em&gt;的过程，在计算过程中，物件被&lt;strong&gt;细分&lt;/strong&gt;进行&lt;strong&gt;单独对待&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;简单来说，每个物件都可以算作一个表示难度的元素，在 PP 系统中，这个元素被称作是&lt;strong&gt; Strain&lt;/strong&gt;. 某 Skill 数值的计算依托于一个 Strain 构成的&lt;strong&gt;集合&lt;/strong&gt;。最终 Skill 数值 (例如: raw_aim, raw_speed), 实际上是对 Strains 进行积分 (以某种方式累加) 的结果.&lt;/p&gt;
&lt;p&gt;戳泡泡中，大部分 Pattern (例如：锐角跳，钝角跳) 都是针对&lt;strong&gt; Strain&lt;/strong&gt; 展开的，即我们在计算单个&lt;strong&gt; Strain&lt;/strong&gt; 的过程中区分这些 Pattern, 并给出最终的 Strain 值。在实际计算 Strain 的过程中，我们可以拿到很多参数辅助我们，决定 &amp;quot;当前物件的难度&amp;quot;.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;osu_curr_obj, osu_last_obj, osu_last_last_obj: 拿到前后的物件&lt;/li&gt;
&lt;li&gt;travel_dist, travel_time, strain_time, jump_dist: 距离，时间等几何量、物理量&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;我们会在之后的讲解中逐步接触和理解这些概念。在之后的讲解中，我们也将以 Strain 计算为重点，&lt;strong&gt;自下而上&lt;/strong&gt;地剖析具体计算过程.&lt;/p&gt;
&lt;p&gt;接下来，我们将逐步探寻 Aim, Speed 两个 Skill, 并且尝试阅读单个&lt;strong&gt; Strain&lt;/strong&gt; 是如何被计算的.&lt;/p&gt;
&lt;h1 id=&#34;探索aim-skill&#34;&gt;&lt;a class=&#34;anchor&#34; href=&#34;#探索aim-skill&#34;&gt;#&lt;/a&gt; 探索 Aim Skill&lt;/h1&gt;
&lt;p&gt;在 rosu-pp 中，我们可以在 /osu/difficulty/skills 目录找到所有 Skill 的定义和实现。这里我们尝试开始阅读 aim.rs 的源代码&lt;/p&gt;
&lt;figure class=&#34;highlight rust&#34;&gt;&lt;figcaption data-lang=&#34;rust&#34;&gt;&lt;/figcaption&gt;&lt;table&gt;&lt;tr&gt;&lt;td data-num=&#34;1&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token keyword&#34;&gt;impl&lt;/span&gt;&lt;span class=&#34;token operator&#34;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&#34;token lifetime-annotation symbol&#34;&gt;&#39;a&lt;/span&gt;&lt;span class=&#34;token operator&#34;&gt;&gt;&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;Skill&lt;/span&gt;&lt;span class=&#34;token operator&#34;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&#34;token lifetime-annotation symbol&#34;&gt;&#39;a&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;Aim&lt;/span&gt;&lt;span class=&#34;token operator&#34;&gt;&gt;&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;&amp;#123;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;2&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;span class=&#34;token keyword&#34;&gt;fn&lt;/span&gt; &lt;span class=&#34;token function-definition function&#34;&gt;strain_value_at&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token operator&#34;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&#34;token keyword&#34;&gt;mut&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;self&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; curr&lt;span class=&#34;token punctuation&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;token operator&#34;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&#34;token lifetime-annotation symbol&#34;&gt;&#39;a&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;OsuDifficultyObject&lt;/span&gt;&lt;span class=&#34;token operator&#34;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&#34;token lifetime-annotation symbol&#34;&gt;&#39;a&lt;/span&gt;&lt;span class=&#34;token operator&#34;&gt;&gt;&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;-&gt;&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;f64&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;&amp;#123;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;3&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;        &lt;span class=&#34;token keyword&#34;&gt;self&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;inner&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;curr_strain &lt;span class=&#34;token operator&#34;&gt;*=&lt;/span&gt; &lt;span class=&#34;token function&#34;&gt;strain_decay&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;curr&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;delta_time&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;token constant&#34;&gt;STRAIN_DECAY_BASE&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;4&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;        &lt;span class=&#34;token keyword&#34;&gt;self&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;inner&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;curr_strain &lt;span class=&#34;token operator&#34;&gt;+=&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;5&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;            &lt;span class=&#34;token class-name&#34;&gt;AimEvaluator&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;::&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;evaluate_diff_of&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;curr&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;self&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;diff_objects&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;self&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;inner&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;with_sliders&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;6&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;                &lt;span class=&#34;token operator&#34;&gt;*&lt;/span&gt; &lt;span class=&#34;token constant&#34;&gt;SKILL_MULTIPLIER&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;7&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;8&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;        &lt;span class=&#34;token keyword&#34;&gt;self&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;inner&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;curr_strain&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;9&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;span class=&#34;token punctuation&#34;&gt;&amp;#125;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;10&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token punctuation&#34;&gt;&amp;#125;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;&lt;p&gt;在针对 &lt;code&gt;Skill&amp;lt;&#39;a, Aim&amp;gt;&lt;/code&gt;  的实现中，最吸引我们注意的是 &lt;code&gt;strain_value_at&lt;/code&gt;  方法，因为 &lt;code&gt;curr_strain&lt;/code&gt;  值正是从这里发源的.&lt;/p&gt;
&lt;p&gt;在上文的解释中，我们已经明确了一个概念：在计算过程中，物件被&lt;strong&gt;细分&lt;/strong&gt;进行&lt;strong&gt;单独对待&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;所以显然， &lt;code&gt;strain_value_at&lt;/code&gt;  会被调用多次，其参数 curr 表示将要被计算的某一物件， &lt;code&gt;self.diff_objects&lt;/code&gt;  是所有物件列表的一个引用.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;code&gt;self.diff_objects&lt;/code&gt;  的存在是必要的，因为在计算单个物件的时候，我们需要获取该物件的&lt;strong&gt;上下文&lt;/strong&gt;.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;figure class=&#34;highlight rust&#34;&gt;&lt;figcaption data-lang=&#34;rust&#34;&gt;&lt;/figcaption&gt;&lt;table&gt;&lt;tr&gt;&lt;td data-num=&#34;1&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token keyword&#34;&gt;pub&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;trait&lt;/span&gt; &lt;span class=&#34;token type-definition class-name&#34;&gt;IDifficultyObject&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;Sized&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;&amp;#123;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;2&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;   &lt;span class=&#34;token keyword&#34;&gt;fn&lt;/span&gt; &lt;span class=&#34;token function-definition function&#34;&gt;idx&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token operator&#34;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&#34;token keyword&#34;&gt;self&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;-&gt;&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;usize&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;3&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;   &lt;span class=&#34;token keyword&#34;&gt;fn&lt;/span&gt; &lt;span class=&#34;token function-definition function&#34;&gt;previous&lt;/span&gt;&lt;span class=&#34;token operator&#34;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&#34;token lifetime-annotation symbol&#34;&gt;&#39;a&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;D&lt;/span&gt;&lt;span class=&#34;token operator&#34;&gt;&gt;&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token operator&#34;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&#34;token keyword&#34;&gt;self&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; backwards_idx&lt;span class=&#34;token punctuation&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;usize&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; diff_objects&lt;span class=&#34;token punctuation&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;token operator&#34;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&#34;token lifetime-annotation symbol&#34;&gt;&#39;a&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;token class-name&#34;&gt;D&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;]&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;-&gt;&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;Option&lt;/span&gt;&lt;span class=&#34;token operator&#34;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&#34;token operator&#34;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&#34;token lifetime-annotation symbol&#34;&gt;&#39;a&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;D&lt;/span&gt;&lt;span class=&#34;token operator&#34;&gt;&gt;&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;&amp;#123;&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;&amp;#125;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;4&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;   &lt;span class=&#34;token keyword&#34;&gt;fn&lt;/span&gt; &lt;span class=&#34;token function-definition function&#34;&gt;next&lt;/span&gt;&lt;span class=&#34;token operator&#34;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&#34;token lifetime-annotation symbol&#34;&gt;&#39;a&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;D&lt;/span&gt;&lt;span class=&#34;token operator&#34;&gt;&gt;&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token operator&#34;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&#34;token keyword&#34;&gt;self&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; forwards_idx&lt;span class=&#34;token punctuation&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;usize&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; diff_objects&lt;span class=&#34;token punctuation&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;token operator&#34;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&#34;token lifetime-annotation symbol&#34;&gt;&#39;a&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;token class-name&#34;&gt;D&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;]&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;-&gt;&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;Option&lt;/span&gt;&lt;span class=&#34;token operator&#34;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&#34;token operator&#34;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&#34;token lifetime-annotation symbol&#34;&gt;&#39;a&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;D&lt;/span&gt;&lt;span class=&#34;token operator&#34;&gt;&gt;&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;&amp;#123;&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;&amp;#125;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;5&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token punctuation&#34;&gt;&amp;#125;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;&lt;p&gt;简单阅读 &lt;code&gt;IDifficultyObject&lt;/code&gt;  的定义，相信读者已经明白 &lt;code&gt;idx&lt;/code&gt; ,  &lt;code&gt;diff_objects&lt;/code&gt;  的必要性和使用场景了.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;接下来，我们将目光放到 &lt;code&gt;AimEvaluator::evaluate_diff_of&lt;/code&gt;  方法上，我们将按行分块解释这个方法.&lt;/p&gt;
&lt;h2 id=&#34;osu_curr_obj&#34;&gt;&lt;a class=&#34;anchor&#34; href=&#34;#osu_curr_obj&#34;&gt;#&lt;/a&gt; osu_curr_obj&lt;/h2&gt;
&lt;figure class=&#34;highlight rust&#34;&gt;&lt;figcaption data-lang=&#34;rust&#34;&gt;&lt;/figcaption&gt;&lt;table&gt;&lt;tr&gt;&lt;td data-num=&#34;1&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token keyword&#34;&gt;let&lt;/span&gt; osu_curr_obj &lt;span class=&#34;token operator&#34;&gt;=&lt;/span&gt; curr&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;2&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;3&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token keyword&#34;&gt;let&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;Some&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;osu_last_last_obj&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; osu_last_obj&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;token operator&#34;&gt;=&lt;/span&gt; curr&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;4&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;previous&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token number&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; diff_objects&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;5&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;zip&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;curr&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;previous&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token number&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; diff_objects&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;6&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;filter&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token closure-params&#34;&gt;&lt;span class=&#34;token closure-punctuation punctuation&#34;&gt;|&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;_&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; last&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token closure-punctuation punctuation&#34;&gt;|&lt;/span&gt;&lt;/span&gt; &lt;span class=&#34;token operator&#34;&gt;!&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;curr&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;base&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;is_spinner&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;token operator&#34;&gt;||&lt;/span&gt; last&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;base&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;is_spinner&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;7&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token keyword&#34;&gt;else&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;&amp;#123;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;8&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;span class=&#34;token keyword&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;token number&#34;&gt;0.0&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;9&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token punctuation&#34;&gt;&amp;#125;&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;&lt;p&gt;这部分主要负责解构出 &lt;code&gt;osu_curr_obj&lt;/code&gt; ,  &lt;code&gt;osu_last_obj&lt;/code&gt; ,  &lt;code&gt;osu_last_last_obj&lt;/code&gt; .&lt;/p&gt;
&lt;p&gt;利用 &lt;code&gt;diff_objects&lt;/code&gt;  和对象储存的物件 ID 就可以还原出上一个物件与下一个物件.&lt;/p&gt;
&lt;p&gt;这里很明显地，当物件为转盘 (Spinner) 时，方法将直接返回 0.0, 不执行后续计算.&lt;/p&gt;
&lt;h2 id=&#34;距离时间速度&#34;&gt;&lt;a class=&#34;anchor&#34; href=&#34;#距离时间速度&#34;&gt;#&lt;/a&gt; 距离 / 时间 = 速度&lt;/h2&gt;
&lt;figure class=&#34;highlight rust&#34;&gt;&lt;figcaption data-lang=&#34;rust&#34;&gt;&lt;/figcaption&gt;&lt;table&gt;&lt;tr&gt;&lt;td data-num=&#34;1&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token comment&#34;&gt;// * Calculate the velocity to the current hitobject, which starts&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;2&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token comment&#34;&gt;// * with a base distance / time assuming the last object is a hitcircle.&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;3&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token keyword&#34;&gt;let&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;mut&lt;/span&gt; curr_vel &lt;span class=&#34;token operator&#34;&gt;=&lt;/span&gt; osu_curr_obj&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;lazy_jump_dist &lt;span class=&#34;token operator&#34;&gt;/&lt;/span&gt; osu_curr_obj&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;strain_time&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;4&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;5&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token comment&#34;&gt;// * But if the last object is a slider, then we extend the travel&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;6&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token comment&#34;&gt;// * velocity through the slider into the current object.&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;7&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token keyword&#34;&gt;if&lt;/span&gt; osu_last_obj&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;base&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;is_slider&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;token operator&#34;&gt;&amp;amp;&amp;amp;&lt;/span&gt; with_sliders &lt;span class=&#34;token punctuation&#34;&gt;&amp;#123;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;8&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;span class=&#34;token comment&#34;&gt;// * calculate the slider velocity from slider head to slider end.&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;9&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;span class=&#34;token keyword&#34;&gt;let&lt;/span&gt; travel_vel &lt;span class=&#34;token operator&#34;&gt;=&lt;/span&gt; osu_last_obj&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;travel_dist &lt;span class=&#34;token operator&#34;&gt;/&lt;/span&gt; osu_last_obj&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;travel_time&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;10&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;span class=&#34;token comment&#34;&gt;// * calculate the movement velocity from slider end to current object&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;11&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;span class=&#34;token keyword&#34;&gt;let&lt;/span&gt; movement_vel &lt;span class=&#34;token operator&#34;&gt;=&lt;/span&gt; osu_curr_obj&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;min_jump_dist &lt;span class=&#34;token operator&#34;&gt;/&lt;/span&gt; osu_curr_obj&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;min_jump_time&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;12&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;13&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;span class=&#34;token comment&#34;&gt;// * take the larger total combined velocity.&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;14&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    curr_vel &lt;span class=&#34;token operator&#34;&gt;=&lt;/span&gt; curr_vel&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;max&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;movement_vel &lt;span class=&#34;token operator&#34;&gt;+&lt;/span&gt; travel_vel&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;15&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token punctuation&#34;&gt;&amp;#125;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;16&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;17&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token comment&#34;&gt;// * As above, do the same for the previous hitobject.&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;18&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token keyword&#34;&gt;let&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;mut&lt;/span&gt; prev_vel &lt;span class=&#34;token operator&#34;&gt;=&lt;/span&gt; osu_last_obj&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;lazy_jump_dist &lt;span class=&#34;token operator&#34;&gt;/&lt;/span&gt; osu_last_obj&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;strain_time&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;19&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;20&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token keyword&#34;&gt;if&lt;/span&gt; osu_last_last_obj&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;base&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;is_slider&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;token operator&#34;&gt;&amp;amp;&amp;amp;&lt;/span&gt; with_sliders &lt;span class=&#34;token punctuation&#34;&gt;&amp;#123;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;21&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;span class=&#34;token keyword&#34;&gt;let&lt;/span&gt; travel_vel &lt;span class=&#34;token operator&#34;&gt;=&lt;/span&gt; osu_last_last_obj&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;travel_dist &lt;span class=&#34;token operator&#34;&gt;/&lt;/span&gt; osu_last_last_obj&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;travel_time&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;22&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;span class=&#34;token keyword&#34;&gt;let&lt;/span&gt; movement_vel &lt;span class=&#34;token operator&#34;&gt;=&lt;/span&gt; osu_last_obj&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;min_jump_dist &lt;span class=&#34;token operator&#34;&gt;/&lt;/span&gt; osu_last_obj&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;min_jump_time&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;23&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;24&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    prev_vel &lt;span class=&#34;token operator&#34;&gt;=&lt;/span&gt; prev_vel&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;max&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;movement_vel &lt;span class=&#34;token operator&#34;&gt;+&lt;/span&gt; travel_vel&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;25&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token punctuation&#34;&gt;&amp;#125;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;26&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;27&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token keyword&#34;&gt;let&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;mut&lt;/span&gt; wide_angle_bonus &lt;span class=&#34;token operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;token number&#34;&gt;0.0&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;28&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token keyword&#34;&gt;let&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;mut&lt;/span&gt; acute_angle_bonus &lt;span class=&#34;token operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;token number&#34;&gt;0.0&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;29&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token keyword&#34;&gt;let&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;mut&lt;/span&gt; slider_bonus &lt;span class=&#34;token operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;token number&#34;&gt;0.0&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;30&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token keyword&#34;&gt;let&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;mut&lt;/span&gt; vel_change_bonus &lt;span class=&#34;token operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;token number&#34;&gt;0.0&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;31&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;32&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token comment&#34;&gt;// * Start strain with regular velocity.&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;33&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token keyword&#34;&gt;let&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;mut&lt;/span&gt; aim_strain &lt;span class=&#34;token operator&#34;&gt;=&lt;/span&gt; curr_vel&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;&lt;p&gt;这部分的主要逻辑是利用距离除以时间算出速度，并且把速度当做初始 &lt;code&gt;aim_strain&lt;/code&gt;  值.&lt;/p&gt;
&lt;p&gt;这里我们解释一些&lt;strong&gt;基本概念&lt;/strong&gt;，以便于具体理解这些参数:&lt;/p&gt;
&lt;hr /&gt;
&lt;h3 id=&#34;strain_time&#34;&gt;&lt;a class=&#34;anchor&#34; href=&#34;#strain_time&#34;&gt;#&lt;/a&gt; strain_time&lt;/h3&gt;
&lt;p&gt;表示当前物件与上一物件的时间间隔，单位为毫秒.&lt;/p&gt;
&lt;p&gt;计算方式为:  &lt;code&gt;当前物件的起始时间 - 上一物件的起始时间&lt;/code&gt; .&lt;/p&gt;
&lt;p&gt;也可以用下面的这种方式进行计算:  &lt;code&gt;(60 / BPM) * 节拍细分 * 1000&lt;/code&gt; .&lt;/p&gt;
&lt;p&gt;对于大部分谱面，符合下面这样的例子:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;对于 180BPM 的跳: (60 / 180) * 1/2 * 1000 = 166.67ms.&lt;/li&gt;
&lt;li&gt;对于 180BPM 的串: (60 / 180) * 1/4 * 1000 = 83.34ms.&lt;/li&gt;
&lt;li&gt;对于 200BPM 的跳: (60 / 200) * 1/2 * 1000 = 150.00ms.&lt;/li&gt;
&lt;li&gt;对于 200BPM 的串: (60 / 200) * 1/4 * 1000 = 75.00ms.&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;
&lt;p&gt;算法中规定，MIN_DELTA_TIME = 25.0, 即 strain_time 的最小值为 25ms.&lt;/p&gt;
&lt;p&gt;关于节拍细分的更多内容可以查看 osu!Wiki: &lt;a href=&#34;https://osu.ppy.sh/wiki/zh/Client/Beatmap_editor/Beat_snap_divisor&#34;&gt;音符时值 (Beat Snap Divisor)&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 id=&#34;lazy_jump_dist&#34;&gt;&lt;a class=&#34;anchor&#34; href=&#34;#lazy_jump_dist&#34;&gt;#&lt;/a&gt; lazy_jump_dist&lt;/h3&gt;
&lt;p&gt;表示当前物件与上一物件的标准化距离，单位为 osu!pixel&lt;/p&gt;
&lt;p&gt;计算方式为:  &lt;code&gt;normalize(||当前物件的位置 - 上一物件的位置||)&lt;/code&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;a href=&#34;https://osu.ppy.sh/wiki/zh/Client/Playfield&#34;&gt;osu!pixel&lt;/a&gt; 的区域为 (0, 0) 到 (512, 384), 在实际游玩中会根据分辨率进行缩放.&lt;/p&gt;
&lt;p&gt;物件的位置会进行标准化，标准化的时候，取圆圈物件的半径为 50 单位长度，注：此时会考虑 CS.&lt;/p&gt;
&lt;p&gt;同时也与作图的&lt;a href=&#34;https://osu.ppy.sh/wiki/zh/Beatmap/Stack_leniency&#34;&gt;堆叠度&lt;/a&gt;配置有关，感兴趣的读者可以自行阅读相关代码&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 id=&#34;travel_dist&#34;&gt;&lt;a class=&#34;anchor&#34; href=&#34;#travel_dist&#34;&gt;#&lt;/a&gt; travel_dist&lt;/h3&gt;
&lt;p&gt;表示&lt;strong&gt;滑条物件&lt;/strong&gt;的滑行距离&lt;/p&gt;
&lt;h3 id=&#34;travel_time&#34;&gt;&lt;a class=&#34;anchor&#34; href=&#34;#travel_time&#34;&gt;#&lt;/a&gt; travel_time&lt;/h3&gt;
&lt;p&gt;表示&lt;strong&gt;滑条物件&lt;/strong&gt;的滑行时间&lt;/p&gt;
&lt;h3 id=&#34;min_jump_time&#34;&gt;&lt;a class=&#34;anchor&#34; href=&#34;#min_jump_time&#34;&gt;#&lt;/a&gt; min_jump_time&lt;/h3&gt;
&lt;p&gt;表示刨除滑行时间后，当前物件与上一滑条&lt;strong&gt;头&lt;/strong&gt;之间的时间间隔.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;通常在上一物件为滑条的情形下使用&lt;/strong&gt;，如果上一物件不是滑条，该值与&lt;strong&gt; strain_time&lt;/strong&gt; 相等.&lt;/p&gt;
&lt;h3 id=&#34;min_jump_dist&#34;&gt;&lt;a class=&#34;anchor&#34; href=&#34;#min_jump_dist&#34;&gt;#&lt;/a&gt; min_jump_dist&lt;/h3&gt;
&lt;p&gt;表示刨除滑行距离后，当前物件与上一滑条&lt;strong&gt;尾&lt;/strong&gt;之间的距离.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;通常在上一物件为滑条的情形下使用&lt;/strong&gt;，如果上一物件不是滑条，该值与&lt;strong&gt; lazy_jump_dist&lt;/strong&gt; 相等.&lt;/p&gt;
&lt;hr /&gt;
&lt;p&gt;接下来我们返回到代码解析部分，相信读者最感兴趣的就是 extend velocity 这部分，我们画图来解释一下&lt;/p&gt;
&lt;p&gt;&lt;img loading=&#34;lazy&#34; data-src=&#34;https://s2.loli.net/2024/07/02/WBSebI6OzCKnd3a.png&#34; alt=&#34;&#34; width=&#34;720px&#34; /&gt;&lt;/p&gt;
&lt;p&gt;请读者将&lt;strong&gt;圆圈 2&lt;/strong&gt; 当做当前物件，很明显，绿色的是第一步计算的 &lt;code&gt;curr_vel&lt;/code&gt; , 接下来，进入 if 逻辑的判断.&lt;/p&gt;
&lt;p&gt;因为上一个物件是滑条，条件满足，这里接下来会分别计算红色部分和橘色部分的速度，并且求和，与原 &lt;code&gt;curr_vel&lt;/code&gt;  取最大值.&lt;/p&gt;
&lt;p&gt;很明显，对于图中这种排列，红色部分和橘色部分的速度和是大于绿色的，故采用后续计算出的值作为 &lt;code&gt;curr_vel&lt;/code&gt; .&lt;/p&gt;
&lt;p&gt;这里客观来讲，extend velocity 更能表现出&lt;strong&gt;滑条 1 与圆圈 2 组合&lt;/strong&gt;的速度.&lt;/p&gt;
&lt;h2 id=&#34;锐角与广角排列&#34;&gt;&lt;a class=&#34;anchor&#34; href=&#34;#锐角与广角排列&#34;&gt;#&lt;/a&gt; 锐角与广角排列&lt;/h2&gt;
&lt;p&gt;在上一部分，我们根据速度计算出了 Strain 的基础值，接下来 PP 算法考虑了经典的锐角和钝角 Pattern, 计算出了有针对性的奖励系数.&lt;/p&gt;
&lt;p&gt;注：此处的代码经过了一些删减、重新排序和提取，在不改变逻辑的情况下提升了可读性.&lt;/p&gt;
&lt;p&gt;这里我们按照注释，将代码分为四部分：节奏判断、基础值计算、锐角增益计算、重复惩罚.&lt;/p&gt;
&lt;figure class=&#34;highlight rust&#34;&gt;&lt;figcaption data-lang=&#34;rust&#34;&gt;&lt;/figcaption&gt;&lt;table&gt;&lt;tr&gt;&lt;td data-num=&#34;1&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token comment&#34;&gt;// * If rhythms are the same (节奏判断).&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;2&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token keyword&#34;&gt;if&lt;/span&gt; osu_curr_obj&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;strain_time&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;max&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;osu_last_obj&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;strain_time&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;3&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;span class=&#34;token operator&#34;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&#34;token number&#34;&gt;1.25&lt;/span&gt; &lt;span class=&#34;token operator&#34;&gt;*&lt;/span&gt; osu_curr_obj&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;strain_time&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;min&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;osu_last_obj&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;strain_time&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;4&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token punctuation&#34;&gt;&amp;#123;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;5&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;span class=&#34;token comment&#34;&gt;// * Rewarding angles, take the smaller velocity as base (基础值计算).&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;6&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;span class=&#34;token keyword&#34;&gt;let&lt;/span&gt; angle_bonus &lt;span class=&#34;token operator&#34;&gt;=&lt;/span&gt; curr_vel&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;min&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;prev_vel&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;7&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;8&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    wide_angle_bonus &lt;span class=&#34;token operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;Self&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;::&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;calc_wide_angle_bonus&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;curr_angle&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;9&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    acute_angle_bonus &lt;span class=&#34;token operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;Self&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;::&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;calc_acute_angle_bonus&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;curr_angle&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;10&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;11&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;span class=&#34;token comment&#34;&gt;// * Only buff deltaTime exceeding 300 bpm 1/2 (锐角增益计算).&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;12&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;span class=&#34;token keyword&#34;&gt;if&lt;/span&gt; osu_curr_obj&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;strain_time &lt;span class=&#34;token operator&#34;&gt;&gt;&lt;/span&gt; &lt;span class=&#34;token number&#34;&gt;100.0&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;&amp;#123;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;13&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;        acute_angle_bonus &lt;span class=&#34;token operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;token number&#34;&gt;0.0&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;14&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;span class=&#34;token punctuation&#34;&gt;&amp;#125;&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;else&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;&amp;#123;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;15&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;        &lt;span class=&#34;token keyword&#34;&gt;let&lt;/span&gt; base1 &lt;span class=&#34;token operator&#34;&gt;=&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;16&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;            &lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token constant&#34;&gt;FRAC_PI_2&lt;/span&gt; &lt;span class=&#34;token operator&#34;&gt;*&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token number&#34;&gt;100.0&lt;/span&gt; &lt;span class=&#34;token operator&#34;&gt;-&lt;/span&gt; osu_curr_obj&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;strain_time&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;token operator&#34;&gt;/&lt;/span&gt; &lt;span class=&#34;token number&#34;&gt;25.0&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;min&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token number&#34;&gt;1.0&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;sin&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;17&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;18&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;        &lt;span class=&#34;token keyword&#34;&gt;let&lt;/span&gt; base2 &lt;span class=&#34;token operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token constant&#34;&gt;FRAC_PI_2&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;19&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;            &lt;span class=&#34;token operator&#34;&gt;*&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;osu_curr_obj&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;lazy_jump_dist&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;clamp&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token number&#34;&gt;50.0&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;token number&#34;&gt;100.0&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;token operator&#34;&gt;-&lt;/span&gt; &lt;span class=&#34;token number&#34;&gt;50.0&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;20&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;            &lt;span class=&#34;token operator&#34;&gt;/&lt;/span&gt; &lt;span class=&#34;token number&#34;&gt;50.0&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;21&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;            &lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;sin&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;22&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;23&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;        &lt;span class=&#34;token comment&#34;&gt;// * Multiply by previous angle, we don&#39;t want to buff unless this is a wiggle type pattern.&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;24&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;        acute_angle_bonus &lt;span class=&#34;token operator&#34;&gt;*=&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;Self&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;::&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;calc_acute_angle_bonus&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;last_angle&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;25&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;        &lt;span class=&#34;token comment&#34;&gt;// * The maximum velocity we buff is equal to 125 / strainTime&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;26&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;            &lt;span class=&#34;token operator&#34;&gt;*&lt;/span&gt; angle_bonus&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;min&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token number&#34;&gt;125.0&lt;/span&gt; &lt;span class=&#34;token operator&#34;&gt;/&lt;/span&gt; osu_curr_obj&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;strain_time&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;27&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;            &lt;span class=&#34;token comment&#34;&gt;// * scale buff from 150 bpm 1/4 to 200 bpm 1/4&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;28&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;            &lt;span class=&#34;token operator&#34;&gt;*&lt;/span&gt; base1&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;powf&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token number&#34;&gt;2.0&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;29&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;                &lt;span class=&#34;token comment&#34;&gt;// * Buff distance exceeding 50 (radius) up to 100 (diameter).&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;30&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;            &lt;span class=&#34;token operator&#34;&gt;*&lt;/span&gt; base2&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;powf&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token number&#34;&gt;2.0&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;31&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;span class=&#34;token punctuation&#34;&gt;&amp;#125;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;32&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;33&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;span class=&#34;token comment&#34;&gt;// * (重复惩罚)&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;34&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;35&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;span class=&#34;token comment&#34;&gt;// * Penalize wide angles if they&#39;re repeated, reducing the penalty as the lastAngle gets more acute.&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;36&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    wide_angle_bonus &lt;span class=&#34;token operator&#34;&gt;*=&lt;/span&gt; angle_bonus&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;37&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;        &lt;span class=&#34;token operator&#34;&gt;*&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token number&#34;&gt;1.0&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;38&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;            &lt;span class=&#34;token operator&#34;&gt;-&lt;/span&gt; wide_angle_bonus&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;min&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token keyword&#34;&gt;Self&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;::&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;calc_wide_angle_bonus&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;last_angle&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;powf&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token number&#34;&gt;3.0&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;39&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;span class=&#34;token comment&#34;&gt;// * Penalize acute angles if they&#39;re repeated, reducing the penalty as the lastLastAngle gets more obtuse.&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;40&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    acute_angle_bonus &lt;span class=&#34;token operator&#34;&gt;*=&lt;/span&gt; &lt;span class=&#34;token number&#34;&gt;0.5&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;41&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;        &lt;span class=&#34;token operator&#34;&gt;+&lt;/span&gt; &lt;span class=&#34;token number&#34;&gt;0.5&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;42&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;            &lt;span class=&#34;token operator&#34;&gt;*&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token number&#34;&gt;1.0&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;43&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;                &lt;span class=&#34;token operator&#34;&gt;-&lt;/span&gt; acute_angle_bonus&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;44&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;                    &lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;min&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token keyword&#34;&gt;Self&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;::&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;calc_acute_angle_bonus&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;last_last_angle&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;powf&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token number&#34;&gt;3.0&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;45&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token punctuation&#34;&gt;&amp;#125;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;&lt;h3 id=&#34;节奏判断&#34;&gt;&lt;a class=&#34;anchor&#34; href=&#34;#节奏判断&#34;&gt;#&lt;/a&gt; 节奏判断&lt;/h3&gt;
&lt;p&gt;在最外层进行了节奏的判断，只有符合条件的物件会得到额外的 Bonus. 这里主要是锐角和广角的 Bonus.&lt;/p&gt;
&lt;p&gt;首先，算法先保证了节奏变化幅度不大，限制当前物件与上一物件的&lt;strong&gt;间隔时间变化在 25% 以内&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;在客观角度考虑，Pattern 存在的必要条件就是节奏变化幅度较小。如果节奏变化太大就不能称为是 Pattern 了.&lt;/p&gt;
&lt;h3 id=&#34;基础值计算&#34;&gt;&lt;a class=&#34;anchor&#34; href=&#34;#基础值计算&#34;&gt;#&lt;/a&gt; 基础值计算&lt;/h3&gt;
&lt;figure class=&#34;highlight rust&#34;&gt;&lt;figcaption data-lang=&#34;rust&#34;&gt;&lt;/figcaption&gt;&lt;table&gt;&lt;tr&gt;&lt;td data-num=&#34;1&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;wide_angle_bonus &lt;span class=&#34;token operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;Self&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;::&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;calc_wide_angle_bonus&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;curr_angle&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;2&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;acute_angle_bonus &lt;span class=&#34;token operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;Self&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;::&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;calc_acute_angle_bonus&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;curr_angle&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;3&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;4&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token keyword&#34;&gt;fn&lt;/span&gt; &lt;span class=&#34;token function-definition function&#34;&gt;calc_wide_angle_bonus&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;angle&lt;span class=&#34;token punctuation&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;f64&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;-&gt;&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;f64&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;&amp;#123;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;5&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token number&#34;&gt;3.0&lt;/span&gt; &lt;span class=&#34;token operator&#34;&gt;/&lt;/span&gt; &lt;span class=&#34;token number&#34;&gt;4.0&lt;/span&gt; &lt;span class=&#34;token operator&#34;&gt;*&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token number&#34;&gt;5.0&lt;/span&gt; &lt;span class=&#34;token operator&#34;&gt;/&lt;/span&gt; &lt;span class=&#34;token number&#34;&gt;6.0&lt;/span&gt; &lt;span class=&#34;token operator&#34;&gt;*&lt;/span&gt; &lt;span class=&#34;token constant&#34;&gt;PI&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;min&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;angle&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;max&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token constant&#34;&gt;PI&lt;/span&gt; &lt;span class=&#34;token operator&#34;&gt;/&lt;/span&gt; &lt;span class=&#34;token number&#34;&gt;6.0&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;token operator&#34;&gt;-&lt;/span&gt; &lt;span class=&#34;token constant&#34;&gt;PI&lt;/span&gt; &lt;span class=&#34;token operator&#34;&gt;/&lt;/span&gt; &lt;span class=&#34;token number&#34;&gt;6.0&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;6&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;sin&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;7&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;powf&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token number&#34;&gt;2.0&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;8&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token punctuation&#34;&gt;&amp;#125;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;9&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;10&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token keyword&#34;&gt;fn&lt;/span&gt; &lt;span class=&#34;token function-definition function&#34;&gt;calc_acute_angle_bonus&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;angle&lt;span class=&#34;token punctuation&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;f64&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;-&gt;&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;f64&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;&amp;#123;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;11&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;span class=&#34;token number&#34;&gt;1.0&lt;/span&gt; &lt;span class=&#34;token operator&#34;&gt;-&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;Self&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;::&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;calc_wide_angle_bonus&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;angle&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;12&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token punctuation&#34;&gt;&amp;#125;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;&lt;p&gt;接下来，算法根据物件之间角度的数值，计算出了广角和锐角增益的基础值，范围为 0 ~ 1.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;((5.0 / 6.0 * PI).min(angle.max(PI / 6.0)) - PI / 6.0)&lt;/code&gt;  计算了角度与 &lt;code&gt;π/6&lt;/code&gt;  的偏移值.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;3/4 * 偏移值&lt;/code&gt;  是在对定义域进行伸缩，伸缩到 (30, 150).&lt;/p&gt;
&lt;p&gt;&lt;code&gt;sin²(偏移值)&lt;/code&gt;  将结果限制到 0 ~ 1 之间&lt;/p&gt;
&lt;p&gt;使用 GeoGebra 可以画出下面这样的曲线，红线代表广角曲线，蓝色代表锐角曲线.&lt;/p&gt;
&lt;p&gt;&lt;img loading=&#34;lazy&#34; data-src=&#34;https://s2.loli.net/2024/07/02/8i6cwDeaE2trk57.png&#34; alt=&#34;Snipaste_2024-07-02_21-42-25.png&#34; width=&#34;720px&#34; /&gt;&lt;/p&gt;
&lt;p&gt;在客观角度考虑，30 度以上，广角增益系数随角度平滑增长. 150 度以下，锐角增益系数随角度平滑增长，是很自然的设计.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;很明显地，算法有意将 30 度以下的角度视作锐角，将 30 度以上的角度视为广角，我们常说的直角、钝角 Pattern 实际上都归属于广角的领域.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 id=&#34;锐角增益计算&#34;&gt;&lt;a class=&#34;anchor&#34; href=&#34;#锐角增益计算&#34;&gt;#&lt;/a&gt; 锐角增益计算&lt;/h3&gt;
&lt;p&gt;首先，锐角增益中 &lt;code&gt;osu_curr_obj.strain_time &amp;gt; 100.0&lt;/code&gt;  限制了只有 300BPM 以上的跳或者 150BPM 以上的串会被增益.&lt;/p&gt;
&lt;p&gt;这意味着&lt;strong&gt;大部分的排列不会吃到锐角增益&lt;/strong&gt;。这个结论很有意思，也很符合逻辑. 300BPM 以上的跳不必多说了.&lt;/p&gt;
&lt;p&gt;这里 150BPM 的串看似几乎没有限制，但是请读者考虑一下，小于 90 度的串甚至已经进入了 aim control 的领域了.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;可能有读者对 acute_angle_bonus 置为 0 有疑问，在下文的讲解中，这个顾虑便会消失.&lt;/p&gt;
&lt;p&gt;实际上，最终增益系数的值是选取锐角和广角增益的&lt;strong&gt;较大者&lt;/strong&gt;，也就说一个物件要么被认为是广角、要么被认为是锐角.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;figure class=&#34;highlight rust&#34;&gt;&lt;figcaption data-lang=&#34;rust&#34;&gt;&lt;/figcaption&gt;&lt;table&gt;&lt;tr&gt;&lt;td data-num=&#34;1&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token keyword&#34;&gt;let&lt;/span&gt; base1 &lt;span class=&#34;token operator&#34;&gt;=&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;2&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token constant&#34;&gt;FRAC_PI_2&lt;/span&gt; &lt;span class=&#34;token operator&#34;&gt;*&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token number&#34;&gt;100.0&lt;/span&gt; &lt;span class=&#34;token operator&#34;&gt;-&lt;/span&gt; osu_curr_obj&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;strain_time&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;token operator&#34;&gt;/&lt;/span&gt; &lt;span class=&#34;token number&#34;&gt;25.0&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;min&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token number&#34;&gt;1.0&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;sin&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;3&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;4&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token keyword&#34;&gt;let&lt;/span&gt; base2 &lt;span class=&#34;token operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token constant&#34;&gt;FRAC_PI_2&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;5&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;span class=&#34;token operator&#34;&gt;*&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;osu_curr_obj&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;lazy_jump_dist&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;clamp&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token number&#34;&gt;50.0&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;token number&#34;&gt;100.0&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;token operator&#34;&gt;-&lt;/span&gt; &lt;span class=&#34;token number&#34;&gt;50.0&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;6&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;span class=&#34;token operator&#34;&gt;/&lt;/span&gt; &lt;span class=&#34;token number&#34;&gt;50.0&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;7&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;sin&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;8&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;9&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token comment&#34;&gt;// * Multiply by previous angle, we don&#39;t want to buff unless this is a wiggle type pattern.&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;10&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;acute_angle_bonus &lt;span class=&#34;token operator&#34;&gt;*=&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;Self&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;::&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;calc_acute_angle_bonus&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;last_angle&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;11&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token comment&#34;&gt;// * The maximum velocity we buff is equal to 125 / strainTime&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;12&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;span class=&#34;token operator&#34;&gt;*&lt;/span&gt; angle_bonus&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;min&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token number&#34;&gt;125.0&lt;/span&gt; &lt;span class=&#34;token operator&#34;&gt;/&lt;/span&gt; osu_curr_obj&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;strain_time&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;13&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;span class=&#34;token comment&#34;&gt;// * scale buff from 150 bpm 1/4 to 200 bpm 1/4&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;14&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;span class=&#34;token operator&#34;&gt;*&lt;/span&gt; base1&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;powf&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token number&#34;&gt;2.0&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;15&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;span class=&#34;token comment&#34;&gt;// * Buff distance exceeding 50 (radius) up to 100 (diameter).&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;16&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;span class=&#34;token operator&#34;&gt;*&lt;/span&gt; base2&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;powf&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token number&#34;&gt;2.0&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;&lt;p&gt;这里英文注释为我们理解提供了极大便利.&lt;/p&gt;
&lt;p&gt;首先 &lt;code&gt;*= Self::calc_acute_angle_bonus(last_angle)&lt;/code&gt; , 这个操作实际上在&lt;strong&gt;规避 &amp;quot;离群值&amp;quot;&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;这里注释中提到，只希望考虑摇摆型的锐角，摇摆型可以理解为：连续的几个物件都符合锐角的特征.&lt;/p&gt;
&lt;p&gt;而偶然性的，突发性的锐角不在增益范围，这也是合情合理的，这种突发性的锐角不应被视作 Pattern.&lt;/p&gt;
&lt;p&gt;接着，对 velocity, strain_time, distance 分别进行了伸缩.&lt;/p&gt;
&lt;p&gt;base1 的增益范围为: 150 ~ 200BPM 的串 (1/4 节拍)&lt;/p&gt;
&lt;p&gt;base2 的增益范围为: 50 ~ 100 单位 (互过圆心的物件～相切的物件)&lt;/p&gt;
&lt;h3 id=&#34;重复惩罚&#34;&gt;&lt;a class=&#34;anchor&#34; href=&#34;#重复惩罚&#34;&gt;#&lt;/a&gt; 重复惩罚&lt;/h3&gt;
&lt;figure class=&#34;highlight rust&#34;&gt;&lt;figcaption data-lang=&#34;rust&#34;&gt;&lt;/figcaption&gt;&lt;table&gt;&lt;tr&gt;&lt;td data-num=&#34;1&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token comment&#34;&gt;// * Penalize wide angles if they&#39;re repeated, reducing the penalty as the lastAngle gets more acute.&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;2&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;wide_angle_bonus &lt;span class=&#34;token operator&#34;&gt;*=&lt;/span&gt; angle_bonus&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;3&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;span class=&#34;token operator&#34;&gt;*&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token number&#34;&gt;1.0&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;4&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;        &lt;span class=&#34;token operator&#34;&gt;-&lt;/span&gt; wide_angle_bonus&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;min&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token keyword&#34;&gt;Self&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;::&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;calc_wide_angle_bonus&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;last_angle&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;powf&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token number&#34;&gt;3.0&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;5&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token comment&#34;&gt;// * Penalize acute angles if they&#39;re repeated, reducing the penalty as the lastLastAngle gets more obtuse.&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;6&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;acute_angle_bonus &lt;span class=&#34;token operator&#34;&gt;*=&lt;/span&gt; &lt;span class=&#34;token number&#34;&gt;0.5&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;7&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;span class=&#34;token operator&#34;&gt;+&lt;/span&gt; &lt;span class=&#34;token number&#34;&gt;0.5&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;8&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;        &lt;span class=&#34;token operator&#34;&gt;*&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token number&#34;&gt;1.0&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;9&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;            &lt;span class=&#34;token operator&#34;&gt;-&lt;/span&gt; acute_angle_bonus&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;10&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;                &lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;min&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token keyword&#34;&gt;Self&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;::&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;calc_acute_angle_bonus&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;last_last_angle&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;powf&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token number&#34;&gt;3.0&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;&lt;p&gt;经过逻辑分析后，我们可以将惩罚的关键部分抽象为 &lt;code&gt;1.0 - f(x)&lt;/code&gt; , f (x) 的增大意味着受到更大的惩罚.&lt;/p&gt;
&lt;p&gt;这里通过比较上一个 Bonus, 可以间接地对重复的广角进行惩罚，如果上一个角度更锐，惩罚因子 f (x) 会减小，从而对重复的广角施加更轻微的惩罚.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;min()&lt;/code&gt;  选择较小的值作为惩罚因子，这个比较是为了确保惩罚因子不会超过 &lt;code&gt;wide_angle_bonus&lt;/code&gt;  的值.&lt;/p&gt;
&lt;h2 id=&#34;速度变化&#34;&gt;&lt;a class=&#34;anchor&#34; href=&#34;#速度变化&#34;&gt;#&lt;/a&gt; 速度变化&lt;/h2&gt;
&lt;p&gt;上一部分我们针对锐角和广角排列进行了奖励，其前提是速度相近，即速度变化在 25% 以内.&lt;/p&gt;
&lt;p&gt;下面我们将引入速度变化奖励，这次与上文有着相反的前提，即速度一定要有不同，我们将采用与上一节类似的策略来分析.&lt;/p&gt;
&lt;p&gt;这里我们按照注释，将代码分为三部分：比率计算、基数计算、节奏变化惩罚.&lt;/p&gt;
&lt;figure class=&#34;highlight rust&#34;&gt;&lt;figcaption data-lang=&#34;rust&#34;&gt;&lt;/figcaption&gt;&lt;table&gt;&lt;tr&gt;&lt;td data-num=&#34;1&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token keyword&#34;&gt;if&lt;/span&gt; prev_vel&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;max&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;curr_vel&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;not_eq&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token number&#34;&gt;0.0&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;&amp;#123;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;2&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;span class=&#34;token comment&#34;&gt;// * (比率计算)&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;3&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;span class=&#34;token comment&#34;&gt;// * We want to use the average velocity over the whole object when awarding&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;4&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;span class=&#34;token comment&#34;&gt;// * differences, not the individual jump and slider path velocities.&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;5&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    prev_vel &lt;span class=&#34;token operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;osu_last_obj&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;lazy_jump_dist &lt;span class=&#34;token operator&#34;&gt;+&lt;/span&gt; osu_last_last_obj&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;travel_dist&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;6&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;        &lt;span class=&#34;token operator&#34;&gt;/&lt;/span&gt; osu_last_obj&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;strain_time&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;7&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    curr_vel &lt;span class=&#34;token operator&#34;&gt;=&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;8&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;        &lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;osu_curr_obj&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;lazy_jump_dist &lt;span class=&#34;token operator&#34;&gt;+&lt;/span&gt; osu_last_obj&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;travel_dist&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;token operator&#34;&gt;/&lt;/span&gt; osu_curr_obj&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;strain_time&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;9&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;10&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;span class=&#34;token comment&#34;&gt;// * Scale with ratio of difference compared to 0.5 * max dist.&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;11&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;span class=&#34;token keyword&#34;&gt;let&lt;/span&gt; dist_ratio_base &lt;span class=&#34;token operator&#34;&gt;=&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;12&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;        &lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token constant&#34;&gt;FRAC_PI_2&lt;/span&gt; &lt;span class=&#34;token operator&#34;&gt;*&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;prev_vel &lt;span class=&#34;token operator&#34;&gt;-&lt;/span&gt; curr_vel&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;abs&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;token operator&#34;&gt;/&lt;/span&gt; prev_vel&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;max&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;curr_vel&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;sin&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;13&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;span class=&#34;token keyword&#34;&gt;let&lt;/span&gt; dist_ratio &lt;span class=&#34;token operator&#34;&gt;=&lt;/span&gt; dist_ratio_base&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;powf&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token number&#34;&gt;2.0&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;14&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;15&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;span class=&#34;token comment&#34;&gt;// * Reward for % distance up to 125 /strainTime for overlaps where velocity is still changing. (基数计算)&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;16&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;span class=&#34;token keyword&#34;&gt;let&lt;/span&gt; overlap_vel_buff &lt;span class=&#34;token operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token number&#34;&gt;125.0&lt;/span&gt; &lt;span class=&#34;token operator&#34;&gt;/&lt;/span&gt; osu_curr_obj&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;strain_time&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;min&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;osu_last_obj&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;strain_time&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;17&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;        &lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;min&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;prev_vel &lt;span class=&#34;token operator&#34;&gt;-&lt;/span&gt; curr_vel&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;abs&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;18&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;19&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    vel_change_bonus &lt;span class=&#34;token operator&#34;&gt;=&lt;/span&gt; overlap_vel_buff &lt;span class=&#34;token operator&#34;&gt;*&lt;/span&gt; dist_ratio&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;20&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;21&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;span class=&#34;token comment&#34;&gt;// * Penalize for rhythm changes. (节奏变化惩罚)&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;22&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;span class=&#34;token keyword&#34;&gt;let&lt;/span&gt; bonus_base &lt;span class=&#34;token operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;osu_curr_obj&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;strain_time&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;min&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;osu_last_obj&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;strain_time&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;23&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;        &lt;span class=&#34;token operator&#34;&gt;/&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;osu_curr_obj&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;strain_time&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;max&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;osu_last_obj&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;strain_time&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;24&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    vel_change_bonus &lt;span class=&#34;token operator&#34;&gt;*=&lt;/span&gt; bonus_base&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;powf&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token number&#34;&gt;2.0&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;25&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token punctuation&#34;&gt;&amp;#125;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;&lt;h3 id=&#34;比率计算&#34;&gt;&lt;a class=&#34;anchor&#34; href=&#34;#比率计算&#34;&gt;#&lt;/a&gt; 比率计算&lt;/h3&gt;
&lt;p&gt;在代码的后半部分，我们可以看到 &lt;code&gt;vel_change_bonus = overlap_vel_buff * dist_ratio&lt;/code&gt; .&lt;/p&gt;
&lt;p&gt;即最终速度变化奖励是利用 &lt;code&gt;基数 * 比率&lt;/code&gt; 的方式进行计算的，这里我们先来看比率的计算方式.&lt;/p&gt;
&lt;p&gt;首先，针对 &lt;code&gt;prev_vel&lt;/code&gt;  和 &lt;code&gt;curr_vel&lt;/code&gt;  进行了重算，使用了之前图 1 中&lt;strong&gt;绿色&lt;/strong&gt;的部分作为速度值.&lt;/p&gt;
&lt;p&gt;接下来， &lt;code&gt;dist_ratio&lt;/code&gt;  与上文计算角度奖励的方式类似，都采用先正弦再平方的方式，很明显地， &lt;code&gt;dist_ratio&lt;/code&gt;  的取值范围为 &lt;code&gt;[0, 1]&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;code&gt;(prev_vel - curr_vel).abs() / prev_vel.max(curr_vel)&lt;/code&gt;  计算了速度变化的大小相对于最大速度的比例，反应了速度变化的剧烈程度.&lt;/p&gt;
&lt;p&gt;速度变化的&lt;strong&gt;剧烈程度&lt;/strong&gt;作为速度奖励的&lt;strong&gt;比率&lt;/strong&gt;，在合适不过了.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;(a - b).abs () /a.max (b) 是一种特征缩放方式，可以将任意量纲的数值范围缩放到 [0, 1] 区间里.&lt;/p&gt;
&lt;p&gt;&lt;img loading=&#34;lazy&#34; data-src=&#34;https://s2.loli.net/2024/07/03/Ez5Aqb7N1nB4R8g.png&#34; alt=&#34;Snipaste_2024-07-03_10-46-29.png&#34; width=&#34;720px&#34; /&gt;&lt;/p&gt;
&lt;p&gt;这里 x, y 可分别看作当前物件与上一物件的速度，而 z 值则为归一化后的速度变化.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;上文两次提到的先正弦再平方实际上也是一种特征映射的方式，可以将原始值 x 映射到 [0, 1] 的范围内，并且平方操作可以增强映射后的值的变化幅度.&lt;/p&gt;
&lt;p&gt;这里考虑到的变化幅度，读者不妨考虑正弦函数各点的斜率 (导数), 其变化幅度便一目了然了.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 id=&#34;基数计算&#34;&gt;&lt;a class=&#34;anchor&#34; href=&#34;#基数计算&#34;&gt;#&lt;/a&gt; 基数计算&lt;/h3&gt;
&lt;p&gt;很明显，这里直接使用了速度变化的大小作为基数的值.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;(prev_vel - curr_vel).abs()&lt;/code&gt;  计算了前后两个物件的速度大小的差值.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;(125.0 / osu_curr_obj.strain_time.min(osu_last_obj.strain_time))&lt;/code&gt;  主要是限制了距离对速度的影响.&lt;/p&gt;
&lt;p&gt;限制可以确保在计算速度变化时，只考虑在一定范围内的速度变化。使奖励值更加关注较近的对象之间的速度变化，而忽略较远对象之间的变化.&lt;/p&gt;
&lt;h3 id=&#34;节奏变化惩罚&#34;&gt;&lt;a class=&#34;anchor&#34; href=&#34;#节奏变化惩罚&#34;&gt;#&lt;/a&gt; 节奏变化惩罚&lt;/h3&gt;
&lt;p&gt;这里节奏变化惩罚也运用了类似的归一化手段. bonus_base (这里其实理解为 penalize_base) 的取值范围为 [0, 1].&lt;/p&gt;
&lt;p&gt;我们画出 bonus_base 的图象，这里 x, y 轴为 strain_time, z 轴为 bonus_base 的值:&lt;/p&gt;
&lt;p&gt;&lt;img loading=&#34;lazy&#34; data-src=&#34;https://s2.loli.net/2024/07/03/BseojKufnCkFx7g.png&#34; alt=&#34;Snipaste_2024-07-03_10-56-15.png&#34; width=&#34;720px&#34; /&gt;&lt;/p&gt;
&lt;p&gt;可以明显地看到，当 strain_time 不变时，几乎没有惩罚 (bonus_base 值为 1).&lt;/p&gt;
&lt;p&gt;当 strain_time 出现变化时，我们假定一个横纵坐标，都有一个较小的 bonus_base 与其对应. &lt;strong&gt;时间变化越大时，惩罚越大&lt;/strong&gt;.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;注：因为 x, y 轴 strain_time 可以等比缩放，这里都设置为了 1 方便查看.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;读者可能不容易理解，为什么要针对时间变化进行惩罚呢，这里我们举出游戏中的一个例子:&lt;/p&gt;
&lt;p&gt;&lt;img loading=&#34;lazy&#34; data-src=&#34;https://s2.loli.net/2024/07/03/FSKkcRu4Qd71YvJ.png&#34; alt=&#34;Snipaste_2024-07-03_11-16-25.png&#34; width=&#34;720px&#34; /&gt;&lt;/p&gt;
&lt;p&gt;我们将&lt;strong&gt;圆圈 3&lt;/strong&gt; 作为当前物件，这里&lt;strong&gt;圆圈 2&lt;/strong&gt; 就是上一物件，计算&lt;strong&gt;圆圈 3&lt;/strong&gt; 的速度变化奖励将利用二者的速度.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;圆圈 3&lt;/strong&gt; 的速度利用绿线计算，属于间距小时间长，速度必然很小.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;圆圈 2&lt;/strong&gt; 的速度利用红线计算，属于间距大时间短，速度必然很大.&lt;/p&gt;
&lt;p&gt;在不引入时间变化惩罚的情况下，算法将认为&lt;strong&gt;圆圈 3&lt;/strong&gt; 的难度较高，因为出现了较大的速度变化.&lt;/p&gt;
&lt;p&gt;但实际上在玩家游玩中，因为&lt;strong&gt;圆圈 3&lt;/strong&gt; 与上一物件有较长的时间间隔，所以已经感受不出速度变化了，此时&lt;strong&gt;圆圈 3&lt;/strong&gt; 的难度很低.&lt;/p&gt;
&lt;p&gt;从另一个角度来说，如果我们将 1, 2, 3, 4 的时间轴拖拽均匀，那么这是一段较难的排列，玩家点击&lt;strong&gt;圆圈 3&lt;/strong&gt; 时需要一定的 aim control 能力.&lt;/p&gt;
&lt;h2 id=&#34;基值-奖励&#34;&gt;&lt;a class=&#34;anchor&#34; href=&#34;#基值-奖励&#34;&gt;#&lt;/a&gt; 基值 *= 奖励&lt;/h2&gt;
&lt;figure class=&#34;highlight rust&#34;&gt;&lt;figcaption data-lang=&#34;rust&#34;&gt;&lt;/figcaption&gt;&lt;table&gt;&lt;tr&gt;&lt;td data-num=&#34;1&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token keyword&#34;&gt;if&lt;/span&gt; osu_last_obj&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;base&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;is_slider&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;&amp;#123;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;2&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;span class=&#34;token comment&#34;&gt;// * Reward sliders based on velocity.&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;3&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    slider_bonus &lt;span class=&#34;token operator&#34;&gt;=&lt;/span&gt; osu_last_obj&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;travel_dist &lt;span class=&#34;token operator&#34;&gt;/&lt;/span&gt; osu_last_obj&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;travel_time&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;4&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token punctuation&#34;&gt;&amp;#125;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;5&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;6&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token comment&#34;&gt;// * Add in acute angle bonus or wide angle bonus + velocity change bonus, whichever is larger.&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;7&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;aim_strain &lt;span class=&#34;token operator&#34;&gt;+=&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;acute_angle_bonus &lt;span class=&#34;token operator&#34;&gt;*&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;Self&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;::&lt;/span&gt;&lt;span class=&#34;token constant&#34;&gt;ACUTE_ANGLE_MULTIPLIER&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;max&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;8&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    wide_angle_bonus &lt;span class=&#34;token operator&#34;&gt;*&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;Self&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;::&lt;/span&gt;&lt;span class=&#34;token constant&#34;&gt;WIDE_ANGLE_MULTIPLIER&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;9&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;        &lt;span class=&#34;token operator&#34;&gt;+&lt;/span&gt; vel_change_bonus &lt;span class=&#34;token operator&#34;&gt;*&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;Self&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;::&lt;/span&gt;&lt;span class=&#34;token constant&#34;&gt;VELOCITY_CHANGE_MULTIPLIER&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;10&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;11&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;12&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token comment&#34;&gt;// * Add in additional slider velocity bonus.&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;13&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token keyword&#34;&gt;if&lt;/span&gt; with_sliders &lt;span class=&#34;token punctuation&#34;&gt;&amp;#123;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;14&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    aim_strain &lt;span class=&#34;token operator&#34;&gt;+=&lt;/span&gt; slider_bonus &lt;span class=&#34;token operator&#34;&gt;*&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;Self&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;::&lt;/span&gt;&lt;span class=&#34;token constant&#34;&gt;SLIDER_MULTIPLIER&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;15&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token punctuation&#34;&gt;&amp;#125;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;16&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;17&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;aim_strain&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;&lt;p&gt;在上面的部分中，我们已经将最终计算所需的各类奖励值和基础值计算好了，总算要到最终计算 &lt;code&gt;aim_strain&lt;/code&gt;  的时刻了.&lt;/p&gt;
&lt;p&gt;首先映入我们眼帘的是一些定义好的常量 (以全大写和下划线命名), 这些常量规定了各增益在最终计算中的占比，反应了参与计算的参数在最终数值中的权&lt;/p&gt;
&lt;p&gt;除常量外，还有一些计算引起了我们的兴趣，第一是滑条奖励的计算，是比较简单粗暴的:  &lt;code&gt;滑条距离 / 滑条时间&lt;/code&gt; .&lt;/p&gt;
&lt;p&gt;这类滑条奖励主要将作用于拥有高速长滑条的一些谱面 (类似源流懐古那种), 偏 Tech 类的基本上增益不到.&lt;/p&gt;
&lt;p&gt;另外， &lt;code&gt;aim_strain&lt;/code&gt;  取用锐角、广角、速度增益的方式也比较有意思，这里是取&lt;strong&gt;锐角&lt;/strong&gt; 或 &lt;strong&gt;广角 + 速度&lt;/strong&gt;中的&lt;strong&gt;较大者&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;在上文我们提过，锐角增益的应用条件是很苛刻的 (300BPM 的跳，150BPM 的串), 这也导致了大部分的物件是不会吃到锐角奖励的.&lt;/p&gt;
&lt;p&gt;在查看常量的取值后我们发现，针对锐角增益的乘数是大于广角增益的乘数的，这为一些高难度、苛刻的 Pattern 提供了更高的奖励.&lt;/p&gt;
&lt;p&gt;简单来说，大部分的物件奖励值都来源于&lt;strong&gt;广角 + 速度&lt;/strong&gt;的计算公式，一些常见的谱面甚至没有机会吃到&lt;strong&gt;锐角&lt;/strong&gt;奖励.&lt;/p&gt;
&lt;h1 id=&#34;探索speed-skill&#34;&gt;&lt;a class=&#34;anchor&#34; href=&#34;#探索speed-skill&#34;&gt;#&lt;/a&gt; 探索 Speed Skill&lt;/h1&gt;
&lt;p&gt;同样的，我们首先来关注 speed.rs 中，针对 Speed 的 ISkill 实现 &lt;code&gt;impl&amp;lt;&#39;a&amp;gt; Skill&amp;lt;&#39;a, Speed&amp;gt;&lt;/code&gt; , 将重点放在 &lt;code&gt;strain_value_at&lt;/code&gt;  方法&lt;/p&gt;
&lt;figure class=&#34;highlight rust&#34;&gt;&lt;figcaption data-lang=&#34;rust&#34;&gt;&lt;/figcaption&gt;&lt;table&gt;&lt;tr&gt;&lt;td data-num=&#34;1&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token keyword&#34;&gt;fn&lt;/span&gt; &lt;span class=&#34;token function-definition function&#34;&gt;strain_value_at&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token operator&#34;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&#34;token keyword&#34;&gt;mut&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;self&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; curr&lt;span class=&#34;token punctuation&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;token operator&#34;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&#34;token lifetime-annotation symbol&#34;&gt;&#39;a&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;OsuDifficultyObject&lt;/span&gt;&lt;span class=&#34;token operator&#34;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&#34;token lifetime-annotation symbol&#34;&gt;&#39;a&lt;/span&gt;&lt;span class=&#34;token operator&#34;&gt;&gt;&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;-&gt;&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;f64&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;&amp;#123;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;2&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;span class=&#34;token keyword&#34;&gt;self&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;inner&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;curr_strain &lt;span class=&#34;token operator&#34;&gt;*=&lt;/span&gt; &lt;span class=&#34;token function&#34;&gt;strain_decay&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;curr&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;strain_time&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;token constant&#34;&gt;STRAIN_DECAY_BASE&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;3&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;span class=&#34;token keyword&#34;&gt;self&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;inner&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;curr_strain &lt;span class=&#34;token operator&#34;&gt;+=&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;4&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;        &lt;span class=&#34;token class-name&#34;&gt;SpeedEvaluator&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;::&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;evaluate_diff_of&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;curr&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;self&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;diff_objects&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;self&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;inner&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;hit_window&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;5&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;            &lt;span class=&#34;token operator&#34;&gt;*&lt;/span&gt; &lt;span class=&#34;token constant&#34;&gt;SKILL_MULTIPLIER&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;6&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;span class=&#34;token keyword&#34;&gt;self&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;inner&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;curr_rhythm &lt;span class=&#34;token operator&#34;&gt;=&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;7&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;        &lt;span class=&#34;token class-name&#34;&gt;RhythmEvaluator&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;::&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;evaluate_diff_of&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;curr&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;self&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;diff_objects&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;self&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;inner&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;hit_window&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;8&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;9&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;span class=&#34;token keyword&#34;&gt;let&lt;/span&gt; total_strain &lt;span class=&#34;token operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;self&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;inner&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;curr_strain &lt;span class=&#34;token operator&#34;&gt;*&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;self&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;inner&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;curr_rhythm&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;10&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;span class=&#34;token keyword&#34;&gt;self&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;inner&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;object_strains&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;push&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;total_strain&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;11&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;12&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    total_strain&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;13&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token punctuation&#34;&gt;&amp;#125;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;&lt;p&gt;经过粗略的阅读，我们发现了此处与上文 Aim 计算略微不同的地方。这里将 &lt;code&gt;curr_strain(speed_strain)&lt;/code&gt;  作为基值，而 &lt;code&gt;curr_rhythm&lt;/code&gt;  作为因数，速度与节奏复杂度相乘作为最终的值，所以我们下文主要分为 SpeedEvaluator 和 RhythmEvaluator 两个板块&lt;/p&gt;
&lt;p&gt;此处的&lt;strong&gt;节奏复杂度&lt;/strong&gt; (RhythmEvaluator) 就是在&lt;a href=&#34;https://osu.ppy.sh/home/news/2021-11-09-performance-points-star-rating-updates&#34;&gt; 2021 年 11 月 Rework&lt;/a&gt; 由 Xexxar 引入的概念.&lt;/p&gt;
&lt;p&gt;这次 Rework 备受关注和争议，但一定程度上避免了 Aim 成为主要获取 PP 的方式，将 osu! 从打地鼠模拟器的边缘拉了回来，下文我们会着重介绍这部分.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;关于节奏复杂度的发展历史:&lt;/p&gt;
&lt;p&gt;abraker95 曾在 2019 年提出过一套节奏复杂度的理论: &lt;a href=&#34;https://docs.google.com/document/d/1YjgfXKzz-8RpWkIT572qH_9fjCL6JltQEhf-BbQgJg4&#34;&gt;On the topic of rhythmic complexity&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;2019 年，社区针对节奏复杂度的理解和讨论: &lt;a href=&#34;https://github.com/ppy/osu-performance/issues/89&#34;&gt;Rhythmic Complexity&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;接下来，我们还是按照代码的顺序来逐个介绍每一块内容.&lt;/p&gt;
&lt;h2 id=&#34;speedevaluator&#34;&gt;&lt;a class=&#34;anchor&#34; href=&#34;#speedevaluator&#34;&gt;#&lt;/a&gt; SpeedEvaluator&lt;/h2&gt;
&lt;p&gt;在开始介绍 SpeedEvaluator 之前，我们先对一些概念进行补充:&lt;/p&gt;
&lt;p&gt;&lt;code&gt;hit_window&lt;/code&gt; : HitWindow 是完成物件打击的时间窗口期，与谱面的 OD 息息相关 (注意，rosu-pp 中所有提到的 &lt;code&gt;hit_window&lt;/code&gt;  均为整段 hit_window, 即下面的计算公式 * 2).&lt;/p&gt;
&lt;p&gt;在游戏内将光标悬停在左上角的谱面三维位置可以看到窗口期 (单位为 ms), osu!standard 模式有这样的计算方式:&lt;/p&gt;
&lt;p&gt;&lt;img loading=&#34;lazy&#34; data-src=&#34;https://s2.loli.net/2024/07/08/FR6DjZHcleKgyY3.png&#34; alt=&#34;Snipaste_2024-07-08_13-29-04.png&#34; /&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;这里推荐阅读 osu!Wiki: &lt;a href=&#34;https://osu.ppy.sh/wiki/zh/Beatmap/Overall_difficulty&#34;&gt;判定严度 (Overall difficulty)&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 id=&#34;单拍双按&#34;&gt;&lt;a class=&#34;anchor&#34; href=&#34;#单拍双按&#34;&gt;#&lt;/a&gt; 单拍？双按！&lt;/h3&gt;
&lt;p&gt;算法总是需要跟随谱师的脑洞不断更新迭代，有创新性的谱面经常能推动 PP 系统的更新，&lt;strong&gt;doubletapness&lt;/strong&gt; 就是这样被引入的&lt;/p&gt;
&lt;p&gt;引入&lt;strong&gt; doubletapness&lt;/strong&gt; 源于哪张谱面呢，请看 VCR (雾):&lt;/p&gt;
&lt;p&gt;&lt;img loading=&#34;lazy&#34; data-src=&#34;https://s2.loli.net/2024/09/16/ioh4fcxyztBLXqp.gif&#34; alt=&#34;low_qualgif.gif&#34; /&gt;&lt;/p&gt;
&lt;p&gt;仔细看的读者应该能发现，片段中多次出现需要双按的单拍，这在 Speed 计算看来，被判定为极高速的切，DTHR 后更会使这种情况加剧.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;在&lt;strong&gt; doubletapness&lt;/strong&gt; 引入之前，这张图 DTHR 后被给予了 13 星的超高难度 (目前为 8.66 星)&lt;/p&gt;
&lt;/blockquote&gt;
&lt;figure class=&#34;highlight rust&#34;&gt;&lt;figcaption data-lang=&#34;rust&#34;&gt;&lt;/figcaption&gt;&lt;table&gt;&lt;tr&gt;&lt;td data-num=&#34;1&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token comment&#34;&gt;// * Nerf doubletappable doubles.&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;2&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token keyword&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;let&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;Some&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;osu_next_obj&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;token operator&#34;&gt;=&lt;/span&gt; osu_next_obj &lt;span class=&#34;token punctuation&#34;&gt;&amp;#123;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;3&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;span class=&#34;token keyword&#34;&gt;let&lt;/span&gt; curr_delta_time &lt;span class=&#34;token operator&#34;&gt;=&lt;/span&gt; osu_curr_obj&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;delta_time&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;max&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token number&#34;&gt;1.0&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;4&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;span class=&#34;token keyword&#34;&gt;let&lt;/span&gt; next_delta_time &lt;span class=&#34;token operator&#34;&gt;=&lt;/span&gt; osu_next_obj&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;delta_time&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;max&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token number&#34;&gt;1.0&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;5&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;span class=&#34;token keyword&#34;&gt;let&lt;/span&gt; delta_diff &lt;span class=&#34;token operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;next_delta_time &lt;span class=&#34;token operator&#34;&gt;-&lt;/span&gt; curr_delta_time&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;abs&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;6&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;span class=&#34;token keyword&#34;&gt;let&lt;/span&gt; speed_ratio &lt;span class=&#34;token operator&#34;&gt;=&lt;/span&gt; curr_delta_time &lt;span class=&#34;token operator&#34;&gt;/&lt;/span&gt; curr_delta_time&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;max&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;delta_diff&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;7&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;span class=&#34;token keyword&#34;&gt;let&lt;/span&gt; window_ratio &lt;span class=&#34;token operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;curr_delta_time &lt;span class=&#34;token operator&#34;&gt;/&lt;/span&gt; hit_window&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;min&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token number&#34;&gt;1.0&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;powf&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token number&#34;&gt;2.0&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;8&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    doubletapness &lt;span class=&#34;token operator&#34;&gt;=&lt;/span&gt; speed_ratio&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;powf&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token number&#34;&gt;1.0&lt;/span&gt; &lt;span class=&#34;token operator&#34;&gt;-&lt;/span&gt; window_ratio&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;9&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token punctuation&#34;&gt;&amp;#125;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;&lt;p&gt;&lt;code&gt;window_ratio&lt;/code&gt;  将&lt;strong&gt;相邻两个物件&lt;/strong&gt;与 300 的 &lt;code&gt;hit_window&lt;/code&gt;  相比较。假定 N 是当前物件，如果 &lt;code&gt;delta(N, N-1)&lt;/code&gt;  比 300 的 hitwindow 小，那么在 (N-2, N-1) 与 (N-1, N) 之间可以认为存在 doubletapness 的情况，这里 &lt;code&gt;window_ratio&lt;/code&gt;  是 &lt;code&gt;doubletapness&lt;/code&gt;  的定性参数&lt;/p&gt;
&lt;p&gt;&lt;code&gt;delta_diff&lt;/code&gt;  表明了本次间隔与下次间隔之间的差值。这里我们以 GIF 中任意一个&lt;strong&gt;圆圈 2&lt;/strong&gt; 举例，&lt;strong&gt;圆圈 2&lt;/strong&gt; 的该值会非常大 (2 与前一个 1 之间间隔非常短，而与下一个 1 之间间隔非常长), 如果我们转而观察任意一个&lt;strong&gt;圆圈 1&lt;/strong&gt;, 可以得到相同的结论。这里我们发现 &lt;code&gt;delta_diff&lt;/code&gt;  就是衡量 &lt;code&gt;doubletapness&lt;/code&gt;  的定量参数&lt;/p&gt;
&lt;p&gt;&lt;code&gt;speed_ratio&lt;/code&gt;  是衰减幅度的一部分。意在将 &lt;code&gt;raw_doubletapness&lt;/code&gt;  归一化到当前量纲&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;这里提到的 300 的 hitwindow, 可以利用上文的公式:  &lt;code&gt;2(80 - 6 * OD)&lt;/code&gt;  计算得到，这里的 2 意味着我们考虑整段 hitwindow, 包含正负段&lt;/p&gt;
&lt;p&gt;doubletapness 由 apollo-dw 在 2022 年引入: &lt;a href=&#34;https://github.com/ppy/osu/pull/18692&#34;&gt;Rework doubletap detection in osu!&#39;s Speed evaluator&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;感兴趣的读者可以到 apollo-dw 提供的图形计算器自己调整参数尝试: &lt;a href=&#34;https://www.desmos.com/calculator/vr8nzfqo4b?lang=zh-CN&#34;&gt;Desmos&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;需要注意的是，在 2021 年前后，&lt;strong&gt;apollo-dw&lt;/strong&gt; 推进了许多与 Speed 计算有关的提案，&lt;strong&gt;doubletapness&lt;/strong&gt; 修复只是&lt;strong&gt; apollo-dw&lt;/strong&gt; 伟大改革的一次未雨绸缪.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;apollo-dw&lt;/strong&gt; 的名字还会在下面的内容中出现.&lt;/p&gt;
&lt;h3 id=&#34;速度为底-距离为因&#34;&gt;&lt;a class=&#34;anchor&#34; href=&#34;#速度为底-距离为因&#34;&gt;#&lt;/a&gt; 速度为底、距离为因&lt;/h3&gt;
&lt;figure class=&#34;highlight rust&#34;&gt;&lt;figcaption data-lang=&#34;rust&#34;&gt;&lt;/figcaption&gt;&lt;table&gt;&lt;tr&gt;&lt;td data-num=&#34;1&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token comment&#34;&gt;// * Cap deltatime to the OD 300 hitwindow.&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;2&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token comment&#34;&gt;// * 0.93 is derived from making sure 260bpm OD8 streams aren&#39;t nerfed harshly, whilst 0.92 limits the effect of the cap.&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;3&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;strain_time &lt;span class=&#34;token operator&#34;&gt;/=&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;strain_time &lt;span class=&#34;token operator&#34;&gt;/&lt;/span&gt; hit_window&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;token operator&#34;&gt;/&lt;/span&gt; &lt;span class=&#34;token number&#34;&gt;0.93&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;clamp&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token number&#34;&gt;0.92&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;token number&#34;&gt;1.0&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;4&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;5&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token comment&#34;&gt;// * derive speedBonus for calculation&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;6&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token keyword&#34;&gt;let&lt;/span&gt; speed_bonus &lt;span class=&#34;token operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;if&lt;/span&gt; strain_time &lt;span class=&#34;token operator&#34;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;Self&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;::&lt;/span&gt;&lt;span class=&#34;token constant&#34;&gt;MIN_SPEED_BONUS&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;&amp;#123;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;7&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;span class=&#34;token keyword&#34;&gt;let&lt;/span&gt; base &lt;span class=&#34;token operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token keyword&#34;&gt;Self&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;::&lt;/span&gt;&lt;span class=&#34;token constant&#34;&gt;MIN_SPEED_BONUS&lt;/span&gt; &lt;span class=&#34;token operator&#34;&gt;-&lt;/span&gt; strain_time&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;token operator&#34;&gt;/&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;Self&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;::&lt;/span&gt;&lt;span class=&#34;token constant&#34;&gt;SPEED_BALANCING_FACTOR&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;8&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;9&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;span class=&#34;token number&#34;&gt;1.0&lt;/span&gt; &lt;span class=&#34;token operator&#34;&gt;+&lt;/span&gt; &lt;span class=&#34;token number&#34;&gt;0.75&lt;/span&gt; &lt;span class=&#34;token operator&#34;&gt;*&lt;/span&gt; base&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;powf&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token number&#34;&gt;2.0&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;10&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token punctuation&#34;&gt;&amp;#125;&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;else&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;&amp;#123;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;11&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;span class=&#34;token number&#34;&gt;1.0&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;12&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token punctuation&#34;&gt;&amp;#125;&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;13&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;14&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token keyword&#34;&gt;let&lt;/span&gt; travel_dist &lt;span class=&#34;token operator&#34;&gt;=&lt;/span&gt; osu_prev_obj&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;map_or&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token number&#34;&gt;0.0&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;token closure-params&#34;&gt;&lt;span class=&#34;token closure-punctuation punctuation&#34;&gt;|&lt;/span&gt;obj&lt;span class=&#34;token closure-punctuation punctuation&#34;&gt;|&lt;/span&gt;&lt;/span&gt; obj&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;travel_dist&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;15&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token keyword&#34;&gt;let&lt;/span&gt; dist &lt;span class=&#34;token operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;Self&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;::&lt;/span&gt;&lt;span class=&#34;token constant&#34;&gt;SINGLE_SPACING_THRESHOLD&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;min&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;travel_dist &lt;span class=&#34;token operator&#34;&gt;+&lt;/span&gt; osu_curr_obj&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;min_jump_dist&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;16&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;17&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;speed_bonus &lt;span class=&#34;token operator&#34;&gt;+&lt;/span&gt; speed_bonus &lt;span class=&#34;token operator&#34;&gt;*&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;dist &lt;span class=&#34;token operator&#34;&gt;/&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;Self&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;::&lt;/span&gt;&lt;span class=&#34;token constant&#34;&gt;SINGLE_SPACING_THRESHOLD&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;powf&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token number&#34;&gt;3.5&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;18&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;span class=&#34;token operator&#34;&gt;*&lt;/span&gt; doubletapness&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;19&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;span class=&#34;token operator&#34;&gt;/&lt;/span&gt; strain_time&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;&lt;p&gt;在计算的开头，我们注意到了这个对于 &lt;code&gt;strain_time&lt;/code&gt;  的限制操作，他实际来源于&lt;strong&gt; apollo-dw&lt;/strong&gt; 的一个 commit: &lt;a href=&#34;https://github.com/ppy/osu/pull/14617&#34;&gt;Remove speed caps in osu! difficulty calculation&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;限制 &lt;code&gt;strain_time&lt;/code&gt;  的本意是防止&lt;strong&gt;高 BPM 低 OD 串&lt;/strong&gt;的谱面算出过高的星数，同时推进移除旧算法直接限制 Speed 值的行为.&lt;/p&gt;
&lt;p&gt;这里考虑了物件之间的间隔时间是否小于 300 的 hitwindow, 如果小于，就会对 strain_time 进行削弱.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;apollo-dw&lt;/strong&gt; 与&lt;strong&gt; emu1337&lt;/strong&gt; 通过上面提到的&lt;strong&gt; doubletapness 修复&lt;/strong&gt;和&lt;strong&gt;限制 strain_time&lt;/strong&gt; 推进了&lt;a href=&#34;https://github.com/ppy/osu/pull/14617&#34;&gt; Remove speed caps in osu! difficulty calculation&lt;/a&gt; 提案.&lt;/p&gt;
&lt;p&gt;在 2024 年看来，这些提案还是非常有前瞻性的，Wooting 的出现大幅提升了头部玩家的高速串能力，旧～333bpm 1/4 的限制移除是大势所趋&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;speed_bonus 是从 75ms 开始的 (200BPM), dist 是从 125osu!pixel 开始的，总体趋势为速度越大，距离越远，strain 值越大.&lt;/p&gt;
&lt;p&gt;这里我们利用 matplotlib 绘制了 &lt;code&gt;speed_bonus&lt;/code&gt;  关于 &lt;code&gt;strain_time&lt;/code&gt;  的曲线提供给读者参考:&lt;/p&gt;
&lt;p&gt;&lt;img loading=&#34;lazy&#34; data-src=&#34;https://s2.loli.net/2024/09/17/bAEjJxSapdctTfw.png&#34; alt=&#34;Figure_1.png&#34; /&gt;&lt;/p&gt;
&lt;p&gt;对于 dist 的计算，这里与 Aim 中的 velocity extends 概念非常相似，这里我们重新引入那张图片:&lt;/p&gt;
&lt;p&gt;&lt;img loading=&#34;lazy&#34; data-src=&#34;https://s2.loli.net/2024/07/02/WBSebI6OzCKnd3a.png&#34; alt=&#34;&#34; width=&#34;720px&#34; /&gt;&lt;/p&gt;
&lt;p&gt;请读者将&lt;strong&gt;圆圈 2&lt;/strong&gt; 当做当前物件，这里&lt;strong&gt;圆圈 2&lt;/strong&gt; 的 dist 就是红色距离与橙色距离的和&lt;/p&gt;
&lt;h2 id=&#34;rhythmevaluator&#34;&gt;&lt;a class=&#34;anchor&#34; href=&#34;#rhythmevaluator&#34;&gt;#&lt;/a&gt; RhythmEvaluator&lt;/h2&gt;
&lt;p&gt;由于 RhythmEvaluator 较为复杂，这里我们先对原理进行总体而粗略的解释，再结合代码分析.&lt;/p&gt;
&lt;p&gt;首先，RhythmEvaluator 本身与上文的 AimEvaluator, SpeedEvaluator 无异，都是针对&lt;strong&gt;具有上下文的单个物件&lt;/strong&gt;算出的值.&lt;/p&gt;
&lt;p&gt;节奏复杂度围绕 &lt;code&gt;effectiveRatio&lt;/code&gt;  这个参数展开。首先，effectiveRatio 会根据初始节奏变化得到一个基值，再通过类似&lt;strong&gt;组间评分&lt;/strong&gt;的方式进行削弱.&lt;/p&gt;
&lt;h3 id=&#34;组间评分&#34;&gt;&lt;a class=&#34;anchor&#34; href=&#34;#组间评分&#34;&gt;#&lt;/a&gt; 组间评分&lt;/h3&gt;
&lt;p&gt;这里，我们利用比较通俗的语言模拟一下组间评分的过程:&lt;/p&gt;
&lt;p&gt;首先，先寻找当前物件&lt;strong&gt; 5s 内&lt;/strong&gt;的&lt;strong&gt; 0~32 个历史物件&lt;/strong&gt;，最终会脱颖而出 &lt;code&gt;rhythmStart&lt;/code&gt;  个历史物件&lt;/p&gt;
&lt;p&gt;接下来，我们会按顺序遍历这些个历史物件，尝试在遍历中对前后相邻的物件进行分组 ( &lt;code&gt;island&lt;/code&gt; ), 分组依据是节奏发生了较大幅度变化 ( &lt;code&gt;deltaTime&lt;/code&gt;  变化超出 25%)&lt;/p&gt;
&lt;p&gt;遍历过后，所有的历史物件都应该被分好了自己的小组 ( &lt;code&gt;island&lt;/code&gt; ), 小组人数为 1~8 人。小组可以容纳超过 8 个物件，但小组人数只会被记作 8&lt;/p&gt;
&lt;p&gt;最后，我们要在小组 ( &lt;code&gt;island&lt;/code&gt; ) 间进行评分，当某些 Pattern 出现时， &lt;code&gt;effectiveRatio&lt;/code&gt;  值会被削弱，例如：人数相等的小组重复出现 (xxx-xxx), 前后小组人数奇偶相同 (xx-xxxx) 等等&lt;/p&gt;
&lt;p&gt;示意图 (图片部分来自&lt;strong&gt; emu1337&lt;/strong&gt; 的截图):&lt;/p&gt;
&lt;p&gt;&lt;img loading=&#34;lazy&#34; data-src=&#34;https://s2.loli.net/2024/09/17/gEfGa1UADmCHtxL.png&#34; alt=&#34;Snipaste_2024-09-17_01-23-45.png&#34; /&gt;&lt;/p&gt;
&lt;h3 id=&#34;实现细节&#34;&gt;&lt;a class=&#34;anchor&#34; href=&#34;#实现细节&#34;&gt;#&lt;/a&gt; 实现细节&lt;/h3&gt;
&lt;p&gt;接下来我们将结合代码对提到的组间评分过程进行分析，需要注意的是，这里为了提高可读性，选择了 C# 版本的代码.&lt;/p&gt;
&lt;h4 id=&#34;筛选对象&#34;&gt;&lt;a class=&#34;anchor&#34; href=&#34;#筛选对象&#34;&gt;#&lt;/a&gt; 筛选对象&lt;/h4&gt;
&lt;figure class=&#34;highlight csharp&#34;&gt;&lt;figcaption data-lang=&#34;C#&#34;&gt;&lt;/figcaption&gt;&lt;table&gt;&lt;tr&gt;&lt;td data-num=&#34;1&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token class-name&#34;&gt;&lt;span class=&#34;token keyword&#34;&gt;int&lt;/span&gt;&lt;/span&gt; historicalNoteCount &lt;span class=&#34;token operator&#34;&gt;=&lt;/span&gt; Math&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;Min&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;current&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;Index&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;token number&#34;&gt;32&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;2&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token class-name&#34;&gt;&lt;span class=&#34;token keyword&#34;&gt;int&lt;/span&gt;&lt;/span&gt; rhythmStart &lt;span class=&#34;token operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;token number&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;3&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token keyword&#34;&gt;while&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;rhythmStart &lt;span class=&#34;token operator&#34;&gt;&amp;lt;&lt;/span&gt; historicalNoteCount &lt;span class=&#34;token operator&#34;&gt;-&lt;/span&gt; &lt;span class=&#34;token number&#34;&gt;2&lt;/span&gt; &lt;span class=&#34;token operator&#34;&gt;&amp;amp;&amp;amp;&lt;/span&gt; current&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;StartTime &lt;span class=&#34;token operator&#34;&gt;-&lt;/span&gt; current&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;Previous&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;rhythmStart&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;StartTime &lt;span class=&#34;token operator&#34;&gt;&amp;lt;&lt;/span&gt; history_time_max&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;4&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    rhythmStart&lt;span class=&#34;token operator&#34;&gt;++&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;&lt;p&gt;相信这里看到 &lt;code&gt;rhythmStart&lt;/code&gt; , 读者应该并不陌生，我们在此处完成了寻找当前物件&lt;strong&gt; 5s 内&lt;/strong&gt;的&lt;strong&gt; 0~32 个历史物件&lt;/strong&gt;的操作.&lt;/p&gt;
&lt;h4 id=&#34;进行分组&#34;&gt;&lt;a class=&#34;anchor&#34; href=&#34;#进行分组&#34;&gt;#&lt;/a&gt; 进行分组&lt;/h4&gt;
&lt;figure class=&#34;highlight csharp&#34;&gt;&lt;figcaption data-lang=&#34;C#&#34;&gt;&lt;/figcaption&gt;&lt;table&gt;&lt;tr&gt;&lt;td data-num=&#34;1&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token keyword&#34;&gt;for&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token class-name&#34;&gt;&lt;span class=&#34;token keyword&#34;&gt;int&lt;/span&gt;&lt;/span&gt; i &lt;span class=&#34;token operator&#34;&gt;=&lt;/span&gt; rhythmStart&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt; i &lt;span class=&#34;token operator&#34;&gt;&gt;&lt;/span&gt; &lt;span class=&#34;token number&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt; i&lt;span class=&#34;token operator&#34;&gt;--&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;2&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token punctuation&#34;&gt;&amp;#123;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;3&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;span class=&#34;token comment&#34;&gt;// 省略了 effectiveRatio 初值的计算&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;4&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;5&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;span class=&#34;token keyword&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;firstDeltaSwitch&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;6&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;span class=&#34;token punctuation&#34;&gt;&amp;#123;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;7&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;        &lt;span class=&#34;token keyword&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token operator&#34;&gt;!&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;prevDelta &lt;span class=&#34;token operator&#34;&gt;&gt;&lt;/span&gt; &lt;span class=&#34;token number&#34;&gt;1.25&lt;/span&gt; &lt;span class=&#34;token operator&#34;&gt;*&lt;/span&gt; currDelta &lt;span class=&#34;token operator&#34;&gt;||&lt;/span&gt; prevDelta &lt;span class=&#34;token operator&#34;&gt;*&lt;/span&gt; &lt;span class=&#34;token number&#34;&gt;1.25&lt;/span&gt; &lt;span class=&#34;token operator&#34;&gt;&amp;lt;&lt;/span&gt; currDelta&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;8&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;        &lt;span class=&#34;token punctuation&#34;&gt;&amp;#123;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;9&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;            &lt;span class=&#34;token keyword&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;islandSize &lt;span class=&#34;token operator&#34;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&#34;token number&#34;&gt;7&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;10&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;                islandSize&lt;span class=&#34;token operator&#34;&gt;++&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt; &lt;span class=&#34;token comment&#34;&gt;// island is still progressing, count size.&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;11&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;        &lt;span class=&#34;token punctuation&#34;&gt;&amp;#125;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;12&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;        &lt;span class=&#34;token keyword&#34;&gt;else&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;13&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;        &lt;span class=&#34;token punctuation&#34;&gt;&amp;#123;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;14&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;            &lt;span class=&#34;token comment&#34;&gt;// 省略了对于 effectiveRatio 的削弱计算&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;15&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;16&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;            &lt;span class=&#34;token comment&#34;&gt;/*&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;17&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;                将分组运算的结果累计到 rhythmComplexitySum 中，后文我们会再提到&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;18&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;                rhythmComplexitySum += Math.Sqrt (effectiveRatio * previousRatio) * currHistoricalDecay * Math.Sqrt (4 + islandSize) / 2 * Math.Sqrt (4 + previousIslandSize) / 2;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;19&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;                previousRatio = effectiveRatio;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;20&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;            */&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;21&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;22&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;            previousIslandSize &lt;span class=&#34;token operator&#34;&gt;=&lt;/span&gt; islandSize&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt; &lt;span class=&#34;token comment&#34;&gt;// log the last island size.&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;23&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;24&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;            &lt;span class=&#34;token keyword&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;prevDelta &lt;span class=&#34;token operator&#34;&gt;*&lt;/span&gt; &lt;span class=&#34;token number&#34;&gt;1.25&lt;/span&gt; &lt;span class=&#34;token operator&#34;&gt;&amp;lt;&lt;/span&gt; currDelta&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;token comment&#34;&gt;// we&#39;re slowing down, stop counting&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;25&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;                firstDeltaSwitch &lt;span class=&#34;token operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;token boolean&#34;&gt;false&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt; &lt;span class=&#34;token comment&#34;&gt;// if we&#39;re speeding up, this stays true and we keep counting island size.&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;26&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;27&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;            islandSize &lt;span class=&#34;token operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;token number&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;28&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;        &lt;span class=&#34;token punctuation&#34;&gt;&amp;#125;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;29&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;span class=&#34;token punctuation&#34;&gt;&amp;#125;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;30&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;span class=&#34;token keyword&#34;&gt;else&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;prevDelta &lt;span class=&#34;token operator&#34;&gt;&gt;&lt;/span&gt; &lt;span class=&#34;token number&#34;&gt;1.25&lt;/span&gt; &lt;span class=&#34;token operator&#34;&gt;*&lt;/span&gt; currDelta&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;token comment&#34;&gt;// we want to be speeding up.&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;31&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;span class=&#34;token punctuation&#34;&gt;&amp;#123;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;32&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;        &lt;span class=&#34;token comment&#34;&gt;// Begin counting island until we change speed again.&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;33&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;        firstDeltaSwitch &lt;span class=&#34;token operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;token boolean&#34;&gt;true&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;34&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;        previousRatio &lt;span class=&#34;token operator&#34;&gt;=&lt;/span&gt; effectiveRatio&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;35&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;        islandSize &lt;span class=&#34;token operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;token number&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;36&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;span class=&#34;token punctuation&#34;&gt;&amp;#125;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;37&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token punctuation&#34;&gt;&amp;#125;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;&lt;p&gt;此处的代码段省略了部分值的计算过程，让我们聚焦于 island 的分组依据和分组过程.&lt;/p&gt;
&lt;p&gt;首先，for 循环让我们对所有的历史物件遍历，显而易见的， &lt;code&gt;firstDeltaSwitch&lt;/code&gt;  是新小组产生的定性判据.&lt;/p&gt;
&lt;p&gt;一旦满足 &lt;code&gt;prevDelta &amp;gt; 1.25 * currDelta&lt;/code&gt; , 即 deltaTime 发生了加速 25% 的变化时，算法认为应该生成一个新的小组了.&lt;/p&gt;
&lt;p&gt;在下一次循环中，由于 &lt;code&gt;firstDeltaSwitch&lt;/code&gt;  被置为 &lt;code&gt;true&lt;/code&gt; , 算法将走入第一个 if 分支中.&lt;/p&gt;
&lt;p&gt;显然，第二个 if 分支决定了分组结果，一旦 &lt;code&gt;prevDelta &amp;gt; 1.25 * currDelta || prevDelta * 1.25 &amp;lt; currDelta&lt;/code&gt;  条件满足，即速度发生了较大变化时，当前小组将结束并进行结算 (else 分支).&lt;/p&gt;
&lt;p&gt;结算的过程就是我们在上文提到的小组 ( &lt;code&gt;island&lt;/code&gt; ) 间进行评分的过程。与伪代码描述不同的是，在真实代码中，分组与结算是接连完成的，而不是先分组再结算的.&lt;/p&gt;
&lt;p&gt;很明显地，结算时我们有意地储存了一些 previousIsland 的状态，不难理解，这是在为下一小组结算时&lt;strong&gt;创建上下文&lt;/strong&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;另外地，这里 &lt;code&gt;if (prevDelta * 1.25 &amp;lt; currDelta) firstDeltaSwitch = false;&lt;/code&gt;  当速度维持上涨趋势时，小组可以接连被创建。当速度不再上涨时，定性判据将被复原，直至下一次满足 &lt;code&gt;prevDelta &amp;gt; 1.25 * currDelta&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;敏锐的读者可能察觉到我们上文示意图中的错误，实际上，单调节奏不是被分为了一个大组，而是很多人数为 1 的小组.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h4 id=&#34;初值计算&#34;&gt;&lt;a class=&#34;anchor&#34; href=&#34;#初值计算&#34;&gt;#&lt;/a&gt; 初值计算&lt;/h4&gt;
&lt;figure class=&#34;highlight csharp&#34;&gt;&lt;figcaption data-lang=&#34;C#&#34;&gt;&lt;/figcaption&gt;&lt;table&gt;&lt;tr&gt;&lt;td data-num=&#34;1&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token class-name&#34;&gt;&lt;span class=&#34;token keyword&#34;&gt;double&lt;/span&gt;&lt;/span&gt; currHistoricalDecay &lt;span class=&#34;token operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;history_time_max &lt;span class=&#34;token operator&#34;&gt;-&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;current&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;StartTime &lt;span class=&#34;token operator&#34;&gt;-&lt;/span&gt; currObj&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;StartTime&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;token operator&#34;&gt;/&lt;/span&gt; history_time_max&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt; &lt;span class=&#34;token comment&#34;&gt;// scales note 0 to 1 from history to now&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;2&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;3&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;currHistoricalDecay &lt;span class=&#34;token operator&#34;&gt;=&lt;/span&gt; Math&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;Min&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token keyword&#34;&gt;double&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;historicalNoteCount &lt;span class=&#34;token operator&#34;&gt;-&lt;/span&gt; i&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;token operator&#34;&gt;/&lt;/span&gt; historicalNoteCount&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; currHistoricalDecay&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt; &lt;span class=&#34;token comment&#34;&gt;// either we&#39;re limited by time or limited by object count.&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;4&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;5&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token class-name&#34;&gt;&lt;span class=&#34;token keyword&#34;&gt;double&lt;/span&gt;&lt;/span&gt; currRatio &lt;span class=&#34;token operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;token number&#34;&gt;1.0&lt;/span&gt; &lt;span class=&#34;token operator&#34;&gt;+&lt;/span&gt; &lt;span class=&#34;token number&#34;&gt;6.0&lt;/span&gt; &lt;span class=&#34;token operator&#34;&gt;*&lt;/span&gt; Math&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;Min&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token number&#34;&gt;0.5&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; Math&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;Pow&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;Math&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;Sin&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;Math&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;PI &lt;span class=&#34;token operator&#34;&gt;/&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;Math&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;Min&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;prevDelta&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; currDelta&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;token operator&#34;&gt;/&lt;/span&gt; Math&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;Max&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;prevDelta&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; currDelta&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;token number&#34;&gt;2&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt; &lt;span class=&#34;token comment&#34;&gt;// fancy function to calculate rhythmbonuses.&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;6&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;7&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token class-name&#34;&gt;&lt;span class=&#34;token keyword&#34;&gt;double&lt;/span&gt;&lt;/span&gt; windowPenalty &lt;span class=&#34;token operator&#34;&gt;=&lt;/span&gt; Math&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;Min&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token number&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; Math&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;Max&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token number&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; Math&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;Abs&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;prevDelta &lt;span class=&#34;token operator&#34;&gt;-&lt;/span&gt; currDelta&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;token operator&#34;&gt;-&lt;/span&gt; currObj&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;HitWindowGreat &lt;span class=&#34;token operator&#34;&gt;*&lt;/span&gt; &lt;span class=&#34;token number&#34;&gt;0.3&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;token operator&#34;&gt;/&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;currObj&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;HitWindowGreat &lt;span class=&#34;token operator&#34;&gt;*&lt;/span&gt; &lt;span class=&#34;token number&#34;&gt;0.3&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;8&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;9&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;windowPenalty &lt;span class=&#34;token operator&#34;&gt;=&lt;/span&gt; Math&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;Min&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token number&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; windowPenalty&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;10&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;11&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token class-name&#34;&gt;&lt;span class=&#34;token keyword&#34;&gt;double&lt;/span&gt;&lt;/span&gt; effectiveRatio &lt;span class=&#34;token operator&#34;&gt;=&lt;/span&gt; windowPenalty &lt;span class=&#34;token operator&#34;&gt;*&lt;/span&gt; currRatio&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;&lt;h5 id=&#34;currhistoricaldecay&#34;&gt;&lt;a class=&#34;anchor&#34; href=&#34;#currhistoricaldecay&#34;&gt;#&lt;/a&gt; currHistoricalDecay&lt;/h5&gt;
&lt;p&gt;在&lt;strong&gt;进行分组&lt;/strong&gt;部分的代码展示中，我们一笔带过了 &lt;code&gt;rhythmComplexitySum&lt;/code&gt;  的计算，其中引用了 &lt;code&gt;currHistoricalDecay&lt;/code&gt;  这个概念.&lt;/p&gt;
&lt;p&gt;重新审视整个过程，我们会针对&lt;strong&gt;每一个物件&lt;/strong&gt;应用 RhythmEvaluator, 在单一物件中提出 Sum 这种概念是很危险的，我们需要将 Sum 平摊到每一个物件的 strain 上.&lt;/p&gt;
&lt;p&gt;最简单的方式是直接将 &lt;code&gt;rhythmComplexitySum&lt;/code&gt;  除以  &lt;code&gt;rhythmStart&lt;/code&gt; , 但这样显然并不明智。所以在这里引入了 &lt;code&gt;currHistoricalDecay&lt;/code&gt; .&lt;/p&gt;
&lt;p&gt;&lt;code&gt;currHistoricalDecay&lt;/code&gt;  根据相差时间进行均匀的伸缩，把 Sum 值平摊到 0~N 个历史物件中.&lt;/p&gt;
&lt;h5 id=&#34;effectiveratio&#34;&gt;&lt;a class=&#34;anchor&#34; href=&#34;#effectiveratio&#34;&gt;#&lt;/a&gt; effectiveRatio&lt;/h5&gt;
&lt;p&gt;&lt;code&gt;currRatio&lt;/code&gt;  的表达式我们并不陌生，&lt;/p&gt;
&lt;p&gt;&lt;code&gt;currRatio&lt;/code&gt;  的函数表达形式 (先正弦再平方) 我们并不陌生，与 Aim 中曾提到的速度变化奖励不能说毫不相干，只能说一模一样.&lt;/p&gt;
&lt;p&gt;这里我们甚至可以引用上文的原文:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;code&gt;(prev_vel - curr_vel).abs() / prev_vel.max(curr_vel)&lt;/code&gt;  计算了速度变化的大小相对于最大速度的比例，反应了速度变化的剧烈程度.&lt;/p&gt;
&lt;p&gt;相同地，这里也采用了先正弦再平方的方式进行因数的归一化，有疑问的读者可以返回上文查看.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Xexxar&lt;/strong&gt; 也放出了该公式的图形计算器: &lt;a href=&#34;https://www.desmos.com/calculator/j6yykmbb6f&#34;&gt;Desmos&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;code&gt;windowPenalty&lt;/code&gt;  主要针对重叠的窗口进行惩罚，这里的玩法类似于之前&lt;strong&gt; apollo-dw&lt;/strong&gt; 的 &lt;code&gt;doubletapness&lt;/code&gt; .&lt;/p&gt;
&lt;h4 id=&#34;组间计算&#34;&gt;&lt;a class=&#34;anchor&#34; href=&#34;#组间计算&#34;&gt;#&lt;/a&gt; 组间计算&lt;/h4&gt;
&lt;figure class=&#34;highlight csharp&#34;&gt;&lt;figcaption data-lang=&#34;C#&#34;&gt;&lt;/figcaption&gt;&lt;table&gt;&lt;tr&gt;&lt;td data-num=&#34;1&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token keyword&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;currObj&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;BaseObject &lt;span class=&#34;token keyword&#34;&gt;is&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;Slider&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;token comment&#34;&gt;// bpm change is into slider, this is easy acc window&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;2&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    effectiveRatio &lt;span class=&#34;token operator&#34;&gt;*=&lt;/span&gt; &lt;span class=&#34;token number&#34;&gt;0.125&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;3&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;4&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token keyword&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;prevObj&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;BaseObject &lt;span class=&#34;token keyword&#34;&gt;is&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;Slider&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;token comment&#34;&gt;// bpm change was from a slider, this is easier typically than circle -&gt; circle&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;5&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    effectiveRatio &lt;span class=&#34;token operator&#34;&gt;*=&lt;/span&gt; &lt;span class=&#34;token number&#34;&gt;0.25&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;6&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;7&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token keyword&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;previousIslandSize &lt;span class=&#34;token operator&#34;&gt;==&lt;/span&gt; islandSize&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;token comment&#34;&gt;// repeated island size (ex: triplet -&gt; triplet)&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;8&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    effectiveRatio &lt;span class=&#34;token operator&#34;&gt;*=&lt;/span&gt; &lt;span class=&#34;token number&#34;&gt;0.25&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;9&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;10&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token keyword&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;previousIslandSize &lt;span class=&#34;token operator&#34;&gt;%&lt;/span&gt; &lt;span class=&#34;token number&#34;&gt;2&lt;/span&gt; &lt;span class=&#34;token operator&#34;&gt;==&lt;/span&gt; islandSize &lt;span class=&#34;token operator&#34;&gt;%&lt;/span&gt; &lt;span class=&#34;token number&#34;&gt;2&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;token comment&#34;&gt;// repeated island polartiy (2 -&gt; 4, 3 -&gt; 5)&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;11&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    effectiveRatio &lt;span class=&#34;token operator&#34;&gt;*=&lt;/span&gt; &lt;span class=&#34;token number&#34;&gt;0.50&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;12&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;13&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token keyword&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;lastDelta &lt;span class=&#34;token operator&#34;&gt;&gt;&lt;/span&gt; prevDelta &lt;span class=&#34;token operator&#34;&gt;+&lt;/span&gt; &lt;span class=&#34;token number&#34;&gt;10&lt;/span&gt; &lt;span class=&#34;token operator&#34;&gt;&amp;amp;&amp;amp;&lt;/span&gt; prevDelta &lt;span class=&#34;token operator&#34;&gt;&gt;&lt;/span&gt; currDelta &lt;span class=&#34;token operator&#34;&gt;+&lt;/span&gt; &lt;span class=&#34;token number&#34;&gt;10&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;token comment&#34;&gt;// previous increase happened a note ago, 1/1-&gt;1/2-1/4, dont want to buff this.&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;14&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    effectiveRatio &lt;span class=&#34;token operator&#34;&gt;*=&lt;/span&gt; &lt;span class=&#34;token number&#34;&gt;0.125&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;&lt;p&gt;通过简单的阅读，我们很容易发现组间削弱的逻辑，同时我们也逐渐意识到节奏分组的优势与可行性所在.&lt;/p&gt;
&lt;hr /&gt;
&lt;p&gt;至此，我们已经通过 AimEvaluator 和 SpeedEvaluator 探索了 osu! 算法的设计逻辑和具体实现。在探究的过程中，笔者希望读者在满足了好奇心的同时，对于音乐类游戏的算法体系有一些简单的理解和感悟。在行文过程中，我们也涉及到了诸如函数拟合、归一化等统计学知识，希望对于启发读者进行数学相关研究有帮助.&lt;/p&gt;
&lt;p&gt;2024-07-01 至 2024-09-19, 兔肉献上.&lt;/p&gt;
</content>
        <category term="osu!" />
        <updated>2024-07-01T01:29:59.000Z</updated>
    </entry>
    <entry>
        <id>https://turou.fun/rambling/rclone-duplicacy/</id>
        <title>Rclone搭配Duplicacy实现小文件高效备份</title>
        <link rel="alternate" href="https://turou.fun/rambling/rclone-duplicacy/"/>
        <content type="html">&lt;p&gt;Duplicacy 是一个开源的，基于无锁去重算法开发的云备份工具，他的核心思路类似 Git 等版本控制软件&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;多台电脑可以备份 / 同步到同一 (云) 储存 (类似远程 Repo 和本地 Repo 的关系)&lt;/li&gt;
&lt;li&gt;可以将工作区还原到任意一个 Revision (类似把 Origin 切换到任意一个 Commit)&lt;/li&gt;
&lt;li&gt;支持文件加密，压缩，打包，快照，同步云服务等功能&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Duplicacy 直接支持下面这些 (云) 存储方式&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Local disk&lt;/li&gt;
&lt;li&gt;SFTP&lt;/li&gt;
&lt;li&gt;Dropbox&lt;/li&gt;
&lt;li&gt;Amazon S3&lt;/li&gt;
&lt;li&gt;Wasabi&lt;/li&gt;
&lt;li&gt;DigitalOcean Spaces&lt;/li&gt;
&lt;li&gt;Google Cloud Storage&lt;/li&gt;
&lt;li&gt;Microsoft Azure&lt;/li&gt;
&lt;li&gt;Backblaze B2&lt;/li&gt;
&lt;li&gt;Google Drive&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Microsoft OneDrive (支持 Personal, Business, 不支持世纪互联)&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;Hubic&lt;/li&gt;
&lt;li&gt;OpenStack Swift&lt;/li&gt;
&lt;li&gt;WebDAV (under beta testing)&lt;/li&gt;
&lt;li&gt;pcloud (via WebDAV)&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;http://Box.com&#34;&gt;Box.com&lt;/a&gt; (via WebDAV)&lt;/li&gt;
&lt;li&gt;File Fabric by &lt;a href=&#34;https://storagemadeeasy.com/&#34;&gt;Storage Made Easy&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Rclone 是一个用于多个云平台之间同步文件和目录的命令行工具，他的定位于 Duplicacy 不同&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;支持将本地文件以 Sync, Move, Copy 等方式直接上传到云储存&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;支持利用 VFS 技术将云储存挂载为本地磁盘&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;支持 OneDrive 世纪互联&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;本文将利用 Rclone 的 VFS 虚拟磁盘，配合 Duplicacy, 实现小文件高效备份到 OneDrive 世纪互联的效果&lt;/p&gt;
&lt;h2 id=&#34;rclone&#34;&gt;&lt;a class=&#34;anchor&#34; href=&#34;#rclone&#34;&gt;#&lt;/a&gt; Rclone&lt;/h2&gt;
&lt;h3 id=&#34;下载&#34;&gt;&lt;a class=&#34;anchor&#34; href=&#34;#下载&#34;&gt;#&lt;/a&gt; 下载&lt;/h3&gt;
&lt;p&gt;首先我们要下载并配置 Rclone, 这里我们以 Windows 和 Linux 为例&lt;/p&gt;
&lt;h4 id=&#34;windows&#34;&gt;&lt;a class=&#34;anchor&#34; href=&#34;#windows&#34;&gt;#&lt;/a&gt; Windows&lt;/h4&gt;
&lt;p&gt;首先，我们要在 Github 下载二进制文件，读者需要选择合适的架构，这里我们选择 amd64&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Rclone: &lt;a href=&#34;https://github.com/rclone/rclone/releases&#34;&gt;https://github.com/rclone/rclone/releases&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;下载后将二进制文件改名为 rclone.exe, 丢入 C:\Windows\System32&lt;/p&gt;
&lt;p&gt;为了后文挂载虚拟磁盘，Windows 系统需要安装 WinFSP 来提供 VFS 的功能，下载后按照步骤安装就可以了&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;WinFSP: &lt;a href=&#34;https://winfsp.dev/rel/&#34;&gt;https://winfsp.dev/rel/&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;
&lt;p&gt;如果读者有安装过天翼云盘，那么就不必再次安装 WinFSP 了，安装天翼云盘的时候会自动帮你安装好 WinFSP&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h4 id=&#34;linux&#34;&gt;&lt;a class=&#34;anchor&#34; href=&#34;#linux&#34;&gt;#&lt;/a&gt; Linux&lt;/h4&gt;
&lt;figure class=&#34;highlight bash&#34;&gt;&lt;figcaption data-lang=&#34;bash&#34;&gt;&lt;/figcaption&gt;&lt;table&gt;&lt;tr&gt;&lt;td data-num=&#34;1&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token function&#34;&gt;curl&lt;/span&gt; https://rclone.org/install.sh &lt;span class=&#34;token operator&#34;&gt;|&lt;/span&gt; &lt;span class=&#34;token function&#34;&gt;sudo&lt;/span&gt; &lt;span class=&#34;token function&#34;&gt;bash&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;&lt;p&gt;为了后文挂载虚拟磁盘，Linux 需要安装 fuse, 请根据发行版选择正确的安装方式&lt;/p&gt;
&lt;figure class=&#34;highlight bash&#34;&gt;&lt;figcaption data-lang=&#34;bash&#34;&gt;&lt;/figcaption&gt;&lt;table&gt;&lt;tr&gt;&lt;td data-num=&#34;1&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token comment&#34;&gt;# Debian/Ubantu&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;2&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token function&#34;&gt;apt-get&lt;/span&gt; update &lt;span class=&#34;token operator&#34;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&#34;token function&#34;&gt;apt-get&lt;/span&gt; &lt;span class=&#34;token function&#34;&gt;install&lt;/span&gt; &lt;span class=&#34;token parameter variable&#34;&gt;-y&lt;/span&gt; fuse&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;3&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token comment&#34;&gt;# CentOS&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;4&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;yum &lt;span class=&#34;token function&#34;&gt;install&lt;/span&gt; &lt;span class=&#34;token parameter variable&#34;&gt;-y&lt;/span&gt; fuse&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;&lt;p&gt;在任意位置打开终端，输入 rclone, 应该已经能看到帮助信息了&lt;/p&gt;
&lt;h3 id=&#34;配置存储库&#34;&gt;&lt;a class=&#34;anchor&#34; href=&#34;#配置存储库&#34;&gt;#&lt;/a&gt; 配置存储库&lt;/h3&gt;
&lt;h4 id=&#34;创建azure应用&#34;&gt;&lt;a class=&#34;anchor&#34; href=&#34;#创建azure应用&#34;&gt;#&lt;/a&gt; 创建 Azure 应用&lt;/h4&gt;
&lt;p&gt;这里我们将使用 Rclone 在本地挂载远端 OneDrive, 然后使用 Duplicacy 在虚拟磁盘中创建存储库&lt;/p&gt;
&lt;p&gt;我们首先需要先配置 OneDrive 的相关鉴权行为，允许 Rclone 访问我们的 OneDrive.&lt;/p&gt;
&lt;p&gt;访问 &lt;a href=&#34;https://portal.azure.cn/&#34;&gt;https://portal.azure.cn/&lt;/a&gt; 登录完成后按下面步骤进行&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;访问 &lt;a href=&#34;https://portal.azure.cn/#view/Microsoft_AAD_RegisteredApps/ApplicationsListBlade&#34;&gt;https://portal.azure.cn/#view/Microsoft_AAD_RegisteredApps/ApplicationsListBlade&lt;/a&gt; -&amp;gt; 点击新注册&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;名称：任意名称 -&amp;gt; 受支持的账户类型: &lt;strong&gt;任何组织目录 (任何 Azure AD 目录 - 多租户) 中的帐户&lt;/strong&gt; -&amp;gt; 重定向 URI: &lt;a href=&#34;http://localhost:53682&#34;&gt;http://localhost:53682&lt;/a&gt; -&amp;gt; 注册&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;img loading=&#34;lazy&#34; data-src=&#34;https://s2.loli.net/2024/02/14/MWtvcQJGEB9IVy2.png&#34; alt=&#34;image.png&#34; /&gt;&lt;/p&gt;
&lt;ol start=&#34;3&#34;&gt;
&lt;li&gt;注册后请记住并保存这里的 应用程序 (客户端) ID, 这是我们一会要用到的 &lt;strong&gt;client_id&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;img loading=&#34;lazy&#34; data-src=&#34;https://s2.loli.net/2024/02/14/OL9Ua2nVlkYXWBJ.png&#34; alt=&#34;image.png&#34; /&gt;&lt;/p&gt;
&lt;ol start=&#34;4&#34;&gt;
&lt;li&gt;点击左侧的 API 权限 -&amp;gt; 点击蓝色的 Microsoft Graph (1) -&amp;gt; 在搜索框中搜索并允许下面这些权限&lt;/li&gt;
&lt;/ol&gt;
&lt;figure class=&#34;highlight bash&#34;&gt;&lt;figcaption data-lang=&#34;bash&#34;&gt;&lt;/figcaption&gt;&lt;table&gt;&lt;tr&gt;&lt;td data-num=&#34;1&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;Files.Read&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;2&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;Files.Read.All&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;3&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;Files.ReadWrite&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;4&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;Files.ReadWrite.All&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;5&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;offline_access&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;6&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;User.Read&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;&lt;p&gt;勾选后，应该是这样的效果&lt;/p&gt;
&lt;p&gt;&lt;img loading=&#34;lazy&#34; data-src=&#34;https://s2.loli.net/2024/02/14/sIvQu9prRC3iL4N.png&#34; alt=&#34;image.png&#34; /&gt;&lt;/p&gt;
&lt;ol start=&#34;5&#34;&gt;
&lt;li&gt;
&lt;p&gt;点击左侧的证书和密码 -&amp;gt; 新客户端密码 -&amp;gt; 任意输入说明 -&amp;gt; 截止期限选 24 个月 -&amp;gt; 点击添加&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;添加后请记住并保存这里的 &lt;strong&gt;值&lt;/strong&gt;，这是我们一会要用到的 &lt;strong&gt;client_secret&lt;/strong&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;img loading=&#34;lazy&#34; data-src=&#34;https://s2.loli.net/2024/02/14/1kZnCUpeGucFxRz.png&#34; alt=&#34;image.png&#34; /&gt;&lt;/p&gt;
&lt;h4 id=&#34;添加rclone远程存储库&#34;&gt;&lt;a class=&#34;anchor&#34; href=&#34;#添加rclone远程存储库&#34;&gt;#&lt;/a&gt; 添加 Rclone 远程存储库&lt;/h4&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;在任意位置打开终端，输入 &lt;code&gt;rclone config&lt;/code&gt; , 输入 n 新建一个远程存储库&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;name&amp;gt; 输入一个你喜欢的名称 (这个名称也会成为挂载后的磁盘名称)&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Storage&amp;gt; 根据提示，找到&lt;strong&gt; Microsoft OneDrive&lt;/strong&gt; 的序号并填入&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;client_id&amp;gt; 填入我们上文刚刚获取到的 client_id&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;client_secret&amp;gt; 填入我们上文刚刚获取到的 client_secret&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;region&amp;gt; 这里选择 &lt;strong&gt;Azure and Office 365 operated by Vnet Group in China&lt;/strong&gt; 的序号&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Edit advanced config? 这里输入 n, 不进行进阶的配置&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Use auto config? 这里输入 y, 自动进行 Token 获取&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;接下来，浏览器应该会弹出世纪互联账户的登录页面，按照要求进行登录就可以了&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Your choice&amp;gt; 这里输入 1, 使用常规的 OneDrive Personal or Business&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Chose drive to use:&amp;gt; 这里输入 0, 因为只能输入 0 (bushi&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Is that okay? 这里确认一下输入的信息无误后，输入 y&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;接下来 Rclone 会进行自动获取 Token 生成配置文件的过程，当获取完毕后，会再次让你确认是否无误，输入 y 确认&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;确认后应该已经能看到自己新配置的存储库了&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;img loading=&#34;lazy&#34; data-src=&#34;https://s2.loli.net/2024/02/14/RlwAicfVHvZYkWC.png&#34; alt=&#34;image.png&#34; /&gt;&lt;/p&gt;
&lt;h3 id=&#34;挂载虚拟磁盘&#34;&gt;&lt;a class=&#34;anchor&#34; href=&#34;#挂载虚拟磁盘&#34;&gt;#&lt;/a&gt; 挂载虚拟磁盘&lt;/h3&gt;
&lt;p&gt;在任意位置打开终端，输入 &lt;code&gt;rclone mount &amp;lt;存储库名称&amp;gt;:/ Z:&lt;/code&gt; , 应该就能在资源管理器中看到刚刚挂载的 Z 盘了&lt;/p&gt;
&lt;p&gt;当我们关闭终端或者 Ctrl+C 结束当前命令的时候，挂载的盘符消失&lt;/p&gt;
&lt;p&gt;关于 rclone mount 的缓存相关配置，可以阅读下面笔者的扩展阅读部分，这里仅给出笔者的配置作为参考&lt;/p&gt;
&lt;p&gt;如果要使用 Duplicacy, 请务必保证&lt;strong&gt;缓存目录的剩余空间大于要备份文件的总大小&lt;/strong&gt;&lt;/p&gt;
&lt;figure class=&#34;highlight bash&#34;&gt;&lt;figcaption data-lang=&#34;bash&#34;&gt;&lt;/figcaption&gt;&lt;table&gt;&lt;tr&gt;&lt;td data-num=&#34;1&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;rclone &lt;span class=&#34;token function&#34;&gt;mount&lt;/span&gt; OneDrive:/ Z: --cache-dir &lt;span class=&#34;token operator&#34;&gt;&amp;lt;&lt;/span&gt;缓存目录&lt;span class=&#34;token operator&#34;&gt;&gt;&lt;/span&gt; --vfs-cache-mode writes --vfs-cache-max-size 30G --vfs-cache-max-age 1h&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;&lt;p&gt;接下来我们要配置后台执行和开机启动，这里我们以 Windows 和 Linux 系统为例&lt;/p&gt;
&lt;h4 id=&#34;windows-2&#34;&gt;&lt;a class=&#34;anchor&#34; href=&#34;#windows-2&#34;&gt;#&lt;/a&gt; Windows&lt;/h4&gt;
&lt;p&gt;这里我们使用 WinSW 来配置 Windows 自动启动服务，在 Github 下载 WinSW&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;WinSW: &lt;a href=&#34;https://github.com/winsw/winsw/releases&#34;&gt;https://github.com/winsw/winsw/releases&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;下载后将二进制文件改名为 winsw.exe, 放到 rclone 的配置文件所在目录 (% AppData%\rclone)&lt;/p&gt;
&lt;p&gt;在相同的目录创建 rclone-mount.xml, 填写以下内容&lt;/p&gt;
&lt;p&gt;需要注意，这里 rclone 的配置文件需要手动指出，这里笔者已经填入了默认配置文件的位置，--config 参数不可删除&lt;/p&gt;
&lt;figure class=&#34;highlight xml&#34;&gt;&lt;figcaption data-lang=&#34;XML&#34;&gt;&lt;/figcaption&gt;&lt;table&gt;&lt;tr&gt;&lt;td data-num=&#34;1&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token tag&#34;&gt;&lt;span class=&#34;token tag&#34;&gt;&lt;span class=&#34;token punctuation&#34;&gt;&amp;lt;&lt;/span&gt;service&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;2&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;span class=&#34;token tag&#34;&gt;&lt;span class=&#34;token tag&#34;&gt;&lt;span class=&#34;token punctuation&#34;&gt;&amp;lt;&lt;/span&gt;id&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;&gt;&lt;/span&gt;&lt;/span&gt;rclone&lt;span class=&#34;token tag&#34;&gt;&lt;span class=&#34;token tag&#34;&gt;&lt;span class=&#34;token punctuation&#34;&gt;&amp;lt;/&lt;/span&gt;id&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;3&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;span class=&#34;token tag&#34;&gt;&lt;span class=&#34;token tag&#34;&gt;&lt;span class=&#34;token punctuation&#34;&gt;&amp;lt;&lt;/span&gt;name&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;&gt;&lt;/span&gt;&lt;/span&gt;rclone&lt;span class=&#34;token tag&#34;&gt;&lt;span class=&#34;token tag&#34;&gt;&lt;span class=&#34;token punctuation&#34;&gt;&amp;lt;/&lt;/span&gt;name&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;4&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;span class=&#34;token tag&#34;&gt;&lt;span class=&#34;token tag&#34;&gt;&lt;span class=&#34;token punctuation&#34;&gt;&amp;lt;&lt;/span&gt;description&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;&gt;&lt;/span&gt;&lt;/span&gt;This service run rclone mount network file systems to local disk&lt;span class=&#34;token tag&#34;&gt;&lt;span class=&#34;token tag&#34;&gt;&lt;span class=&#34;token punctuation&#34;&gt;&amp;lt;/&lt;/span&gt;description&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;5&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;span class=&#34;token tag&#34;&gt;&lt;span class=&#34;token tag&#34;&gt;&lt;span class=&#34;token punctuation&#34;&gt;&amp;lt;&lt;/span&gt;executable&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;&gt;&lt;/span&gt;&lt;/span&gt;rclone&lt;span class=&#34;token tag&#34;&gt;&lt;span class=&#34;token tag&#34;&gt;&lt;span class=&#34;token punctuation&#34;&gt;&amp;lt;/&lt;/span&gt;executable&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;6&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;span class=&#34;token tag&#34;&gt;&lt;span class=&#34;token tag&#34;&gt;&lt;span class=&#34;token punctuation&#34;&gt;&amp;lt;&lt;/span&gt;arguments&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;&gt;&lt;/span&gt;&lt;/span&gt;mount OneDrive:/ Z: --config %BASE%\rclone.conf --vfs-cache-mode writes --vfs-cache-max-size 30G --vfs-cache-max-age 1h&lt;span class=&#34;token tag&#34;&gt;&lt;span class=&#34;token tag&#34;&gt;&lt;span class=&#34;token punctuation&#34;&gt;&amp;lt;/&lt;/span&gt;arguments&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;7&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;span class=&#34;token tag&#34;&gt;&lt;span class=&#34;token tag&#34;&gt;&lt;span class=&#34;token punctuation&#34;&gt;&amp;lt;&lt;/span&gt;log&lt;/span&gt; &lt;span class=&#34;token attr-name&#34;&gt;mode&lt;/span&gt;&lt;span class=&#34;token attr-value&#34;&gt;&lt;span class=&#34;token punctuation attr-equals&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;&#34;&lt;/span&gt;roll&lt;span class=&#34;token punctuation&#34;&gt;&#34;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;/&gt;&lt;/span&gt;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;8&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token tag&#34;&gt;&lt;span class=&#34;token tag&#34;&gt;&lt;span class=&#34;token punctuation&#34;&gt;&amp;lt;/&lt;/span&gt;service&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;&lt;p&gt;使用终端打开当前目录，输入 &lt;code&gt;winsw install rclone-mount.xml&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;现在，这个目录的结构大概应该是这样的，同时，如果读者打开资源管理器，应该已经看到挂载的盘符了&lt;/p&gt;
&lt;p&gt;&lt;img loading=&#34;lazy&#34; data-src=&#34;https://s2.loli.net/2024/02/14/WlarEdiZFM15gYx.png&#34; alt=&#34;image.png&#34; /&gt;&lt;/p&gt;
&lt;h4 id=&#34;linux-2&#34;&gt;&lt;a class=&#34;anchor&#34; href=&#34;#linux-2&#34;&gt;#&lt;/a&gt; Linux&lt;/h4&gt;
&lt;p&gt;Linux 推荐使用 systemctl 配置开机自启服务，这里我们创建 rclone-mount 服务&lt;/p&gt;
&lt;figure class=&#34;highlight bash&#34;&gt;&lt;figcaption data-lang=&#34;bash&#34;&gt;&lt;/figcaption&gt;&lt;table&gt;&lt;tr&gt;&lt;td data-num=&#34;1&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token function&#34;&gt;vi&lt;/span&gt; /usr/lib/systemd/system/rclone-mount.service&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;&lt;p&gt;填入并按需修改以下内容&lt;/p&gt;
&lt;figure class=&#34;highlight bash&#34;&gt;&lt;figcaption data-lang=&#34;bash&#34;&gt;&lt;/figcaption&gt;&lt;table&gt;&lt;tr&gt;&lt;td data-num=&#34;1&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token punctuation&#34;&gt;[&lt;/span&gt;Unit&lt;span class=&#34;token punctuation&#34;&gt;]&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;2&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token assign-left variable&#34;&gt;Description&lt;/span&gt;&lt;span class=&#34;token operator&#34;&gt;=&lt;/span&gt;This &lt;span class=&#34;token function&#34;&gt;service&lt;/span&gt; run rclone &lt;span class=&#34;token function&#34;&gt;mount&lt;/span&gt; network &lt;span class=&#34;token function&#34;&gt;file&lt;/span&gt; systems to &lt;span class=&#34;token builtin class-name&#34;&gt;local&lt;/span&gt; disk&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;3&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token assign-left variable&#34;&gt;After&lt;/span&gt;&lt;span class=&#34;token operator&#34;&gt;=&lt;/span&gt;network.target&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;4&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;5&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token punctuation&#34;&gt;[&lt;/span&gt;Service&lt;span class=&#34;token punctuation&#34;&gt;]&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;6&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token assign-left variable&#34;&gt;Type&lt;/span&gt;&lt;span class=&#34;token operator&#34;&gt;=&lt;/span&gt;simple&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;7&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token assign-left variable&#34;&gt;User&lt;/span&gt;&lt;span class=&#34;token operator&#34;&gt;=&lt;/span&gt;root&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;8&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token assign-left variable&#34;&gt;ExecStartPre&lt;/span&gt;&lt;span class=&#34;token operator&#34;&gt;=&lt;/span&gt;/bin/sh &lt;span class=&#34;token parameter variable&#34;&gt;-c&lt;/span&gt; &lt;span class=&#34;token string&#34;&gt;&#39;until ping -c1 baidu.com; do sleep 1; done;&#39;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;9&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token assign-left variable&#34;&gt;ExecStart&lt;/span&gt;&lt;span class=&#34;token operator&#34;&gt;=&lt;/span&gt;rclone &lt;span class=&#34;token function&#34;&gt;mount&lt;/span&gt; OneDrive:/ /mnt/OneDrive --allow-other --allow-non-empty --vfs-cache-mode writes --vfs-cache-max-size 30G --vfs-cache-max-age 1h&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;10&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token assign-left variable&#34;&gt;ExecStop&lt;/span&gt;&lt;span class=&#34;token operator&#34;&gt;=&lt;/span&gt;fusermount &lt;span class=&#34;token parameter variable&#34;&gt;-u&lt;/span&gt; /mnt/OneDrive&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;11&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;12&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token punctuation&#34;&gt;[&lt;/span&gt;Install&lt;span class=&#34;token punctuation&#34;&gt;]&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;13&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token assign-left variable&#34;&gt;WantedBy&lt;/span&gt;&lt;span class=&#34;token operator&#34;&gt;=&lt;/span&gt;multi-user.target&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;&lt;p&gt;之后，我们配置服务的开机自启:  &lt;code&gt;systemctl enable rclone-mount.service&lt;/code&gt;&lt;/p&gt;
&lt;h2 id=&#34;duplicacy&#34;&gt;&lt;a class=&#34;anchor&#34; href=&#34;#duplicacy&#34;&gt;#&lt;/a&gt; Duplicacy&lt;/h2&gt;
&lt;h3 id=&#34;下载-2&#34;&gt;&lt;a class=&#34;anchor&#34; href=&#34;#下载-2&#34;&gt;#&lt;/a&gt; 下载&lt;/h3&gt;
&lt;p&gt;接下来我们下载 Duplicacy, 这里我们依旧以 Windows 和 Linux 为例&lt;/p&gt;
&lt;h4 id=&#34;windows-3&#34;&gt;&lt;a class=&#34;anchor&#34; href=&#34;#windows-3&#34;&gt;#&lt;/a&gt; Windows&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;Duplicacy: &lt;a href=&#34;https://github.com/gilbertchen/duplicacy/releases&#34;&gt;https://github.com/gilbertchen/duplicacy/releases&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;下载后将二进制文件改名为 duplicacy.exe, 丢入 C:\Windows\System32&lt;/p&gt;
&lt;h4 id=&#34;linux-3&#34;&gt;&lt;a class=&#34;anchor&#34; href=&#34;#linux-3&#34;&gt;#&lt;/a&gt; Linux&lt;/h4&gt;
&lt;figure class=&#34;highlight bash&#34;&gt;&lt;figcaption data-lang=&#34;bash&#34;&gt;&lt;/figcaption&gt;&lt;table&gt;&lt;tr&gt;&lt;td data-num=&#34;1&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token function&#34;&gt;wget&lt;/span&gt; &lt;span class=&#34;token parameter variable&#34;&gt;-O&lt;/span&gt; /usr/local/bin/duplicacy https://github.com/gilbertchen/duplicacy/releases/download/v3.2.3/duplicacy_linux_x64_3.2.3&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;2&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token function&#34;&gt;chmod&lt;/span&gt; 0755 /usr/local/bin/duplicacy&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;&lt;p&gt;在任意位置打开终端，输入 duplicacy, 应该已经能看到帮助信息了&lt;/p&gt;
&lt;h3 id=&#34;配置存储库-2&#34;&gt;&lt;a class=&#34;anchor&#34; href=&#34;#配置存储库-2&#34;&gt;#&lt;/a&gt; 配置存储库&lt;/h3&gt;
&lt;p&gt;Duplicacy 把位于计算机本地的待备份目录称为 Repository, 这里笔者在 E 盘创建了一个名为 Vault 的文件夹，我们接下来将对这个文件夹进行备份&lt;/p&gt;
&lt;p&gt;首先，我们需要先切换到 Vault 文件夹中，并且创建 Duplicacy 的仓库，这里 vault 为仓库的标识名，后面的路径为远程库的路径&lt;/p&gt;
&lt;p&gt;这里我们使用上文创建好的 Rclone 虚拟磁盘作为远程库.&lt;/p&gt;
&lt;figure class=&#34;highlight bash&#34;&gt;&lt;figcaption data-lang=&#34;bash&#34;&gt;&lt;/figcaption&gt;&lt;table&gt;&lt;tr&gt;&lt;td data-num=&#34;1&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token builtin class-name&#34;&gt;cd&lt;/span&gt; E:&lt;span class=&#34;token punctuation&#34;&gt;\&lt;/span&gt;Vault&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;2&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;duplicacy.exe init &lt;span class=&#34;token parameter variable&#34;&gt;-c&lt;/span&gt; 128M &lt;span class=&#34;token parameter variable&#34;&gt;-min&lt;/span&gt; 32M &lt;span class=&#34;token parameter variable&#34;&gt;-max&lt;/span&gt; 1024M vault Z:&lt;span class=&#34;token punctuation&#34;&gt;\&lt;/span&gt;Vault&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;&lt;p&gt;Duplicacy 使用 Hash 块来储存打包后的文件，我们通过 - c -min -max 参数来指定单个块的大小.&lt;/p&gt;
&lt;p&gt;块过小会导致文件零散，上传时性能比较低，块过大会导致微小的更改就需要重建整个块，这里请读者根据需要进行修改.&lt;/p&gt;
&lt;p&gt;如果读者的数据不常修改，推荐使用稍大一些的块&lt;/p&gt;
&lt;h3 id=&#34;进行备份&#34;&gt;&lt;a class=&#34;anchor&#34; href=&#34;#进行备份&#34;&gt;#&lt;/a&gt; 进行备份&lt;/h3&gt;
&lt;p&gt;备份的线程越多，占用的 CPU 和内存越多，备份速度越快.&lt;/p&gt;
&lt;p&gt;这里 32 线程已经能吃到 16G 以上的内存，请读者酌情配置.&lt;/p&gt;
&lt;figure class=&#34;highlight bash&#34;&gt;&lt;figcaption data-lang=&#34;bash&#34;&gt;&lt;/figcaption&gt;&lt;table&gt;&lt;tr&gt;&lt;td data-num=&#34;1&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token builtin class-name&#34;&gt;cd&lt;/span&gt; E:&lt;span class=&#34;token punctuation&#34;&gt;\&lt;/span&gt;Vault&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;2&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;duplicacy.exe backup &lt;span class=&#34;token parameter variable&#34;&gt;-threads&lt;/span&gt; &lt;span class=&#34;token number&#34;&gt;32&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;&lt;h3 id=&#34;更多操作&#34;&gt;&lt;a class=&#34;anchor&#34; href=&#34;#更多操作&#34;&gt;#&lt;/a&gt; 更多操作&lt;/h3&gt;
&lt;p&gt;这里笔者就不过多介绍 Duplicacy 了，如果读者希望进一步了解使用，欢迎阅读其他博主的文章.&lt;br /&gt;
Duplicacy CLI 备份工具的基本使用: &lt;a href=&#34;https://blog.dejavu.moe/posts/duplicacy-cli-basic-guide/&#34;&gt;https://blog.dejavu.moe/posts/duplicacy-cli-basic-guide/&lt;/a&gt;&lt;/p&gt;
&lt;h2 id=&#34;扩展阅读&#34;&gt;&lt;a class=&#34;anchor&#34; href=&#34;#扩展阅读&#34;&gt;#&lt;/a&gt; 扩展阅读&lt;/h2&gt;
&lt;h3 id=&#34;rclone-传输优化&#34;&gt;&lt;a class=&#34;anchor&#34; href=&#34;#rclone-传输优化&#34;&gt;#&lt;/a&gt; Rclone 传输优化&lt;/h3&gt;
&lt;p&gt;当传输大文件时，为了让 Rclone 的传输速率达到最大，这里我们可以配置下面几个参数&lt;/p&gt;
&lt;p&gt;要注意的是，过度拉高这些参数可能会导致高内存占用，不建议在 VPS 等性能较弱的机器上调整&lt;/p&gt;
&lt;h4 id=&#34;-cache-chunk-size&#34;&gt;&lt;a class=&#34;anchor&#34; href=&#34;#-cache-chunk-size&#34;&gt;#&lt;/a&gt; --cache-chunk-size&lt;/h4&gt;
&lt;p&gt;The size of a chunk (partial file data).&lt;/p&gt;
&lt;p&gt;Use lower numbers for slower connections. If the chunk size is changed, any downloaded chunks will be invalid and cache-chunk-path will need to be cleared or unexpected EOF errors will occur.&lt;/p&gt;
&lt;h4 id=&#34;-transfers&#34;&gt;&lt;a class=&#34;anchor&#34; href=&#34;#-transfers&#34;&gt;#&lt;/a&gt; --transfers&lt;/h4&gt;
&lt;p&gt;文件并行数量，不建议设置太多，即使多线程，也受 OneDrive 的速度限制，这里建议测试一下，寻找能达到最大速度的最少线程数&lt;/p&gt;
&lt;h4 id=&#34;-onedrive-chunk-size&#34;&gt;&lt;a class=&#34;anchor&#34; href=&#34;#-onedrive-chunk-size&#34;&gt;#&lt;/a&gt; --onedrive-chunk-size&lt;/h4&gt;
&lt;p&gt;Onedrive 的 chunk-size 设置 必须是 320KB 的倍数，取 100MB 适合 G 口服务器，如果我们的接口速率达不到那么大，没必要设置的太大&lt;/p&gt;
&lt;p&gt;这里笔者仅给出自己的配置作为参考&lt;/p&gt;
&lt;figure class=&#34;highlight bash&#34;&gt;&lt;figcaption data-lang=&#34;bash&#34;&gt;&lt;/figcaption&gt;&lt;table&gt;&lt;tr&gt;&lt;td data-num=&#34;1&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;rclone &lt;span class=&#34;token function&#34;&gt;sync&lt;/span&gt; Vault OneDrive:Vault &lt;span class=&#34;token parameter variable&#34;&gt;-P&lt;/span&gt; --cache-chunk-size 4M &lt;span class=&#34;token parameter variable&#34;&gt;--transfers&lt;/span&gt;&lt;span class=&#34;token operator&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;token number&#34;&gt;16&lt;/span&gt; --onedrive-chunk-size &lt;span class=&#34;token number&#34;&gt;10240&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;&lt;h3 id=&#34;rclone-vfs优化&#34;&gt;&lt;a class=&#34;anchor&#34; href=&#34;#rclone-vfs优化&#34;&gt;#&lt;/a&gt; Rclone VFS 优化&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;rclone mount&lt;/code&gt;  关于缓存方式有下面几个参数，我们这里直接对重要的几个参数进行讲解&lt;/p&gt;
&lt;h4 id=&#34;-vfs-cache-mode&#34;&gt;&lt;a class=&#34;anchor&#34; href=&#34;#-vfs-cache-mode&#34;&gt;#&lt;/a&gt; --vfs-cache-mode&lt;/h4&gt;
&lt;p&gt;vfs-cache-mode: 虚拟磁盘的缓存方式，主要有下面几种选择&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;--vfs-cache-mode off: 所有读取和写入都直接操作远程&lt;/li&gt;
&lt;li&gt;--vfs-cache-mode minimal: 只读和只写打开的文件直接从远程读取，读 / 写的文件首先缓冲到磁盘.&lt;/li&gt;
&lt;li&gt;--vfs-cache-mode writes: 只读打开的文件直接从远程读取，只写和读 / 写的文件首先缓冲到磁盘.&lt;/li&gt;
&lt;li&gt;--vfs-cache-mode full: 所有读取和写入都缓冲到磁盘&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;off &amp;lt; minimal &amp;lt; writes &amp;lt; full, 需要注意的是，OneDrive 只支持大于等于 writes 的模式&lt;/p&gt;
&lt;p&gt;这里更为详细的介绍推荐阅读官网的相关文档: &lt;a href=&#34;https://rclone.org/commands/rclone_mount/&#34;&gt;https://rclone.org/commands/rclone_mount/&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;这里笔者只针对几个情形进行针对性的讲解和分析:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;writes 模式：适合磁盘空余空间较大的读者，适合除了 Duplicacy 备份，还想平常使用 OneDrive 的读者。当使用 writes 模式向 OneDrive 上传文件时，这些文件会首先储存在本地 VFS 的缓存目录中慢慢上传，等文件全部上传到云端后，再释放掉缓存。这会导致如果用户第一次使用 Duplicacy 备份 100G 的文件时，缓存目录会先占满约 100G 的空间，再慢慢释放.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;off 模式：适合磁盘没有空余空间的读者，或者要备份的文件占用空间过大的读者。但可惜 OneDrive 不支持流式传输，所以读者在使用 OneDrive 时不能选择这个模式&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id=&#34;-vfs-cache-max-size&#34;&gt;&lt;a class=&#34;anchor&#34; href=&#34;#-vfs-cache-max-size&#34;&gt;#&lt;/a&gt; --vfs-cache-max-size&lt;/h4&gt;
&lt;p&gt;当启用缓存时，文件缓存的最大占用空间。这个最大空间限制&lt;strong&gt;只对从远端读取时生效&lt;/strong&gt;，例如，如果你向网盘中复制 100G 的文件，缓存目录会先占用约 100G 的空间，然后再慢慢释放&lt;/p&gt;
&lt;h4 id=&#34;-vfs-cache-max-age&#34;&gt;&lt;a class=&#34;anchor&#34; href=&#34;#-vfs-cache-max-age&#34;&gt;#&lt;/a&gt; --vfs-cache-max-age&lt;/h4&gt;
&lt;p&gt;当启用缓存时，文件缓存的最大存活时间。这个最大时间限制&lt;strong&gt;只对从远端读取时生效&lt;/strong&gt;，例如，如果你观看网盘中的电影，Rclone 会从网盘中边下边播这部电影，在超过存活时间后，再次观看，又要从头开始重新下载&lt;/p&gt;
&lt;h4 id=&#34;-buffer-size&#34;&gt;&lt;a class=&#34;anchor&#34; href=&#34;#-buffer-size&#34;&gt;#&lt;/a&gt; --buffer-size&lt;/h4&gt;
&lt;p&gt;设置内存缓存大小，这里如果日常使用建议 100M 左右就够了，如果只作为 Duplicacy 备份使用，那建议不配置&lt;/p&gt;
&lt;h4 id=&#34;-cache-dir&#34;&gt;&lt;a class=&#34;anchor&#34; href=&#34;#-cache-dir&#34;&gt;#&lt;/a&gt; --cache-dir&lt;/h4&gt;
&lt;p&gt;文件缓存的目录，如果不设置，默认目录为 C:\Users\chenb\AppData\Local\rclone\&lt;/p&gt;
&lt;p&gt;如果要使用 Duplicacy, 请务必保证&lt;strong&gt;缓存目录的剩余空间大于要备份文件的总大小&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;这里笔者仅给出自己的配置作为参考，注意 --vfs-cache-mode 参数在使用 OneDrive 时必须大于等于 writes&lt;/p&gt;
&lt;figure class=&#34;highlight bash&#34;&gt;&lt;figcaption data-lang=&#34;bash&#34;&gt;&lt;/figcaption&gt;&lt;table&gt;&lt;tr&gt;&lt;td data-num=&#34;1&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;rclone &lt;span class=&#34;token function&#34;&gt;mount&lt;/span&gt; OneDrive:/ Z: --vfs-cache-mode writes --vfs-cache-max-size 30G --vfs-cache-max-age 1h&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;&lt;h3 id=&#34;duplicacy世纪互联&#34;&gt;&lt;a class=&#34;anchor&#34; href=&#34;#duplicacy世纪互联&#34;&gt;#&lt;/a&gt; Duplicacy 世纪互联&lt;/h3&gt;
&lt;p&gt;Duplicacy 论坛有这样一个帖子: &lt;a href=&#34;https://forum.duplicacy.com/t/add-support-for-onedrive-business-china/3869&#34;&gt;https://forum.duplicacy.com/t/add-support-for-onedrive-business-china/3869&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;这个博主只是修改了登录的 Entrypoint, 没有修改 client_id, 因为 Duplicacy 在世纪互联没有注册相应的应用，必然不能用&lt;/p&gt;
&lt;p&gt;我们按照上文的方法创建自己的 client_id 并授予响应的权限、打开响应的回调 URL 后，再次尝试。这次我们成功从微软相关网站跳转到了 Duplicacy 的 odb_start 页面，但是 odb_start 无法获取到相应的 token 和 refresh_token. 因为 OAuth 回访获得 token 也需要传入对应的 client_id, client_secret, 但是 odb_start 中没有实现用户侧填写这些信息.&lt;/p&gt;
&lt;p&gt;再次整理后，我们发现，可以手动获取 token, refresh_token 并且填入 json 中，将 json 提供给 Duplicacy. 但由于 refresh_token 的过程仍需响应的 client_id, client_secret. 这意味着我们需要使用外部的一个程序负责给 Duplicacy 获得的 token 进行续约操作。这种操作比较复杂.&lt;/p&gt;
&lt;p&gt;希望 Duplicacy 早日提供支持世纪互联的接口，或者将 client_id, client_secret 侧做的更灵活.&lt;/p&gt;
</content>
        <category term="杂谈" />
        <updated>2024-02-14T03:00:04.000Z</updated>
    </entry>
    <entry>
        <id>https://turou.fun/rambling/gal-indust-development/</id>
        <title>浅谈国内外恋爱冒险游戏产业的发展</title>
        <link rel="alternate" href="https://turou.fun/rambling/gal-indust-development/"/>
        <content type="html">&lt;h2 id=&#34;前言&#34;&gt;&lt;a class=&#34;anchor&#34; href=&#34;#前言&#34;&gt;#&lt;/a&gt; 前言&lt;/h2&gt;
&lt;p&gt;在快节奏生活的社会背景下，游戏产业发展日新月异。大逃杀，roguelike 等新兴游戏类型在技术迭代和市场表现上拔得头筹，占领了巨大的玩家社区和游戏市场。游戏引擎、游戏厂商不断在玩家和市场的推动下翻滚，构成了现在暗流翻涌的主流游戏舞台。&lt;br /&gt;
在当今游戏产业的浪潮中，恋爱冒险类型的游戏却难以称为主流，难以激起巨大的浪花。恋爱冒险游戏的历史相当悠久，它由角色扮演的游戏类型衍生而来，由于早期计算机和游戏主机的性能限制，文字是游戏的主要表达载体。随着历史的发展，技术的不断迭代，角色扮演游戏的游戏性和画面感不断提升，因此衍生出了多重表现形式，恋爱冒险游戏即为其表现形式之一。&lt;/p&gt;
&lt;p&gt;当今，文字冒险游戏是以精彩的剧情为卖点的游戏，以文字叙述为主，并以 CG 或动画为辅演出剧情，并设有分支和多个结局。是冒险游戏的一个分支，简称 ADV。&lt;/p&gt;
&lt;p&gt;下面，笔者将从产业现状、市场分析、未来发展三个方面谈谈自己对国内外恋爱冒险游戏产业发展的理解。&lt;/p&gt;
&lt;h2 id=&#34;产业现状&#34;&gt;&lt;a class=&#34;anchor&#34; href=&#34;#产业现状&#34;&gt;#&lt;/a&gt; 产业现状&lt;/h2&gt;
&lt;p&gt;目前，恋爱冒险游戏的产业主阵地在日本，但其市场却遍布世界各地，我们&lt;br /&gt;
将主要以中国、日本、欧美的恋爱冒险产业发展为例，探究其行业现状。&lt;/p&gt;
&lt;p&gt;国内 ADV 游戏厂商大多为小型或个人开发者，比较知名的开发商有曾制作三色绘恋，高考恋爱 100 天的 SakuraGame，代理商有目前极其活跃的 HikariField。也有早年在女性向市场更为活跃的橙光游戏。日本比较知名的成人 ADV 游戏会社有制作魔女的夜宴，千恋万花，天使纷扰的 YuzuSoft，制作全年龄恋爱冒险游戏的 Key 会社 (其母公司 VISUAL ARTS 现以被腾讯全价收购) 等。欧美 ADV 游戏的知名作主要有 DDLC 等，也有部分制作 3D 作品的社团，组织方式与以厂商为单位中国和日本不同。&lt;/p&gt;
&lt;p&gt;综合分析，国内外恋爱冒险游戏厂商的显著特征就是规模较小，很少有大型公司涉猎，实际上，这与产品本身的规模有关，恋爱冒险游戏本质上类似于文学作品或艺术作品，其创作资本小，用户群体也较为固定，会社往往不需要极大的成本投入便可制作内容丰富的游戏，这既是恋爱冒险游戏品类得天独厚的优势，同时也是会社难以做大做强的一个阻碍之一。&lt;/p&gt;
&lt;h2 id=&#34;市场分析&#34;&gt;&lt;a class=&#34;anchor&#34; href=&#34;#市场分析&#34;&gt;#&lt;/a&gt; 市场分析&lt;/h2&gt;
&lt;p&gt;这里我们主要探讨社会背景、玩家群体对恋爱冒险游戏市场的影响。上个世纪，游戏主要以光盘和卡带等介质进行传播，游戏大多在线下直接售卖，恋爱冒险游戏品类也不例外，盗版的现象虽有，但仍不成气候。2000 年后，互联网技术在世界范围内飞速发展，互联网初期阶段，DRM 等领域还没有健全，盗版传播的现象屡禁不止，许多盗版传播平台，例如俄罗斯的海盗湾，我国的 3DM 等平台均成为了盗版游戏的分发者。许多恋爱冒险游戏开发厂商因此受到了第一次重创。&lt;/p&gt;
&lt;p&gt;2000 年初，我国颁布游戏机禁令，中国游戏玩家进入了长达 13 年的海外游戏空窗期。这十三年间，中国游戏玩家无法接触到来自海外的正版游戏光盘和卡带，盗版的传播便成为海外游戏进入寻常百姓家的唯一方式，这也形成国内游戏玩家默许盗版的一种 “默契”，另外，来自海外的恋爱冒险游戏往往需要翻译成中文呈现给玩家，汉化组为了修改字库和游戏脚本，往往需要先对游戏进行破解再进行分发，这也是盗版盛行的一大原因&lt;/p&gt;
&lt;p&gt;如今，DRM 已经被大家接受，购买正版的风气盛行，Steam 等平台知名度逐渐攀升，国内玩家已经逐渐成为支持正版的主力军，许多厂商已经积极发展中国市场，与国内代理商合作发行正版中文 ADV 游戏。日本游戏厂商 YuzuSoft 多次与 HikariField (由中国员工组成的日本企业) 合作，为旗下多款知名 ADV 游戏制作官方中文版，许多热爱原作的玩家纷纷 “补票”，国内外市场的社会背景正在向积极的方向发展。&lt;br /&gt;
玩家群体也在很大程度上影响游戏市场，在这方面，恋爱冒险游戏并不占据优势。一方面，ADV 游戏没有激烈的战斗和刺激的感官体验，需要玩家用心体会创作者的创作意图，试图与创作者产生共鸣，这已经将一部分玩家拒之门外。另一方面，ADV 游戏主要以文字驱动，总游戏时长在十几小时到几十小时不等，往往具有深邃的世界观，在当今快节奏的生活中，又有多少人愿意静下心来感受文字中流动的情感迸发呢。多种原因共同作用，使 ADV 游戏的玩家群体很难壮大，甚至不断流失，对市场的可持续发展非常不利。&lt;/p&gt;
&lt;h2 id=&#34;未来发展&#34;&gt;&lt;a class=&#34;anchor&#34; href=&#34;#未来发展&#34;&gt;#&lt;/a&gt; 未来发展&lt;/h2&gt;
&lt;p&gt;恋爱冒险游戏作为一个历史悠久的游戏类型，其自身的张力和潜力仍然等待我们发掘，许多厂商已经采取各种各样的行动来 “振兴” 业界。2020 年 6 月 19 日，ANIPLEX.EXE 携手 Frontwing 和枕社共同制作的 ADV 游戏《ATRI – My Dear Moments》发行，这部作品取得了巨大的成功，个人认为，这部作品的开发模式可能昭示着未来恋爱冒险游戏产业的发展。首先，ANIPLEX.EXE 是日本最大的动漫企划、版权方 Aniplex（其背后的母公司是索尼音乐）专攻视觉小说制作的子品牌，属于恋爱冒险游戏中 “大厂” 身份的一个代表。而 Frontwing 和枕社在世界观和剧情深度塑造上均居于业内领先。Frontwing 的《ISLAND》在 2016 年批评空间上评分第一，枕社的《樱之诗》在 2015 年的批评空间、getchu、2ch、萌赏中均获得第一名的成绩，其在哲学方面的深度至今被玩家津津乐道，哲学深度在目前恋爱冒险品类游戏中尚未被超越。这两个厂商没有很大的经济体量，但在 ADV 游戏创作方面有巨大能力。&lt;/p&gt;
&lt;p&gt;很明显，这里我们看到了大厂带动小厂的未来，实际上 ANIPLEX.EXE 是本次游戏制作的主要出资方，有了充足的资金，ATRI 在游戏引擎、声优、画面表现等方面均无可挑剔，再配合前翼和枕社精良的剧本，该作品的成功只是水到渠成。&lt;/p&gt;
&lt;p&gt;另一个方面，玩家群体方面也要有精深的考虑。ATRI 实际上篇幅不大，仅需十几小时便可明晰作品的世界观和表达意图，感受到制作者想要传达给我们的东西，缩短篇幅实际上也是游戏会社在面对新时代的快节奏社会做出的考量之一。&lt;/p&gt;
&lt;p&gt;其次，游戏在全球知名的数字游戏平台 Steam 发售，并且是中日英三语共同发售，这也为游戏国际化打下了坚实而牢固的基础。实际上，在 2023 年 10 月 26 日，前翼社推出新作《GINKA》，发售路线和题材方向延续上一作 ATRI，许多中国玩家（包括笔者）在游戏发售的第一时间便购买了正版。无论是 Aniplex 还是前翼社，他们对国际化的重视程度有目共睹、有口皆碑。&lt;/p&gt;
&lt;p&gt;笔者认为，恋爱冒险游戏的未来想要健康发展，以下几点必不可少。小厂与大厂达成合作，获得坚实的经济基础。调整作品篇幅，迎合玩家的节奏。积极融入市场，考虑全球玩家的感受。&lt;/p&gt;
&lt;p&gt;21 世纪，次时代游戏厂商必须紧握玩家和市场大旗，才能有机会杀出重围，探视近些年口碑内容和商业成绩双收的作品少之又少，希望恋爱冒险游戏厂商及时调转跑道，敢为人先，开创更大的市场。&lt;/p&gt;
&lt;p&gt;&lt;img loading=&#34;lazy&#34; data-src=&#34;https://s2.loli.net/2024/01/26/oxiQPIpVeEm9JGb.webp&#34; alt=&#34;2d0fe2f266b5aab93b495949445afa9f543770921.png@1256w_708h_!web-article-pic.webp&#34; /&gt;&lt;/p&gt;
</content>
        <category term="Galgame" />
        <category term="杂谈" />
        <updated>2024-01-26T03:19:59.000Z</updated>
    </entry>
    <entry>
        <id>https://turou.fun/minecraft/render-tutor/</id>
        <title>深入浅出高版本渲染系统  —— GuiComponent的消逝与GuiGraphics的新生</title>
        <link rel="alternate" href="https://turou.fun/minecraft/render-tutor/"/>
        <content type="html">&lt;p&gt;渲染一直是困扰众多开发者的难题，国内外开发文档较少的同时，不断迭代的版本更是让现状雪上加霜，如何让想象的美观界面元素化为现实，成为了很多开发者不断追求的目标.&lt;/p&gt;
&lt;p&gt;笔者也不例外，经常阅读版本快讯的读者可能发现，在 Minecraft 1.20 系列版本快讯的评论区内，&amp;quot;优化&amp;quot; 一词多次出现，难道 Mojang 真的负重前行，成功优化了渲染系统吗？&lt;/p&gt;
&lt;p&gt;众多问题和想法推动笔者查阅相关资料和实现，笔者希望借此机会与各位读者共同探清高版本渲染的迷雾，共同了解高版本横空出世的 GuiGraphics 类，及其背后的三两事.&lt;/p&gt;
&lt;p&gt;按照笔者的行文习惯，我们会通过游戏中几个常见的特性出发，衍生出需求，再以思维为导向来解决问题，进而在实践中摸清渲染的基本逻辑.&lt;/p&gt;
&lt;p&gt;笔者希望传授的不是知识本身，更是获取知识的方式.&lt;/p&gt;
&lt;p&gt;本文编写使用环境: Minecraft 1.20.1 Forge 47.1.0 Parchment 2023.07.23-1.20.1&lt;/p&gt;
&lt;p&gt;本文的受众对象:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;对 Minecraft 有着基本认识和了解的读者&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;对 Fabric 或 Forge 开发比较熟悉的读者&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;有独立阅读相关文档，解决问题能力的读者&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;曾经接触过渲染，或者阅读过笔者有关渲染的文章&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;从-1193-到-1201-究竟发生了什么&#34;&gt;&lt;a class=&#34;anchor&#34; href=&#34;#从-1193-到-1201-究竟发生了什么&#34;&gt;#&lt;/a&gt; 从 1.19.3 到 1.20.1 究竟发生了什么&lt;/h2&gt;
&lt;p&gt;实际上，帖子评论中所谓优化并不是空穴来风，Mojang 在这几个版本中对代码结构进行了大量优化，对渲染相关代码进行了拆分和整理，最终诞生了 GuiGraphics&lt;/p&gt;
&lt;p&gt;过去，对于渲染的过程，我们经常要与 PoseStack, GuiComponent, RenderSystem, AbstractGui 这几个概念打交道，经常的，我们有下面这样的逻辑.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;从渲染上下文中获取到PoseStack实例, 对PoseStack堆栈进行操作
调用RenderSystem的一些方法修改OpenGL状态机
使用GuiComponent封装好的一些方法绘制Minecraft元素 (物品, 文字, 等等)
使用AbstractGui的blit方法绘制贴图
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;实际上，PoseStack, GuiComponent, RenderSystem, BufferSource 几个概念往往是同时出现，其逻辑又是强耦合的，Mojang 当然也意识到了这些.&lt;/p&gt;
&lt;p&gt;在 1.19.3 -&amp;gt; 1.19.4 版本迭代中，GuiComponent 中所有的方法都变成了静态方法，这为逻辑拆分做了准备. renderOutline, blitNineSliced, setBlitOffset, getBlitOffset 等方法被拆分，并入 PoseStack 类中，offset 的概念转变为 PostStack 的 z 轴概念.&lt;/p&gt;
&lt;p&gt;在 1.19.4 -&amp;gt; 1.20 版本迭代中，Mojang 决定淡化 GuiComponent, RenderSystem 的概念，将有关 PoseStack 操作的方法封装为一个 GuiGraphics 类.&lt;/p&gt;
&lt;p&gt;实际上，GuiGraphics 可以看作是为 PostStack 进行了具象化 (使其具有 Minecraft 风格).&lt;/p&gt;
&lt;p&gt;不难猜想，PoseStack 实例成为了 GuiGraphics 对象的成员变量，曾经 GuiComponent.doSomething (poseStack) 的形式已经变为了 guiGraphics.doSomething ().&lt;/p&gt;
&lt;p&gt;实际上，这次优化是对多对耦合关系进行了集成和封装，之后，我们只需将视线放在 GuiGraphics 类身上，便可以解决渲染的大部分问题了.&lt;/p&gt;
&lt;p&gt;限于篇幅，这里笔者仅举一例。在新 GuiGraphics 类中，出现了一个名为 flush 的方法，它实现了在关闭深度测试的情况下处理 BufferSource.&lt;/p&gt;
&lt;figure class=&#34;highlight java&#34;&gt;&lt;figcaption data-lang=&#34;java&#34;&gt;&lt;/figcaption&gt;&lt;table&gt;&lt;tr&gt;&lt;td data-num=&#34;1&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;void&lt;/span&gt; &lt;span class=&#34;token function&#34;&gt;flush&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;&amp;#123;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;2&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;span class=&#34;token class-name&#34;&gt;RenderSystem&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;disableDepthTest&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;3&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;span class=&#34;token keyword&#34;&gt;this&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;bufferSource&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;endBatch&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;4&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;span class=&#34;token class-name&#34;&gt;RenderSystem&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;enableDepthTest&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;5&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token punctuation&#34;&gt;&amp;#125;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;&lt;p&gt;bufferSource 也是 GuiGraphics 的成员变量，这使得 填充缓存 和 清空缓存 在渲染上下文中变得一气呵成，方便了许多.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;实际上，flush 的逻辑在 GUI 批渲染中大量出现，因为大多数情况下二维渲染不需要深度测试，深度测试某种意义上来说是一个三维概念.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;关于 BufferSource 和 VertexConsumer, 笔者这里暂不展开讨论，将作为进阶思考内容在后日谈中与读者深入探讨&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;在我们日常游玩中，附魔台中灵活运动的书本的渲染就在新版本得到了改善.&lt;/p&gt;
&lt;p&gt;&lt;img loading=&#34;lazy&#34; data-src=&#34;https://s2.loli.net/2023/10/02/vkt9ruxPWGpwQB2.gif&#34; alt=&#34;enchant.gif&#34; /&gt;&lt;/p&gt;
&lt;figure class=&#34;highlight java&#34;&gt;&lt;figcaption data-lang=&#34;java&#34;&gt;&lt;/figcaption&gt;&lt;table&gt;&lt;tr&gt;&lt;td data-num=&#34;1&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token comment&#34;&gt;// 1.20.1 : net.minecraft.client.gui.screens.inventory.EnchantmentScreen&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;2&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token keyword&#34;&gt;protected&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;void&lt;/span&gt; &lt;span class=&#34;token function&#34;&gt;renderBg&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token class-name&#34;&gt;GuiGraphics&lt;/span&gt; pGuiGraphics&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;float&lt;/span&gt; pPartialTick&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;int&lt;/span&gt; pMouseX&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;int&lt;/span&gt; pMouseY&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;&amp;#123;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;3&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    pGuiGraphics&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;blit&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token constant&#34;&gt;ENCHANTING_TABLE_LOCATION&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; i&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; j&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;token number&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;token number&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;this&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;imageWidth&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;this&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;imageHeight&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;4&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;span class=&#34;token keyword&#34;&gt;this&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;renderBook&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;pGuiGraphics&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; i&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; j&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; pPartialTick&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;5&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token punctuation&#34;&gt;&amp;#125;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;6&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;7&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token keyword&#34;&gt;private&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;void&lt;/span&gt; &lt;span class=&#34;token function&#34;&gt;renderBook&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token class-name&#34;&gt;GuiGraphics&lt;/span&gt; pGuiGraphics&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;int&lt;/span&gt; pX&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;int&lt;/span&gt; pY&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;float&lt;/span&gt; pPartialTick&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;&amp;#123;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;8&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    pGuiGraphics&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;pose&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;pushPose&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;9&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    pGuiGraphics&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;pose&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;translate&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token keyword&#34;&gt;float&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;pX &lt;span class=&#34;token operator&#34;&gt;+&lt;/span&gt; &lt;span class=&#34;token number&#34;&gt;33.0F&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token keyword&#34;&gt;float&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;pY &lt;span class=&#34;token operator&#34;&gt;+&lt;/span&gt; &lt;span class=&#34;token number&#34;&gt;31.0F&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;token number&#34;&gt;100.0F&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;10&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;span class=&#34;token class-name&#34;&gt;VertexConsumer&lt;/span&gt; vertexconsumer &lt;span class=&#34;token operator&#34;&gt;=&lt;/span&gt; pGuiGraphics&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;bufferSource&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;getBuffer&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token keyword&#34;&gt;this&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;bookModel&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;renderType&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token constant&#34;&gt;ENCHANTING_BOOK_LOCATION&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;11&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;span class=&#34;token keyword&#34;&gt;this&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;bookModel&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;renderToBuffer&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;pGuiGraphics&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;pose&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; vertexconsumer&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;token number&#34;&gt;15728880&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;OverlayTexture&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token constant&#34;&gt;NO_OVERLAY&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;token number&#34;&gt;1.0F&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;token number&#34;&gt;1.0F&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;token number&#34;&gt;1.0F&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;token number&#34;&gt;1.0F&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;12&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    pGuiGraphics&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;flush&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;13&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    pGuiGraphics&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;pose&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;popPose&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;14&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token punctuation&#34;&gt;&amp;#125;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;&lt;figure class=&#34;highlight java&#34;&gt;&lt;figcaption data-lang=&#34;java&#34;&gt;&lt;/figcaption&gt;&lt;table&gt;&lt;tr&gt;&lt;td data-num=&#34;1&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token comment&#34;&gt;// 1.19.3 : net.minecraft.client.gui.screens.inventory.EnchantmentScreen&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;2&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token keyword&#34;&gt;protected&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;void&lt;/span&gt; &lt;span class=&#34;token function&#34;&gt;renderBg&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token class-name&#34;&gt;PoseStack&lt;/span&gt; pPoseStack&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;float&lt;/span&gt; pPartialTick&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;int&lt;/span&gt; pX&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;int&lt;/span&gt; pY&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;&amp;#123;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;3&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;span class=&#34;token keyword&#34;&gt;this&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;blit&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;pPoseStack&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; i&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; j&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;token number&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;token number&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;this&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;imageWidth&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;this&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;imageHeight&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;4&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    pPoseStack&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;pushPose&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;5&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    pPoseStack&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;translate&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token number&#34;&gt;1.0F&lt;/span&gt; &lt;span class=&#34;token operator&#34;&gt;-&lt;/span&gt; f1&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;token operator&#34;&gt;*&lt;/span&gt; &lt;span class=&#34;token number&#34;&gt;0.2F&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token number&#34;&gt;1.0F&lt;/span&gt; &lt;span class=&#34;token operator&#34;&gt;-&lt;/span&gt; f1&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;token operator&#34;&gt;*&lt;/span&gt; &lt;span class=&#34;token number&#34;&gt;0.1F&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token number&#34;&gt;1.0F&lt;/span&gt; &lt;span class=&#34;token operator&#34;&gt;-&lt;/span&gt; f1&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;token operator&#34;&gt;*&lt;/span&gt; &lt;span class=&#34;token number&#34;&gt;0.25F&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;6&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;span class=&#34;token class-name&#34;&gt;MultiBufferSource&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;BufferSource&lt;/span&gt; multibuffersource$buffersource &lt;span class=&#34;token operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;MultiBufferSource&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;immediate&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token class-name&#34;&gt;Tesselator&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;getInstance&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;getBuilder&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;7&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;span class=&#34;token class-name&#34;&gt;VertexConsumer&lt;/span&gt; vertexconsumer &lt;span class=&#34;token operator&#34;&gt;=&lt;/span&gt; multibuffersource$buffersource&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;getBuffer&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token keyword&#34;&gt;this&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;bookModel&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;renderType&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token constant&#34;&gt;ENCHANTING_BOOK_LOCATION&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;8&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;span class=&#34;token keyword&#34;&gt;this&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;bookModel&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;renderToBuffer&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;pPoseStack&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; vertexconsumer&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;token number&#34;&gt;15728880&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;OverlayTexture&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token constant&#34;&gt;NO_OVERLAY&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;token number&#34;&gt;1.0F&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;token number&#34;&gt;1.0F&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;token number&#34;&gt;1.0F&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;token number&#34;&gt;1.0F&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;9&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    multibuffersource$buffersource&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;endBatch&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;10&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    pPoseStack&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;popPose&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;11&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token punctuation&#34;&gt;&amp;#125;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;&lt;p&gt;首先，新版本将 PoseStack, BufferSource 并入 GuiGraphics, 关于这一点我们不必多说&lt;/p&gt;
&lt;p&gt;之后，新版本把 blit 统一放入 GuiGraphics, 而不是作为 GuiComponent 的内部方法&lt;/p&gt;
&lt;p&gt;最后，新版本在关闭深度测试以后再对 Buffer 进行批处理 (flush 方法), 优化了部分性能&lt;/p&gt;
&lt;p&gt;总结一下，新版本对于渲染系统的改进主要在提升集成度和规范代码风格上，对于性能优化有部分改进，但没有大改。对于使用过去渲染类教程的读者，相信阅读到这里已经知道如何将之前自己熟悉的各类方法和函数迁移到新版的 GuiGraphics 上了&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;实际上，Mojang 还在 1.20 对光照系统进行了大改，修改后的光照系统的性能有较大幅度的提升，但由于篇幅和笔者技术水平原因，这里暂不展开讲解，感兴趣的读者可以自行查阅，或者到致谢部分查看具体的技术细节，以便进一步查阅了解&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id=&#34;lets-talk-about-tooltips&#34;&gt;&lt;a class=&#34;anchor&#34; href=&#34;#lets-talk-about-tooltips&#34;&gt;#&lt;/a&gt; Let&#39;s talk about Tooltips&lt;/h2&gt;
&lt;p&gt;实际上，无论是 GuiComponent 还是 GuiGraphics, 其名字中从未消失的就是 GUI. 在日常游戏中，我们常把 可以打开的界面 称作是 GUI, 但在开发层面，大多数游戏界面中的 2D 元素都可以看作是 GUI. 这里笔者希望以一个常见的 2D 元素 —— Tooltips 为例，带领读者从实例出发，由浅入深了解新版本渲染.&lt;/p&gt;
&lt;p&gt;当然，渲染是一个很深的话题，这里笔者仅选取常见需求的一个剖面，希望读者阅读过后能对流程有一个较为熟悉的认知，产生自己探索更多知识的能力.&lt;/p&gt;
&lt;p&gt;话不多说，这里笔者先放出最终效果图，激发读者的阅读兴趣.&lt;/p&gt;
&lt;p&gt;&lt;img loading=&#34;lazy&#34; data-src=&#34;https://s2.loli.net/2023/10/02/kctUTqpYGB2hxR6.png&#34; alt=&#34;sword.png&#34; /&gt;&lt;br /&gt;
&lt;img loading=&#34;lazy&#34; data-src=&#34;https://s2.loli.net/2023/10/02/T2SvB9NmfhCWGxX.png&#34; alt=&#34;chestplate.png&#34; /&gt;&lt;/p&gt;
&lt;p&gt;从图片中，读者能看到多少自己熟悉的渲染内容呢？下面请读着跟随笔者的步伐，一步一步完成精美的 Tooltip, 并且探究其中的奥妙&lt;/p&gt;
&lt;h3 id=&#34;欲穷千里目&#34;&gt;&lt;a class=&#34;anchor&#34; href=&#34;#欲穷千里目&#34;&gt;#&lt;/a&gt; 欲穷千里目&lt;/h3&gt;
&lt;p&gt;为了绘制我们自己的 Tooltip 元素，我们首先需要去除原版的样式。关于 Tooltip, Forge 很贴心的为我们准备了 RenderTooltipEvent 的事件，通过监听 RenderTooltipEvent, 我们可以对 Tooltip 元素进行一定程度的自定义&lt;/p&gt;
&lt;p&gt;查阅 RenderTooltipEvent 的源码，我们发现下面这三部分:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;RenderTooltipEvent.GatherComponents 在 Tooltip 组件被添加的时候触发，在这个阶段通常可以自定义修改 Components 列表&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;RenderTooltipEvent.Pre 在 Tooltip 被渲染前触发，这个阶段我们可以拿到 GuiGraphics 上下文，或者调整 Tooltip 的位置&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;RenderTooltipEvent.Color 在 Tooltip 渲染背景和边框时触发，这个阶段我们可以修改背景和边框的颜色，或者进行一定程度的颜色自定义&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;不难发现，按照描述，我们应该监听 RenderTooltipEvent.Color, 在渲染背景和边框的时候取消掉原来的颜色，并且插入我们的渲染逻辑.&lt;/p&gt;
&lt;p&gt;先创建 ClientEventHandler 类，按照我们的想法尝试一下&lt;/p&gt;
&lt;figure class=&#34;highlight java&#34;&gt;&lt;figcaption data-lang=&#34;java&#34;&gt;&lt;/figcaption&gt;&lt;table&gt;&lt;tr&gt;&lt;td data-num=&#34;1&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token comment&#34;&gt;// 位于: forgedev (opengl-1.20.1) dev.nogu.turou.forgedev.ClientEventHandler&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;2&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;3&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token annotation punctuation&#34;&gt;@Mod.EventBusSubscriber&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;modid &lt;span class=&#34;token operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;ForgeDev&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token constant&#34;&gt;MODID&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; bus &lt;span class=&#34;token operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;Mod&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;EventBusSubscriber&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;Bus&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token constant&#34;&gt;FORGE&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; value &lt;span class=&#34;token operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;Dist&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token constant&#34;&gt;CLIENT&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;4&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;class&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;ClientEventHandler&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;&amp;#123;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;5&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;span class=&#34;token annotation punctuation&#34;&gt;@SubscribeEvent&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;priority &lt;span class=&#34;token operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;EventPriority&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token constant&#34;&gt;LOWEST&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;6&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;span class=&#34;token keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;static&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;void&lt;/span&gt; &lt;span class=&#34;token function&#34;&gt;colorTooltip&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token class-name&#34;&gt;RenderTooltipEvent&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;Color&lt;/span&gt; event&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;&amp;#123;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;7&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;        &lt;span class=&#34;token class-name&#34;&gt;TooltipRenderer&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;removeColors&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;event&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;8&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;span class=&#34;token punctuation&#34;&gt;&amp;#125;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;9&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token punctuation&#34;&gt;&amp;#125;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;&lt;figure class=&#34;highlight java&#34;&gt;&lt;figcaption data-lang=&#34;java&#34;&gt;&lt;/figcaption&gt;&lt;table&gt;&lt;tr&gt;&lt;td data-num=&#34;1&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token comment&#34;&gt;// 位于: forgedev (opengl-1.20.1) dev.nogu.turou.forgedev.render.TooltipRenderer&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;2&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;3&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;static&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;void&lt;/span&gt; &lt;span class=&#34;token function&#34;&gt;removeColors&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token class-name&#34;&gt;RenderTooltipEvent&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;Color&lt;/span&gt; event&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;&amp;#123;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;4&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;span class=&#34;token comment&#34;&gt;// make the border and background transparent here so that we can redraw it later.&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;5&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    event&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;setBorderStart&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token number&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;6&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    event&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;setBorderEnd&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token number&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;7&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    event&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;setBackgroundStart&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token number&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;8&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    event&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;setBackgroundEnd&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token number&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;9&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token punctuation&#34;&gt;&amp;#125;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;&lt;p&gt;这里我们订阅事件，并且调用 event 实例的相关方法，成功去掉了 Tooltip 的边框和背景，现在我们进入游戏，应该是这样的效果:&lt;/p&gt;
&lt;p&gt;&lt;img loading=&#34;lazy&#34; data-src=&#34;https://s2.loli.net/2023/10/12/gjw9lP7VdfoB4cz.png&#34; alt=&#34;transparent.png&#34; /&gt;&lt;/p&gt;
&lt;p&gt;光秃秃的下界合金剑显然并不美观，笔者想要为我们的剑添加一个娘化形象悬浮在左侧，我们先来看一下效果:&lt;/p&gt;
&lt;p&gt;&lt;img loading=&#34;lazy&#34; data-src=&#34;https://s2.loli.net/2023/10/12/ejqN4IUOEcYCz6T.png&#34; alt=&#34;sword_middle.png&#34; /&gt;&lt;/p&gt;
&lt;p&gt;结合上文对 GuiGraphics 的介绍，相信有一定经验的读者已经略有头绪，我们或许可以在 GuiGraphics 类中找到 blit 方法，然后在适当的时候渲染一张图片.&lt;/p&gt;
&lt;p&gt;当然，按照我们的想法，在 RenderTooltipEvent.Color 时期渲染这张图片再适合不过了，我们可以顺理成章的认为这个图片也属于边框 (背景) 的一部分，但是真的能如我们所愿吗？&lt;/p&gt;
&lt;p&gt;首先，我们很清楚的是，现在我们迫切需要 GuiGraphics 上下文，只有获得了 GuiGraphics 实例，我们才能便利地调用那些封装的成员方法。不幸的是，event 对象中并没有类似 getGraphics 这样的方法.&lt;/p&gt;
&lt;p&gt;不过，这并不能直接难倒我们，我们可以尝试观察一下 Pre 或者 GatherComponents 事件中是否能提供 GuiGraphics 上下文，实际上，笔者上文已经提过，Pre 事件中可以拿到 GuiGraphics 实例。拿到以后我们敢不敢存起来用呢，这里笔者考察了一下 RenderTooltipEvent 被触发的实际情况.&lt;/p&gt;
&lt;p&gt;&lt;img loading=&#34;lazy&#34; data-src=&#34;https://s2.loli.net/2023/10/12/JthXPuSYOUN2Rvf.png&#34; alt=&#34;pre_event_constructor.png&#34; /&gt;&lt;/p&gt;
&lt;p&gt;通过对 RenderTooltipEvent.Pre 的构造方法的查找用法，我们找到了 ForgeHooksClient 中的对应方法，在进一步查找，我们找到了位于 Minecraft 源码&lt;strong&gt; GuiGraphics&lt;/strong&gt; 中的 renderTooltipInternal 方法，很容易可以猜到，这个方法其实是 Minecraft 本身真正负责渲染 Tooltip 的内部方法，我们的大部分事件都是在合理的位置 Hook 进去的.&lt;/p&gt;
&lt;p&gt;到这里，其实我们已经不必细致阅读这个内部方法了，不难看出，实际上被引用的实例都是 &amp;quot;&lt;strong&gt;this&lt;/strong&gt;&amp;quot; 对应的，我们可以大胆地暂存 Pre 事件中获取到的 GuiGraphics 实例的引用地址了.&lt;/p&gt;
&lt;figure class=&#34;highlight java&#34;&gt;&lt;figcaption data-lang=&#34;java&#34;&gt;&lt;/figcaption&gt;&lt;table&gt;&lt;tr&gt;&lt;td data-num=&#34;1&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token comment&#34;&gt;// 位于: forgedev (opengl-1.20.1) dev.nogu.turou.forgedev.render.TooltipRenderer&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;2&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;class&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;TooltipRenderer&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;&amp;#123;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;3&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;span class=&#34;token keyword&#34;&gt;private&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;static&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;GuiGraphics&lt;/span&gt; guiGraphicsContext&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;4&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;5&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;span class=&#34;token keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;static&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;void&lt;/span&gt; &lt;span class=&#34;token function&#34;&gt;setGuiGraphicsContext&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token class-name&#34;&gt;GuiGraphics&lt;/span&gt; guiGraphics&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;&amp;#123;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;6&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;        guiGraphicsContext &lt;span class=&#34;token operator&#34;&gt;=&lt;/span&gt; guiGraphics&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;7&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;span class=&#34;token punctuation&#34;&gt;&amp;#125;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;8&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token punctuation&#34;&gt;&amp;#125;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;9&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;10&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token comment&#34;&gt;// 位于: forgedev (opengl-1.20.1) dev.nogu.turou.forgedev.ClientEventHandler&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;11&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;class&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;ClientEventHandler&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;&amp;#123;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;12&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;span class=&#34;token annotation punctuation&#34;&gt;@SubscribeEvent&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;priority &lt;span class=&#34;token operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;EventPriority&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token constant&#34;&gt;LOWEST&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;13&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;span class=&#34;token keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;static&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;void&lt;/span&gt; &lt;span class=&#34;token function&#34;&gt;preInitTooltip&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token class-name&#34;&gt;RenderTooltipEvent&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;Pre&lt;/span&gt; event&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;&amp;#123;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;14&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;        &lt;span class=&#34;token class-name&#34;&gt;TooltipRenderer&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;setGuiGraphicsContext&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;event&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;getGraphics&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;15&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;span class=&#34;token punctuation&#34;&gt;&amp;#125;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;16&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token punctuation&#34;&gt;&amp;#125;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;&lt;p&gt;这里我们已经将 GuiGraphics 上下文保存在 TooltipRenderer 类中了，我们已经可以兴奋地准备进行 blit 了.&lt;/p&gt;
&lt;figure class=&#34;highlight java&#34;&gt;&lt;figcaption data-lang=&#34;java&#34;&gt;&lt;/figcaption&gt;&lt;table&gt;&lt;tr&gt;&lt;td data-num=&#34;1&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token comment&#34;&gt;// 位于: forgedev (opengl-1.20.1) dev.nogu.turou.forgedev.TooltipRenderer&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;2&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;class&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;TooltipRenderer&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;&amp;#123;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;3&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;span class=&#34;token keyword&#34;&gt;private&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;static&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;ResourceLocation&lt;/span&gt; &lt;span class=&#34;token function&#34;&gt;getPath&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token class-name&#34;&gt;String&lt;/span&gt; part&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;&amp;#123;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;4&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;        &lt;span class=&#34;token keyword&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;new&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;ResourceLocation&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token class-name&#34;&gt;ForgeDev&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token constant&#34;&gt;MODID&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;token string&#34;&gt;&#34;textures/tooltip/&#34;&lt;/span&gt; &lt;span class=&#34;token operator&#34;&gt;+&lt;/span&gt; part &lt;span class=&#34;token operator&#34;&gt;+&lt;/span&gt; &lt;span class=&#34;token string&#34;&gt;&#34;.png&#34;&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;5&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;span class=&#34;token punctuation&#34;&gt;&amp;#125;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;6&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;7&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;span class=&#34;token keyword&#34;&gt;private&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;static&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;void&lt;/span&gt; &lt;span class=&#34;token function&#34;&gt;innerBlit&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token class-name&#34;&gt;ResourceLocation&lt;/span&gt; pAtlasLocation&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;int&lt;/span&gt; pX&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;int&lt;/span&gt; pY&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;int&lt;/span&gt; pWidth&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;int&lt;/span&gt; pHeight&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;&amp;#123;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;8&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;        guiGraphicsContext&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;blit&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;pAtlasLocation&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; pX&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; pY&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;token number&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;token number&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; pWidth&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; pHeight&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; pWidth&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; pHeight&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;9&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;span class=&#34;token punctuation&#34;&gt;&amp;#125;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;10&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;11&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;span class=&#34;token keyword&#34;&gt;private&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;static&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;void&lt;/span&gt; &lt;span class=&#34;token function&#34;&gt;renderSwordCharacter&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token keyword&#34;&gt;int&lt;/span&gt; x&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;int&lt;/span&gt; y&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;&amp;#123;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;12&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;        guiGraphicsContext&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;pose&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;pushPose&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;13&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;        guiGraphicsContext&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;pose&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;translate&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token number&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;token number&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;token number&#34;&gt;400&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;14&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;        &lt;span class=&#34;token class-name&#34;&gt;RenderSystem&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;enableBlend&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;15&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;        &lt;span class=&#34;token function&#34;&gt;innerBlit&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;getPath&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token string&#34;&gt;&#34;sword_character&#34;&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; x &lt;span class=&#34;token operator&#34;&gt;-&lt;/span&gt; &lt;span class=&#34;token number&#34;&gt;66&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; y &lt;span class=&#34;token operator&#34;&gt;-&lt;/span&gt; &lt;span class=&#34;token number&#34;&gt;8&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;token number&#34;&gt;48&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;token number&#34;&gt;76&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;16&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;        &lt;span class=&#34;token class-name&#34;&gt;RenderSystem&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;disableBlend&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;17&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;        guiGraphicsContext&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;pose&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;popPose&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;18&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;span class=&#34;token punctuation&#34;&gt;&amp;#125;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;19&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;20&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;span class=&#34;token keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;static&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;void&lt;/span&gt; &lt;span class=&#34;token function&#34;&gt;tryRenderCharacter&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token class-name&#34;&gt;ItemStack&lt;/span&gt; itemStack&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;int&lt;/span&gt; x&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;int&lt;/span&gt; y&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;&amp;#123;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;21&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;        &lt;span class=&#34;token keyword&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;itemStack&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;getItem&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;instanceof&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;SwordItem&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;&amp;#123;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;22&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;            &lt;span class=&#34;token function&#34;&gt;renderSwordCharacter&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;x&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; y&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;23&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;        &lt;span class=&#34;token punctuation&#34;&gt;&amp;#125;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;24&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;span class=&#34;token punctuation&#34;&gt;&amp;#125;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;25&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token punctuation&#34;&gt;&amp;#125;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;26&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;27&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token comment&#34;&gt;// 位于: forgedev (opengl-1.20.1) dev.nogu.turou.forgedev.ClientEventHandler&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;28&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;class&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;ClientEventHandler&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;&amp;#123;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;29&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;span class=&#34;token annotation punctuation&#34;&gt;@SubscribeEvent&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;priority &lt;span class=&#34;token operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;EventPriority&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token constant&#34;&gt;LOWEST&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;30&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;span class=&#34;token keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;static&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;void&lt;/span&gt; &lt;span class=&#34;token function&#34;&gt;colorTooltip&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token class-name&#34;&gt;RenderTooltipEvent&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;Color&lt;/span&gt; event&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;&amp;#123;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;31&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;        &lt;span class=&#34;token class-name&#34;&gt;TooltipRenderer&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;removeColors&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;event&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;32&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;        &lt;span class=&#34;token class-name&#34;&gt;TooltipRenderer&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;tryRenderCharacter&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;event&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;getItemStack&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; event&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;getX&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; event&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;getY&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;33&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;span class=&#34;token punctuation&#34;&gt;&amp;#125;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;34&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token punctuation&#34;&gt;&amp;#125;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;&lt;p&gt;这里我们主要关注一下&lt;strong&gt; renderSwordCharacter&lt;/strong&gt; 方法，相信熟悉渲染的读者已经对这些方法很熟悉了，我们再次复习一下.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;pushPose 保存OpenGL状态机原来的状态
translate 对光标相对位置进行偏移
enableBlend 启动Alpha混合, 方便我们渲染透明通道
blit Minecraft为我们封装好的渲染图片的方法
disableBlend 还原Blend状态
popPose 恢复OpenGL状态机的原状态
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;这里笔者对 blit 进行了简单的封装，这里相信已经不必多说，读者已经能够理解这个渲染图片的过程了.&lt;/p&gt;
&lt;p&gt;当然，对于附加元素的渲染在 Color 阶段当然无可厚非，这里的 X, Y 是指 Tooltip 左上角的坐标。如果笔者尝试在 Pre 阶段进行，是无法获取到有关位置的任何信息的，实际上，位置的相关处理是在 Pre 事件之后进行的.&lt;/p&gt;
&lt;p&gt;至此，读者应该可以进入游戏，查看效果了.&lt;/p&gt;
&lt;hr /&gt;
&lt;p&gt;接下来，我们来给胸甲类物品制作左侧大图展示，具体的操作方式与绘制图片类似，但这次我们要调用的是来自 GuiGraphics 提供的 renderItem 方法.&lt;/p&gt;
&lt;figure class=&#34;highlight java&#34;&gt;&lt;figcaption data-lang=&#34;java&#34;&gt;&lt;/figcaption&gt;&lt;table&gt;&lt;tr&gt;&lt;td data-num=&#34;1&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token comment&#34;&gt;// 位于: forgedev (opengl-1.20.1) dev.nogu.turou.forgedev.TooltipRenderer&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;2&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;static&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;void&lt;/span&gt; &lt;span class=&#34;token function&#34;&gt;tryRenderCharacter&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token class-name&#34;&gt;ItemStack&lt;/span&gt; itemStack&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;int&lt;/span&gt; x&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;int&lt;/span&gt; y&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;&amp;#123;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;3&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;span class=&#34;token keyword&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;itemStack&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;getItem&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;instanceof&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;SwordItem&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;token function&#34;&gt;renderSwordCharacter&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;x&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; y&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;4&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;span class=&#34;token keyword&#34;&gt;else&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;itemStack&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;getItem&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;instanceof&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;ArmorItem&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;token function&#34;&gt;renderArmorItem&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;itemStack&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; x &lt;span class=&#34;token operator&#34;&gt;-&lt;/span&gt; &lt;span class=&#34;token number&#34;&gt;54&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; y &lt;span class=&#34;token operator&#34;&gt;+&lt;/span&gt; &lt;span class=&#34;token number&#34;&gt;16&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;5&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token punctuation&#34;&gt;&amp;#125;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;6&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;7&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token keyword&#34;&gt;private&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;static&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;void&lt;/span&gt; &lt;span class=&#34;token function&#34;&gt;renderArmorItem&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token class-name&#34;&gt;ItemStack&lt;/span&gt; itemStack&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;int&lt;/span&gt; x&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;int&lt;/span&gt; y&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;&amp;#123;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;8&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    guiGraphicsContext&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;renderItem&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;itemStack&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; x&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; y&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;9&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token punctuation&#34;&gt;&amp;#125;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;&lt;p&gt;进入游戏后，我们发现效果不尽人意，显然，我们需要对 PoseStack 进行 scale 操作，将胸甲渲染的大小进行调整.&lt;/p&gt;
&lt;p&gt;查找了 renderItem 的所有重载后我们尴尬地发现，没有与 scale 相关的重载，我们只好不复用 renderItem 方法，而只复用逻辑了.&lt;/p&gt;
&lt;p&gt;找到 renderItem 一系列重载方法的根方法后，我们剔除了不必要的部分，并且将 this 引用改为了我们自己的 GuiGraphics 上下文.&lt;/p&gt;
&lt;figure class=&#34;highlight java&#34;&gt;&lt;figcaption data-lang=&#34;java&#34;&gt;&lt;/figcaption&gt;&lt;table&gt;&lt;tr&gt;&lt;td data-num=&#34;1&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token comment&#34;&gt;// 位于: forgedev (opengl-1.20.1) dev.nogu.turou.forgedev.TooltipRenderer&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;2&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token keyword&#34;&gt;private&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;static&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;void&lt;/span&gt; &lt;span class=&#34;token function&#34;&gt;renderArmorItem&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token class-name&#34;&gt;ItemStack&lt;/span&gt; itemStack&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;int&lt;/span&gt; x&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;int&lt;/span&gt; y&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;&amp;#123;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;3&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;span class=&#34;token class-name&#34;&gt;Minecraft&lt;/span&gt; minecraft &lt;span class=&#34;token operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;Minecraft&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;getInstance&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;4&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;span class=&#34;token class-name&#34;&gt;BakedModel&lt;/span&gt; bakedmodel &lt;span class=&#34;token operator&#34;&gt;=&lt;/span&gt; minecraft&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;getItemRenderer&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;getModel&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;itemStack&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; minecraft&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;level&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;null&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;token number&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;5&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    guiGraphicsContext&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;pose&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;pushPose&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;6&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    guiGraphicsContext&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;pose&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;translate&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token keyword&#34;&gt;float&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;x &lt;span class=&#34;token operator&#34;&gt;+&lt;/span&gt; &lt;span class=&#34;token number&#34;&gt;8&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token keyword&#34;&gt;float&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;y &lt;span class=&#34;token operator&#34;&gt;+&lt;/span&gt; &lt;span class=&#34;token number&#34;&gt;8&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;token number&#34;&gt;399f&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;7&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    guiGraphicsContext&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;pose&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;mulPoseMatrix&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token keyword&#34;&gt;new&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;Matrix4f&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;scaling&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token number&#34;&gt;1.0F&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;token operator&#34;&gt;-&lt;/span&gt;&lt;span class=&#34;token number&#34;&gt;1.0F&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;token number&#34;&gt;1.0F&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;8&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    guiGraphicsContext&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;pose&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;scale&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token number&#34;&gt;64.0f&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;token number&#34;&gt;64.0f&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;token number&#34;&gt;1.0f&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt; &lt;span class=&#34;token comment&#34;&gt;// Enlarge our chestplate.&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;9&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;span class=&#34;token keyword&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token operator&#34;&gt;!&lt;/span&gt;bakedmodel&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;usesBlockLight&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;&amp;#123;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;10&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;        &lt;span class=&#34;token class-name&#34;&gt;Lighting&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;setupForFlatItems&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;11&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;span class=&#34;token punctuation&#34;&gt;&amp;#125;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;12&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;13&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    minecraft&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;getItemRenderer&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;render&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;itemStack&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;ItemDisplayContext&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token constant&#34;&gt;GUI&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;token boolean&#34;&gt;false&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; guiGraphicsContext&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;pose&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; guiGraphicsContext&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;bufferSource&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;token number&#34;&gt;15728880&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;OverlayTexture&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token constant&#34;&gt;NO_OVERLAY&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; bakedmodel&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;14&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    guiGraphicsContext&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;flush&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;15&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;span class=&#34;token keyword&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token operator&#34;&gt;!&lt;/span&gt;bakedmodel&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;usesBlockLight&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;&amp;#123;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;16&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;        &lt;span class=&#34;token class-name&#34;&gt;Lighting&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;setupFor3DItems&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;17&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;span class=&#34;token punctuation&#34;&gt;&amp;#125;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;18&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    guiGraphicsContext&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;pose&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;popPose&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;19&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token punctuation&#34;&gt;&amp;#125;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;&lt;p&gt;由于 GuiGraphics 中的 Minecraft 实例是私有的，我们不妨自己获取 Minecraft 实例，这里我们调用了熟悉的  &lt;code&gt;Minecraft.getInstance()&lt;/code&gt;  方法.&lt;/p&gt;
&lt;p&gt;现在进入游戏，相信读者已经看到了想要的效果.&lt;/p&gt;
&lt;h3 id=&#34;更上一层楼&#34;&gt;&lt;a class=&#34;anchor&#34; href=&#34;#更上一层楼&#34;&gt;#&lt;/a&gt; 更上一层楼&lt;/h3&gt;
&lt;p&gt;仅仅渲染一张图片显然是不够的，对于 Tooltip, 我们还能做什么炫酷的效果呢。在开发过程中，很多开发者希望让自己的模组更有风格化，希望能够自定义 Tooltip 的背景或者边框，很显然，我们现在似乎已经有了实现的能力.&lt;/p&gt;
&lt;p&gt;分析一下需求，我们需要知道渲染边框的起点位置，以及 Tooltip 的宽度和高度。为了实现边框的大小自适应变化，我们最好对边框进行切割，然后按照一定的次序和位置组合起来，形成一个自适应的整体.&lt;/p&gt;
&lt;p&gt;相信阅读过上文的读者已经迫不及待地监听了 RenderTooltipEvent.Color 方法准备大展身手了，但是，事情好像没有那么简单......&lt;/p&gt;
&lt;p&gt;当读者试图 Color 事件读取 Tooltip 的 Width, Height 等字段时，发现这并不是一件可以做到的事情，我们被迫返回 renderTooltipInternal 方法里面一探究竟&lt;/p&gt;
&lt;p&gt;这里我们部分选取一些对我们有意义的代码来展示，这里我们为了展示清晰，也略去了一部分参数&lt;/p&gt;
&lt;figure class=&#34;highlight java&#34;&gt;&lt;figcaption data-lang=&#34;java&#34;&gt;&lt;/figcaption&gt;&lt;table&gt;&lt;tr&gt;&lt;td data-num=&#34;1&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token comment&#34;&gt;// 1.20.1: net.minecraft.client.gui.GuiGraphics&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;2&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token keyword&#34;&gt;private&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;void&lt;/span&gt; &lt;span class=&#34;token function&#34;&gt;renderTooltipInternal&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token class-name&#34;&gt;ItemStack&lt;/span&gt; itemStack&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;&amp;#123;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;3&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;      &lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;4&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;span class=&#34;token class-name&#34;&gt;RenderTooltipEvent&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;Pre&lt;/span&gt; preEvent &lt;span class=&#34;token operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;ForgeHooksClient&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;onRenderTooltipPre&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;5&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;6&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;span class=&#34;token keyword&#34;&gt;int&lt;/span&gt; i &lt;span class=&#34;token operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;token number&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;7&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;span class=&#34;token keyword&#34;&gt;int&lt;/span&gt; j &lt;span class=&#34;token operator&#34;&gt;=&lt;/span&gt; pComponents&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;size&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;token operator&#34;&gt;==&lt;/span&gt; &lt;span class=&#34;token number&#34;&gt;1&lt;/span&gt; &lt;span class=&#34;token operator&#34;&gt;?&lt;/span&gt; &lt;span class=&#34;token operator&#34;&gt;-&lt;/span&gt;&lt;span class=&#34;token number&#34;&gt;2&lt;/span&gt; &lt;span class=&#34;token operator&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;token number&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;8&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;9&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;span class=&#34;token keyword&#34;&gt;for&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token class-name&#34;&gt;ClientTooltipComponent&lt;/span&gt; clienttooltipcomponent &lt;span class=&#34;token operator&#34;&gt;:&lt;/span&gt; pComponents&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;&amp;#123;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;10&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;        &lt;span class=&#34;token keyword&#34;&gt;int&lt;/span&gt; k &lt;span class=&#34;token operator&#34;&gt;=&lt;/span&gt; clienttooltipcomponent&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;getWidth&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;preEvent&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;getFont&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;11&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;        &lt;span class=&#34;token keyword&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;k &lt;span class=&#34;token operator&#34;&gt;&gt;&lt;/span&gt; i&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;&amp;#123;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;12&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;        i &lt;span class=&#34;token operator&#34;&gt;=&lt;/span&gt; k&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;13&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;        &lt;span class=&#34;token punctuation&#34;&gt;&amp;#125;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;14&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;15&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;        j &lt;span class=&#34;token operator&#34;&gt;+=&lt;/span&gt; clienttooltipcomponent&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;getHeight&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;16&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;span class=&#34;token punctuation&#34;&gt;&amp;#125;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;17&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;18&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;span class=&#34;token class-name&#34;&gt;Vector2ic&lt;/span&gt; vector2ic &lt;span class=&#34;token operator&#34;&gt;=&lt;/span&gt; pTooltipPositioner&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;positionTooltip&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;19&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;20&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;span class=&#34;token keyword&#34;&gt;this&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;pose&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;pushPose&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;21&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;span class=&#34;token keyword&#34;&gt;this&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;drawManaged&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;token operator&#34;&gt;-&gt;&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;&amp;#123;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;22&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;        &lt;span class=&#34;token class-name&#34;&gt;RenderTooltipEvent&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;Color&lt;/span&gt; colorEvent &lt;span class=&#34;token operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;ForgeHooksClient&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;onRenderTooltipColor&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token keyword&#34;&gt;this&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;tooltipStack&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;this&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; l&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; i1&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; preEvent&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;getFont&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; pComponents&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;23&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;        &lt;span class=&#34;token class-name&#34;&gt;TooltipRenderUtil&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;renderTooltipBackground&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token keyword&#34;&gt;this&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; l&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; i1&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; i2&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; j2&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;token number&#34;&gt;400&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; colorEvent&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;getBackgroundStart&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; colorEvent&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;getBackgroundEnd&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; colorEvent&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;getBorderStart&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; colorEvent&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;getBorderEnd&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;24&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;span class=&#34;token punctuation&#34;&gt;&amp;#125;&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;25&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;26&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;span class=&#34;token keyword&#34;&gt;this&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;pose&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;popPose&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;27&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token punctuation&#34;&gt;&amp;#125;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;&lt;p&gt;在 Pre 和 Color 事件之间，这段对于 ClientTooltipComponent 的迭代引起了我们的注意，很容易想象，Tooltip 的宽度和高度应该是由组成 Tooltip 的组件 (TooltipComponent) 决定的，宽度应该是最宽的一个组件决定的，高度则是组件高度的叠加。分析相关逻辑后，读者应该已经肯定了我们的想法.&lt;/p&gt;
&lt;p&gt;很明显，这里的参数 i 则是整体的宽度，而 j 则是整体的高度，令我们沮丧的是，i 和 j 没有被传入 ForgeHooksClient.onRenderTooltipColor 的参数列表中，这意味着我们没有办法通过监听 Forge 提供的事件来获得到宽度和高度了。相信读者已经与笔者不约而同的想到了 &lt;strong&gt;Mixin&lt;/strong&gt;, 不过，我们所想可能略有不同&lt;/p&gt;
&lt;p&gt;读者可能会想到，我们可以直接 Hook &lt;strong&gt;TooltipRenderUtil&lt;/strong&gt; 类，对 &lt;strong&gt;renderTooltipBackground&lt;/strong&gt; 进行修改，仔细查看后，这确实契合我们的需求，不过这也会导致一个问题.&lt;/p&gt;
&lt;p&gt;调用&lt;strong&gt; renderTooltipBackground&lt;/strong&gt; 时并没有传入 itemStack 实例，这意味着 itemStack 在调用链中丢失了，在渲染背景元素的时候我们就无法针对 itemStack 的内容做一些进阶操作.&lt;/p&gt;
&lt;p&gt;所以，笔者考虑的是如何让保留 itemStack 上下文成为可能，那显然我们不能脱离 renderTooltipInternal 的方法体，最终的答案，我们要 Hook 掉 GuiGraphics 类&lt;/p&gt;
&lt;p&gt;首先，我们需要对 Mixin 环境进行简单的配置，对这部分的细节感兴趣的读者也可以查阅笔者之前有关 Mixin 的文章&lt;/p&gt;
&lt;figure class=&#34;highlight js&#34;&gt;&lt;figcaption data-lang=&#34;JavaScript&#34;&gt;&lt;/figcaption&gt;&lt;table&gt;&lt;tr&gt;&lt;td data-num=&#34;1&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token comment&#34;&gt;// 位于: forgedev (opengl-1.20.1) build.gradle&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;2&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;plugins &lt;span class=&#34;token punctuation&#34;&gt;&amp;#123;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;3&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    id &lt;span class=&#34;token string&#34;&gt;&#39;org.spongepowered.mixin&#39;&lt;/span&gt; version &lt;span class=&#34;token string&#34;&gt;&#39;0.7-SNAPSHOT&#39;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;4&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token punctuation&#34;&gt;&amp;#125;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;5&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;6&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;dependencies &lt;span class=&#34;token punctuation&#34;&gt;&amp;#123;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;7&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    annotationProcessor &lt;span class=&#34;token string&#34;&gt;&#39;org.spongepowered:mixin:0.8.5:processor&#39;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;8&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token punctuation&#34;&gt;&amp;#125;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;9&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;10&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;mixin &lt;span class=&#34;token punctuation&#34;&gt;&amp;#123;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;11&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    add sourceSets&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;main&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;token string&#34;&gt;&#39;forgedev.refmap.json&#39;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;12&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    config &lt;span class=&#34;token string&#34;&gt;&#39;forgedev.mixins.json&#39;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;13&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token punctuation&#34;&gt;&amp;#125;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;&lt;figure class=&#34;highlight json&#34;&gt;&lt;figcaption data-lang=&#34;JSON&#34;&gt;&lt;/figcaption&gt;&lt;table&gt;&lt;tr&gt;&lt;td data-num=&#34;1&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token comment&#34;&gt;// 位于: resources/forgedev.mixins.json&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;2&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token punctuation&#34;&gt;&amp;#123;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;3&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;  &lt;span class=&#34;token property&#34;&gt;&#34;required&#34;&lt;/span&gt;&lt;span class=&#34;token operator&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;token boolean&#34;&gt;true&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;4&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;  &lt;span class=&#34;token property&#34;&gt;&#34;refmap&#34;&lt;/span&gt;&lt;span class=&#34;token operator&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;token string&#34;&gt;&#34;forgedev.refmap.json&#34;&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;5&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;  &lt;span class=&#34;token property&#34;&gt;&#34;package&#34;&lt;/span&gt;&lt;span class=&#34;token operator&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;token string&#34;&gt;&#34;dev.nogu.turou.forgedev.mixin&#34;&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;6&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;  &lt;span class=&#34;token property&#34;&gt;&#34;compatibilityLevel&#34;&lt;/span&gt;&lt;span class=&#34;token operator&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;token string&#34;&gt;&#34;JAVA_17&#34;&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;7&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;  &lt;span class=&#34;token property&#34;&gt;&#34;client&#34;&lt;/span&gt;&lt;span class=&#34;token operator&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;[&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;8&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;span class=&#34;token string&#34;&gt;&#34;GuiGraphicsMixin&#34;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;9&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;  &lt;span class=&#34;token punctuation&#34;&gt;]&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;10&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;  &lt;span class=&#34;token property&#34;&gt;&#34;injectors&#34;&lt;/span&gt;&lt;span class=&#34;token operator&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;&amp;#123;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;11&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;span class=&#34;token property&#34;&gt;&#34;defaultRequire&#34;&lt;/span&gt;&lt;span class=&#34;token operator&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;token number&#34;&gt;1&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;12&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;  &lt;span class=&#34;token punctuation&#34;&gt;&amp;#125;&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;13&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;  &lt;span class=&#34;token property&#34;&gt;&#34;minVersion&#34;&lt;/span&gt;&lt;span class=&#34;token operator&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;token string&#34;&gt;&#34;0.8.5&#34;&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;14&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;  &lt;span class=&#34;token property&#34;&gt;&#34;target&#34;&lt;/span&gt;&lt;span class=&#34;token operator&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;token string&#34;&gt;&#34;@env(DEFAULT)&#34;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;15&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token punctuation&#34;&gt;&amp;#125;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;&lt;p&gt;为了实现需求，我们仍然需要事先将背景颜色置为透明，然后使用 Mixin 来 Hook 掉 &lt;strong&gt;GuiGraphics&lt;/strong&gt; 类，在 &lt;strong&gt;popPose()&lt;/strong&gt; 处插入我们的渲染逻辑&lt;/p&gt;
&lt;figure class=&#34;highlight java&#34;&gt;&lt;figcaption data-lang=&#34;java&#34;&gt;&lt;/figcaption&gt;&lt;table&gt;&lt;tr&gt;&lt;td data-num=&#34;1&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token comment&#34;&gt;// 位于: forgedev (opengl-1.20.1) dev.nogu.turou.forgedev.mixin.GuiGraphics&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;2&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token annotation punctuation&#34;&gt;@Mixin&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token class-name&#34;&gt;GuiGraphics&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token keyword&#34;&gt;class&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;3&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;class&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;GuiGraphicsMixin&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;&amp;#123;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;4&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;span class=&#34;token annotation punctuation&#34;&gt;@Final&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;5&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;span class=&#34;token annotation punctuation&#34;&gt;@Shadow&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;6&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;span class=&#34;token keyword&#34;&gt;private&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;PoseStack&lt;/span&gt; pose&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;7&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;8&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;span class=&#34;token annotation punctuation&#34;&gt;@Inject&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;method &lt;span class=&#34;token operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;token string&#34;&gt;&#34;renderTooltipInternal&#34;&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;9&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;            at &lt;span class=&#34;token operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;token annotation punctuation&#34;&gt;@At&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;value &lt;span class=&#34;token operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;token string&#34;&gt;&#34;INVOKE&#34;&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; target &lt;span class=&#34;token operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;token string&#34;&gt;&#34;Lcom/mojang/blaze3d/vertex/PoseStack;popPose()V&#34;&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; shift &lt;span class=&#34;token operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;At&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;Shift&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token constant&#34;&gt;AFTER&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; locals &lt;span class=&#34;token operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;LocalCapture&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token constant&#34;&gt;CAPTURE_FAILHARD&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;10&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;span class=&#34;token keyword&#34;&gt;private&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;void&lt;/span&gt; &lt;span class=&#34;token function&#34;&gt;renderTooltipInternalMixin&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token class-name&#34;&gt;Font&lt;/span&gt; font&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;List&lt;/span&gt;&lt;span class=&#34;token generics&#34;&gt;&lt;span class=&#34;token punctuation&#34;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&#34;token class-name&#34;&gt;ClientTooltipComponent&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;&gt;&lt;/span&gt;&lt;/span&gt; components&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;int&lt;/span&gt; x&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;int&lt;/span&gt; y&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;ClientTooltipPositioner&lt;/span&gt; positioner&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;CallbackInfo&lt;/span&gt; ci&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;RenderTooltipEvent&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;Pre&lt;/span&gt; preEvent&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;int&lt;/span&gt; width&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;int&lt;/span&gt; height&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;int&lt;/span&gt; i2&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;int&lt;/span&gt; j2&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;Vector2ic&lt;/span&gt; postPos&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;&amp;#123;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;11&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;        &lt;span class=&#34;token comment&#34;&gt;// Do render calls&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;12&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;span class=&#34;token punctuation&#34;&gt;&amp;#125;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;13&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token punctuation&#34;&gt;&amp;#125;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;&lt;p&gt;使用 Final, Shadow 注解拿到原来类中 private final 的 PoseStack 实例&lt;/p&gt;
&lt;p&gt;Inject 的 @At 可以传入 target 参数来标识实际的注入位置，这里我们标识了 popPose 方法，由于这个方法中只调用过一次 popPose 方法，我们即可精确定位到该位置.&lt;/p&gt;
&lt;p&gt;参数表前半段为方法的参数表，继续向后延伸即可获取到方法体内部的局部变量，这里不建议使用 IDEA 的自动补全，以及 LocalCapture.PRINT 提供的信息，他们都不够准确.&lt;/p&gt;
&lt;p&gt;基于方法体内部的上下文，我们可以从方法定义向后延伸，捕获到 i, j 两个局部变量，延伸方式大概如下图所示.&lt;/p&gt;
&lt;p&gt;&lt;img loading=&#34;lazy&#34; data-src=&#34;https://s2.loli.net/2023/11/12/gzwcWsNpBETik4S.png&#34; alt=&#34;mixin_args.png&#34; /&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;可能有读者抱有疑问，为什么我们要定位 popPose () 方法的位置，而不是直接  &lt;code&gt;at = @At(value = &amp;quot;TAIL&amp;quot;)&lt;/code&gt;  呢，首先，我们的渲染逻辑应该是在 Components 判空的作用域范围内的，如果注入在 TAIL, 我们就需要进行二次判空的处理。其次，重新审视我们的逻辑，我们是要在渲染结束后进行 Post 处理，在 popPose () 后面再适合不过，如果放在 TAIL, 虽然最终效果相同，但造成了更大的影响疮面，更容易与其他 Mixin 产生冲突，这是使用 Mixin 的良好习惯.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;接下来，我们来分析绘制边框的过程，为了绘制自适应的边框，我们肯定需要根据一定的规律拆分边框，并且分片进行渲染，才能得到最好的效果.&lt;/p&gt;
&lt;p&gt;&lt;img loading=&#34;lazy&#34; data-src=&#34;https://s2.loli.net/2023/11/12/4N6OEn8cuUB3aZy.png&#34; alt=&#34;depart.png&#34; /&gt;&lt;/p&gt;
&lt;p&gt;红色矩形部分应该是固定比例渲染的。橙色矩形部分是应该根据长宽进行自适应的。橙色圆形的部分是浮于背后的背景。红色矩形框住的星星是应该始终居中渲染的.&lt;/p&gt;
&lt;p&gt;读者可以根据需要对自己的边框进行切割，尽量减小拉伸对效果的影响，这里我们对自适应的部分直接拉伸，由于大部分自适应的部分是渐变色的直线，所以几乎不会影响最终效果.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;对于边框的自适应部分，读者也可以直接选择绘制原生的渐变色直线，推荐参考 TooltipRenderUtil 类的 Gradient 相关方法&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;使用 &lt;a href=&#34;http://Paint.NET&#34;&gt;Paint.NET&lt;/a&gt; 或 Photoshop 等工具切割缩放后，我们应该会得到 10 个透明 PNG 文件，这里我们提前将他们丢到 resources\assets\forgedev\textures 目录下&lt;/p&gt;
&lt;p&gt;&lt;img loading=&#34;lazy&#34; data-src=&#34;https://s2.loli.net/2023/11/12/e2MtVc1mfGqWLOa.png&#34; alt=&#34;depart_explorer.png&#34; /&gt;&lt;/p&gt;
&lt;p&gt;接下来，我们需要对这 10 个贴图按照一定的次序和组织渲染出来.&lt;/p&gt;
&lt;figure class=&#34;highlight java&#34;&gt;&lt;figcaption data-lang=&#34;java&#34;&gt;&lt;/figcaption&gt;&lt;table&gt;&lt;tr&gt;&lt;td data-num=&#34;1&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token comment&#34;&gt;// 位于: forgedev (opengl-1.20.1) dev.nogu.turou.forgedev.mixin.GuiGraphic&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;2&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token annotation punctuation&#34;&gt;@Inject&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;3&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token keyword&#34;&gt;private&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;void&lt;/span&gt; &lt;span class=&#34;token function&#34;&gt;renderTooltipInternalMixin&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;&amp;#123;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;4&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;span class=&#34;token class-name&#34;&gt;TooltipRenderer&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;tryRenderBorder&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;preEvent&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;getItemStack&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; pose&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; postPos&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;x&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; postPos&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;y&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; font&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; width&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; height&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; components&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;5&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token punctuation&#34;&gt;&amp;#125;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;6&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;7&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token comment&#34;&gt;// 位于: forgedev (opengl-1.20.1) dev.nogu.turou.forgedev.TooltipRenderer&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;8&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;static&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;void&lt;/span&gt; &lt;span class=&#34;token function&#34;&gt;tryRenderBorder&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token class-name&#34;&gt;ItemStack&lt;/span&gt; itemStack&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;PoseStack&lt;/span&gt; pose&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;int&lt;/span&gt; x&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;int&lt;/span&gt; y&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;Font&lt;/span&gt; font&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;int&lt;/span&gt; width&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;int&lt;/span&gt; height&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;List&lt;/span&gt;&lt;span class=&#34;token generics&#34;&gt;&lt;span class=&#34;token punctuation&#34;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&#34;token class-name&#34;&gt;ClientTooltipComponent&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;&gt;&lt;/span&gt;&lt;/span&gt; components&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;&amp;#123;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;9&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;span class=&#34;token keyword&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;itemStack&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;getItem&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;instanceof&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;SwordItem&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;token function&#34;&gt;renderFancyBorder&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;x&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; y&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; width&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; height&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;10&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token punctuation&#34;&gt;&amp;#125;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;11&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;12&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token keyword&#34;&gt;private&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;static&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;void&lt;/span&gt; &lt;span class=&#34;token function&#34;&gt;renderFancyBorder&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token keyword&#34;&gt;int&lt;/span&gt; x&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;int&lt;/span&gt; y&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;int&lt;/span&gt; width&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;int&lt;/span&gt; height&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;&amp;#123;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;13&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    guiGraphicsContext&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;pose&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;pushPose&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;14&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    guiGraphicsContext&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;pose&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;translate&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token number&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;token number&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;token number&#34;&gt;400&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt; &lt;span class=&#34;token comment&#34;&gt;// beneath the text and upon other items&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;15&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;span class=&#34;token class-name&#34;&gt;RenderSystem&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;enableBlend&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;16&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;17&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;span class=&#34;token comment&#34;&gt;// render for border&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;18&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;span class=&#34;token keyword&#34;&gt;var&lt;/span&gt; widthOffset &lt;span class=&#34;token operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;token number&#34;&gt;2&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;19&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    width &lt;span class=&#34;token operator&#34;&gt;+=&lt;/span&gt; widthOffset&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;20&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;span class=&#34;token keyword&#34;&gt;var&lt;/span&gt; horizonStart &lt;span class=&#34;token operator&#34;&gt;=&lt;/span&gt; x &lt;span class=&#34;token operator&#34;&gt;-&lt;/span&gt; &lt;span class=&#34;token number&#34;&gt;16&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;21&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;span class=&#34;token keyword&#34;&gt;var&lt;/span&gt; horizonDuration &lt;span class=&#34;token operator&#34;&gt;=&lt;/span&gt; width &lt;span class=&#34;token operator&#34;&gt;-&lt;/span&gt; &lt;span class=&#34;token number&#34;&gt;55&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;22&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;span class=&#34;token keyword&#34;&gt;var&lt;/span&gt; horizonEnd &lt;span class=&#34;token operator&#34;&gt;=&lt;/span&gt; x &lt;span class=&#34;token operator&#34;&gt;-&lt;/span&gt; &lt;span class=&#34;token number&#34;&gt;30&lt;/span&gt; &lt;span class=&#34;token operator&#34;&gt;+&lt;/span&gt; width&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;23&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;span class=&#34;token keyword&#34;&gt;var&lt;/span&gt; verticalStart &lt;span class=&#34;token operator&#34;&gt;=&lt;/span&gt; y &lt;span class=&#34;token operator&#34;&gt;-&lt;/span&gt; &lt;span class=&#34;token number&#34;&gt;14&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;24&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;span class=&#34;token keyword&#34;&gt;var&lt;/span&gt; verticalDuration &lt;span class=&#34;token operator&#34;&gt;=&lt;/span&gt; height &lt;span class=&#34;token operator&#34;&gt;-&lt;/span&gt; &lt;span class=&#34;token number&#34;&gt;38&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;25&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;span class=&#34;token keyword&#34;&gt;var&lt;/span&gt; verticalEnd &lt;span class=&#34;token operator&#34;&gt;=&lt;/span&gt; y &lt;span class=&#34;token operator&#34;&gt;-&lt;/span&gt; &lt;span class=&#34;token number&#34;&gt;20&lt;/span&gt; &lt;span class=&#34;token operator&#34;&gt;+&lt;/span&gt; height&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;26&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;span class=&#34;token keyword&#34;&gt;var&lt;/span&gt; horizonCenter &lt;span class=&#34;token operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token keyword&#34;&gt;int&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;horizonStart &lt;span class=&#34;token operator&#34;&gt;+&lt;/span&gt; &lt;span class=&#34;token number&#34;&gt;0.5&lt;/span&gt; &lt;span class=&#34;token operator&#34;&gt;*&lt;/span&gt; width&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;27&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;span class=&#34;token function&#34;&gt;innerBlit&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;getBorderPath&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token string&#34;&gt;&#34;left_top&#34;&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; horizonStart&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; verticalStart&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;token number&#34;&gt;41&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;token number&#34;&gt;37&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;28&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;span class=&#34;token function&#34;&gt;innerBlit&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;getBorderPath&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token string&#34;&gt;&#34;top&#34;&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; horizonStart &lt;span class=&#34;token operator&#34;&gt;+&lt;/span&gt; &lt;span class=&#34;token number&#34;&gt;41&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; verticalStart &lt;span class=&#34;token operator&#34;&gt;+&lt;/span&gt; &lt;span class=&#34;token number&#34;&gt;7&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; horizonDuration&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;token number&#34;&gt;4&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;29&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;span class=&#34;token function&#34;&gt;innerBlit&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;getBorderPath&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token string&#34;&gt;&#34;right_top&#34;&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; horizonEnd&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; verticalStart&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;token number&#34;&gt;40&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;token number&#34;&gt;37&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;30&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;span class=&#34;token function&#34;&gt;innerBlit&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;getBorderPath&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token string&#34;&gt;&#34;left_bottom&#34;&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; horizonStart&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; verticalEnd&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;token number&#34;&gt;46&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;token number&#34;&gt;32&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;31&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;span class=&#34;token function&#34;&gt;innerBlit&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;getBorderPath&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token string&#34;&gt;&#34;left&#34;&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; horizonStart &lt;span class=&#34;token operator&#34;&gt;+&lt;/span&gt; &lt;span class=&#34;token number&#34;&gt;9&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; verticalStart &lt;span class=&#34;token operator&#34;&gt;+&lt;/span&gt; &lt;span class=&#34;token number&#34;&gt;37&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;token number&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; verticalDuration&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;32&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;span class=&#34;token function&#34;&gt;innerBlit&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;getBorderPath&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token string&#34;&gt;&#34;right&#34;&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; horizonEnd &lt;span class=&#34;token operator&#34;&gt;+&lt;/span&gt; &lt;span class=&#34;token number&#34;&gt;30&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; verticalStart &lt;span class=&#34;token operator&#34;&gt;+&lt;/span&gt; &lt;span class=&#34;token number&#34;&gt;37&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;token number&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; verticalDuration&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;33&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;span class=&#34;token function&#34;&gt;innerBlit&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;getBorderPath&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token string&#34;&gt;&#34;right_bottom&#34;&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; horizonEnd &lt;span class=&#34;token operator&#34;&gt;-&lt;/span&gt; &lt;span class=&#34;token number&#34;&gt;6&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; verticalEnd&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;token number&#34;&gt;46&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;token number&#34;&gt;32&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;34&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;span class=&#34;token function&#34;&gt;innerBlit&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;getBorderPath&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token string&#34;&gt;&#34;bottom&#34;&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; horizonStart &lt;span class=&#34;token operator&#34;&gt;+&lt;/span&gt; &lt;span class=&#34;token number&#34;&gt;46&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; verticalEnd &lt;span class=&#34;token operator&#34;&gt;+&lt;/span&gt; &lt;span class=&#34;token number&#34;&gt;25&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; width &lt;span class=&#34;token operator&#34;&gt;-&lt;/span&gt; &lt;span class=&#34;token number&#34;&gt;66&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;token number&#34;&gt;2&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;35&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;span class=&#34;token function&#34;&gt;innerBlit&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;getBorderPath&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token string&#34;&gt;&#34;stars&#34;&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; horizonCenter &lt;span class=&#34;token operator&#34;&gt;+&lt;/span&gt; &lt;span class=&#34;token number&#34;&gt;7&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; verticalStart &lt;span class=&#34;token operator&#34;&gt;+&lt;/span&gt; &lt;span class=&#34;token number&#34;&gt;8&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;token number&#34;&gt;12&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;token number&#34;&gt;3&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;36&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;37&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;span class=&#34;token comment&#34;&gt;// render for background&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;38&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    guiGraphicsContext&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;pose&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;translate&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token number&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;token number&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;token operator&#34;&gt;-&lt;/span&gt;&lt;span class=&#34;token number&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt; &lt;span class=&#34;token comment&#34;&gt;// beneath the text and upon other items (under the border)&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;39&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;span class=&#34;token function&#34;&gt;innerBlit&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;getPath&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token string&#34;&gt;&#34;fancy_bg&#34;&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; x &lt;span class=&#34;token operator&#34;&gt;-&lt;/span&gt; &lt;span class=&#34;token number&#34;&gt;6&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; y &lt;span class=&#34;token operator&#34;&gt;-&lt;/span&gt; &lt;span class=&#34;token number&#34;&gt;3&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; width &lt;span class=&#34;token operator&#34;&gt;+&lt;/span&gt; &lt;span class=&#34;token number&#34;&gt;8&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; height &lt;span class=&#34;token operator&#34;&gt;+&lt;/span&gt; &lt;span class=&#34;token number&#34;&gt;6&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;40&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;41&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;span class=&#34;token class-name&#34;&gt;RenderSystem&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;disableBlend&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;42&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    guiGraphicsContext&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;pose&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;popPose&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;43&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token punctuation&#34;&gt;&amp;#125;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;&lt;p&gt;相信读者很容易想到笔者这样的代码形式，但对每一个物件定位的动态思维过程是怎样的呢，这里笔者希望读者稍作思考.&lt;/p&gt;
&lt;p&gt;这里笔者将从几个点出发，来与读者共同发觉代码中的常量和计算过程是如何得来的.&lt;/p&gt;
&lt;p&gt;首先，width 和 height 是由原图切片的大小比例缩放得来的，这里笔者大约是将原图的宽高都除以 14 取整，得到了游戏内较为合适的大小。笔者在处理自己的边框宽高时，可以先针对一个部分 (比如 left_top) 进行等比缩放，找到自己比较满意的转换比例，然后按照这种方式来缩放所有的部分.&lt;/p&gt;
&lt;p&gt;之后，我们来进行定位，很容易想到，我们应该先从 left_top 部分进行定位，观察代码块，很明显这里笔者找到的数值是 (-16, -14), 这个点应该是边框的左上角，我们将这个坐标作为起点，命名为 horizonStart, verticalStart&lt;/p&gt;
&lt;p&gt;之后，right_top 应该与 left_top 位于同一高度，他们应该具有相同的 y = verticalStart. 经过多次的修改和调试，我们能得到 horizonEnd 对应的数值，同时计算出 horizonDuration 的长度.&lt;/p&gt;
&lt;p&gt;通过填写确定的部分和推测不确定的部分，我们最终成功将所有图片以较小的误差拼起.&lt;/p&gt;
&lt;p&gt;之后，通过  &lt;code&gt;horizonStart + 0.5 * width&lt;/code&gt;  我们可以计算出 Tooltip 中点的位置，最终经过一定的 offset, 我们能找到 stars 应该被放置的位置.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;将 z-axis translate 到 400 可以保证边框在物品上方，在文字下方。我们渲染背景的时候将 z-axis 又向下 offset 了 - 1, 即此时 z-axis 为 399, 399 也能保证在物品上方和文字下方，同时在边框下方.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;这里我们修改 offset 查看效果的过程主要用到了调试。当我们使用调试按钮启动客户端的时候，如果修改一些常量和方法体内部的逻辑，可以直接应用更改而无需重启游戏.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;img loading=&#34;lazy&#34; data-src=&#34;https://s2.loli.net/2023/11/12/8uCUlOMPnWkcVXb.png&#34; alt=&#34;debug_client.png&#34; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img loading=&#34;lazy&#34; data-src=&#34;https://s2.loli.net/2023/11/12/5qypBTZCP32u74X.png&#34; alt=&#34;reload_class.png&#34; /&gt;&lt;/p&gt;
&lt;p&gt;至此，我们已经成功将具有科技感的边框添加到所有剑类物品上.&lt;/p&gt;
&lt;h2 id=&#34;后日谈&#34;&gt;&lt;a class=&#34;anchor&#34; href=&#34;#后日谈&#34;&gt;#&lt;/a&gt; 后日谈&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;注：这里的部分知识属于较为高阶的知识，其中涉及到渲染的原理、计算机图形学等知识，读者可以选择阅读.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;首先，阅读过笔者先前 TileEntitySpecialRender 相关文章的读者可能对 OpenGL 渲染顶点的过程有一定的了解:&lt;/p&gt;
&lt;p&gt;&lt;img loading=&#34;lazy&#34; data-src=&#34;https://s2.loli.net/2023/11/12/zQXr9ijxZeyLP3O.png&#34; alt=&#34;Snipaste_2021-02-06_12-28-05.png&#34; /&gt;&lt;/p&gt;
&lt;p&gt;在渲染过程中，&lt;strong&gt;数据&lt;/strong&gt;与&lt;strong&gt;指令&lt;/strong&gt;是分离的，指令标识了读取数据的方式。我们适时执行 draw (), 告知 OpenGL 可以开始按照指令开始渲染了.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;
我们可以收集四个顶点, 调用 draw() 绘制一个矩形 (此时draw方法调用一次)

我们可以重复上面这个操作两次, 绘制两个矩形 (此时draw方法调用两次)

我们可以收集八个顶点, 调用 draw() 绘制两个矩形 (此时draw方法调用一次)

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;在可以允许的范围内，我们希望尽量少的调用 draw 方法以提升性能，我们可以尽量多的收集信息，并且一次性将他们提交.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;3TUSK: 这个效果实际上也正是旧版 Forge 提供的 FastTESR 的效果。当 TileEntity 的数量大到一定程度的时候，反复调用 Tessellator.draw () 会显著影响性能。通过批量 (“batch”) 收集顶点数据，我们可以将调用 draw () 的开销降到最低。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;在 FastTESR 中，我们已经把可允许的范围扩大到 TileEntity 的一次 tick, 那么这种范围在原版的渲染中有没有体现呢，那必然是存在的&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;3TUSK: 题外话，当年给 Forge 写出 FastTESR 的 RainWarrior（或者，我们更多称他为 fry） 现在在 Mojang 工作有三年多了。虽然没有证据，但 Modder 们普遍认为是他把这玩意写进了原版底层，因为他也是当时 Forge 开发团队中唯一精通渲染的开发者，同时也是 Forge 那些渲染相关类库的作者。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;重新展开上文我们提到的渲染附魔台 GUI 界面的部分代码，我们应该能看到这种逻辑的影子&lt;/p&gt;
&lt;figure class=&#34;highlight java&#34;&gt;&lt;figcaption data-lang=&#34;java&#34;&gt;&lt;/figcaption&gt;&lt;table&gt;&lt;tr&gt;&lt;td data-num=&#34;1&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token keyword&#34;&gt;private&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;void&lt;/span&gt; &lt;span class=&#34;token function&#34;&gt;renderBook&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token class-name&#34;&gt;GuiGraphics&lt;/span&gt; pGuiGraphics&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;int&lt;/span&gt; pX&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;int&lt;/span&gt; pY&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;float&lt;/span&gt; pPartialTick&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;&amp;#123;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;2&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;3&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;span class=&#34;token class-name&#34;&gt;VertexConsumer&lt;/span&gt; vertexconsumer &lt;span class=&#34;token operator&#34;&gt;=&lt;/span&gt; pGuiGraphics&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;bufferSource&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;getBuffer&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token keyword&#34;&gt;this&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;bookModel&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;renderType&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token constant&#34;&gt;ENCHANTING_BOOK_LOCATION&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;4&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;span class=&#34;token keyword&#34;&gt;this&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;bookModel&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;renderToBuffer&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;pGuiGraphics&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;pose&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; vertexconsumer&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;token number&#34;&gt;15728880&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;OverlayTexture&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token constant&#34;&gt;NO_OVERLAY&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;token number&#34;&gt;1.0F&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;token number&#34;&gt;1.0F&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;token number&#34;&gt;1.0F&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;token number&#34;&gt;1.0F&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;5&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    pGuiGraphics&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;flush&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;6&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token punctuation&#34;&gt;&amp;#125;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;&lt;p&gt;VertexConsumer 是一个接口，BufferBuilder 是这个接口的一个实现，BufferBuilder 实际上就是我们上文提到的数据的一个序列.&lt;/p&gt;
&lt;p&gt;那 RenderType 如何理解呢，我们是根据 RenderType 生成的 BufferBuilder, RenderType 实际上就是指令.&lt;/p&gt;
&lt;h3 id=&#34;vertexconsumer&#34;&gt;&lt;a class=&#34;anchor&#34; href=&#34;#vertexconsumer&#34;&gt;#&lt;/a&gt; VertexConsumer&lt;/h3&gt;
&lt;p&gt;这里我们观察一下 bufferSource 的 getBuffer 方法&lt;/p&gt;
&lt;figure class=&#34;highlight java&#34;&gt;&lt;figcaption data-lang=&#34;java&#34;&gt;&lt;/figcaption&gt;&lt;table&gt;&lt;tr&gt;&lt;td data-num=&#34;1&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;VertexConsumer&lt;/span&gt; &lt;span class=&#34;token function&#34;&gt;getBuffer&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token class-name&#34;&gt;RenderType&lt;/span&gt; pRenderType&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;&amp;#123;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;2&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;span class=&#34;token class-name&#34;&gt;BufferBuilder&lt;/span&gt; bufferbuilder &lt;span class=&#34;token operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;this&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;getBuilderRaw&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;pRenderType&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;3&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;span class=&#34;token keyword&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token keyword&#34;&gt;this&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;startedBuffers&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;add&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;bufferbuilder&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;&amp;#123;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;4&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;        bufferbuilder&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;begin&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;pRenderType&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;mode&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; pRenderType&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;format&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;5&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;span class=&#34;token punctuation&#34;&gt;&amp;#125;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;6&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;span class=&#34;token keyword&#34;&gt;return&lt;/span&gt; bufferbuilder&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;7&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token punctuation&#34;&gt;&amp;#125;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;&lt;p&gt;非常明显的看到，BufferBuilder 引入了基于 RenderType 的模板 (mode, format), 应该是保存顶点数据时进行校验使用的.&lt;/p&gt;
&lt;p&gt;进一步查看 getBuilderRaw 方法可以看到，BufferBuilder 对于 RenderType 单例，这非常说得通。相同类型的操作，当然要合并在一起执行才高效.&lt;/p&gt;
&lt;figure class=&#34;highlight java&#34;&gt;&lt;figcaption data-lang=&#34;java&#34;&gt;&lt;/figcaption&gt;&lt;table&gt;&lt;tr&gt;&lt;td data-num=&#34;1&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token keyword&#34;&gt;private&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;BufferBuilder&lt;/span&gt; &lt;span class=&#34;token function&#34;&gt;getBuilderRaw&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token class-name&#34;&gt;RenderType&lt;/span&gt; pRenderType&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;&amp;#123;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;2&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;span class=&#34;token keyword&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;this&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;fixedBuffers&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;getOrDefault&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;pRenderType&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;this&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;builder&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;3&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token punctuation&#34;&gt;&amp;#125;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;&lt;h3 id=&#34;rendertobuffer&#34;&gt;&lt;a class=&#34;anchor&#34; href=&#34;#rendertobuffer&#34;&gt;#&lt;/a&gt; renderToBuffer&lt;/h3&gt;
&lt;p&gt;顺着  &lt;code&gt;renderToBuffer -&amp;gt; render -&amp;gt; root.render -&amp;gt; compile -&amp;gt; cube.compile&lt;/code&gt;  一条线查下去，我们顺利找到了正在偷偷向 VertexConsumer 中填充数据的方法，这符合我们的想法.&lt;/p&gt;
&lt;p&gt;&lt;img loading=&#34;lazy&#34; data-src=&#34;https://s2.loli.net/2023/11/12/FQ1NkOZAVJtYK8X.png&#34; alt=&#34;Snipaste_2023-11-12_21-49-30.png&#34; /&gt;&lt;/p&gt;
&lt;h3 id=&#34;flush&#34;&gt;&lt;a class=&#34;anchor&#34; href=&#34;#flush&#34;&gt;#&lt;/a&gt; flush&lt;/h3&gt;
&lt;p&gt;这里我们回看我们之前提到的 flush 方法，我们可能对 bufferSource 的存在不再感觉到神秘.&lt;/p&gt;
&lt;figure class=&#34;highlight java&#34;&gt;&lt;figcaption data-lang=&#34;java&#34;&gt;&lt;/figcaption&gt;&lt;table&gt;&lt;tr&gt;&lt;td data-num=&#34;1&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;void&lt;/span&gt; &lt;span class=&#34;token function&#34;&gt;flush&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;&amp;#123;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;2&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;span class=&#34;token class-name&#34;&gt;RenderSystem&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;disableDepthTest&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;3&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;span class=&#34;token keyword&#34;&gt;this&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;bufferSource&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;endBatch&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;4&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;span class=&#34;token class-name&#34;&gt;RenderSystem&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;enableDepthTest&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;5&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token punctuation&#34;&gt;&amp;#125;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;6&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;7&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;void&lt;/span&gt; &lt;span class=&#34;token function&#34;&gt;endBatch&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;&amp;#123;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;8&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;span class=&#34;token keyword&#34;&gt;for&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token class-name&#34;&gt;RenderType&lt;/span&gt; rendertype &lt;span class=&#34;token operator&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;this&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;fixedBuffers&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;keySet&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;&amp;#123;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;9&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;        &lt;span class=&#34;token keyword&#34;&gt;this&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;endBatch&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;rendertype&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;10&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;span class=&#34;token punctuation&#34;&gt;&amp;#125;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;11&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token punctuation&#34;&gt;&amp;#125;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;&lt;p&gt;至于 fixedBuffers 是什么，为什么以 RenderType 为单位进行批量渲染，相信阅读到这里的读者心中已经自然得到答案了.&lt;/p&gt;
&lt;h2 id=&#34;尾声&#34;&gt;&lt;a class=&#34;anchor&#34; href=&#34;#尾声&#34;&gt;#&lt;/a&gt; 尾声&lt;/h2&gt;
&lt;p&gt;本文的引子是 GuiComponent 的消逝与 GuiGraphics 的新生，但是笔者在创作过程中又旁伸出了许多与渲染有关的实用知识与底层知识，虽然本文不能带领读者完全走进渲染的世界，不能对渲染系统的方方面面加以解读，但是笔者希望借此方式使读者对渲染产生兴趣，同时对渲染不再胆怯。除此之外，希望读者也能在笔者的耳濡目染之下建立起正确的思考方式，从而能够按照这种思维范式解决自己的问题，实现自己的需求.&lt;/p&gt;
&lt;p&gt;这里特别感谢:&lt;/p&gt;
&lt;p&gt;3TUSK: 在 TESR 文章下方的回复对笔者产生了巨大启发&lt;/p&gt;
&lt;p&gt;shblock_(Integrated Proxy): 对渲染底层的一部分解读，解答笔者的疑惑&lt;/p&gt;
&lt;p&gt;BloCamLimb: 一些渲染问题的帮助，精神支持&lt;/p&gt;
&lt;p&gt;DAYGood_Time: 与笔者共同讨论 Mojang 的前世今生，提供临时 1.19.3 开发环境&lt;/p&gt;
</content>
        <category term="Minecraft" />
        <category term="模组开发" />
        <updated>2023-11-12T03:19:59.000Z</updated>
    </entry>
    <entry>
        <id>https://turou.fun/contest/tencent-rings-ng/</id>
        <title>基于虚拟化和后台集成的腾讯会议自动打铃管理系统</title>
        <link rel="alternate" href="https://turou.fun/contest/tencent-rings-ng/"/>
        <content type="html">&lt;p&gt;&lt;strong&gt;第 37 届全国青少年科技创新大赛 参赛作品&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;荣获第 37 届全国青少年科技创新大赛 三等奖&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;img loading=&#34;lazy&#34; data-src=&#34;https://s2.loli.net/2024/01/26/v3MtlSbHXuWR7nr.webp&#34; alt=&#34;1706236406658.webp&#34; /&gt;&lt;/p&gt;
&lt;h2 id=&#34;研究背景&#34;&gt;&lt;a class=&#34;anchor&#34; href=&#34;#研究背景&#34;&gt;#&lt;/a&gt; 研究背景&lt;/h2&gt;
&lt;p&gt;如今，国内已进入后疫情时代，网课掀起的在线会议浪潮尚未停息。越来越多的教育机构转为线上，腾讯会议成为了许多在线教育平台面对面施教的重要平台。作为腾讯会议网上教学的受益者之一，笔者逐渐对腾讯会议的优缺点有了更深的了解，这个过程中，自动打铃逐渐引起了笔者的注意。&lt;/p&gt;
&lt;p&gt;学校的网课教学中，教师往往需要时刻注意当前的时间，使网上教学工作量加大，如果将提示上下课的工作交给学生，也会一定程度影响学生的上课质量和上课效率。在教育机构的网课教学中，课程提醒等需求均需要助教或老师手动进行，增大了工作量的同时不易管理。是否可以搭建一个平台，批量管理多个会议室的打铃日程，同时提供稳定可靠的打铃服务呢？这成为了笔者曾思考的问题。&lt;/p&gt;
&lt;p&gt;经过仔细的思考和对相关技术的进一步了解和学习，笔者撰写了此文，希望能对未来的后学有启示作用。笔者开展相关研究经历了两个研究周期，项目也进行了多个版本迭代。从仅基于虚拟化的硬编码日程的打铃软件，到现在基于虚拟化和后台集成的打铃管理系统，需要的相关技术也逐渐多样和丰满，最终构建出了这样一个包含客户端、服务端、网页前端的多实例的打铃管理系统。&lt;/p&gt;
&lt;h2 id=&#34;研究目的&#34;&gt;&lt;a class=&#34;anchor&#34; href=&#34;#研究目的&#34;&gt;#&lt;/a&gt; 研究目的&lt;/h2&gt;
&lt;p&gt;为在线会议室提供一套易部署、高灵活、可复制的自动打铃解决方案，包含前端管理面板、基于后端的日程表系统和 VITS 语音合成等部分。&lt;/p&gt;
&lt;h2 id=&#34;研究方法&#34;&gt;&lt;a class=&#34;anchor&#34; href=&#34;#研究方法&#34;&gt;#&lt;/a&gt; 研究方法&lt;/h2&gt;
&lt;p&gt;使用从需求到实现的溯源性思维，结合现有的虚拟化 (VT-d) 技术，虚拟音视频技术，前后端分离的网页开发技术，TCP 网络编程等技术，利用 C#、Python、JavaScript 编程语言开发相关的客户端、前端、后端。并且调用外部 VITS 接口提供语音合成。&lt;/p&gt;
&lt;p&gt;探究过程分为项目速览、概念突破、技术解读、研究过程几个部分，从理论到实践全方位进行探究。&lt;/p&gt;
&lt;h2 id=&#34;项目速览&#34;&gt;&lt;a class=&#34;anchor&#34; href=&#34;#项目速览&#34;&gt;#&lt;/a&gt; 项目速览&lt;/h2&gt;
&lt;p&gt;这里笔者将针对项目中的关键部分简单剖析，使读者快速理解本项目的特点&lt;/p&gt;
&lt;p&gt;虚拟化：为多会议实例提供可能。在虚拟机容器中运行腾讯会议和虚拟摄像头、虚拟声卡等程序，将打铃程序的音视频呈递到腾讯会议中。单一物理机经由虚拟化可以承载多个会议室，同时做到了稳定可靠和易扩展的特点。&lt;/p&gt;
&lt;p&gt;美观的桌面程序：给参会者直观的课程展示和提醒。精美的 UI 界面实时在会议室中展示，方便参会者实时了解课程信息，为课程做好准备。&lt;/p&gt;
&lt;p&gt;后台集成：解决了各教师实时调整课程表，管理者统一管理的需求。使用服务端统一分发课程表，并且与前端配合，使教师和管理者可以在前端统一查看会议室状态，随时更改课程。&lt;/p&gt;
&lt;p&gt;VITS 语音合成：使用 TTS 语音合成进行课程提醒。上课后使用 AI 语音合成提醒所上课节，可随时切换各角色的语音模型，为枯燥无趣的学习生活加点料。&lt;/p&gt;
&lt;h2 id=&#34;概念突破&#34;&gt;&lt;a class=&#34;anchor&#34; href=&#34;#概念突破&#34;&gt;#&lt;/a&gt; 概念突破&lt;/h2&gt;
&lt;h3 id=&#34;虚拟化技术与打铃&#34;&gt;&lt;a class=&#34;anchor&#34; href=&#34;#虚拟化技术与打铃&#34;&gt;#&lt;/a&gt; 虚拟化技术与打铃&lt;/h3&gt;
&lt;p&gt;服务端（也称 wakatta 后端）作为数据源给各客户端（也称 wakaru 客户端）提供信息，使各个客户端在相互平行的虚拟机容器中提供打铃服务。&lt;/p&gt;
&lt;p&gt;&lt;img loading=&#34;lazy&#34; data-src=&#34;https://s2.loli.net/2024/01/26/SLQFMxveEOni9V7.png&#34; alt=&#34;图片1.png&#34; /&gt;&lt;/p&gt;
&lt;h3 id=&#34;后台集成&#34;&gt;&lt;a class=&#34;anchor&#34; href=&#34;#后台集成&#34;&gt;#&lt;/a&gt; 后台集成&lt;/h3&gt;
&lt;p&gt;为了实现数据的分发，我们搭建了客户端和服务端，而服务端的数据需要做到可视化地修改，因此我们搭建了网页前端来进行数据的增删改查。这里服务端将作为中转的桥梁，承载所有的网络请求和数据库连接。&lt;/p&gt;
&lt;p&gt;&lt;img loading=&#34;lazy&#34; data-src=&#34;https://s2.loli.net/2024/01/26/er7wlXHLvfWK1AF.png&#34; alt=&#34;图片2.png&#34; /&gt;&lt;/p&gt;
&lt;p&gt;客户端采用心跳包模型来保活，每秒向服务端发送请求证明自己的存活，并且从服务端的消息队列中拉取最新的消息（Packet），以保证网页前端调用的部分操作即时抵达客户端。&lt;/p&gt;
&lt;p&gt;这里客户端的操作包含刷新日程表、重连、手动播放上下课铃、VITS 语音提醒等&lt;/p&gt;
&lt;h3 id=&#34;vits语音合成&#34;&gt;&lt;a class=&#34;anchor&#34; href=&#34;#vits语音合成&#34;&gt;#&lt;/a&gt; VITS 语音合成&lt;/h3&gt;
&lt;p&gt;VITS 属于一种基于机器学习的 TTS 技术，在上课前我们希望提醒学生所上课节，在网页前端使用语音提醒时希望客户端可以读出我们的文本内容，所以我们使用了 VITS&lt;/p&gt;
&lt;p&gt;由于承载服务端的 VPS 往往缺乏较高性能的显卡，语音演算往往需要在其他算力机上进行，这里我们使用了 frp 进行了反向代理，使客户端经由服务端作为跳板后，可以间接访问到算力机。&lt;/p&gt;
&lt;p&gt;&lt;img loading=&#34;lazy&#34; data-src=&#34;https://s2.loli.net/2024/01/26/pbgKP4YhsUt6yA5.png&#34; alt=&#34;图片3.png&#34; /&gt;&lt;/p&gt;
&lt;p&gt;在测试环境中，若算力机与服务端位于同一台主机或处于同一网段，也可省去反向代理的过程&lt;/p&gt;
&lt;h2 id=&#34;技术解读&#34;&gt;&lt;a class=&#34;anchor&#34; href=&#34;#技术解读&#34;&gt;#&lt;/a&gt; 技术解读&lt;/h2&gt;
&lt;p&gt;除虚拟化技术外，我们项目主要由服务端（wakatta）、客户端（wakaru）、网页前端（wakatta-web）以及 vits-simple-api 构成。&lt;/p&gt;
&lt;h3 id=&#34;服务端wakatta&#34;&gt;&lt;a class=&#34;anchor&#34; href=&#34;#服务端wakatta&#34;&gt;#&lt;/a&gt; 服务端（wakatta）&lt;/h3&gt;
&lt;p&gt;服务端主要承载客户端和网页前端的网络请求，使用 Python 编写，基于 FastAPI 异步 Web 框架开发，遵循 restful 接口设计范式。由于设计数据的存取和增删改查，数据库选用了高可靠性的 MySQL。这里我们使用 SQLAlchemy 框架做到了数据库的全 ORM，便于后续开发，提升了代码的可读性。服务端有时需要执行一些诸如每天更新当天日程表、定时清除失活客户端等方法，这里使用了 APScheduler 库，做到了定时的计划任务。&lt;/p&gt;
&lt;p&gt;下面是服务端部分关键的设计逻辑：&lt;/p&gt;
&lt;p&gt;当客户端的课程在网页前端被修改，向后端发出 PATCH 请求时，后端会先请求数据库的 session，然后异步调用 SQLAlchemy 获取对应课程的 ORM 对象，进行相关修改并且 commit 到数据库中。之后会调用位于 clients.py 的_notify_client 方法，将 “刷新日程表” 这一 “包” 压入暂存队列中，当下次客户端发送心跳包（最慢 1 秒）时，将所有暂存的 “包” 从队列中取出，将包含 packet_id 和 payload 的 “包” 作为 response 传给客户端。客户端收到后会重新请求日程表，做到刷新课程的效果。&lt;/p&gt;
&lt;p&gt;当一天过去时，往往需要加载下一天的日程（比如周一跨到周二），APScheduler 会在每天零点调用 schedules.py 的 update_subscription 方法，该方法将替换所有订阅了该日程表的客户端的课程，加载对应星期的课程表。同时会调用 clients.py 的_notify_client 方法，要求所有在线客户端刷新日程表来应用最新的课程表。&lt;/p&gt;
&lt;p&gt;当修改日程表中的课程时，进行数据库相关操作后，会调用 schedules.py 的_broadcast_schedule 方法，该方法将会把课程更改应用到所有订阅该课程表的客户端中。&lt;/p&gt;
&lt;h3 id=&#34;客户端wakaru&#34;&gt;&lt;a class=&#34;anchor&#34; href=&#34;#客户端wakaru&#34;&gt;#&lt;/a&gt; 客户端（wakaru）&lt;/h3&gt;
&lt;p&gt;客户端部分主要承载打铃服务，并且与后端通信获取最新的日程表，同时需要有美观且直观的界面，方便会议室中的参会人员可以对当前状态和日程一目了然&lt;/p&gt;
&lt;p&gt;界面部分采用 WPF 编写，仅使用 xaml 标记语言便可搭建出美观的界面布局，并且支持动态绑定和组件化编程。Wakaru 中的每一个卡片均对应 Views 中的一个组件。&lt;/p&gt;
&lt;p&gt;逻辑部分采用 C# 语言编写，基于.net6.0，&lt;a href=&#34;http://xn--q8qs0pek895mfjb2ysbum.net&#34;&gt;运行需要准备好.net&lt;/a&gt; 6.0 运行时。客户端使用 Quartz 提供定时任务的功能，Quartz 不仅提供打铃任务（位于 RingJobs.cs）的定时执行，同时也包括每秒发送心跳包（位于 WakattaHeartbeat.cs）等过程。&lt;/p&gt;
&lt;p&gt;下面是客户端部分关键的设计逻辑：&lt;/p&gt;
&lt;p&gt;在客户端程序启动时，会向后端发送 get_client 请求，后端会返回客户端的所有基本信息，包括当前的日程表、客户端 ID 和名称、上下课铃等。之后客户端会创建 WakattaHeartbeat 的定时任务，向服务端每秒发送心跳包。WakattaHeartbeat 中同时还包含处理收到的 “包” 的 HandlePacket 方法&lt;/p&gt;
&lt;p&gt;RingJobs 类中存放着所有关于播放上下课铃的相关代码，通过调用 SoundPlayer 的 Play 方法，即可实现播放指定音频。若 VITS 可用，也会在此时向相关算力机发送合成 VITS 音频的请求，合成的音频传回后也会直接进行播放&lt;/p&gt;
&lt;h3 id=&#34;网页前端wakatta-web&#34;&gt;&lt;a class=&#34;anchor&#34; href=&#34;#网页前端wakatta-web&#34;&gt;#&lt;/a&gt; 网页前端（wakatta-web）&lt;/h3&gt;
&lt;p&gt;网页前端主要提供了美观且便利的数据增删改查操作环境，包含会议管理、日程管理、用户系统等部分。&lt;/p&gt;
&lt;p&gt;前端部分使用 Vue 搭建，UI 部分基于 element-plus 框架和 tailwind-css，使用 pinia 进行数据持久化，axios 处理与后端的网络请求。当发送网络请求时，会与后端基于 jwt 进行鉴权，只有具有相关权限才能修改课程等数据。没有相关权限的用户会被 vue-router 路由到 DeniedView.vue 页面中。&lt;/p&gt;
&lt;h3 id=&#34;vits-simple-api&#34;&gt;&lt;a class=&#34;anchor&#34; href=&#34;#vits-simple-api&#34;&gt;#&lt;/a&gt; vits-simple-api&lt;/h3&gt;
&lt;p&gt;为了承载客户端对 VITS 合成的需求，这里我们需要引入一个项目提供语音合成部分的支持，为了不干扰 wakatta 后端项目，这里我们引入了一个第三方项目来提供我们的需求，vits-simple-api 由 Artrajz 开发，使用 MIT 协议开源，使用该项目可快速调用 VITS 的相关接口&lt;br /&gt;
根据需求，我们主要调用了 voice/speakers 获取模型中所有的角色，调用 voice 进行语音合成。VITS 项目本身需要 pytorch 和 NVIDIA CUDA 的支持，这里笔者强烈建议使用一台额外的算力机搭载该项目。&lt;/p&gt;
&lt;p&gt;关于模型，我们使用了 zomehwh 基于公开语音训练的 vits-uma-genshin-honkai 模型，需要注意的是，该模型不属于本项目的一部分，详见项目仓库中的 vits-models 目录。&lt;/p&gt;
&lt;h3 id=&#34;关于生产环境&#34;&gt;&lt;a class=&#34;anchor&#34; href=&#34;#关于生产环境&#34;&gt;#&lt;/a&gt; 关于生产环境&lt;/h3&gt;
&lt;p&gt;在本地环境调试本项目无需 VPS、云服务器等主机，但若要承载多台宿主机中的虚拟化容器，或希望身处不同网络环境中的教师、管理者在家便能编辑课程等信息，项目上云是必不可少的，在后文中笔者会以个人香港服务器为例，介绍如何基于宝塔面板快速将该项目的后端和网页前端上云。&lt;/p&gt;
&lt;h2 id=&#34;研究过程&#34;&gt;&lt;a class=&#34;anchor&#34; href=&#34;#研究过程&#34;&gt;#&lt;/a&gt; 研究过程&lt;/h2&gt;
&lt;p&gt;这部分将着重讲述一下如何从零开始基于 HyperV 和本地主机打算调试环境，并简要描述如何在生产环境搭建本项目。在研究的过程中，笔者也会探究该项目的思维过程，建立需求引导解决问题的溯源性思维。&lt;/p&gt;
&lt;h3 id=&#34;实现自动打铃&#34;&gt;&lt;a class=&#34;anchor&#34; href=&#34;#实现自动打铃&#34;&gt;#&lt;/a&gt; 实现自动打铃&lt;/h3&gt;
&lt;p&gt;实现自动打铃主要分为两步，即播放音视频和呈现音视频。播放音视频是指将我们预先设置好的音视频在适当的时机播放出来，呈现音视频是指将播放的音视频以某种方式投射或呈现到腾讯会议中。&lt;/p&gt;
&lt;p&gt;定时播放音视频是一个十分常见的需求，市面上也有许多现成可复用的解决方案，但对于在线会议来说，我们的播放需求较为复杂，存在切换日程、显示状态、提示课节等需求，这也是笔者决定使用 C# + WPF 来开发客户端程序的原因。&lt;/p&gt;
&lt;p&gt;呈现音视频的难度较高，基于我们的需求分析，我们希望腾讯会议能提供一个接口或者方法来捕捉我们提供的音视频。这里读者可能联想到腾讯会议的屏幕分享功能，笔者当然也考虑并尝试过这种方式。实际上，会议室中同时只有一位用户可以屏幕分享，如果需要对打铃程序使用屏幕分享，就会严重妨碍主持人正常使用屏幕分享功能，这显然违背了我们的初心，为线上教学带来了不便，我们固然需要舍弃这种方案。&lt;/p&gt;
&lt;p&gt;反向来思考，怎样做能多个用户共享音视频呢，很容易想到，同学们在会议室中打开摄像头、打开麦克风正是一种 “共享音视频” 的形式，如果我们能对这种形式加以利用，便可以在不妨碍正常屏幕共享的前提下提供音频播放和视频播放的功能。显然，我们需要 “伪装” 一个麦克风和摄像头，来让腾讯会议主动地呈现我们的音视频。&lt;/p&gt;
&lt;p&gt;伪装麦克风，即虚拟一个麦克风，虚拟麦克风可以模拟一个麦克风硬件，该麦克风将虚拟地捕获到目标音频，这里我们希望将扬声器播放的声音重新传入虚拟麦克风，所以我们使用了 Virtual Audio Cable 软件 (下文简称 “VAC”)，该软件可以满足我们的需求&lt;/p&gt;
&lt;p&gt;伪装摄像头，即虚拟一个摄像头，虚拟的摄像头模拟了一个摄像头硬件，摄像头捕捉的画面可以由我们提供，这里我们希望客户端程序美观的界面可以被传入摄像头，方便同学们查看课节信息和下课时间，我们使用了开源的 Open Broadcaster Software 软件 (下文简称 “OBS”)，该软件可以将预设的场景变为虚拟摄像头，只需将场景中添加窗口捕获，来捕获我们的 GUI 程序，便可以满足我们的需求了&lt;/p&gt;
&lt;p&gt;解决了音视频的播放和呈现之后，另一个问题便浮出水面，显然打铃程序需要运行在一台计算机上，并且该计算机需要让 VAC 接管音频，使用 OBS 来捕捉打铃程序的 GUI 画面。该计算机最好不受外界操作的干扰，单独负责腾讯会议自动打铃的服务。不仅如此，设想一下，我们可能希望为多个会议室提供打铃服务，如何使我们的方案变得可扩展而且高灵活呢。显然，购置多台计算机来分布式完成这个工作是及其不合理的，打铃服务显然不需要极强的计算能力，购置计算机也放大了没有必要的成本，虚拟化技术便是解决该问题的唯一答案。&lt;/p&gt;
&lt;p&gt;虚拟化技术可以将一台物理机拆分为几个虚拟层，即做到一台电脑模拟多台电脑的效果。借助 Intel 的 VT-d 技术，我们可以高性能地将一台计算机的硬件 IO 虚拟化，使用统一的层来处理物理机和虚拟机的指令，大大减少虚拟化的性能损耗。这里，我们可以将物理计算机拆分为多台虚拟机，为每一个会议室单独提供虚拟的一台计算机，在虚拟计算机中运行我们上述提到的音视频播放和呈现功能。&lt;/p&gt;
&lt;p&gt;Hyper-V 是 Windows 附带的虚拟机平台，它可以批量管理多台虚拟机，并且方便地进行虚拟机复制和容器内操作，并且不需要支付额外的使用费用，这里我们使用 HyperV 来作为虚拟机容器，实际上，也可以使用 VirtualBox 等开源免费虚拟机平台。&lt;/p&gt;
&lt;p&gt;总结一下，我们可以将实现自动打铃的需求抽象成下面的流程图，这个流程图其实就是上文概念突破中的流程图 1 中的一部分&lt;/p&gt;
&lt;p&gt;&lt;img loading=&#34;lazy&#34; data-src=&#34;https://s2.loli.net/2024/01/26/U9MdWHIC13spufr.png&#34; alt=&#34;图片4.png&#34; /&gt;&lt;/p&gt;
&lt;p&gt;接下来我们进行实践，动手创建 HyperV 环境并且部署客户端相关程序，首先我们要准备 HyperV 虚拟机容器，并且将所需程序复制入虚拟机容器中&lt;/p&gt;
&lt;p&gt;首先需要在 Windows 的可选功能中开启 Hyper-V 和虚拟机平台，开启后需要重启计算机，Windows 将以 Windows 更新的方式来安装所需的组件，安装过后，就可以在开始菜单打开 Hyper-V 管理器了&lt;/p&gt;
&lt;p&gt;&lt;img loading=&#34;lazy&#34; data-src=&#34;https://s2.loli.net/2024/01/26/UqOJZpsmRyEckal.png&#34; alt=&#34;图片2.png&#34; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img loading=&#34;lazy&#34; data-src=&#34;https://s2.loli.net/2024/01/26/dLyXfitvouheZqn.png&#34; alt=&#34;图片3.png&#34; /&gt;&lt;/p&gt;
&lt;p&gt;这里笔者的 Hyper-V 中已经包含了一些虚拟机了，为了创建专门用于自动打铃的虚拟机，我们可以新建一个空白的虚拟机，点击新建 -&amp;gt; 虚拟机来创建，具体参数笔者这里不做过多介绍，仅给出一些推荐参数给读者参考&lt;/p&gt;
&lt;p&gt;内存：推荐分配 1024-2048MB，防止腾讯会议因内存不足而发生错误。硬盘：推荐分配 30GB 以上，Hyper-V 默认会按需扩张磁盘大小。系统：推荐使用 LTSB 或 LTSC 来避免 Windows 更新，同时缩减因虚拟机系统带来的不必要的性能损失&lt;/p&gt;
&lt;p&gt;接下来我们启动创建的虚拟机，完成第一次安装 Windows 的 OOBC 过程，直至成功进入系统桌面。之后我们需要安装自动打铃中涉及的一些软件，这部分笔者不做过多赘述，读者可以按照在物理机安装软件的方法来安装软件&lt;/p&gt;
&lt;p&gt;接下来我们重点介绍有关客户端开发的内容，由于代码量较大，这里笔者只能抓住几个关键点略讲一二，如若希望深入了解，可以 clone 本项目仓库，在 wakaru 目录找到相关代码，也可以在原始实验数据文档中找到所有源代码。&lt;/p&gt;
&lt;p&gt;首先，我们先总览一下项目结构，并且关注一下程序的入口&lt;/p&gt;
&lt;p&gt;&lt;img loading=&#34;lazy&#34; data-src=&#34;https://s2.loli.net/2024/01/26/yHwrpNS8dl6FcDb.png&#34; alt=&#34;Snipaste_2024-01-26_10-15-36.png&#34; /&gt;&lt;/p&gt;
&lt;p&gt;项目主要由 Online 包、Quartz 包、Views 包构成，Online 包主要包含了与后端的网络请求相关代码，是整个项目数据承载的核心。WakattaClient 描述了整个客户端，其中包含客户端的 Json 模型等内容，在右侧图 1 可以看出，WakattaClient.Create () 方法是项目的入口之一，右侧图 2 主要展示了客户端创建的过程，在软件启动之后，客户端会尝试获取 HardwareID 并且向后端请求客户端相关数据，之后会准备日程表和合成相关信息，应用当前上下课状态，并且准备 VITS。很容易看出，所有的过程都是异步方法，这意味着所有网络请求不会阻塞客户端的主线程，获取数据都在后台进行。&lt;/p&gt;
&lt;p&gt;Quartz 包主要包含了关于打铃的相关内容，ScheduleManager 类储存了日程表的实例，同时也提供一些便利的方法方便客户端创建打铃的定时任务，RingJobs 中存放了打铃的相关实现。这里打铃时会检测 VITS 是否可用，如果可用，也会将合成的音频在铃声播放过后一并播放&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-c#&#34;&gt;public class ClassBeginRingJob : RingJob
&amp;#123;
    public override Task Execute(IJobExecutionContext context)
    &amp;#123;
        if (WakattaClient.CurrentClient != null)
        &amp;#123;
            var label = context.Get(&amp;quot;label&amp;quot;)?.ToString();
            Task.Run(async () =&amp;gt; &amp;#123;
                PlaySoundSync(WakattaClient.CurrentClient.ClassBeginRingtone);
                if (label != null) await VITSManager.PlaySoundFormat(label);
            &amp;#125;);
            WakattaClient.ApplyStatus();
        &amp;#125;
        return Task.CompletedTask;
    &amp;#125;
&amp;#125;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;VITSManager 类中的 PlaySound 方法会调用上文提到的 vits-simple-api 接口，将获得的 wav 文件暂时储存在内存流中，并且让 SoundPlayer 播放该内存流。&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-c#&#34;&gt;public static async Task PlaySound(string content)
&amp;#123;
    if (!vitsObject.Enabled) return;
    if (WakattaClient.CurrentClient == null) return;
    var vitsId = WakattaClient.CurrentClient.VITSId;
    using HttpClient client = new();
    string url = vitsObject.Entrypoint + $&amp;quot;voice?id=&amp;#123;vitsId&amp;#125;&amp;amp;text=&amp;#123;content&amp;#125;&amp;quot;;
    HttpResponseMessage response = await client.GetAsync(url);
    MemoryStream memoryStream = new();
    using (Stream stream = await response.Content.ReadAsStreamAsync())
    &amp;#123;
        await stream.CopyToAsync(memoryStream);
    &amp;#125;
    memoryStream.Seek(0, SeekOrigin.Begin);

    SoundPlayer player = new(memoryStream);
    player.Play();

    response.Dispose();
    memoryStream.Dispose();
&amp;#125;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Views 包中主要包含了主页中显示的所有卡片对应的组件，这里我们以 SchedulePanel 为例简要说明一下，SchedulePanel 组件中运用了 WPF 的动态绑定，在 ListView 中展示所有的课节&lt;/p&gt;
&lt;figure class=&#34;highlight xml&#34;&gt;&lt;figcaption data-lang=&#34;XML&#34;&gt;&lt;/figcaption&gt;&lt;table&gt;&lt;tr&gt;&lt;td data-num=&#34;1&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token tag&#34;&gt;&lt;span class=&#34;token tag&#34;&gt;&lt;span class=&#34;token punctuation&#34;&gt;&amp;lt;&lt;/span&gt;ListView&lt;/span&gt; &lt;span class=&#34;token attr-name&#34;&gt;Name&lt;/span&gt;&lt;span class=&#34;token attr-value&#34;&gt;&lt;span class=&#34;token punctuation attr-equals&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;&#34;&lt;/span&gt;SchedulerListView&lt;span class=&#34;token punctuation&#34;&gt;&#34;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;2&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;span class=&#34;token tag&#34;&gt;&lt;span class=&#34;token tag&#34;&gt;&lt;span class=&#34;token punctuation&#34;&gt;&amp;lt;&lt;/span&gt;ListView.ItemTemplate&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;3&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;        &lt;span class=&#34;token tag&#34;&gt;&lt;span class=&#34;token tag&#34;&gt;&lt;span class=&#34;token punctuation&#34;&gt;&amp;lt;&lt;/span&gt;DataTemplate&lt;/span&gt; &lt;span class=&#34;token attr-name&#34;&gt;DataType&lt;/span&gt;&lt;span class=&#34;token attr-value&#34;&gt;&lt;span class=&#34;token punctuation attr-equals&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;&#34;&lt;/span&gt;&amp;#123;x:Type local:WakattaClass&amp;#125;&lt;span class=&#34;token punctuation&#34;&gt;&#34;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;4&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;            &lt;span class=&#34;token tag&#34;&gt;&lt;span class=&#34;token tag&#34;&gt;&lt;span class=&#34;token punctuation&#34;&gt;&amp;lt;&lt;/span&gt;DockPanel&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;5&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;                &lt;span class=&#34;token tag&#34;&gt;&lt;span class=&#34;token tag&#34;&gt;&lt;span class=&#34;token punctuation&#34;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&#34;token namespace&#34;&gt;materialDesign:&lt;/span&gt;PackIcon&lt;/span&gt; &lt;span class=&#34;token attr-name&#34;&gt;Margin&lt;/span&gt;&lt;span class=&#34;token attr-value&#34;&gt;&lt;span class=&#34;token punctuation attr-equals&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;&#34;&lt;/span&gt;0 1 5 0&lt;span class=&#34;token punctuation&#34;&gt;&#34;&lt;/span&gt;&lt;/span&gt; &lt;span class=&#34;token attr-name&#34;&gt;Kind&lt;/span&gt;&lt;span class=&#34;token attr-value&#34;&gt;&lt;span class=&#34;token punctuation attr-equals&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;&#34;&lt;/span&gt;Book&lt;span class=&#34;token punctuation&#34;&gt;&#34;&lt;/span&gt;&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;/&gt;&lt;/span&gt;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;6&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;                &lt;span class=&#34;token tag&#34;&gt;&lt;span class=&#34;token tag&#34;&gt;&lt;span class=&#34;token punctuation&#34;&gt;&amp;lt;&lt;/span&gt;TextBlock&lt;/span&gt; &lt;span class=&#34;token attr-name&#34;&gt;Text&lt;/span&gt;&lt;span class=&#34;token attr-value&#34;&gt;&lt;span class=&#34;token punctuation attr-equals&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;&#34;&lt;/span&gt;&amp;#123;Binding Label&amp;#125;&lt;span class=&#34;token punctuation&#34;&gt;&#34;&lt;/span&gt;&lt;/span&gt; &lt;span class=&#34;token attr-name&#34;&gt;HorizontalAlignment&lt;/span&gt;&lt;span class=&#34;token attr-value&#34;&gt;&lt;span class=&#34;token punctuation attr-equals&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;&#34;&lt;/span&gt;Center&lt;span class=&#34;token punctuation&#34;&gt;&#34;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;token tag&#34;&gt;&lt;span class=&#34;token tag&#34;&gt;&lt;span class=&#34;token punctuation&#34;&gt;&amp;lt;/&lt;/span&gt;TextBlock&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;7&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;                &lt;span class=&#34;token tag&#34;&gt;&lt;span class=&#34;token tag&#34;&gt;&lt;span class=&#34;token punctuation&#34;&gt;&amp;lt;&lt;/span&gt;Separator&lt;/span&gt; &lt;span class=&#34;token attr-name&#34;&gt;Visibility&lt;/span&gt;&lt;span class=&#34;token attr-value&#34;&gt;&lt;span class=&#34;token punctuation attr-equals&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;&#34;&lt;/span&gt;Hidden&lt;span class=&#34;token punctuation&#34;&gt;&#34;&lt;/span&gt;&lt;/span&gt; &lt;span class=&#34;token attr-name&#34;&gt;Width&lt;/span&gt;&lt;span class=&#34;token attr-value&#34;&gt;&lt;span class=&#34;token punctuation attr-equals&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;&#34;&lt;/span&gt;20&lt;span class=&#34;token punctuation&#34;&gt;&#34;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;/&gt;&lt;/span&gt;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;8&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;                &lt;span class=&#34;token tag&#34;&gt;&lt;span class=&#34;token tag&#34;&gt;&lt;span class=&#34;token punctuation&#34;&gt;&amp;lt;&lt;/span&gt;StackPanel&lt;/span&gt; &lt;span class=&#34;token attr-name&#34;&gt;Orientation&lt;/span&gt;&lt;span class=&#34;token attr-value&#34;&gt;&lt;span class=&#34;token punctuation attr-equals&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;&#34;&lt;/span&gt;Horizontal&lt;span class=&#34;token punctuation&#34;&gt;&#34;&lt;/span&gt;&lt;/span&gt; &lt;span class=&#34;token attr-name&#34;&gt;HorizontalAlignment&lt;/span&gt;&lt;span class=&#34;token attr-value&#34;&gt;&lt;span class=&#34;token punctuation attr-equals&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;&#34;&lt;/span&gt;Right&lt;span class=&#34;token punctuation&#34;&gt;&#34;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;9&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;                    &lt;span class=&#34;token tag&#34;&gt;&lt;span class=&#34;token tag&#34;&gt;&lt;span class=&#34;token punctuation&#34;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&#34;token namespace&#34;&gt;materialDesign:&lt;/span&gt;Chip&lt;/span&gt; &lt;span class=&#34;token attr-name&#34;&gt;Content&lt;/span&gt;&lt;span class=&#34;token attr-value&#34;&gt;&lt;span class=&#34;token punctuation attr-equals&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;&#34;&lt;/span&gt;&amp;#123;Binding TimeString&amp;#125;&lt;span class=&#34;token punctuation&#34;&gt;&#34;&lt;/span&gt;&lt;/span&gt; &lt;span class=&#34;token attr-name&#34;&gt;Padding&lt;/span&gt;&lt;span class=&#34;token attr-value&#34;&gt;&lt;span class=&#34;token punctuation attr-equals&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;&#34;&lt;/span&gt;0&lt;span class=&#34;token punctuation&#34;&gt;&#34;&lt;/span&gt;&lt;/span&gt; &lt;span class=&#34;token attr-name&#34;&gt;Margin&lt;/span&gt;&lt;span class=&#34;token attr-value&#34;&gt;&lt;span class=&#34;token punctuation attr-equals&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;&#34;&lt;/span&gt;0&lt;span class=&#34;token punctuation&#34;&gt;&#34;&lt;/span&gt;&lt;/span&gt; &lt;span class=&#34;token attr-name&#34;&gt;Height&lt;/span&gt;&lt;span class=&#34;token attr-value&#34;&gt;&lt;span class=&#34;token punctuation attr-equals&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;&#34;&lt;/span&gt;20&lt;span class=&#34;token punctuation&#34;&gt;&#34;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;token tag&#34;&gt;&lt;span class=&#34;token tag&#34;&gt;&lt;span class=&#34;token punctuation&#34;&gt;&amp;lt;/&lt;/span&gt;&lt;span class=&#34;token namespace&#34;&gt;materialDesign:&lt;/span&gt;Chip&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;10&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;                    &lt;span class=&#34;token tag&#34;&gt;&lt;span class=&#34;token tag&#34;&gt;&lt;span class=&#34;token punctuation&#34;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&#34;token namespace&#34;&gt;materialDesign:&lt;/span&gt;Chip&lt;/span&gt; &lt;span class=&#34;token attr-name&#34;&gt;Content&lt;/span&gt;&lt;span class=&#34;token attr-value&#34;&gt;&lt;span class=&#34;token punctuation attr-equals&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;&#34;&lt;/span&gt;&amp;#123;Binding TimeDurationString&amp;#125;&lt;span class=&#34;token punctuation&#34;&gt;&#34;&lt;/span&gt;&lt;/span&gt; &lt;span class=&#34;token attr-name&#34;&gt;Padding&lt;/span&gt;&lt;span class=&#34;token attr-value&#34;&gt;&lt;span class=&#34;token punctuation attr-equals&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;&#34;&lt;/span&gt;0&lt;span class=&#34;token punctuation&#34;&gt;&#34;&lt;/span&gt;&lt;/span&gt; &lt;span class=&#34;token attr-name&#34;&gt;Margin&lt;/span&gt;&lt;span class=&#34;token attr-value&#34;&gt;&lt;span class=&#34;token punctuation attr-equals&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;&#34;&lt;/span&gt;0&lt;span class=&#34;token punctuation&#34;&gt;&#34;&lt;/span&gt;&lt;/span&gt; &lt;span class=&#34;token attr-name&#34;&gt;Height&lt;/span&gt;&lt;span class=&#34;token attr-value&#34;&gt;&lt;span class=&#34;token punctuation attr-equals&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;&#34;&lt;/span&gt;20&lt;span class=&#34;token punctuation&#34;&gt;&#34;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;token tag&#34;&gt;&lt;span class=&#34;token tag&#34;&gt;&lt;span class=&#34;token punctuation&#34;&gt;&amp;lt;/&lt;/span&gt;&lt;span class=&#34;token namespace&#34;&gt;materialDesign:&lt;/span&gt;Chip&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;11&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;                &lt;span class=&#34;token tag&#34;&gt;&lt;span class=&#34;token tag&#34;&gt;&lt;span class=&#34;token punctuation&#34;&gt;&amp;lt;/&lt;/span&gt;StackPanel&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;12&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;            &lt;span class=&#34;token tag&#34;&gt;&lt;span class=&#34;token tag&#34;&gt;&lt;span class=&#34;token punctuation&#34;&gt;&amp;lt;/&lt;/span&gt;DockPanel&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;13&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;        &lt;span class=&#34;token tag&#34;&gt;&lt;span class=&#34;token tag&#34;&gt;&lt;span class=&#34;token punctuation&#34;&gt;&amp;lt;/&lt;/span&gt;DataTemplate&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;14&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;span class=&#34;token tag&#34;&gt;&lt;span class=&#34;token tag&#34;&gt;&lt;span class=&#34;token punctuation&#34;&gt;&amp;lt;/&lt;/span&gt;ListView.ItemTemplate&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;15&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token tag&#34;&gt;&lt;span class=&#34;token tag&#34;&gt;&lt;span class=&#34;token punctuation&#34;&gt;&amp;lt;/&lt;/span&gt;ListView&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;&lt;pre&gt;&lt;code class=&#34;language-c#&#34;&gt;public WakattaSchedule()
&amp;#123;
    Instance = this;
    InitializeComponent();
    SchedulerListView.ItemsSource = ClassList;
    SetSelectedIndex(Utils.IndexClass(ClassList));
&amp;#125;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;只需给 ListView 的 ItemsSource 指定一个集合，便可做到动态绑定，当集合中元素被修改时，UI 中对应的列表也会随着修改&lt;/p&gt;
&lt;p&gt;接下来运行 wakaru 项目，并且运行必要的服务端，即可看到客户端界面&lt;/p&gt;
&lt;p&gt;&lt;img loading=&#34;lazy&#34; data-src=&#34;https://s2.loli.net/2024/01/26/kos6m2JtSvZKrq3.png&#34; alt=&#34;图片5.png&#34; /&gt;&lt;/p&gt;
&lt;p&gt;接下来我们将把客户端部署到虚拟机中，注意的是，由于该项目经历过多次迭代，虚拟机中的 UI 已经与目前开发进度中的 UI 样式不同，不过这并不影响部署到虚拟机中这一过程的说明与讲解。&lt;/p&gt;
&lt;p&gt;我们将编译好的打铃软件复制到虚拟机中，&lt;a href=&#34;http://xn--phqx14afic551e.NET&#34;&gt;并且安装.NET&lt;/a&gt; 6.0 运行时，保证虚拟机能够运行打铃软件，为了使 SoundPlayer 播放的音频正确输入到虚拟声卡中，我们需要将系统输出音频设置为 CABLE Input (VB-Audio Virtual Cable)，C# 的 SoundPlayer 将在系统当前默认扬声器中播放声音。&lt;/p&gt;
&lt;p&gt;这里读者可能遇到虚拟机的音频仅有 “远程音频” 这一个选项，这是因为 Hyper-V 增强会话默认会启用音频转发，将虚拟机中的音频自动转发到宿主机，这显然不满足我们的预期，这里我们可以在连接虚拟机的时候不使用增强会话，即出现 “连接至 XX” 对话框时不点击连接，直接关掉该对话框，即可正确识别虚拟机内安装的虚拟声卡服务。&lt;/p&gt;
&lt;p&gt;增强会话同时也会导致关闭虚拟机连接时使远程计算机自动休眠，这会导致腾讯会议的麦克风自动关闭，自动打铃受到阻碍，也可以通过上述方法来禁用增强会话，从而使自动打铃服务可以完全隐藏在宿主机的后台静默运行。&lt;/p&gt;
&lt;p&gt;接下来我们需要配置 OBS 的场景，来将打铃程序的界面呈现到腾讯会议中，注意，为了使 OBS 支持虚拟摄像头功能，需要安装版本号为 26 以上的 OBS。&lt;/p&gt;
&lt;p&gt;Hyper-V 默认没有虚拟化 NVENC QSV 等编码器的能力，这意味着我们开启虚拟摄像头时需要利用 CPU 的 x264 进行软件编码，我们最好适量降低 OBS 的编码质量来达到最大的能效比，这里给出笔者使用的参数来供读者参考。编码器：软件（x264），比特率：200 Kbps，输出分辨率：786x446，缩小方法：双直线法，帧率：10FPS。这里输出分辨率可以根据打铃程序界面的大小进行合理的调整。&lt;/p&gt;
&lt;p&gt;之后我们创建 “窗口采集” 作为来源，并且选中打铃程序的 GUI 窗体，接下来便能看到 OBS 中已经成功捕获打铃程序的界面，这里需要注意的是，OBS 无法捕捉最小化的窗口，这里我们需要保证打铃程序位于前台。&lt;/p&gt;
&lt;p&gt;勾选启动虚拟摄像机并启动腾讯会议，接下来我们将在腾讯会议中应用我们配置的虚拟摄像头和虚拟声卡。&lt;/p&gt;
&lt;p&gt;摄像头中选中 OBS Virtual Camera，麦克风选中 CABLE Output (VB-Audio Virtual Cable)，音频选项可以按照下图设置，以保证铃声音乐播放的流畅性&lt;/p&gt;
&lt;p&gt;&lt;img loading=&#34;lazy&#34; data-src=&#34;https://s2.loli.net/2024/01/26/SgpomVOfCXyIqFL.png&#34; alt=&#34;图片10.png&#34; /&gt;&lt;/p&gt;
&lt;p&gt;至此，我们完成了客户端部分的搭建&lt;/p&gt;
&lt;p&gt;如果读者有多会议室的需求，也可以克隆虚拟机，同时运行多个虚拟机以承载多个会议室的自动打铃服务，这里笔者不再过多赘述。除此之外，当读者想要关闭虚拟机时，可以使用 Hyper-V 虚拟机的 “保存” 功能，该功能将拍摄当前虚拟机状态为一个快照，方便下次启动虚拟机时还原快照，避免重复配置多次。&lt;/p&gt;
&lt;h3 id=&#34;搭建后端与网页前端&#34;&gt;&lt;a class=&#34;anchor&#34; href=&#34;#搭建后端与网页前端&#34;&gt;#&lt;/a&gt; 搭建后端与网页前端&lt;/h3&gt;
&lt;p&gt;项目仓库地址：&lt;a href=&#34;https://github.com/TrueRou/wakatta&#34;&gt;https://github.com/TrueRou/wakatta&lt;/a&gt;&lt;br /&gt;
 关于安装 Python3 和 Nodejs 开发环境这里笔者不再过多赘述，笔者假设读者具有完成基本软件开发的能力，下面读者可以同笔者一起搭建该项目的后端与网页前端，并且将其部署到生产环境&lt;/p&gt;
&lt;p&gt;首先，为了部署项目，需要先拉取前后端的代码，需要注意的是，该项目已经在 Github 上开源，读者可以 Fork 或者 Clone 项目的源码到自己的仓库，或者调试机器、生产环境机器上。注意，本项目遵循 GPLv3 开源协议。这里的部署代码适用于 Linux，如果在 Windows 上部署大同小异，这里笔者不过多赘述&lt;/p&gt;
&lt;p&gt;首先，打开终端，克隆项目并安装相关依赖，&lt;a href=&#34;http://xn--config-9q7i412b8ywr58cpfku8mjt0d.py&#34;&gt;并且配置后端的 config.py&lt;/a&gt;&lt;/p&gt;
&lt;figure class=&#34;highlight bash&#34;&gt;&lt;figcaption data-lang=&#34;bash&#34;&gt;&lt;/figcaption&gt;&lt;table&gt;&lt;tr&gt;&lt;td data-num=&#34;1&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token function&#34;&gt;git&lt;/span&gt; clone https://github.com/TrueRou/wakatta.git&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;2&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token builtin class-name&#34;&gt;cd&lt;/span&gt; wakatta&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt; pip &lt;span class=&#34;token function&#34;&gt;install&lt;/span&gt; &lt;span class=&#34;token parameter variable&#34;&gt;-r&lt;/span&gt; requirements.txt&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;3&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token builtin class-name&#34;&gt;cd&lt;/span&gt; wakatta-web&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt; &lt;span class=&#34;token function&#34;&gt;npm&lt;/span&gt; &lt;span class=&#34;token function&#34;&gt;install&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;&lt;p&gt;之后，在 wakatta 项目下运行 python3 main.py 即可启动后端项目，在 wakatta-web 下运行 npm run dev 即可在调试环境下临时运行前端项目，实际上，我们可以将前端项目使用 npm run build 语句构建成静态文件，使用 nginx 进行托管，这部分笔者将在下面生产环境部署中详细讲解&lt;/p&gt;
&lt;p&gt;接下来我们将抽出后端项目中的几个细节进行简单讲解，实际上，部分逻辑在上面的技术解读中已经讲解过，这里我们将展示相关的代码。&lt;/p&gt;
&lt;p&gt;&lt;img loading=&#34;lazy&#34; data-src=&#34;https://s2.loli.net/2024/01/26/pFoOl2W7ETB3DIc.png&#34; alt=&#34;图片6.png&#34; /&gt;&lt;/p&gt;
&lt;p&gt;在 models.py 中，我们定义了数据库 ORM 中的实体类，实际上，根据笔者的经验，使用 Python 构建前后端项目时，实体类往往有两种存在形式，即服务于 SQLAlchemy 的实体类，&lt;a href=&#34;http://xn--models-vy7i7j146b85ak72gyt9d4on0i6c4dg.py&#34;&gt;这里笔者习惯命名为 models.py&lt;/a&gt;，还有服务于 FastAPI 的 Pydantic 实体类，这部分主要用于后端请求的类型检查和 response_model，&lt;a href=&#34;http://xn--schemas-e73k5k441cg9aly3g694e9pos59cesg.py&#34;&gt;这里笔者习惯命名为 schemas.py&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;利用全 ORM 的好处便是易于指定 relationship，这也是关系型数据的关键之一，这里比如客户端应该拥有多个课程，通过外键 class.id 连接，客户端可能有一个订阅的日程表，通过外键 schedule.id 连接，这里读者可以自行阅读相关的代码，或者查阅 SQLAlchemy 的文档，需要注意的是，这里笔者使用的是 SQLAlchemy2.0，读者需要自行阅读较新的文档，或者阅读了开启 future=True 的较旧文档&lt;/p&gt;
&lt;p&gt;&lt;img loading=&#34;lazy&#34; data-src=&#34;https://s2.loli.net/2024/01/26/jyIrkluwpSBA9iP.png&#34; alt=&#34;图片7.png&#34; /&gt;&lt;/p&gt;
&lt;p&gt;在 sessions.py 中，储存了 online_clients 列表，client_data 字典，以及 client_packets 字典，client_data 主要包含客户端运行返回的实时数据，通常包含在心跳包的请求中，client_packets 则是上文提到过的消息缓存队列，会在客户端发送心跳包时取出&lt;/p&gt;
&lt;p&gt;tick_session 方法会在每秒执行，主要为了踢出不活跃的客户端，保持 online_clients 列表有效，而 tick_client 方法则是每次心跳包送达时被执行，主要是为了记录客户端活跃时间，方便超时踢出&lt;/p&gt;
&lt;p&gt;&lt;img loading=&#34;lazy&#34; data-src=&#34;https://s2.loli.net/2024/01/26/wAbhe2Np1t7kYPH.png&#34; alt=&#34;图片8.png&#34; /&gt;&lt;/p&gt;
&lt;p&gt;在 schedule.py 中，当日程表有应用操作时，会调用 apply_schedule 方法，具体逻辑可以参考技术解读部分&lt;/p&gt;
&lt;p&gt;前端部分基于 vue3 和 element-plus，鉴于前端项目较差的组织性，笔者这里不单独展开分析前端的代码，仅放出前端的部分界面，使读者感受到前端的可操作性&lt;/p&gt;
&lt;p&gt;&lt;img loading=&#34;lazy&#34; data-src=&#34;https://s2.loli.net/2024/01/26/mHuaXIpfFqVOxSg.png&#34; alt=&#34;图片9.png&#34; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img loading=&#34;lazy&#34; data-src=&#34;https://s2.loli.net/2024/01/26/JwKpmT2AkeYSOl8.png&#34; alt=&#34;图片10.png&#34; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img loading=&#34;lazy&#34; data-src=&#34;https://s2.loli.net/2024/01/26/VXY7Il4n9d5zHLh.png&#34; alt=&#34;图片11.png&#34; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img loading=&#34;lazy&#34; data-src=&#34;https://s2.loli.net/2024/01/26/nwf84Q9WPYjgD1t.png&#34; alt=&#34;图片12.png&#34; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img loading=&#34;lazy&#34; data-src=&#34;https://s2.loli.net/2024/01/26/zsFC2OeZMX4akKL.png&#34; alt=&#34;图片13.png&#34; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img loading=&#34;lazy&#34; data-src=&#34;https://s2.loli.net/2024/01/26/C7Za345LDEdQNXS.png&#34; alt=&#34;图片14.png&#34; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img loading=&#34;lazy&#34; data-src=&#34;https://s2.loli.net/2024/01/26/4qgsL6ZSxRJtDmv.png&#34; alt=&#34;图片15.png&#34; /&gt;&lt;/p&gt;
&lt;h3 id=&#34;生产环境搭建&#34;&gt;&lt;a class=&#34;anchor&#34; href=&#34;#生产环境搭建&#34;&gt;#&lt;/a&gt; 生产环境搭建&lt;/h3&gt;
&lt;p&gt;接下来我们讲述一下如何在生产环境搭建该项目，我们主要以反向代理 nginx 和 frp 的配置为主，其他搭建方式可以参考上文调试环境搭建的相关内容&lt;/p&gt;
&lt;p&gt;在配置前，请确保云服务器已经搭载宝塔面板，并且安装了 Nginx 和 MySQL 的较新版本，这里笔者不推荐使用 MySQL8，MySQL8 的性能弱于 5.5。之后，将 MySQL 生成的账号密码填入 wakatta 项目的 config.py 中，之后运行后端项目&lt;/p&gt;
&lt;p&gt;接下来我们为后端项目配置反代，我们希望在 “&lt;a href=&#34;https://xn--eqrt2g/wakatta/api%E2%80%9D%E8%B7%AF%E5%BE%84%E4%B8%AD%E8%AE%BF%E9%97%AE%E5%88%B0%E5%90%8E%E7%AB%AF%EF%BC%8C%E6%89%80%E4%BB%A5%E6%88%91%E4%BB%AC%E4%BF%AE%E6%94%B9%E5%AF%B9%E5%BA%94%E5%9F%9F%E5%90%8D%E7%9A%84%E9%85%8D%E7%BD%AE%E6%96%87%E4%BB%B6&#34;&gt;https:// 域名 /wakatta/api” 路径中访问到后端，所以我们修改对应域名的配置文件&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;img loading=&#34;lazy&#34; data-src=&#34;https://s2.loli.net/2024/01/26/TmepUhZogOqlQKV.png&#34; alt=&#34;图片16.png&#34; /&gt;&lt;/p&gt;
&lt;p&gt;使用 nginx 提供的 proxy_pass，即可完成反代&lt;/p&gt;
&lt;p&gt;接下来我们部署网页前端，进入 wakatta-web 目录，输入 npm run build 来构建出静态页面，并且把静态页面复制到宝塔对应网站目录下，这里需要注意的是，请保证前端项目 config.js 中对应的后端地址已经指向生产环境的后端地址，而不是之前调试环境中的。为了免除不必要的跨域问题，请不要把 API 和前端放在不同域名下，在 nginx 中，我们可以使用 alias 进行目录的路由&lt;/p&gt;
&lt;p&gt;&lt;img loading=&#34;lazy&#34; data-src=&#34;https://s2.loli.net/2024/01/26/Vng7PXcLKtabkYI.png&#34; alt=&#34;图片17.png&#34; /&gt;&lt;/p&gt;
&lt;p&gt;这样当我们访问&lt;a href=&#34;https://xn--eqrt2g/wakatta%EF%BC%8C%E5%B0%B1%E4%BC%9A%E8%A2%AB%E8%B7%B3%E8%BD%AC%E5%88%B0%E6%88%91%E4%BB%AC%E7%9A%84%E9%9D%99%E6%80%81%E9%A1%B5%E9%9D%A2%E4%B8%AD%EF%BC%8C%E8%87%B3%E6%AD%A4%EF%BC%8C%E5%89%8D%E5%90%8E%E7%AB%AF%E7%9A%84%E9%83%A8%E7%BD%B2%E5%AE%8C%E6%AF%95%E3%80%82&#34;&gt; https:// 域名 /wakatta，就会被跳转到我们的静态页面中，至此，前后端的部署完毕。&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Frp 的配置要点主要在于配置 frps.ini 和 frpc.ini，这里限于篇幅，笔者决定不再展开叙述，这部分内容读者可以自行阅读 frp 的相关文档&lt;/p&gt;
&lt;h3 id=&#34;后记&#34;&gt;&lt;a class=&#34;anchor&#34; href=&#34;#后记&#34;&gt;#&lt;/a&gt; 后记&lt;/h3&gt;
&lt;h4 id=&#34;性能调优&#34;&gt;&lt;a class=&#34;anchor&#34; href=&#34;#性能调优&#34;&gt;#&lt;/a&gt; 性能调优&lt;/h4&gt;
&lt;p&gt;我们希望打铃服务能尽可能减少性能的占用，以提高能效比，在单位物理机中可容纳更多的打铃服务，这里笔者给出自己的几点看法和思路。&lt;/p&gt;
&lt;p&gt;首先，腾讯会议本身会随着会议室人数的增多而占用更多处理单位，我们可以通过关闭接收参会者视频的方式来缓解这个问题。在会议室的常规设置中勾选语音模式，即可暂停接收参会者视频，降低性能消耗。&lt;/p&gt;
&lt;p&gt;同时，腾讯会议可能默认会对虚拟摄像头的画面进行降噪或者美颜处理，这也会导致没有必要的性能开销，这里我们进入视频设置，取消勾选所有的对钩，来关闭所有的视频处理功能。&lt;br /&gt;
对于 OBS，其内置的场景预览也会造成一定的性能消耗，这里可以右键预览的部分，将开启预览的功能关闭。如果读者不需要虚拟摄像头展示打铃情况，仅仅需要播放打铃音乐的功能，也可以跳过 OBS 的部署，彻底消除编码器造成的性能消耗。&lt;/p&gt;
&lt;p&gt;针对操作系统层面，精简程度较高的系统能减少额外开销，如果可以的话可以选择 32 位的系统来降低内存需求，但这里笔者不推荐使用 Windows 7，Windows 8 等操作系统，这会降低程序运行的稳定性，因为这些系统已经不再被微软维护，&lt;a href=&#34;http://xn--fiqw3f7wgypby58ey3za18k.NET&#34;&gt;在其环境中运行.NET&lt;/a&gt; 6.0 程序也可能产生不必要的麻烦。这里笔者使用的是极限精简的 Tiny10 镜像，这里不再过多赘述&lt;/p&gt;
&lt;h4 id=&#34;后记-2&#34;&gt;&lt;a class=&#34;anchor&#34; href=&#34;#后记-2&#34;&gt;#&lt;/a&gt; 后记&lt;/h4&gt;
&lt;p&gt;从 2023 年 2 月，笔者完成了 Wakaru 的第一个版本，到 2023 年 5 月，笔者将项目完全重构。从单一的 C# + WPF 桌面项目，到综合三个语言的客户端 + 后台集成项目，笔者在紧张的开发周期中也学到了许多，获得了许多，笔者认为，创新本身不仅仅在于研究结果，更在于创新的研究思路和研究过程。在这个过程中，我们创新的不仅仅是知识本身，更是获取知识的方式。&lt;/p&gt;
</content>
        <category term="比赛作品" />
        <category term="腾讯会议" />
        <updated>2023-08-20T03:19:59.000Z</updated>
    </entry>
    <entry>
        <id>https://turou.fun/contest/tencent-rings/</id>
        <title>基于HyperV和VAC的腾讯会议自动打铃解决方案</title>
        <link rel="alternate" href="https://turou.fun/contest/tencent-rings/"/>
        <content type="html">&lt;p&gt;&lt;strong&gt;第 37 届吉林省青少年科技创新大赛 参赛作品&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;荣获第 37 届吉林省青少年科技创新大赛 最佳作品奖&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;img loading=&#34;lazy&#34; data-src=&#34;https://s2.loli.net/2024/01/26/QNbg8OYEeFRrHyW.jpg&#34; alt=&#34;1706233693783.jpg&#34; /&gt;&lt;/p&gt;
&lt;h2 id=&#34;研究目的&#34;&gt;&lt;a class=&#34;anchor&#34; href=&#34;#研究目的&#34;&gt;#&lt;/a&gt; 研究目的&lt;/h2&gt;
&lt;p&gt;为腾讯会议创建的会议室提供一套易部署、高灵活、可复制的自动打铃解决方案&lt;/p&gt;
&lt;h2 id=&#34;研究方法&#34;&gt;&lt;a class=&#34;anchor&#34; href=&#34;#研究方法&#34;&gt;#&lt;/a&gt; 研究方法&lt;/h2&gt;
&lt;p&gt;使用从需求到实现的溯源性思维，结合现有的虚拟化 (VT-d) 技术，虚拟音视频技术，利用 C# 编程语言，提供了一套切实可行的解决方案&lt;/p&gt;
&lt;h2 id=&#34;研究结论&#34;&gt;&lt;a class=&#34;anchor&#34; href=&#34;#研究结论&#34;&gt;#&lt;/a&gt; 研究结论&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;腾讯会议本身未提供第三方播放音视频的接口，需要模拟一位用户来播放音视频&lt;/li&gt;
&lt;li&gt;播放音频本质是腾讯会议监听系统麦克风，为了将本地音频模拟成麦克风，需要借助虚拟声卡等软件模拟系统麦克风&lt;/li&gt;
&lt;li&gt;播放视频本质是腾讯会议调用系统摄像头，为了将目标画面投射到系统摄像头中，需要借助 Open Broadcaster Software 等软件模拟摄像头&lt;/li&gt;
&lt;li&gt;为了按需求灵活适应打铃的时间需求，需要编写一个 GUI 程序来实时显示日程状态，并且在适当时机播放打铃音频&lt;/li&gt;
&lt;li&gt;C# + WPF 可以编写美观且高性能的 GUI 程序&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;前言&#34;&gt;&lt;a class=&#34;anchor&#34; href=&#34;#前言&#34;&gt;#&lt;/a&gt; 前言&lt;/h2&gt;
&lt;p&gt;腾讯会议作为目前最流行的在线即时会议软件之一，在空中课堂、网上教学等领域发挥重要作用。笔者作为腾讯会议网上教学的受益者之一，在使用中逐渐对腾讯会议的优缺点有了更深的了解，这个过程中，上下课铃播放的问题逐渐引起了笔者的注意。&lt;/p&gt;
&lt;p&gt;在过去，教师往往需要时刻注意当前的时间，使网上教学工作量加大，如果将提示上下课的工作交给学生，也会一定程度影响学生的上课质量和上课效率，如何将打铃的过程自动化，成为了笔者近期思考的问题。&lt;/p&gt;
&lt;p&gt;经过笔者的思考和对相关技术的学习和了解，笔者撰写了此文，希望能对未来的后学有启示作用。&lt;/p&gt;
&lt;h2 id=&#34;研究过程&#34;&gt;&lt;a class=&#34;anchor&#34; href=&#34;#研究过程&#34;&gt;#&lt;/a&gt; 研究过程&lt;/h2&gt;
&lt;h3 id=&#34;确定研究思路&#34;&gt;&lt;a class=&#34;anchor&#34; href=&#34;#确定研究思路&#34;&gt;#&lt;/a&gt; 确定研究思路&lt;/h3&gt;
&lt;p&gt;实现自动打铃主要分为两步，即播放音视频和呈现音视频。播放音视频是指将我们预先设置好的音视频在适当的时机播放出来，呈现音视频是指将播放的音视频以某种方式投射或呈现到腾讯会议中。&lt;/p&gt;
&lt;p&gt;定时播放音视频是一个十分常见的需求，市面上也有许多现成可复用的解决方案，但对于空中课堂来说，我们的播放需求较为复杂，存在切换日程、显示状态、提示课节等需求，这里笔者决定使用目前流行的 Windows 桌面程序开发方案 C# + WPF 来开发定时播放音视频的程序 (下文简称 “打铃程序”)&lt;br /&gt;
 呈现音视频的难度较高，基于我们的需求分析，我们希望腾讯会议能提供一个接口或者方法来捕捉我们提供的音视频。这里读者可能联想到腾讯会议的屏幕分享功能，笔者当然也考虑并尝试过这种方式。实际上，会议室中同时只有一位用户可以屏幕分享，如果需要对打铃程序使用屏幕分享，就会严重妨碍主持人正常使用屏幕分享功能，这显然违背了我们的初心，为线上教学带来了不便，我们固然需要舍弃这种方案。&lt;/p&gt;
&lt;p&gt;反向来思考，怎样做能多个用户共享音视频呢，很容易想到，同学们在会议室中打开摄像头、打开麦克风正是一种 “共享音视频” 的形式，如果我们能对这种形式加以利用，便可以在不妨碍正常屏幕共享的前提下提供音频播放和视频播放的功能。显然，我们需要 “伪装” 一个麦克风和摄像头，来让腾讯会议主动地呈现我们的音视频。&lt;/p&gt;
&lt;p&gt;伪装麦克风，即虚拟一个麦克风，虚拟麦克风可以模拟一个麦克风硬件，该麦克风将虚拟地捕获到目标音频，这里我们希望将扬声器播放的声音重新传入虚拟麦克风，所以我们使用了 Virtual Audio Cable 软件 (下文简称 “VAC”)，该软件可以满足我们的需求&lt;/p&gt;
&lt;p&gt;伪装摄像头，即虚拟一个摄像头，虚拟的摄像头模拟了一个摄像头硬件，摄像头捕捉的画面可以由我们提供，这里我们希望打铃程序美观的界面可以被传入摄像头，方便同学们查看课节信息和下课时间，我们使用了开源的 Open Broadcaster Software 软件 (下文简称 “OBS”)，该软件可以将预设的场景变为虚拟摄像头，只需将场景中添加窗口捕获，来捕获我们的 GUI 程序，便可以满足我们的需求了&lt;br /&gt;
解决了音视频的播放和呈现之后，另一个问题便浮出水面，显然打铃程序需要运行在一台计算机上，并且该计算机需要让 VAC 接管音频，使用 OBS 来捕捉打铃程序的 GUI 画面。该计算机最好不受外界操作的干扰，单独负责腾讯会议自动打铃的服务。不仅如此，设想一下，我们可能希望为多个会议室提供打铃服务，如何使我们的方案变得可扩展而且高灵活呢。显然，购置多台计算机来分布式完成这个工作是及其不合理的，打铃服务显然不需要极强的计算能力，购置计算机也放大了没有必要的成本，虚拟化技术便是解决该问题的唯一答案。&lt;/p&gt;
&lt;p&gt;虚拟化技术可以将一台物理机拆分为几个虚拟层，即做到一台电脑模拟多台电脑的效果。借助 Intel 的 VT-d 技术，我们可以高性能地将一台计算机的硬件 IO 虚拟化，使用统一的层来处理物理机和虚拟机的指令，大大减少虚拟化的性能损耗。这里，我们可以将物理计算机拆分为多台虚拟机，为每一个会议室单独提供虚拟的一台计算机，在虚拟计算机中运行我们上述提到的音视频播放和呈现功能。&lt;/p&gt;
&lt;p&gt;Hyper-V 是 Windows 附带的虚拟机平台，它可以批量管理多台虚拟机，并且方便地进行虚拟机复制和容器内操作，并且不需要支付额外的使用费用，这里我们使用 HyperV 来作为虚拟机容器，实际上，也可以使用 VirtualBox 等开源免费虚拟机平台。&lt;/p&gt;
&lt;p&gt;总结一下，我们可以将实现自动打铃的需求抽象成下面的流程图&lt;/p&gt;
&lt;p&gt;&lt;img loading=&#34;lazy&#34; data-src=&#34;https://s2.loli.net/2024/01/26/AOhWmTYf1lZouvz.png&#34; alt=&#34;图片1.png&#34; /&gt;&lt;/p&gt;
&lt;h3 id=&#34;进行实践&#34;&gt;&lt;a class=&#34;anchor&#34; href=&#34;#进行实践&#34;&gt;#&lt;/a&gt; 进行实践&lt;/h3&gt;
&lt;h4 id=&#34;准备hyper-v虚拟机容器&#34;&gt;&lt;a class=&#34;anchor&#34; href=&#34;#准备hyper-v虚拟机容器&#34;&gt;#&lt;/a&gt; 准备 Hyper-V 虚拟机容器&lt;/h4&gt;
&lt;p&gt;首先需要在 Windows 的可选功能中开启 Hyper-V 和虚拟机平台，开启后需要重启计算机，Windows 将以 Windows 更新的方式来安装所需的组件，安装过后，就可以在开始菜单打开 Hyper-V 管理器了&lt;/p&gt;
&lt;p&gt;&lt;img loading=&#34;lazy&#34; data-src=&#34;https://s2.loli.net/2024/01/26/UqOJZpsmRyEckal.png&#34; alt=&#34;图片2.png&#34; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img loading=&#34;lazy&#34; data-src=&#34;https://s2.loli.net/2024/01/26/dLyXfitvouheZqn.png&#34; alt=&#34;图片3.png&#34; /&gt;&lt;/p&gt;
&lt;p&gt;这里笔者的 Hyper-V 中已经包含了一些虚拟机了，为了创建专门用于自动打铃的虚拟机，我们可以新建一个空白的虚拟机，点击新建 -&amp;gt; 虚拟机来创建，具体参数笔者这里不做过多介绍，仅给出一些推荐参数给读者参考&lt;/p&gt;
&lt;p&gt;内存：推荐分配 1024-2048MB，防止腾讯会议因内存不足而发生错误。硬盘：推荐分配 30GB 以上，Hyper-V 默认会按需扩张磁盘大小。系统：推荐使用 LTSB 或 LTSC 来避免 Windows 更新，同时缩减因虚拟机系统带来的不必要的性能损失&lt;/p&gt;
&lt;p&gt;接下来我们启动创建的虚拟机，完成第一次安装 Windows 的 OOBC 过程，直至成功进入系统桌面。之后我们需要安装自动打铃中涉及的一些软件，这部分笔者不做过多赘述，读者可以按照在物理机安装软件的方法来安装软件。&lt;/p&gt;
&lt;p&gt;这里我们需要安装腾讯会议，OBS，和 VAC 软件，VAC 软件可能在安装的过程中需要装额外的音频驱动，这里我们选择同意安装驱动即可。&lt;/p&gt;
&lt;p&gt;至此，对于虚拟机的提前准备工作已经完成，我们可以着手进行打铃程序的开发了&lt;/p&gt;
&lt;h4 id=&#34;开发打铃程序&#34;&gt;&lt;a class=&#34;anchor&#34; href=&#34;#开发打铃程序&#34;&gt;#&lt;/a&gt; 开发打铃程序&lt;/h4&gt;
&lt;p&gt;我们的打铃程序基于 C# 和 WPF 开发，首先我们要安装 IDE（集成开发环境）来进行程序的开发和打包工作。这里我们选用流行的 Visual Studio 开发环境（以下简称 VS）来进行开发工作。由于我们的软件用于个人和学习用途，这里我们选择 Community 版本，这里笔者不再过多赘述&lt;/p&gt;
&lt;p&gt;之后我们选择新建项目，创建一个新的 WPF 窗口程序（&lt;a href=&#34;http://xn--2rqy14b7tipji.NET&#34;&gt;注意使用.NET&lt;/a&gt; CORE）这样我们就可以利用 WPF 来快速开发一个美观的 GUI 界面了。&lt;/p&gt;
&lt;p&gt;我们先专注于业务的核心逻辑，即定时打铃，为了使定时更符合我们的需求并且减小开发压力，我们使用 C# 流行的日程库 Quartz 来设计定时任务，至于打铃（播放音频），我们可以直接用.NET 核心库中的 SoundPlayer 类来实现音频播放。&lt;/p&gt;
&lt;p&gt;C# 是一门面向对象编程语言，这里我们先实现一个抽象的 Class 类来承载课节这一信息，我们的课节应该包含上课时间、课节长度、以及下课的休息时间，并且我们的抽象课节应该可以被添加到 Quartz 的 Scheduler 中，成为 Quartz 的计划任务。Quartz 的计划任务添加至 Scheduler 时需要实现 IJob 接口的 Execute 方法，这个方法描述了当计划任务被执行时要执行的一系列代码。整理我们的思路，可以得到以下 UML 图&lt;/p&gt;
&lt;p&gt;&lt;img loading=&#34;lazy&#34; data-src=&#34;https://s2.loli.net/2024/01/26/cg68EM9PlIRrhLO.png&#34; alt=&#34;图片4.png&#34; /&gt;&lt;/p&gt;
&lt;p&gt;根据 UML 图，我们搭建了 Class 类并补全了方法的具体实现&lt;/p&gt;
&lt;p&gt;&lt;img loading=&#34;lazy&#34; data-src=&#34;https://s2.loli.net/2024/01/26/urgk6jplSKqHYRA.png&#34; alt=&#34;图片5.png&#34; /&gt;&lt;/p&gt;
&lt;p&gt;这里代码中我们有调用 MainWindow 的部分方法，这些方法与 GUI 界面的展示和刷新有关，下面我们来编写与 GUI 有关的代码&lt;/p&gt;
&lt;p&gt;WPF 的界面设计分为 XAML 设计和代码逻辑编写两部分，实际页面控件的添加使用 WPF 独创的 XAML 语法，而对控件的逻辑控制都采用 C# 代码来进行，这里我们为了使界面更美观使用了 MaterialDesignThemes 包，这个包内置了一些符合 MD 设计规范的设计模式控件样式，简化了我们的开发流程。&lt;/p&gt;
&lt;p&gt;这里笔者不再赘述如何导入 MaterialDesignThemes 包，仅贴出最终效果和 XAML 代码供读者参考，在界面中，我们预留了暂停打铃，切换日程，更换铃声等功能，我们之后将在控件逻辑部分补全他们的实现&lt;/p&gt;
&lt;p&gt;&lt;img loading=&#34;lazy&#34; data-src=&#34;https://s2.loli.net/2024/01/26/zm1OnbVtJsUDiqW.png&#34; alt=&#34;图片6.png&#34; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img loading=&#34;lazy&#34; data-src=&#34;https://s2.loli.net/2024/01/26/xpPJfTusUv7m1RA.png&#34; alt=&#34;图片7.png&#34; /&gt;&lt;/p&gt;
&lt;p&gt;之后我们来补全 GUI 的逻辑，这里同时也包含程序启动时的初始化进程，以及切换打铃模式等常见逻辑的实现，这里我们仅挑出几个方法加以展示&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-c#&#34;&gt;public MainWindow()
&amp;#123;
    Instance = this;
    foreach (var item in Directory.GetFiles(SoundsPath, &amp;quot;*.wav&amp;quot;))
    &amp;#123;
        sounds.Add(Path.GetFileNameWithoutExtension(item));
    &amp;#125;
    AddLog(&amp;quot;Wakaru正在启动...&amp;quot;);
    InitializeComponent();
    ClassBeginBox.ItemsSource = sounds;
    ClassOverBox.ItemsSource = sounds;
    ChangeStatus(Status.WAIT_FOR_CLASS);
    DispatcherTimer dispatcherTimer = new DispatcherTimer()
    &amp;#123;
        Interval = new TimeSpan(0, 0, 0, 1, 0)
    &amp;#125;;
    dispatcherTimer.Tick += new EventHandler((o, e) =&amp;gt; &amp;#123;
        Instance.Dispatcher.BeginInvoke(new Action(() =&amp;gt; &amp;#123;
            Instance.TimeNowText.Text = &amp;quot;当前时间: &amp;quot; + DateTime.Now.ToString();
            Instance.TimeNextText.Text = &amp;quot;&amp;quot;;
            Instance.TimeIntervalText.Text = &amp;quot;&amp;quot;;
            if (NextTime != DateTime.MinValue) 
            &amp;#123;
                Instance.TimeNextText.Text = &amp;quot;下一时间: &amp;quot; + NextTime.ToString();
                Instance.TimeIntervalText.Text = &amp;quot;剩余时间: &amp;quot; + new TimeSpan(NextTime.Ticks - DateTime.Now.Ticks).ToString(@&amp;quot;hh\:mm\:ss&amp;quot;);
            &amp;#125;
            
        &amp;#125;));
    &amp;#125;);
    dispatcherTimer.Start();
&amp;#125;
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code class=&#34;language-c#&#34;&gt;public async static void LoadClasses(List&amp;lt;Class&amp;gt; classes)
&amp;#123;
    if (scheduler == null)
    &amp;#123;
        var schedulerFactory = new StdSchedulerFactory();
        scheduler = await schedulerFactory.GetScheduler();
    &amp;#125;
    ClearLog();
    await scheduler.Clear();
    foreach (var item in classes)
    &amp;#123;
        scheduler = item.AddToScheduler(scheduler);
    &amp;#125;
    AddLog(&amp;quot;加载了&amp;quot; + classes.Count + &amp;quot;个课节, 时间: &amp;quot; + DateTime.Now.ToString());
    await scheduler.Start();
&amp;#125;
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code class=&#34;language-c#&#34;&gt;public static void ChangeStatus(Status status)
&amp;#123;
    Instance.Dispatcher.BeginInvoke(new Action(() =&amp;gt; &amp;#123;
        Instance.StateNowText.Text = &amp;quot;当前状态: &amp;quot; + GetStatusString(status);
        Instance.StateNextText.Text = &amp;quot;下一状态: &amp;quot; + GetStatusString(status + 1);
        if (status == Status.IN_CLASS)
        &amp;#123;
            Instance.ClassStateText.Text = &amp;quot;上课中&amp;quot;;
            Instance.IconPic.Kind = MaterialDesignThemes.Wpf.PackIconKind.Book;
        &amp;#125;
        if (status == Status.CLASS_OVER)
        &amp;#123;
            Instance.ClassStateText.Text = &amp;quot;下课中&amp;quot;;
            Instance.IconPic.Kind = MaterialDesignThemes.Wpf.PackIconKind.ExitRun;
        &amp;#125;
        if (status == Status.WAIT_FOR_CLASS)
        &amp;#123;
            Instance.ClassStateText.Text = &amp;quot;等待上课中&amp;quot;;
            Instance.IconPic.Kind = MaterialDesignThemes.Wpf.PackIconKind.Sleep;
        &amp;#125;
    &amp;#125;));
&amp;#125;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;至此，我们完成了对于打铃程序的开发，笔者实际上在稍早的时候就已经完成了这个打铃程序项目，已经在 Github 开放源代码，本文涉及的所有源代码都基于 CC-BY 4.0 协议开源，如有使用请遵守开源协议&lt;/p&gt;
&lt;p&gt;需要注意的是，使用 VS 编译项目时，默认生成的是 64 位 Win32 程序，如果读者的虚拟机操作系统为 32 位，需要修改目标平台至 x86 架构&lt;/p&gt;
&lt;h4 id=&#34;将音视频呈现到腾讯会议中&#34;&gt;&lt;a class=&#34;anchor&#34; href=&#34;#将音视频呈现到腾讯会议中&#34;&gt;#&lt;/a&gt; 将音视频呈现到腾讯会议中&lt;/h4&gt;
&lt;p&gt;利用 Hyper-V 的增强会话功能，我们可以轻松地将宿主机的文件直接复制到虚拟机中，这为我们的部署提供了便利。我们将编译好的打铃软件复制到虚拟机中，&lt;a href=&#34;http://xn--phqx14afic551e.NET&#34;&gt;并且安装.NET&lt;/a&gt; 6.0 运行时，保证虚拟机能够运行打铃软件，这里是笔者部署好以后的情况&lt;/p&gt;
&lt;p&gt;&lt;img loading=&#34;lazy&#34; data-src=&#34;https://s2.loli.net/2024/01/26/K3kOvMcXCi7oRbh.png&#34; alt=&#34;图片9.png&#34; /&gt;&lt;/p&gt;
&lt;p&gt;为了使 SoundPlayer 播放的音频正确输入到虚拟声卡中，我们需要将系统输出音频设置为 CABLE Input (VB-Audio Virtual Cable)，C# 的 SoundPlayer 将在系统当前默认扬声器中播放声音。&lt;/p&gt;
&lt;p&gt;这里读者可能遇到虚拟机的音频仅有 “远程音频” 这一个选项，这是因为 Hyper-V 增强会话默认会启用音频转发，将虚拟机中的音频自动转发到宿主机，这显然不满足我们的预期，这里我们可以在连接虚拟机的时候不使用增强会话，即出现 “连接至 XX” 对话框时不点击连接，直接关掉该对话框，即可正确识别虚拟机内安装的虚拟声卡服务。&lt;/p&gt;
&lt;p&gt;增强会话同时也会导致关闭虚拟机连接时使远程计算机自动休眠，这会导致腾讯会议的麦克风自动关闭，自动打铃受到阻碍，也可以通过上述方法来禁用增强会话，从而使自动打铃服务可以完全隐藏在宿主机的后台静默运行。&lt;/p&gt;
&lt;p&gt;接下来我们需要配置 OBS 的场景，来将打铃程序的界面呈现到腾讯会议中，注意，为了使 OBS 支持虚拟摄像头功能，需要安装版本号为 26 以上的 OBS。&lt;/p&gt;
&lt;p&gt;Hyper-V 默认没有虚拟化 NVENC QSV 等编码器的能力，这意味着我们开启虚拟摄像头时需要利用 CPU 的 x264 进行软件编码，我们最好适量降低 OBS 的编码质量来达到最大的能效比，这里给出笔者使用的参数来供读者参考。编码器：软件（x264），比特率：200 Kbps，输出分辨率：786x446，缩小方法：双直线法，帧率：10FPS。这里输出分辨率可以根据打铃程序界面的大小进行合理的调整。&lt;/p&gt;
&lt;p&gt;之后我们创建 “窗口采集” 作为来源，并且选中打铃程序的 GUI 窗体，接下来便能看到 OBS 中已经成功捕获打铃程序的界面，这里需要注意的是，OBS 无法捕捉最小化的窗口，这里我们需要保证打铃程序位于前台。&lt;/p&gt;
&lt;p&gt;勾选启动虚拟摄像机并启动腾讯会议，接下来我们将在腾讯会议中应用我们配置的虚拟摄像头和虚拟声卡。&lt;/p&gt;
&lt;p&gt;摄像头中选中 OBS Virtual Camera，麦克风选中 CABLE Output (VB-Audio Virtual Cable)，音频选项可以按照下图设置，以保证铃声音乐播放的流畅性&lt;/p&gt;
&lt;p&gt;&lt;img loading=&#34;lazy&#34; data-src=&#34;https://s2.loli.net/2024/01/26/SgpomVOfCXyIqFL.png&#34; alt=&#34;图片10.png&#34; /&gt;&lt;/p&gt;
&lt;p&gt;至此，我们完成了基于 Hyper-V 和 VAC 的腾讯会议自动打铃系统。&lt;/p&gt;
&lt;p&gt;如果读者有多会议室的需求，也可以克隆虚拟机，同时运行多个虚拟机以承载多个会议室的自动打铃服务，这里笔者不再过多赘述。除此之外，当读者想要关闭虚拟机时，可以使用 Hyper-V 虚拟机的 “保存” 功能，该功能将拍摄当前虚拟机状态为一个快照，方便下次启动虚拟机时还原快照，避免重复配置多次。&lt;/p&gt;
&lt;p&gt;这里笔者同时运行两台虚拟机来承载自动打铃服务，在笔者 i5-9400F 的处理器上共占约 9% 的性能&lt;/p&gt;
&lt;p&gt;&lt;img loading=&#34;lazy&#34; data-src=&#34;https://s2.loli.net/2024/01/26/NsItjbe8qTnBhEF.png&#34; alt=&#34;图片11.png&#34; /&gt;&lt;/p&gt;
&lt;h3 id=&#34;性能调优与反思&#34;&gt;&lt;a class=&#34;anchor&#34; href=&#34;#性能调优与反思&#34;&gt;#&lt;/a&gt; 性能调优与反思&lt;/h3&gt;
&lt;h4 id=&#34;性能调优&#34;&gt;&lt;a class=&#34;anchor&#34; href=&#34;#性能调优&#34;&gt;#&lt;/a&gt; 性能调优&lt;/h4&gt;
&lt;p&gt;我们希望打铃服务能尽可能减少性能的占用，以提高能效比，在单位物理机中可容纳更多的打铃服务，这里笔者给出自己的几点看法和思路。&lt;/p&gt;
&lt;p&gt;首先，腾讯会议本身会随着会议室人数的增多而占用更多处理单位，我们可以通过关闭接收参会者视频的方式来缓解这个问题。在会议室的常规设置中勾选语音模式，即可暂停接收参会者视频，降低性能消耗。&lt;/p&gt;
&lt;p&gt;同时，腾讯会议可能默认会对虚拟摄像头的画面进行降噪或者美颜处理，这也会导致没有必要的性能开销，这里我们进入视频设置，取消勾选所有的对钩，来关闭所有的视频处理功能。&lt;/p&gt;
&lt;p&gt;对于 OBS，其内置的场景预览也会造成一定的性能消耗，这里可以右键预览的部分，将开启预览的功能关闭。如果读者不需要虚拟摄像头展示打铃情况，仅仅需要播放打铃音乐的功能，也可以跳过 OBS 的部署，彻底消除编码器造成的性能消耗。&lt;/p&gt;
&lt;p&gt;针对操作系统层面，精简程度较高的系统能减少额外开销，如果可以的话可以选择 32 位的系统来降低内存需求，但这里笔者不推荐使用 Windows 7，Windows 8 等操作系统，这会降低程序运行的稳定性，因为这些系统已经不再被微软维护，&lt;a href=&#34;http://xn--fiqw3f7wgypby58ey3za18k.NET&#34;&gt;在其环境中运行.NET&lt;/a&gt; 6.0 程序也可能产生不必要的麻烦。这里笔者使用的是极限精简的 Tiny10 镜像，这里不再过多赘述&lt;/p&gt;
&lt;h4 id=&#34;反思&#34;&gt;&lt;a class=&#34;anchor&#34; href=&#34;#反思&#34;&gt;#&lt;/a&gt; 反思&lt;/h4&gt;
&lt;p&gt;限于个人能力与精力，笔者的方案仍有许多不足之处，Windows 系统本身造成的额外开销是难以消除或被忽略的，如果想要扩大自动打铃的规模，或者使用 Docker 技术统一管理，基于 Linux 级的实现是不可避免的。&lt;/p&gt;
&lt;p&gt;由于腾讯会议提供了 Linux 版的支持，.NET 6.0 也默认具有跨平台的特性，OBS 也对 Linux 平台有支持。仅需找到虚拟声卡的解决方案，就有可能将整个项目部署在 Linux 上，如果有机会，笔者将就这一课题继续研究。&lt;/p&gt;
</content>
        <category term="比赛作品" />
        <category term="腾讯会议" />
        <updated>2023-03-05T03:19:59.000Z</updated>
    </entry>
    <entry>
        <id>https://turou.fun/minecraft/arch-tutor/network/</id>
        <title>跨加载器开发挑战 - 网络通信和热键</title>
        <link rel="alternate" href="https://turou.fun/minecraft/arch-tutor/network/"/>
        <content type="html">&lt;p&gt;关于网络通信，ArchitecturyAPI 提供了类似 Fabric 的 Packet 和类似 Forge 的 Channel 这样两种解决方案，这里笔者将带领读者以两个具体实例切入这两种解决方案，读者可以实际感受后选择自己喜欢的方式来进行网络通信.&lt;/p&gt;
&lt;h3 id=&#34;使用networkmanager进行网络通信&#34;&gt;&lt;a class=&#34;anchor&#34; href=&#34;#使用networkmanager进行网络通信&#34;&gt;#&lt;/a&gt; 使用 NetworkManager 进行网络通信&lt;/h3&gt;
&lt;p&gt;本节将使用类似 Fabric 的 NetworkManager 来写一个处理客户端鼠标滚动事件的实例。这里笔者给上文我们提到的经验储存器添加了一个小功能，可以让玩家用 Shift + 滚动来调整单次经验的存取量.&lt;/p&gt;
&lt;p&gt;很显然，我们的需求涉及一个 C2S 的网络包请求。熟悉模组开发的读者应该清楚，滚轮事件是由客户端发出的，其产生的影响 (这里是改变经验储存器的一个 NBT 值) 是体现在逻辑服务端的。一个携带数据的网络包需要从玩家的客户端发出，到达逻辑服务器产生影响.&lt;/p&gt;
&lt;p&gt;按照需求，我们在 trou.arch 下创建 network 包，并且创建 ModPackets 类&lt;/p&gt;
&lt;figure class=&#34;highlight java&#34;&gt;&lt;figcaption data-lang=&#34;java&#34;&gt;&lt;/figcaption&gt;&lt;table&gt;&lt;tr&gt;&lt;td data-num=&#34;1&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token comment&#34;&gt;// 位于 common: trou/arch/network/ModPackets.java&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;2&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;class&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;ModPackets&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;&amp;#123;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;3&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;span class=&#34;token keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;static&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;final&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;ResourceLocation&lt;/span&gt; &lt;span class=&#34;token constant&#34;&gt;EXP_CONTAINER_MODE&lt;/span&gt; &lt;span class=&#34;token operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;new&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;ResourceLocation&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token string&#34;&gt;&#34;arch&#34;&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;token string&#34;&gt;&#34;exp_container_change_mode&#34;&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;4&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;5&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;span class=&#34;token keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;static&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;void&lt;/span&gt; &lt;span class=&#34;token function&#34;&gt;register&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;&amp;#123;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;6&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;        &lt;span class=&#34;token class-name&#34;&gt;NetworkManager&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;registerReceiver&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token class-name&#34;&gt;NetworkManager&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;Side&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token constant&#34;&gt;C2S&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;token constant&#34;&gt;EXP_CONTAINER_MODE&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;buf&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; context&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;token operator&#34;&gt;-&gt;&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;&amp;#123;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;7&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;            &lt;span class=&#34;token class-name&#34;&gt;Player&lt;/span&gt; player &lt;span class=&#34;token operator&#34;&gt;=&lt;/span&gt; context&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;getPlayer&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;8&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;            &lt;span class=&#34;token keyword&#34;&gt;int&lt;/span&gt; deltaMode &lt;span class=&#34;token operator&#34;&gt;=&lt;/span&gt; buf&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;readInt&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;9&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;            &lt;span class=&#34;token class-name&#34;&gt;ItemStack&lt;/span&gt; stack &lt;span class=&#34;token operator&#34;&gt;=&lt;/span&gt; player&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;getItemInHand&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token class-name&#34;&gt;InteractionHand&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token constant&#34;&gt;MAIN_HAND&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;10&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;            &lt;span class=&#34;token class-name&#34;&gt;ItemExpContainer&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;raiseMode&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;stack&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; deltaMode&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;11&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;        &lt;span class=&#34;token punctuation&#34;&gt;&amp;#125;&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;12&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;span class=&#34;token punctuation&#34;&gt;&amp;#125;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;13&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;14&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;span class=&#34;token keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;static&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;void&lt;/span&gt; &lt;span class=&#34;token function&#34;&gt;sendExpContainerMousePacket&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token keyword&#34;&gt;int&lt;/span&gt; deltaMode&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;&amp;#123;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;15&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;        &lt;span class=&#34;token class-name&#34;&gt;FriendlyByteBuf&lt;/span&gt; buf &lt;span class=&#34;token operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;new&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;FriendlyByteBuf&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token class-name&#34;&gt;Unpooled&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;buffer&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;16&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;        buf&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;writeInt&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;deltaMode&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;17&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;        &lt;span class=&#34;token class-name&#34;&gt;NetworkManager&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;sendToServer&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token constant&#34;&gt;EXP_CONTAINER_MODE&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; buf&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;18&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;span class=&#34;token punctuation&#34;&gt;&amp;#125;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;19&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token punctuation&#34;&gt;&amp;#125;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;&lt;p&gt;FriendlyByteBuf 是针对 ByteBuf 的一个封装，其中包含了常用的 Minecraft 对象的序列化和反序列化，读者需要时可以自行查看&lt;/p&gt;
&lt;p&gt;需要注意的是，由于 ByteBuf 的特性，数据被写入的顺序和读取的顺序必须一致&lt;/p&gt;
&lt;p&gt;显然，我们调用了 NetworkManager 提供的方法并且完成了包的发送和接收，包的数据由一个 ByteBuf 携带，相信读者已经对这种模式不陌生了.&lt;/p&gt;
&lt;p&gt;注册包的接收端时需要提供 Side, 由于我们是由逻辑客户端发向逻辑服务端，所以我们选择 Side.C2S&lt;/p&gt;
&lt;p&gt;context 包含了一些随包携带的常用参数，包含发起的玩家和 Environment, 方便我们直接进行使用和判断.&lt;/p&gt;
&lt;p&gt;最后，不要忘记在主类中&lt;strong&gt;调用&lt;/strong&gt;我们 ModPackets 的&lt;strong&gt; register 方法&lt;/strong&gt;&lt;/p&gt;
&lt;h3 id=&#34;处理raisemode方法&#34;&gt;&lt;a class=&#34;anchor&#34; href=&#34;#处理raisemode方法&#34;&gt;#&lt;/a&gt; 处理 raiseMode 方法&lt;/h3&gt;
&lt;p&gt;接下来我们要处理逻辑服务端触发的 raiseMode 方法，我们希望通过这个方法改变手中经验储存器的单次存取量.&lt;/p&gt;
&lt;figure class=&#34;highlight java&#34;&gt;&lt;figcaption data-lang=&#34;java&#34;&gt;&lt;/figcaption&gt;&lt;table&gt;&lt;tr&gt;&lt;td data-num=&#34;1&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token comment&#34;&gt;// 位于 common: trou/arch/item/ItemExpContainer.java&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;2&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;static&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;void&lt;/span&gt; &lt;span class=&#34;token function&#34;&gt;raiseMode&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token class-name&#34;&gt;ItemStack&lt;/span&gt; stack&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;int&lt;/span&gt; value&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;&amp;#123;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;3&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;span class=&#34;token keyword&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token operator&#34;&gt;!&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;stack&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;getItem&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;instanceof&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;ItemExpContainer&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;return&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;4&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;span class=&#34;token class-name&#34;&gt;CompoundTag&lt;/span&gt; tag &lt;span class=&#34;token operator&#34;&gt;=&lt;/span&gt; stack&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;hasTag&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;token operator&#34;&gt;?&lt;/span&gt; stack&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;getTag&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;token operator&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;new&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;CompoundTag&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;5&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;span class=&#34;token keyword&#34;&gt;assert&lt;/span&gt; tag &lt;span class=&#34;token operator&#34;&gt;!=&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;null&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;6&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;span class=&#34;token keyword&#34;&gt;int&lt;/span&gt; newValue &lt;span class=&#34;token operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;Math&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;max&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;tag&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;getInt&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token string&#34;&gt;&#34;mode&#34;&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;token operator&#34;&gt;+&lt;/span&gt; value&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;token number&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt; &lt;span class=&#34;token comment&#34;&gt;// 限制不为负&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;7&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    tag&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;putInt&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token string&#34;&gt;&#34;mode&#34;&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; newValue&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;8&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token punctuation&#34;&gt;&amp;#125;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;&lt;p&gt;相信不用笔者过多赘述，读者可以理解其中的逻辑.&lt;/p&gt;
&lt;p&gt;我们还需要修改存取经验时的逻辑，使其按照我们设定的数值进行存取&lt;/p&gt;
&lt;figure class=&#34;highlight java&#34;&gt;&lt;figcaption data-lang=&#34;java&#34;&gt;&lt;/figcaption&gt;&lt;table&gt;&lt;tr&gt;&lt;td data-num=&#34;1&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token comment&#34;&gt;// 位于 common: trou/arch/item/ItemExpContainer.java&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;2&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token annotation punctuation&#34;&gt;@Override&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;3&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;InteractionResultHolder&lt;/span&gt;&lt;span class=&#34;token generics&#34;&gt;&lt;span class=&#34;token punctuation&#34;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&#34;token class-name&#34;&gt;ItemStack&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;&gt;&lt;/span&gt;&lt;/span&gt; &lt;span class=&#34;token function&#34;&gt;use&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token annotation punctuation&#34;&gt;@NotNull&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;Level&lt;/span&gt; level&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;token annotation punctuation&#34;&gt;@NotNull&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;Player&lt;/span&gt; player&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;token annotation punctuation&#34;&gt;@NotNull&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;InteractionHand&lt;/span&gt; usedHand&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;&amp;#123;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;4&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;span class=&#34;token class-name&#34;&gt;ItemStack&lt;/span&gt; stack &lt;span class=&#34;token operator&#34;&gt;=&lt;/span&gt; player&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;getItemInHand&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;usedHand&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;5&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;span class=&#34;token keyword&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;level&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;isClientSide &lt;span class=&#34;token operator&#34;&gt;||&lt;/span&gt; usedHand &lt;span class=&#34;token operator&#34;&gt;==&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;InteractionHand&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token constant&#34;&gt;OFF_HAND&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;InteractionResultHolder&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;fail&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;stack&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;6&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;span class=&#34;token class-name&#34;&gt;CompoundTag&lt;/span&gt; tag &lt;span class=&#34;token operator&#34;&gt;=&lt;/span&gt; stack&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;hasTag&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;token operator&#34;&gt;?&lt;/span&gt; stack&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;getTag&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;token operator&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;new&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;CompoundTag&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;7&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;span class=&#34;token keyword&#34;&gt;assert&lt;/span&gt; tag &lt;span class=&#34;token operator&#34;&gt;!=&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;null&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;8&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;span class=&#34;token keyword&#34;&gt;int&lt;/span&gt; mode &lt;span class=&#34;token operator&#34;&gt;=&lt;/span&gt; tag&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;getInt&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token string&#34;&gt;&#34;mode&#34;&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;9&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;span class=&#34;token keyword&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;player&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;isShiftKeyDown&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;&amp;#123;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;10&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;        &lt;span class=&#34;token keyword&#34;&gt;int&lt;/span&gt; storedExp &lt;span class=&#34;token operator&#34;&gt;=&lt;/span&gt; tag&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;getInt&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token string&#34;&gt;&#34;exp&#34;&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;11&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;        &lt;span class=&#34;token keyword&#34;&gt;int&lt;/span&gt; amount &lt;span class=&#34;token operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;Math&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;min&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;mode&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; storedExp&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;12&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;        player&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;giveExperiencePoints&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;amount&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;13&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;        tag&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;putInt&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token string&#34;&gt;&#34;exp&#34;&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; storedExp &lt;span class=&#34;token operator&#34;&gt;-&lt;/span&gt; amount&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;14&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;span class=&#34;token punctuation&#34;&gt;&amp;#125;&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;else&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;&amp;#123;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;15&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;        &lt;span class=&#34;token keyword&#34;&gt;int&lt;/span&gt; ownedExp &lt;span class=&#34;token operator&#34;&gt;=&lt;/span&gt; player&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;totalExperience&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;16&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;        &lt;span class=&#34;token keyword&#34;&gt;int&lt;/span&gt; cost &lt;span class=&#34;token operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;Math&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;min&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;ownedExp&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; mode&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;17&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;        player&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;giveExperiencePoints&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token operator&#34;&gt;-&lt;/span&gt;cost&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;18&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;        tag&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;putInt&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token string&#34;&gt;&#34;exp&#34;&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; tag&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;getInt&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token string&#34;&gt;&#34;exp&#34;&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;token operator&#34;&gt;+&lt;/span&gt; cost&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;19&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;span class=&#34;token punctuation&#34;&gt;&amp;#125;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;20&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    stack&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;setTag&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;tag&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;21&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;span class=&#34;token keyword&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;InteractionResultHolder&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;sidedSuccess&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;stack&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; level&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;isClientSide&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;22&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token punctuation&#34;&gt;&amp;#125;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;&lt;p&gt;最后使单次存取量可以在 Tooltip 显示，并添加相关 i18n 词条&lt;/p&gt;
&lt;p&gt;&amp;lt;pre class=&amp;quot;language-java&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;lang-java&amp;quot;&amp;gt; 位于 common: trou/arch/item/ItemExpContainer.java&lt;br /&gt;
&amp;lt;strong&amp;gt;public static void appendTooltip(ItemStack stack, List&amp;lt;Component&amp;gt; lines, TooltipFlag flag) {&lt;br /&gt;
&amp;lt;/strong&amp;gt;    if (stack.getItem() instanceof ItemExpContainer) {&lt;br /&gt;
CompoundTag tag = stack.hasTag() ? stack.getTag() : new CompoundTag();&lt;br /&gt;
assert tag != null;&lt;br /&gt;
lines.add(new TranslatableComponent(&amp;quot;tooltip.exp_container&amp;quot;, tag.getInt(&amp;quot;exp&amp;quot;)));&lt;br /&gt;
lines.add(new TranslatableComponent(&amp;quot;tooltip.exp_container_mode&amp;quot;, tag.getInt(&amp;quot;mode&amp;quot;)));&lt;br /&gt;
}&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;&lt;/p&gt;
&lt;h3 id=&#34;监听鼠标滚动事件&#34;&gt;&lt;a class=&#34;anchor&#34; href=&#34;#监听鼠标滚动事件&#34;&gt;#&lt;/a&gt; 监听鼠标滚动事件&lt;/h3&gt;
&lt;p&gt;接下来我们来完成监听鼠标滚轮的相关事件，相信读过上一节的读者已经对此有头绪了。经过寻找，我们命中了 ClientRawInputEvent.MOUSE_SCROLLED 事件，这个事件会在玩家进入世界后滚动滚轮触发，并且在打开 GUI 等界面时不会触发，完美符合我们的需求.&lt;/p&gt;
&lt;p&gt;修改 ModEvents 类来监听这个事件，并且在 ItemExpContainer 类中编写事件处理器&lt;/p&gt;
&lt;figure class=&#34;highlight java&#34;&gt;&lt;figcaption data-lang=&#34;java&#34;&gt;&lt;/figcaption&gt;&lt;table&gt;&lt;tr&gt;&lt;td data-num=&#34;1&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token comment&#34;&gt;// 位于 common: trou/arch/object/ModEvents.java&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;2&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;class&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;ModEvents&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;&amp;#123;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;3&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;span class=&#34;token keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;static&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;void&lt;/span&gt; &lt;span class=&#34;token function&#34;&gt;register&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;&amp;#123;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;4&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;        &lt;span class=&#34;token class-name&#34;&gt;ClientTooltipEvent&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token constant&#34;&gt;ITEM&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;register&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token class-name&#34;&gt;ItemExpContainer&lt;/span&gt;&lt;span class=&#34;token operator&#34;&gt;::&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;appendTooltip&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;5&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;        &lt;span class=&#34;token class-name&#34;&gt;ClientRawInputEvent&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token constant&#34;&gt;MOUSE_SCROLLED&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;register&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token class-name&#34;&gt;ItemExpContainer&lt;/span&gt;&lt;span class=&#34;token operator&#34;&gt;::&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;mouseScroll&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;6&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;span class=&#34;token punctuation&#34;&gt;&amp;#125;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;7&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token punctuation&#34;&gt;&amp;#125;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;&lt;p&gt;显然 ClientRawInputEvent 只会在客户端进入世界后触发，我们可以直接用 minecraft.player 来拿到非空的本地玩家&lt;/p&gt;
&lt;figure class=&#34;highlight java&#34;&gt;&lt;figcaption data-lang=&#34;java&#34;&gt;&lt;/figcaption&gt;&lt;table&gt;&lt;tr&gt;&lt;td data-num=&#34;1&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token comment&#34;&gt;// 位于 common: trou/arch/item/ItemExpContainer.java&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;2&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token comment&#34;&gt;// 向上滚动 v = 1.0 向下滚动 v = -1.0&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;3&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;static&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;EventResult&lt;/span&gt; &lt;span class=&#34;token function&#34;&gt;mouseScroll&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token class-name&#34;&gt;Minecraft&lt;/span&gt; minecraft&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;double&lt;/span&gt; v&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;&amp;#123;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;4&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;span class=&#34;token keyword&#34;&gt;assert&lt;/span&gt; minecraft&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;player &lt;span class=&#34;token operator&#34;&gt;!=&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;null&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt; &lt;span class=&#34;token comment&#34;&gt;// 断定 minecraft.player 不为空&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;5&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;span class=&#34;token class-name&#34;&gt;ItemStack&lt;/span&gt; heldItem &lt;span class=&#34;token operator&#34;&gt;=&lt;/span&gt; minecraft&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;player&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;getItemInHand&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token class-name&#34;&gt;InteractionHand&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token constant&#34;&gt;MAIN_HAND&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;6&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;span class=&#34;token keyword&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;heldItem&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;getItem&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;instanceof&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;ItemExpContainer&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;&amp;#123;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;7&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;        &lt;span class=&#34;token keyword&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;minecraft&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;player&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;isShiftKeyDown&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;&amp;#123;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;8&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;            &lt;span class=&#34;token class-name&#34;&gt;ModPackets&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;sendExpContainerMousePacket&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token keyword&#34;&gt;int&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt; v&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;9&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;            &lt;span class=&#34;token keyword&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;EventResult&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;interruptFalse&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;10&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;        &lt;span class=&#34;token punctuation&#34;&gt;&amp;#125;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;11&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;span class=&#34;token punctuation&#34;&gt;&amp;#125;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;12&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;span class=&#34;token keyword&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;EventResult&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;pass&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;13&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token punctuation&#34;&gt;&amp;#125;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;&lt;p&gt;EventResult.interruptFalse () 会阻碍接下来滚轮的滚动，使事件冒泡停止&lt;/p&gt;
&lt;p&gt;EventResult.pass () 不会影响接下来滚轮的滚动&lt;/p&gt;
&lt;p&gt;到这里，我们的功能已经写好了，读者可以启动游戏验证我们的代码，确保其在 Forge 和 Fabric 均能正常使用&lt;/p&gt;
&lt;p&gt;&amp;lt;figure&amp;gt;&amp;lt;img src=&amp;quot;&lt;a href=&#34;https://s2.loli.net/2023/01/29/gtjy83Q7NnvPMoI.png&#34;&gt;https://s2.loli.net/2023/01/29/gtjy83Q7NnvPMoI.png&lt;/a&gt;&amp;quot;alt=&amp;quot;&amp;quot;&amp;gt;&amp;lt;figcaption&amp;gt;&amp;lt;p &amp;gt; 经验储存器 &amp;lt;/p&amp;gt;&amp;lt;/figcaption&amp;gt;&amp;lt;/figure&amp;gt;&lt;/p&gt;
&lt;h3 id=&#34;使用networkchannel进行网络通信&#34;&gt;&lt;a class=&#34;anchor&#34; href=&#34;#使用networkchannel进行网络通信&#34;&gt;#&lt;/a&gt; 使用 NetworkChannel 进行网络通信&lt;/h3&gt;
&lt;p&gt;本节我们将使用类似 Forge 的 NetworkChannel 进行网络通信，做出一个点击热键即可将储存器中的经验丢给盯着的玩家的功能&lt;/p&gt;
&lt;p&gt;NetworkChannel 的核心概念是 Channel 和 Message, 当网络通信时，我们的视觉效果是 Message 在 Channel 中传递，显然，丢经验这个动作应该属于一条 Message&lt;/p&gt;
&lt;p&gt;所以我们在 network 包中创建 MessageThrowExp 类，来诠释丢经验这条消息&lt;/p&gt;
&lt;figure class=&#34;highlight java&#34;&gt;&lt;figcaption data-lang=&#34;java&#34;&gt;&lt;/figcaption&gt;&lt;table&gt;&lt;tr&gt;&lt;td data-num=&#34;1&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token comment&#34;&gt;// 位于 common: trou/arch/network/MessageThrowExp.java&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;2&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;class&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;MessageThrowExp&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;&amp;#123;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;3&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;span class=&#34;token keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;final&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;UUID&lt;/span&gt; targetPlayerUUID&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;4&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;5&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;span class=&#34;token comment&#34;&gt;// 收到消息时，会调用这个方法来从 buf 中取出数据&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;6&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;span class=&#34;token keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;MessageThrowExp&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token class-name&#34;&gt;FriendlyByteBuf&lt;/span&gt; buf&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;&amp;#123;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;7&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;        &lt;span class=&#34;token keyword&#34;&gt;this&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;buf&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;readUUID&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;8&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;span class=&#34;token punctuation&#34;&gt;&amp;#125;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;9&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;10&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;span class=&#34;token comment&#34;&gt;// 创建消息时提供数据的构造器&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;11&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;span class=&#34;token keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;MessageThrowExp&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token class-name&#34;&gt;UUID&lt;/span&gt; targetPlayerUUID&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;&amp;#123;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;12&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;        &lt;span class=&#34;token keyword&#34;&gt;this&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;targetPlayerUUID &lt;span class=&#34;token operator&#34;&gt;=&lt;/span&gt; targetPlayerUUID&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;13&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;span class=&#34;token punctuation&#34;&gt;&amp;#125;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;14&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;15&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;span class=&#34;token comment&#34;&gt;// 发送消息时将数据写入 buf 的方法&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;16&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;span class=&#34;token keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;void&lt;/span&gt; &lt;span class=&#34;token function&#34;&gt;encode&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token class-name&#34;&gt;FriendlyByteBuf&lt;/span&gt; buf&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;&amp;#123;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;17&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;        buf&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;writeUUID&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;targetPlayerUUID&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;18&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;span class=&#34;token punctuation&#34;&gt;&amp;#125;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;19&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;20&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;span class=&#34;token comment&#34;&gt;// 收到消息后，调用的处理事件&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;21&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;span class=&#34;token keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;void&lt;/span&gt; &lt;span class=&#34;token function&#34;&gt;apply&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token class-name&#34;&gt;Supplier&lt;/span&gt;&lt;span class=&#34;token generics&#34;&gt;&lt;span class=&#34;token punctuation&#34;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&#34;token class-name&#34;&gt;NetworkManager&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;PacketContext&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;&gt;&lt;/span&gt;&lt;/span&gt; contextSupplier&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;&amp;#123;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;22&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;        &lt;span class=&#34;token class-name&#34;&gt;Player&lt;/span&gt; self &lt;span class=&#34;token operator&#34;&gt;=&lt;/span&gt; contextSupplier&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;get&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;getPlayer&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;23&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;        &lt;span class=&#34;token class-name&#34;&gt;Player&lt;/span&gt; target &lt;span class=&#34;token operator&#34;&gt;=&lt;/span&gt; self&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;level&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;getPlayerByUUID&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;targetPlayerUUID&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;24&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;        &lt;span class=&#34;token keyword&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;target &lt;span class=&#34;token operator&#34;&gt;==&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;null&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;return&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;25&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;        &lt;span class=&#34;token class-name&#34;&gt;ItemStack&lt;/span&gt; stack &lt;span class=&#34;token operator&#34;&gt;=&lt;/span&gt; self&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;getItemInHand&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token class-name&#34;&gt;InteractionHand&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token constant&#34;&gt;MAIN_HAND&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;26&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;        &lt;span class=&#34;token class-name&#34;&gt;ItemExpContainer&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;doThrow&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;stack&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; target&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;27&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;span class=&#34;token punctuation&#34;&gt;&amp;#125;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;28&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token punctuation&#34;&gt;&amp;#125;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;&lt;p&gt;请永远使用 UUID 来作为玩家的标识符，而不要传递 Player 对象或者玩家的名字&lt;/p&gt;
&lt;p&gt;显然，这里我们要承载的数据是指向的玩家的 UUID, 我们需要编写一个构造器来提供成员变量的值，并且在 encode 时将值编码到 ByteBuf 中&lt;/p&gt;
&lt;p&gt;apply 方法会在 ByteBuf 的值序列化到实例中后被逻辑服务器调用，这里可以处理我们的逻辑。与上文中的 context 相类似，contextSupplier 会提供 Player, Environment 等可能用到的上下文对象.&lt;/p&gt;
&lt;p&gt;完成了 Message, 接下来我们需要定义并注册承载它的 Channel, 在 network 包下创建 ModChannels 类&lt;/p&gt;
&lt;figure class=&#34;highlight java&#34;&gt;&lt;figcaption data-lang=&#34;java&#34;&gt;&lt;/figcaption&gt;&lt;table&gt;&lt;tr&gt;&lt;td data-num=&#34;1&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token comment&#34;&gt;// 位于 common: trou/arch/network/ModChannels.java&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;2&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;class&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;ModChannels&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;&amp;#123;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;3&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;span class=&#34;token keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;static&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;final&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;NetworkChannel&lt;/span&gt; &lt;span class=&#34;token constant&#34;&gt;CHANNEL&lt;/span&gt; &lt;span class=&#34;token operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;NetworkChannel&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;create&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token keyword&#34;&gt;new&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;ResourceLocation&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token string&#34;&gt;&#34;arch&#34;&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;token string&#34;&gt;&#34;exp_container&#34;&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;4&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;5&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;span class=&#34;token keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;static&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;void&lt;/span&gt; &lt;span class=&#34;token function&#34;&gt;register&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;&amp;#123;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;6&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;        &lt;span class=&#34;token constant&#34;&gt;CHANNEL&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;register&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token class-name&#34;&gt;MessageThrowExp&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token keyword&#34;&gt;class&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;MessageThrowExp&lt;/span&gt;&lt;span class=&#34;token operator&#34;&gt;::&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;encode&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;MessageThrowExp&lt;/span&gt;&lt;span class=&#34;token operator&#34;&gt;::&lt;/span&gt;&lt;span class=&#34;token keyword&#34;&gt;new&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;MessageThrowExp&lt;/span&gt;&lt;span class=&#34;token operator&#34;&gt;::&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;apply&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;7&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;span class=&#34;token punctuation&#34;&gt;&amp;#125;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;8&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token punctuation&#34;&gt;&amp;#125;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;&lt;p&gt;根据读者对 Channel 的不同理解，你可以在一个 Mod 使用多个 Channel 承载不同功能，也可以只使用一个 Channel 来承载该 Mod 所有的 Message, 这只是设计模式的差别，读者可以根据自己的喜好来决定.&lt;/p&gt;
&lt;p&gt;可以看到这里笔者创建的 Channel 的标识符中提到了 exp_container, 即为经验储存器创建了一个单独的 Channel&lt;/p&gt;
&lt;p&gt;最后，我们需要在主类中调用 ModChannels.register () 方法&lt;/p&gt;
&lt;h3 id=&#34;处理dothrow方法&#34;&gt;&lt;a class=&#34;anchor&#34; href=&#34;#处理dothrow方法&#34;&gt;#&lt;/a&gt; 处理 doThrow 方法&lt;/h3&gt;
&lt;p&gt;相信这里不必笔者多说，读者应该已经想出了相关逻辑&lt;/p&gt;
&lt;figure class=&#34;highlight java&#34;&gt;&lt;figcaption data-lang=&#34;java&#34;&gt;&lt;/figcaption&gt;&lt;table&gt;&lt;tr&gt;&lt;td data-num=&#34;1&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token comment&#34;&gt;// 位于 common: trou/arch/item/ItemExpContainer.java&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;2&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;static&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;void&lt;/span&gt; &lt;span class=&#34;token function&#34;&gt;doThrow&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token class-name&#34;&gt;ItemStack&lt;/span&gt; stack&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;Player&lt;/span&gt; target&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;&amp;#123;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;3&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;span class=&#34;token keyword&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token operator&#34;&gt;!&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;stack&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;getItem&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;instanceof&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;ItemExpContainer&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;return&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;4&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;span class=&#34;token class-name&#34;&gt;CompoundTag&lt;/span&gt; tag &lt;span class=&#34;token operator&#34;&gt;=&lt;/span&gt; stack&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;hasTag&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;token operator&#34;&gt;?&lt;/span&gt; stack&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;getTag&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;token operator&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;new&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;CompoundTag&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;5&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;span class=&#34;token keyword&#34;&gt;assert&lt;/span&gt; tag &lt;span class=&#34;token operator&#34;&gt;!=&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;null&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;6&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;span class=&#34;token keyword&#34;&gt;int&lt;/span&gt; storedExp &lt;span class=&#34;token operator&#34;&gt;=&lt;/span&gt; tag&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;getInt&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token string&#34;&gt;&#34;exp&#34;&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;7&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;span class=&#34;token keyword&#34;&gt;int&lt;/span&gt; mode &lt;span class=&#34;token operator&#34;&gt;=&lt;/span&gt; tag&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;getInt&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token string&#34;&gt;&#34;mode&#34;&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;8&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;span class=&#34;token keyword&#34;&gt;int&lt;/span&gt; amount &lt;span class=&#34;token operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;Math&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;min&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;mode&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; storedExp&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;9&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    target&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;giveExperiencePoints&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;amount&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;10&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    tag&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;putInt&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token string&#34;&gt;&#34;exp&#34;&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; storedExp &lt;span class=&#34;token operator&#34;&gt;-&lt;/span&gt; amount&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;11&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token punctuation&#34;&gt;&amp;#125;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;&lt;p&gt;当执行这个方法的时候，会从经验提取器中提取指定量的经验，给予目标玩家&lt;/p&gt;
&lt;h3 id=&#34;添加一个热键&#34;&gt;&lt;a class=&#34;anchor&#34; href=&#34;#添加一个热键&#34;&gt;#&lt;/a&gt; 添加一个热键&lt;/h3&gt;
&lt;p&gt;ArchitecturyAPI 为我们提供了通用的添加热键的方式.&lt;/p&gt;
&lt;p&gt;创建 trou.arch.client 包，并创建 ModKeys 类，创建我们的 KeyMapping&lt;/p&gt;
&lt;figure class=&#34;highlight java&#34;&gt;&lt;figcaption data-lang=&#34;java&#34;&gt;&lt;/figcaption&gt;&lt;table&gt;&lt;tr&gt;&lt;td data-num=&#34;1&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token comment&#34;&gt;// 位于 common: trou/arch/client/ModKeys.java&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;2&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;class&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;ModKeys&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;&amp;#123;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;3&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;span class=&#34;token keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;static&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;final&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;KeyMapping&lt;/span&gt; &lt;span class=&#34;token constant&#34;&gt;THROW_EXP&lt;/span&gt; &lt;span class=&#34;token operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;new&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;KeyMapping&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token string&#34;&gt;&#34;key.arch.throw_exp&#34;&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;InputConstants&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;Type&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token constant&#34;&gt;KEYSYM&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;InputConstants&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token constant&#34;&gt;KEY_Z&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;token string&#34;&gt;&#34;category.arch&#34;&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;4&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;5&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;span class=&#34;token keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;static&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;void&lt;/span&gt; &lt;span class=&#34;token function&#34;&gt;register&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;&amp;#123;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;6&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;        &lt;span class=&#34;token class-name&#34;&gt;KeyMappingRegistry&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;register&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token constant&#34;&gt;THROW_EXP&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;7&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;span class=&#34;token punctuation&#34;&gt;&amp;#125;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;8&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token punctuation&#34;&gt;&amp;#125;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;&lt;p&gt;KeyMapping 的构造器需要四个参数&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;热键的本地化名称：将显示在选项 - 控制中&lt;/li&gt;
&lt;li&gt;热键的类型：可选值为 KEYSYM, SCANCODE, MOUSE. 分别对应键盘，组合键，鼠标&lt;/li&gt;
&lt;li&gt;热键的值：具体取决于类型，这里 InputConstants.KEY_Z 代表键盘的 Z 键&lt;/li&gt;
&lt;li&gt;热键类别的本地化名称：也将显示在选项 - 控制中&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;接下来我们要为热键赋予功能，根据 ArchitecturyAPI 文档中的介绍，我们要监听 ClientTickEvent, 并且使用一个 while 来判断热键是否被按下.&lt;/p&gt;
&lt;p&gt;&amp;lt;figure&amp;gt;&amp;lt;img src=&amp;quot;&lt;a href=&#34;https://s2.loli.net/2023/01/29/argPSelXFoEHBzq.png&#34;&gt;https://s2.loli.net/2023/01/29/argPSelXFoEHBzq.png&lt;/a&gt;&amp;quot;alt=&amp;quot;&amp;quot;&amp;gt;&amp;lt;figcaption&amp;gt;&amp;lt;p&amp;gt;ArchitecturyAPI 文档 &amp;lt;/p&amp;gt;&amp;lt;/figcaption&amp;gt;&amp;lt;/figure&amp;gt;&lt;/p&gt;
&lt;p&gt;这里我们在 ModEvents 中监听事件，并且编写事件处理器&lt;/p&gt;
&lt;figure class=&#34;highlight java&#34;&gt;&lt;figcaption data-lang=&#34;java&#34;&gt;&lt;/figcaption&gt;&lt;table&gt;&lt;tr&gt;&lt;td data-num=&#34;1&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token comment&#34;&gt;// 位于 common: trou/arch/object/ModEvents.java&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;2&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;class&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;ModEvents&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;&amp;#123;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;3&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;span class=&#34;token keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;static&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;void&lt;/span&gt; &lt;span class=&#34;token function&#34;&gt;register&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;&amp;#123;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;4&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;        &lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;5&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;        &lt;span class=&#34;token class-name&#34;&gt;ClientTickEvent&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token constant&#34;&gt;CLIENT_POST&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;register&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token class-name&#34;&gt;ModKeys&lt;/span&gt;&lt;span class=&#34;token operator&#34;&gt;::&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;handleThrowExpKey&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;6&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;span class=&#34;token punctuation&#34;&gt;&amp;#125;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;7&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token punctuation&#34;&gt;&amp;#125;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;8&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;9&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token comment&#34;&gt;// 位于位于 common: trou/arch/client/ModKeys.java&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;10&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;static&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;void&lt;/span&gt; &lt;span class=&#34;token function&#34;&gt;handleThrowExpKey&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token class-name&#34;&gt;Minecraft&lt;/span&gt; minecraft&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;&amp;#123;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;11&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;span class=&#34;token keyword&#34;&gt;while&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token constant&#34;&gt;THROW_EXP&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;consumeClick&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;&amp;#123;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;12&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;        &lt;span class=&#34;token keyword&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;minecraft&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;player &lt;span class=&#34;token operator&#34;&gt;==&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;null&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;return&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;13&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;        &lt;span class=&#34;token comment&#34;&gt;//minecraft.crosshairPickEntity 可以获取到玩家光标瞄准的实体&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;14&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;        &lt;span class=&#34;token keyword&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;minecraft&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;crosshairPickEntity &lt;span class=&#34;token keyword&#34;&gt;instanceof&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;Player&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;&amp;#123;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;15&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;            &lt;span class=&#34;token class-name&#34;&gt;UUID&lt;/span&gt; playerUUID &lt;span class=&#34;token operator&#34;&gt;=&lt;/span&gt; minecraft&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;crosshairPickEntity&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;getUUID&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;16&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;            &lt;span class=&#34;token comment&#34;&gt;// Channel 提供了发送 Message 的几个方法&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;17&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;            &lt;span class=&#34;token comment&#34;&gt;// 这里使用了我们创建的 Message 的构造器来提供 playerUUID 数据&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;18&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;            &lt;span class=&#34;token class-name&#34;&gt;ModChannels&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token constant&#34;&gt;CHANNEL&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;sendToServer&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token keyword&#34;&gt;new&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;MessageThrowExp&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;playerUUID&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;19&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;        &lt;span class=&#34;token punctuation&#34;&gt;&amp;#125;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;20&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;span class=&#34;token punctuation&#34;&gt;&amp;#125;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;21&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token punctuation&#34;&gt;&amp;#125;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;&lt;p&gt;最后，我们在主类中调用 ModKeys.register 方法来注册热键，为了防止问题，我们让热键在事件之前注册&lt;/p&gt;
&lt;p&gt;此时进入游戏，你应该能看到添加的热键了，并且能够使用热键给玩家丢经验了&lt;/p&gt;
&lt;p&gt;&amp;lt;figure&amp;gt;&amp;lt;img src=&amp;quot;&lt;a href=&#34;https://s2.loli.net/2023/01/29/cMLXQseYhElibv3.png&#34;&gt;https://s2.loli.net/2023/01/29/cMLXQseYhElibv3.png&lt;/a&gt;&amp;quot; alt=&amp;quot;&amp;quot;&amp;gt;&amp;lt;figcaption&amp;gt;&amp;lt;/figcaption&amp;gt;&amp;lt;/figure&amp;gt;&lt;/p&gt;
&lt;h3 id=&#34;总结&#34;&gt;&lt;a class=&#34;anchor&#34; href=&#34;#总结&#34;&gt;#&lt;/a&gt; 总结&lt;/h3&gt;
&lt;p&gt;阅读上文的代码后，你的主类应该是这样的&lt;/p&gt;
&lt;figure class=&#34;highlight java&#34;&gt;&lt;figcaption data-lang=&#34;java&#34;&gt;&lt;/figcaption&gt;&lt;table&gt;&lt;tr&gt;&lt;td data-num=&#34;1&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token comment&#34;&gt;// 位于 common: trou/arch/Arch.java&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;2&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;class&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;Arch&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;&amp;#123;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;3&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;span class=&#34;token keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;static&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;final&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;String&lt;/span&gt; &lt;span class=&#34;token constant&#34;&gt;MOD_ID&lt;/span&gt; &lt;span class=&#34;token operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;token string&#34;&gt;&#34;arch&#34;&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;4&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;5&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;span class=&#34;token keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;static&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;void&lt;/span&gt; &lt;span class=&#34;token function&#34;&gt;init&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;&amp;#123;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;6&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;        &lt;span class=&#34;token class-name&#34;&gt;System&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;out&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;println&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token string&#34;&gt;&#34;Hello Architectury!&#34;&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;7&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;        &lt;span class=&#34;token class-name&#34;&gt;Storyteller&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;tellStory&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;8&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;        &lt;span class=&#34;token class-name&#34;&gt;ModItems&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;register&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;9&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;        &lt;span class=&#34;token class-name&#34;&gt;ModKeys&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;register&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;10&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;        &lt;span class=&#34;token class-name&#34;&gt;ModEvents&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;register&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;11&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;        &lt;span class=&#34;token class-name&#34;&gt;ModPackets&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;register&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;12&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;        &lt;span class=&#34;token class-name&#34;&gt;ModChannels&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;register&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;13&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;span class=&#34;token punctuation&#34;&gt;&amp;#125;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;14&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token punctuation&#34;&gt;&amp;#125;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;&lt;p&gt;本章我们主要介绍了两种网络通信的方式，使用 NetworkManager 较为简便，使用 NetworkChannel 较为灵活，这里读者可以根据自己的需求来选择.&lt;/p&gt;
&lt;p&gt;如果你对 Fabric 熟悉，或需求较为单一，推荐使用 NetworkManager&lt;/p&gt;
&lt;p&gt;如果你对 Forge 熟悉，或需求较为复杂，希望可读性强一些，推荐使用 NetworkChannel&lt;/p&gt;
</content>
        <category term="Minecraft" />
        <category term="模组开发" />
        <category term="ArchitecturyAPI" />
        <updated>2022-09-12T03:35:12.000Z</updated>
    </entry>
    <entry>
        <id>https://turou.fun/minecraft/arch-tutor/development/</id>
        <title>跨加载器开发实战 - 经验储存器</title>
        <link rel="alternate" href="https://turou.fun/minecraft/arch-tutor/development/"/>
        <content type="html">&lt;p&gt;&lt;img loading=&#34;lazy&#34; data-src=&#34;https://s2.loli.net/2022/04/07/FMYQtNmyU5n9Wdb.png&#34; alt=&#34;效果图&#34; /&gt;&lt;/p&gt;
&lt;p&gt;观察图片可以猜测，本节运用到的知识内容:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;DeferredRegister&lt;/strong&gt; - 创建并注册游戏元素&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;I18n&lt;/strong&gt; - 为元素添加本地化和材质&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;CompoundTag&lt;/strong&gt; - 为物品添加功能&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;ClientTooltipEvent&lt;/strong&gt; - 为物品添加 Tooltips (使用封装好的事件)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;下面我们来基于 Architectury API 来编写我们的第一个物品 —— 经验储存器.&lt;/p&gt;
&lt;h3 id=&#34;创建并注册物品&#34;&gt;&lt;a class=&#34;anchor&#34; href=&#34;#创建并注册物品&#34;&gt;#&lt;/a&gt; 创建并注册物品&lt;/h3&gt;
&lt;p&gt;在 &lt;strong&gt;trou.arch.item&lt;/strong&gt; 包下创建 &lt;strong&gt;ItemExpContainer&lt;/strong&gt; 类，使其继承自&lt;strong&gt;原版的&lt;/strong&gt; Item 类&lt;/p&gt;
&lt;figure class=&#34;highlight java&#34;&gt;&lt;figcaption data-lang=&#34;java&#34;&gt;&lt;/figcaption&gt;&lt;table&gt;&lt;tr&gt;&lt;td data-num=&#34;1&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token comment&#34;&gt;// 位于 common: trou/arch/item/ItemExpContainer.java&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;2&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;class&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;ItemExpContainer&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;extends&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;Item&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;&amp;#123;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;3&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;span class=&#34;token keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;ItemExpContainer&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;&amp;#123;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;4&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;        &lt;span class=&#34;token keyword&#34;&gt;super&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token keyword&#34;&gt;new&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;Properties&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;stacksTo&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token number&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;tab&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token class-name&#34;&gt;CreativeModeTab&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token constant&#34;&gt;TAB_TOOLS&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;5&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;span class=&#34;token punctuation&#34;&gt;&amp;#125;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;6&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token punctuation&#34;&gt;&amp;#125;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;&lt;p&gt;熟悉 Forge 开发的读者在这里可能产生疑问，读者可能尝试设置 RegistryName 和 TranslationKey, 结果发现没有这样的方法。&lt;/p&gt;
&lt;p&gt;实际上，Forge 为原版的 Item 进行了许多层封装，读者可以尝试查阅 forge 模块中的 Item 类，会发现它实现了 IForgeItem, 继承自 ForgeRegistryEntry. Forge 为了易用性舍弃了轻量的结构. Forge 将注册名的设置封装在了物品内部，这与原版的逻辑不同.&lt;/p&gt;
&lt;p&gt;而 Fabric 中并没有额外的封装，而是基于 Minecraft 原本的样子。读者可能意识到，实际上 Architectury 是更倾向于原版，或者说 Fabric 的。所以我们在开发中，RegistryName 实际上是在注册的时候提供的。&lt;/p&gt;
&lt;p&gt;Architectury 这里采用原版 (Fabric) 的架构，而为 Forge 进行兼容是合理的。在跨平台开发中，往往要&lt;strong&gt;基于低&lt;/strong&gt;而&lt;strong&gt;适配高&lt;/strong&gt;，这也是可理解的。正如 DisplayPort 转 HDMI 容易，而 HDMI 转 DisplayPort 困难的道理。&lt;/p&gt;
&lt;p&gt;下面我们要注册这个物品，我们采用 Architectury 封装的&lt;strong&gt; DeferredRegister&lt;/strong&gt; 进行注册。相同的，如果读者要注册其他的方块、附魔、BlockEntityType 等元素，也可以使用相同的方法。读者可以查阅 Registry 中的常量&lt;/p&gt;
&lt;p&gt;在 &lt;strong&gt;trou.arch.object&lt;/strong&gt; 包下创建 &lt;strong&gt;ModItems&lt;/strong&gt; 类&lt;/p&gt;
&lt;figure class=&#34;highlight java&#34;&gt;&lt;figcaption data-lang=&#34;java&#34;&gt;&lt;/figcaption&gt;&lt;table&gt;&lt;tr&gt;&lt;td data-num=&#34;1&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token comment&#34;&gt;// 位于 common: trou/arch/object/ModItems.java&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;2&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;class&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;ModItems&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;&amp;#123;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;3&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;span class=&#34;token keyword&#34;&gt;private&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;static&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;final&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;DeferredRegister&lt;/span&gt;&lt;span class=&#34;token generics&#34;&gt;&lt;span class=&#34;token punctuation&#34;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&#34;token class-name&#34;&gt;Item&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;&gt;&lt;/span&gt;&lt;/span&gt; &lt;span class=&#34;token constant&#34;&gt;ITEMS&lt;/span&gt; &lt;span class=&#34;token operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;DeferredRegister&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;create&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token class-name&#34;&gt;Arch&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token constant&#34;&gt;MOD_ID&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;Registry&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token constant&#34;&gt;ITEM_REGISTRY&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;4&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;span class=&#34;token keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;static&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;final&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;RegistrySupplier&lt;/span&gt;&lt;span class=&#34;token generics&#34;&gt;&lt;span class=&#34;token punctuation&#34;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&#34;token class-name&#34;&gt;Item&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;&gt;&lt;/span&gt;&lt;/span&gt; &lt;span class=&#34;token constant&#34;&gt;EXP_CONTAINER_ITEM&lt;/span&gt; &lt;span class=&#34;token operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;token constant&#34;&gt;ITEMS&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;register&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token string&#34;&gt;&#34;exp_container_item&#34;&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;ItemExpContainer&lt;/span&gt;&lt;span class=&#34;token operator&#34;&gt;::&lt;/span&gt;&lt;span class=&#34;token keyword&#34;&gt;new&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;5&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;span class=&#34;token keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;static&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;void&lt;/span&gt; &lt;span class=&#34;token function&#34;&gt;register&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;&amp;#123;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;6&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;        &lt;span class=&#34;token constant&#34;&gt;ITEMS&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;register&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt; &lt;span class=&#34;token comment&#34;&gt;//DeferredRegister 的 register 方法需要在 init 阶段被调用&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;7&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;span class=&#34;token punctuation&#34;&gt;&amp;#125;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;8&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token punctuation&#34;&gt;&amp;#125;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;&lt;p&gt;为了注册物品，我们调用 &lt;code&gt;DeferredRegister.create&lt;/code&gt;  来创建一个注册器&lt;/p&gt;
&lt;p&gt;针对每一个物品，我们需要调用注册器的 &lt;code&gt;register&lt;/code&gt;  方法来注册，需要传入注册名和构造方法&lt;/p&gt;
&lt;p&gt;之后，我们在模组的&lt;strong&gt; init&lt;/strong&gt; 阶段调用 &lt;code&gt;ModItems.register()&lt;/code&gt;  方法&lt;/p&gt;
&lt;p&gt;&amp;lt;mark style=&amp;quot;color:red;&amp;quot;&amp;gt;&lt;strong&gt; 因为 Forge 的某些限制，我们需要在 forge 模块中手动注册一次 Architectury 的事件总线&lt;/strong&gt; &amp;lt;/mark&amp;gt;&lt;/p&gt;
&lt;figure class=&#34;highlight java&#34;&gt;&lt;figcaption data-lang=&#34;java&#34;&gt;&lt;/figcaption&gt;&lt;table&gt;&lt;tr&gt;&lt;td data-num=&#34;1&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token comment&#34;&gt;// 位于 forge: trou/arch/forge/ArchForge.java&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;2&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token annotation punctuation&#34;&gt;@Mod&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token class-name&#34;&gt;Arch&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token constant&#34;&gt;MOD_ID&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;3&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;class&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;ArchForge&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;&amp;#123;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;4&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;span class=&#34;token keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;ArchForge&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;&amp;#123;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;5&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;        &lt;span class=&#34;token comment&#34;&gt;// 这里一定要注册上 Architectury 的 EventBus&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;6&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;        &lt;span class=&#34;token class-name&#34;&gt;EventBuses&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;registerModEventBus&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token class-name&#34;&gt;Arch&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token constant&#34;&gt;MOD_ID&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;FMLJavaModLoadingContext&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;get&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;getModEventBus&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;7&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;        &lt;span class=&#34;token class-name&#34;&gt;Arch&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;init&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;8&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;span class=&#34;token punctuation&#34;&gt;&amp;#125;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;9&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token punctuation&#34;&gt;&amp;#125;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;&lt;p&gt;之后分别进入 fabric 和 forge 客户端，应该都能看到我们的紫黑块物品了。&lt;/p&gt;
&lt;p&gt;笔者这里储存了 ITEMS.register 返回的 RegistrySupplier, 如果在代码的别处需要用到 ItemExpContainer 的实例时，可以调用 RegistrySupplier 的 get 方法.&lt;/p&gt;
&lt;p&gt;不难理解，其实 RegistrySupplier 充当了保存对象和它的注册名的功能&lt;/p&gt;
&lt;h3 id=&#34;为物品添加本地化和材质&#34;&gt;&lt;a class=&#34;anchor&#34; href=&#34;#为物品添加本地化和材质&#34;&gt;#&lt;/a&gt; 为物品添加本地化和材质&lt;/h3&gt;
&lt;p&gt;common 模块中的 assets 在编译的时候会被合并进 forge 和 fabric 模块中，所以我们只需按照熟悉的方式将本地化文件和材质放在 common 模块中就可以了.&lt;/p&gt;
&lt;figure class=&#34;highlight json&#34;&gt;&lt;figcaption data-lang=&#34;JSON&#34;&gt;&lt;/figcaption&gt;&lt;table&gt;&lt;tr&gt;&lt;td data-num=&#34;1&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token comment&#34;&gt;// 位于 common: assets/arch/lang/zh_cn.json&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;2&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token punctuation&#34;&gt;&amp;#123;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;3&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;  &lt;span class=&#34;token property&#34;&gt;&#34;item.arch.exp_container_item&#34;&lt;/span&gt;&lt;span class=&#34;token operator&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;token string&#34;&gt;&#34;经验储存器&#34;&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;4&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;  &lt;span class=&#34;token property&#34;&gt;&#34;tooltip.exp_container&#34;&lt;/span&gt;&lt;span class=&#34;token operator&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;token string&#34;&gt;&#34;已储存经验: %s exp&#34;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;5&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token punctuation&#34;&gt;&amp;#125;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;6&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;7&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token comment&#34;&gt;// 位于 common: assets/arch/lang/en_us.json&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;8&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token punctuation&#34;&gt;&amp;#123;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;9&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;  &lt;span class=&#34;token property&#34;&gt;&#34;item.arch.exp_container_item&#34;&lt;/span&gt;&lt;span class=&#34;token operator&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;token string&#34;&gt;&#34;Experience Container&#34;&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;10&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;  &lt;span class=&#34;token property&#34;&gt;&#34;tooltip.exp_container&#34;&lt;/span&gt;&lt;span class=&#34;token operator&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;token string&#34;&gt;&#34;Stored experience: %s exp&#34;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;11&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token punctuation&#34;&gt;&amp;#125;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;12&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;13&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token comment&#34;&gt;// 位于 common: assets/arch/models/item/exp_container_item.json&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;14&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token punctuation&#34;&gt;&amp;#123;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;15&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;  &lt;span class=&#34;token property&#34;&gt;&#34;parent&#34;&lt;/span&gt;&lt;span class=&#34;token operator&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;token string&#34;&gt;&#34;item/generated&#34;&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;16&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;  &lt;span class=&#34;token property&#34;&gt;&#34;textures&#34;&lt;/span&gt;&lt;span class=&#34;token operator&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;&amp;#123;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;17&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;span class=&#34;token property&#34;&gt;&#34;layer0&#34;&lt;/span&gt;&lt;span class=&#34;token operator&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;token string&#34;&gt;&#34;arch:item/exp_container_item&#34;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;18&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;  &lt;span class=&#34;token punctuation&#34;&gt;&amp;#125;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;19&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token punctuation&#34;&gt;&amp;#125;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;20&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;21&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token comment&#34;&gt;// 位于 common: assets/arch/textures/item/exp_container_item.png&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;22&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token comment&#34;&gt;// 在这里放置物品的材质&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;&lt;h3 id=&#34;为物品添加功能&#34;&gt;&lt;a class=&#34;anchor&#34; href=&#34;#为物品添加功能&#34;&gt;#&lt;/a&gt; 为物品添加功能&lt;/h3&gt;
&lt;p&gt;根据我们的需求，需要覆盖物品的 use 方法，并编写我们的逻辑，这里相信有经验的读者可以完成&lt;/p&gt;
&lt;figure class=&#34;highlight java&#34;&gt;&lt;figcaption data-lang=&#34;java&#34;&gt;&lt;/figcaption&gt;&lt;table&gt;&lt;tr&gt;&lt;td data-num=&#34;1&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token comment&#34;&gt;// 位于 common: trou/arch/item/ItemExpContainer.java&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;2&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token annotation punctuation&#34;&gt;@Override&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;3&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;InteractionResultHolder&lt;/span&gt;&lt;span class=&#34;token generics&#34;&gt;&lt;span class=&#34;token punctuation&#34;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&#34;token class-name&#34;&gt;ItemStack&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;&gt;&lt;/span&gt;&lt;/span&gt; &lt;span class=&#34;token function&#34;&gt;use&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token annotation punctuation&#34;&gt;@NotNull&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;Level&lt;/span&gt; level&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;token annotation punctuation&#34;&gt;@NotNull&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;Player&lt;/span&gt; player&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;token annotation punctuation&#34;&gt;@NotNull&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;InteractionHand&lt;/span&gt; usedHand&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;&amp;#123;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;4&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;span class=&#34;token class-name&#34;&gt;ItemStack&lt;/span&gt; stack &lt;span class=&#34;token operator&#34;&gt;=&lt;/span&gt; player&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;getItemInHand&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;usedHand&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;5&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;span class=&#34;token keyword&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;level&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;isClientSide &lt;span class=&#34;token operator&#34;&gt;||&lt;/span&gt; usedHand &lt;span class=&#34;token operator&#34;&gt;==&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;InteractionHand&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token constant&#34;&gt;OFF_HAND&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;InteractionResultHolder&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;fail&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;stack&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;6&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;span class=&#34;token class-name&#34;&gt;CompoundTag&lt;/span&gt; tag &lt;span class=&#34;token operator&#34;&gt;=&lt;/span&gt; stack&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;hasTag&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;token operator&#34;&gt;?&lt;/span&gt; stack&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;getTag&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;token operator&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;new&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;CompoundTag&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;7&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;span class=&#34;token keyword&#34;&gt;assert&lt;/span&gt; tag &lt;span class=&#34;token operator&#34;&gt;!=&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;null&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;8&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;span class=&#34;token keyword&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;player&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;isShiftKeyDown&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;&amp;#123;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;9&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;        player&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;giveExperiencePoints&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;tag&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;getInt&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token string&#34;&gt;&#34;exp&#34;&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;10&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;        tag&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;putInt&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token string&#34;&gt;&#34;exp&#34;&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;token number&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;11&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;span class=&#34;token punctuation&#34;&gt;&amp;#125;&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;else&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;&amp;#123;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;12&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;        &lt;span class=&#34;token keyword&#34;&gt;int&lt;/span&gt; exp &lt;span class=&#34;token operator&#34;&gt;=&lt;/span&gt; player&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;totalExperience&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;13&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;        player&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;giveExperiencePoints&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token operator&#34;&gt;-&lt;/span&gt;exp&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;14&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;        tag&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;putInt&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token string&#34;&gt;&#34;exp&#34;&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; tag&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;getInt&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token string&#34;&gt;&#34;exp&#34;&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;token operator&#34;&gt;+&lt;/span&gt; exp&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;15&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;span class=&#34;token punctuation&#34;&gt;&amp;#125;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;16&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    stack&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;setTag&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;tag&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;17&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;span class=&#34;token keyword&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;InteractionResultHolder&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;sidedSuccess&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;stack&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; level&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;isClientSide&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;18&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token punctuation&#34;&gt;&amp;#125;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;&lt;h3 id=&#34;监听tooltip事件&#34;&gt;&lt;a class=&#34;anchor&#34; href=&#34;#监听tooltip事件&#34;&gt;#&lt;/a&gt; 监听 Tooltip 事件&lt;/h3&gt;
&lt;hr /&gt;
&lt;p&gt;&lt;strong&gt;ArchtecturyAPI&lt;/strong&gt; 建立了一套位于 Forge 和 Fabric 之上的事件封装层。在 Forge 中，我们创建方法并添加 @SubscribeEvent 注解来让 Forge 注册我们的事件。而在&lt;strong&gt; ArchtecturyAPI&lt;/strong&gt; 中，我们使用一种更为 &amp;quot;静态&amp;quot; 的方式&lt;/p&gt;
&lt;p&gt;ArchitecturyAPI 为我们提供了一些常用的事件，在上一章节可以找到. &lt;/p&gt;
&lt;p&gt;这里我们需要监听&lt;strong&gt; ClientTooltipEvent&lt;/strong&gt; 事件，首先创建一个类来注册所有的事件&lt;/p&gt;
&lt;figure class=&#34;highlight java&#34;&gt;&lt;figcaption data-lang=&#34;java&#34;&gt;&lt;/figcaption&gt;&lt;table&gt;&lt;tr&gt;&lt;td data-num=&#34;1&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token comment&#34;&gt;// 位于 common: trou/arch/object/ModEvents.java&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;2&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;class&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;ModEvents&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;&amp;#123;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;3&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;span class=&#34;token keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;static&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;void&lt;/span&gt; &lt;span class=&#34;token function&#34;&gt;register&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;&amp;#123;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;4&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;        &lt;span class=&#34;token comment&#34;&gt;// 需要传入的是事件处理器的方法&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;5&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;        &lt;span class=&#34;token class-name&#34;&gt;ClientTooltipEvent&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token constant&#34;&gt;ITEM&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;register&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token class-name&#34;&gt;ItemExpContainer&lt;/span&gt;&lt;span class=&#34;token operator&#34;&gt;::&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;append&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;6&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;span class=&#34;token punctuation&#34;&gt;&amp;#125;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;7&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token punctuation&#34;&gt;&amp;#125;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;8&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;9&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token comment&#34;&gt;// 位于 common: trou/arch/Arch.java&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;10&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;class&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;Arch&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;&amp;#123;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;11&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;span class=&#34;token keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;static&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;void&lt;/span&gt; &lt;span class=&#34;token function&#34;&gt;init&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;&amp;#123;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;12&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;        &lt;span class=&#34;token class-name&#34;&gt;ModEvents&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;register&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt; &lt;span class=&#34;token comment&#34;&gt;// 在 init 阶段注册事件&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;13&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;span class=&#34;token punctuation&#34;&gt;&amp;#125;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;14&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token punctuation&#34;&gt;&amp;#125;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;&lt;p&gt;然后我们按照代码提示，在 ItemExpContainer 类中补全 append 方法来处理事件&lt;/p&gt;
&lt;figure class=&#34;highlight java&#34;&gt;&lt;figcaption data-lang=&#34;java&#34;&gt;&lt;/figcaption&gt;&lt;table&gt;&lt;tr&gt;&lt;td data-num=&#34;1&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token comment&#34;&gt;// 位于 common: trou/arch/item/ItemExpContainer.java&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;2&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;static&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;void&lt;/span&gt; &lt;span class=&#34;token function&#34;&gt;append&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token class-name&#34;&gt;ItemStack&lt;/span&gt; stack&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;List&lt;/span&gt;&lt;span class=&#34;token generics&#34;&gt;&lt;span class=&#34;token punctuation&#34;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&#34;token class-name&#34;&gt;Component&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;&gt;&lt;/span&gt;&lt;/span&gt; lines&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;TooltipFlag&lt;/span&gt; flag&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;&amp;#123;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;3&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;span class=&#34;token keyword&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;stack&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;getItem&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;instanceof&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;ItemExpContainer&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;&amp;#123;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;4&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;        &lt;span class=&#34;token class-name&#34;&gt;CompoundTag&lt;/span&gt; tag &lt;span class=&#34;token operator&#34;&gt;=&lt;/span&gt; stack&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;hasTag&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;token operator&#34;&gt;?&lt;/span&gt; stack&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;getTag&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;token operator&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;new&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;CompoundTag&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;5&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;        &lt;span class=&#34;token keyword&#34;&gt;assert&lt;/span&gt; tag &lt;span class=&#34;token operator&#34;&gt;!=&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;null&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;6&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;        lines&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;add&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token keyword&#34;&gt;new&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;TranslatableComponent&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token string&#34;&gt;&#34;tooltip.exp_container&#34;&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; tag&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;getInt&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token string&#34;&gt;&#34;exp&#34;&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;7&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;span class=&#34;token punctuation&#34;&gt;&amp;#125;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;8&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token punctuation&#34;&gt;&amp;#125;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;&lt;p&gt;ClientTooltipEvent.ITEM.register 背后实际上是利用 @ExpectPlatform 来处理的&lt;/p&gt;
&lt;p&gt;针对两个不同的平台，API 分别写了相应的事件处理器的实现，因此简化了我们对于事件的注册&lt;/p&gt;
&lt;p&gt;对于一个特定的需求，首先我们要检查 API 是否已经为我们提供了相应的方法&lt;/p&gt;
&lt;p&gt;如果没有提供相应的方法，我们再去手动针对两个平台进行实现&lt;/p&gt;
&lt;p&gt;最后进入游戏，我们的第一个物品已经成功被加入了&lt;/p&gt;
&lt;h1 id=&#34;再次开始&#34;&gt;&lt;a class=&#34;anchor&#34; href=&#34;#再次开始&#34;&gt;#&lt;/a&gt; 再次开始&lt;/h1&gt;
&lt;p&gt;首先我们先从官方文档出发，看看 Architectury API 都为我们封装了哪些易于方法的方法和类。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;读者可以暂时跳过这部分，进入下文的实例环节，在以后的开发中如果有需要再来查阅.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;注：某些类的描述添加了笔者自己的理解，并且附加了可能的应用方法，以便于读者自行摸索下文的例子中没有提到的使用方法。&lt;/p&gt;
&lt;h3 id=&#34;实用的抽象层&#34;&gt;&lt;a class=&#34;anchor&#34; href=&#34;#实用的抽象层&#34;&gt;#&lt;/a&gt; 实用的抽象层&lt;/h3&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;类名&lt;/th&gt;
&lt;th&gt;描述&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Platform&lt;/td&gt;
&lt;td&gt;提供了获取当前加载器的一些规范的类 (比如配置文件位置，Mod 列表等方法)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Registries&lt;/td&gt;
&lt;td&gt;提供了注册物品，方块等元素的一系列方法和接口的类 (比如 RegistryProvider)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;KeyBindings&lt;/td&gt;
&lt;td&gt;提供了一些关于按键绑定的方法&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;CreativeTabs&lt;/td&gt;
&lt;td&gt;提供了创建 CreativeTab 的方法&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;MenuRegistry&lt;/td&gt;
&lt;td&gt;提供了关于 Menu 界面 (例如统计信息，设置) 的一些注册方法&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;RenderTypes&lt;/td&gt;
&lt;td&gt;统一了两个加载器的 RenderTypes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ReloadListeners&lt;/td&gt;
&lt;td&gt;重载 Listeners 的注册器，现在已经改成了 ReloadListenerRegistry, 但是官方文档还没有更新&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;CriteriaTriggersRegistry&lt;/td&gt;
&lt;td&gt;提供了一些关于成就触发器的方法，现在这个类已经弃用，如果读者要使用，请调用 CriteriaTriggers&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ColorHandlers&lt;/td&gt;
&lt;td&gt;提供了一些关于颜色的方法&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;BlockEntityRenderers&lt;/td&gt;
&lt;td&gt;提供了一些绑定 BlockEntity 渲染器的方法&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;BiomeModifications&lt;/td&gt;
&lt;td&gt;提供了一些关于生物群系的方法&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;PackRepositoryHooks&lt;/td&gt;
&lt;td&gt;提供了一些关于高版本行为包的钩子方法&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h3 id=&#34;网络和网络包的抽象层&#34;&gt;&lt;a class=&#34;anchor&#34; href=&#34;#网络和网络包的抽象层&#34;&gt;#&lt;/a&gt; 网络和网络包的抽象层&lt;/h3&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;类名&lt;/th&gt;
&lt;th&gt;描述&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;NetworkManager&lt;/td&gt;
&lt;td&gt;提供了一个类似于 Fabric 的网络包管理系统&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;NetworkChannel&lt;/td&gt;
&lt;td&gt;提供了一个类似 Forge Channel 的包收发系统&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h3 id=&#34;抽象出来的一些钩子方法&#34;&gt;&lt;a class=&#34;anchor&#34; href=&#34;#抽象出来的一些钩子方法&#34;&gt;#&lt;/a&gt; 抽象出来的一些钩子方法&lt;/h3&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;类名&lt;/th&gt;
&lt;th&gt;描述&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;BiomeHooks&lt;/td&gt;
&lt;td&gt;提供了一套重新封装的生物群系属性。需要获取生物群系属性的可以调用这个类中的方法&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;BlockEntityHooks&lt;/td&gt;
&lt;td&gt;将 BlockEntity 的数据变化推送至客户端的方法，会调用区块的 blockChanged 方法来推送&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;DyeColorHooks&lt;/td&gt;
&lt;td&gt;提供了将 DyeColor 转换成颜色的方法&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;EntityHooks&lt;/td&gt;
&lt;td&gt;提供了关于 Entity 的 Collision 相关方法，源文档中提供的已弃用&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ExplosionHooks&lt;/td&gt;
&lt;td&gt;提供了从爆炸 (Explosion) 获取爆炸位置的方法&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ItemEntityHooks&lt;/td&gt;
&lt;td&gt;可以获取一个物品实体的 lifespan&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;PlayerHooks&lt;/td&gt;
&lt;td&gt;可以获取玩家是否是 fakePlayer&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ScreenHooks&lt;/td&gt;
&lt;td&gt;提供了一些关于在屏幕上渲染的方法&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h3 id=&#34;抽象出来的一些事件&#34;&gt;&lt;a class=&#34;anchor&#34; href=&#34;#抽象出来的一些事件&#34;&gt;#&lt;/a&gt; 抽象出来的一些事件&lt;/h3&gt;
&lt;p&gt;&lt;img loading=&#34;lazy&#34; data-src=&#34;https://s2.loli.net/2022/04/07/DxcKzeACnqY41iN.png&#34; alt=&#34;事件&#34; /&gt;&lt;/p&gt;
&lt;p&gt;Architectury API 提供了一些我们常用的事件，这里笔者可以根据名称自行理解用途.&lt;/p&gt;
</content>
        <category term="Minecraft" />
        <category term="模组开发" />
        <category term="ArchitecturyAPI" />
        <updated>2022-07-12T03:35:12.000Z</updated>
    </entry>
    <entry>
        <id>https://turou.fun/minecraft/arch-tutor/arch-api/</id>
        <title>深入了解Architectury API</title>
        <link rel="alternate" href="https://turou.fun/minecraft/arch-tutor/arch-api/"/>
        <content type="html">&lt;p&gt;首先我们先从官方文档出发，看看 Architectury API 都为我们封装了哪些易于方法的方法和类。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;读者可以暂时跳过这部分，进入下文的实例环节，在以后的开发中如果有需要再来查阅.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;注：某些类的描述添加了笔者自己的理解，并且附加了可能的应用方法，以便于读者自行摸索下文的例子中没有提到的使用方法。&lt;/p&gt;
&lt;h3 id=&#34;实用的抽象层&#34;&gt;&lt;a class=&#34;anchor&#34; href=&#34;#实用的抽象层&#34;&gt;#&lt;/a&gt; 实用的抽象层&lt;/h3&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;类名&lt;/th&gt;
&lt;th&gt;描述&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Platform&lt;/td&gt;
&lt;td&gt;提供了获取当前加载器的一些规范的类 (比如配置文件位置，Mod 列表等方法)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Registries&lt;/td&gt;
&lt;td&gt;提供了注册物品，方块等元素的一系列方法和接口的类 (比如 RegistryProvider)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;KeyBindings&lt;/td&gt;
&lt;td&gt;提供了一些关于按键绑定的方法&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;CreativeTabs&lt;/td&gt;
&lt;td&gt;提供了创建 CreativeTab 的方法&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;MenuRegistry&lt;/td&gt;
&lt;td&gt;提供了关于 Menu 界面 (例如统计信息，设置) 的一些注册方法&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;RenderTypes&lt;/td&gt;
&lt;td&gt;统一了两个加载器的 RenderTypes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ReloadListeners&lt;/td&gt;
&lt;td&gt;重载 Listeners 的注册器，现在已经改成了 ReloadListenerRegistry, 但是官方文档还没有更新&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;CriteriaTriggersRegistry&lt;/td&gt;
&lt;td&gt;提供了一些关于成就触发器的方法，现在这个类已经弃用，如果读者要使用，请调用 CriteriaTriggers&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ColorHandlers&lt;/td&gt;
&lt;td&gt;提供了一些关于颜色的方法&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;BlockEntityRenderers&lt;/td&gt;
&lt;td&gt;提供了一些绑定 BlockEntity 渲染器的方法&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;BiomeModifications&lt;/td&gt;
&lt;td&gt;提供了一些关于生物群系的方法&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;PackRepositoryHooks&lt;/td&gt;
&lt;td&gt;提供了一些关于高版本行为包的钩子方法&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h3 id=&#34;网络和网络包的抽象层&#34;&gt;&lt;a class=&#34;anchor&#34; href=&#34;#网络和网络包的抽象层&#34;&gt;#&lt;/a&gt; 网络和网络包的抽象层&lt;/h3&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;类名&lt;/th&gt;
&lt;th&gt;描述&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;NetworkManager&lt;/td&gt;
&lt;td&gt;提供了一个类似于 Fabric 的网络包管理系统&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;NetworkChannel&lt;/td&gt;
&lt;td&gt;提供了一个类似 Forge Channel 的包收发系统&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h3 id=&#34;抽象出来的一些钩子方法&#34;&gt;&lt;a class=&#34;anchor&#34; href=&#34;#抽象出来的一些钩子方法&#34;&gt;#&lt;/a&gt; 抽象出来的一些钩子方法&lt;/h3&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;类名&lt;/th&gt;
&lt;th&gt;描述&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;BiomeHooks&lt;/td&gt;
&lt;td&gt;提供了一套重新封装的生物群系属性。需要获取生物群系属性的可以调用这个类中的方法&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;BlockEntityHooks&lt;/td&gt;
&lt;td&gt;将 BlockEntity 的数据变化推送至客户端的方法，会调用区块的 blockChanged 方法来推送&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;DyeColorHooks&lt;/td&gt;
&lt;td&gt;提供了将 DyeColor 转换成颜色的方法&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;EntityHooks&lt;/td&gt;
&lt;td&gt;提供了关于 Entity 的 Collision 相关方法，源文档中提供的已弃用&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ExplosionHooks&lt;/td&gt;
&lt;td&gt;提供了从爆炸 (Explosion) 获取爆炸位置的方法&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ItemEntityHooks&lt;/td&gt;
&lt;td&gt;可以获取一个物品实体的 lifespan&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;PlayerHooks&lt;/td&gt;
&lt;td&gt;可以获取玩家是否是 fakePlayer&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ScreenHooks&lt;/td&gt;
&lt;td&gt;提供了一些关于在屏幕上渲染的方法&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h3 id=&#34;抽象出来的一些事件&#34;&gt;&lt;a class=&#34;anchor&#34; href=&#34;#抽象出来的一些事件&#34;&gt;#&lt;/a&gt; 抽象出来的一些事件&lt;/h3&gt;
&lt;p&gt;&lt;img loading=&#34;lazy&#34; data-src=&#34;https://s2.loli.net/2022/04/07/DxcKzeACnqY41iN.png&#34; alt=&#34;事件&#34; /&gt;&lt;/p&gt;
&lt;p&gt;Architectury API 提供了一些我们常用的事件，这里笔者可以根据名称自行理解用途.&lt;/p&gt;
</content>
        <category term="Minecraft" />
        <category term="模组开发" />
        <category term="ArchitecturyAPI" />
        <updated>2022-07-01T03:35:12.000Z</updated>
    </entry>
    <entry>
        <id>https://turou.fun/minecraft/arch-tutor/arch-plugin/</id>
        <title>深入了解Architectury Plugin</title>
        <link rel="alternate" href="https://turou.fun/minecraft/arch-tutor/arch-plugin/"/>
        <content type="html">&lt;h1 id=&#34;expectplatform&#34;&gt;&lt;a class=&#34;anchor&#34; href=&#34;#expectplatform&#34;&gt;#&lt;/a&gt; ExpectPlatform&lt;/h1&gt;
&lt;h3 id=&#34;expectplatform-2&#34;&gt;&lt;a class=&#34;anchor&#34; href=&#34;#expectplatform-2&#34;&gt;#&lt;/a&gt; ExpectPlatform&lt;/h3&gt;
&lt;p&gt;读者应该已经对 ExpectPlatform 有了一个基本的认识，ExpectPlatform 是 Architectury Plugin 的一个重要注解，它可以根据平台的不同将&lt;strong&gt;一个方法&lt;/strong&gt;映射到&lt;strong&gt;不同的实现&lt;/strong&gt;上去.&lt;/p&gt;
&lt;p&gt;Architectury 对 ExpectPlatform 修饰的方法有一些规范和要求: &lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;被修饰的方法必须是&lt;strong&gt;公开&lt;/strong&gt;且&lt;strong&gt;静态&lt;/strong&gt;的&lt;/li&gt;
&lt;li&gt;方法的实现，命名必须是&lt;strong&gt;原方法名 + Impl&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;方法的实现，应该存放到 forge 模块，相同包下名为 forge 的包中&lt;/li&gt;
&lt;li&gt;方法的实现，应该存放到 fabric 模块，相同包下名为 fabric 的包中&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;在 common 模块中创建类 Storyteller, 并创建一个&lt;strong&gt;公开静态&lt;/strong&gt;方法，添加 ExpectPlatform 注解&lt;/p&gt;
&lt;figure class=&#34;highlight java&#34;&gt;&lt;figcaption data-lang=&#34;java&#34;&gt;&lt;/figcaption&gt;&lt;table&gt;&lt;tr&gt;&lt;td data-num=&#34;1&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;位于common&lt;span class=&#34;token operator&#34;&gt;:&lt;/span&gt; trou&lt;span class=&#34;token operator&#34;&gt;/&lt;/span&gt;arch&lt;span class=&#34;token operator&#34;&gt;/&lt;/span&gt;&lt;span class=&#34;token class-name&#34;&gt;Storyteller&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;java&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;2&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token annotation punctuation&#34;&gt;@ExpectPlatform&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;3&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;static&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;void&lt;/span&gt; &lt;span class=&#34;token function&#34;&gt;tellStory&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;&amp;#123;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;4&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;span class=&#34;token comment&#34;&gt;// 方法体为空，Architectury 会根据不同的模组加载器自动填充&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;5&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token punctuation&#34;&gt;&amp;#125;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;&lt;p&gt;在 fabric 和 forge 模块中&lt;strong&gt;相同位置&lt;/strong&gt;创建类 Storyteller&lt;strong&gt;Impl&lt;/strong&gt;, 为它添加实现&lt;/p&gt;
&lt;figure class=&#34;highlight java&#34;&gt;&lt;figcaption data-lang=&#34;java&#34;&gt;&lt;/figcaption&gt;&lt;table&gt;&lt;tr&gt;&lt;td data-num=&#34;1&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;位于fabric&lt;span class=&#34;token operator&#34;&gt;:&lt;/span&gt; trou&lt;span class=&#34;token operator&#34;&gt;/&lt;/span&gt;arch&lt;span class=&#34;token operator&#34;&gt;/&lt;/span&gt;fabric&lt;span class=&#34;token operator&#34;&gt;/&lt;/span&gt;&lt;span class=&#34;token class-name&#34;&gt;StorytellerImpl&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;java&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;2&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;class&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;StorytellerImpl&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;&amp;#123;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;3&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;span class=&#34;token keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;static&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;void&lt;/span&gt; &lt;span class=&#34;token function&#34;&gt;tellStory&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;&amp;#123;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;4&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;        &lt;span class=&#34;token class-name&#34;&gt;System&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;out&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;println&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token string&#34;&gt;&#34;I&#39;m fabric, your new friend.&#34;&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;5&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;span class=&#34;token punctuation&#34;&gt;&amp;#125;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;6&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token punctuation&#34;&gt;&amp;#125;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;&lt;figure class=&#34;highlight java&#34;&gt;&lt;figcaption data-lang=&#34;java&#34;&gt;&lt;/figcaption&gt;&lt;table&gt;&lt;tr&gt;&lt;td data-num=&#34;1&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;位于forge&lt;span class=&#34;token operator&#34;&gt;:&lt;/span&gt; trou&lt;span class=&#34;token operator&#34;&gt;/&lt;/span&gt;arch&lt;span class=&#34;token operator&#34;&gt;/&lt;/span&gt;forge&lt;span class=&#34;token operator&#34;&gt;/&lt;/span&gt;&lt;span class=&#34;token class-name&#34;&gt;StorytellerImpl&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;java&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;2&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;class&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;StorytellerImpl&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;&amp;#123;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;3&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;span class=&#34;token keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;static&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;void&lt;/span&gt; &lt;span class=&#34;token function&#34;&gt;tellStory&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;&amp;#123;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;4&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;        &lt;span class=&#34;token class-name&#34;&gt;System&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;out&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;println&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token string&#34;&gt;&#34;I&#39;m forge, your old friend.&#34;&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;5&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;span class=&#34;token punctuation&#34;&gt;&amp;#125;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;6&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token punctuation&#34;&gt;&amp;#125;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;&lt;p&gt;在 common 包中，我们就可以按需调用 tellStory 方法，编译时 Architectury 会根据平台的不同补齐方法&lt;/p&gt;
&lt;figure class=&#34;highlight java&#34;&gt;&lt;figcaption data-lang=&#34;java&#34;&gt;&lt;/figcaption&gt;&lt;table&gt;&lt;tr&gt;&lt;td data-num=&#34;1&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;位于common&lt;span class=&#34;token operator&#34;&gt;:&lt;/span&gt; trou&lt;span class=&#34;token operator&#34;&gt;/&lt;/span&gt;arch&lt;span class=&#34;token operator&#34;&gt;/&lt;/span&gt;&lt;span class=&#34;token class-name&#34;&gt;Arch&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;java&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;2&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;static&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;void&lt;/span&gt; &lt;span class=&#34;token function&#34;&gt;init&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;&amp;#123;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;3&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;span class=&#34;token class-name&#34;&gt;System&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;out&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;println&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token string&#34;&gt;&#34;Hello Architectury!&#34;&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;4&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;span class=&#34;token class-name&#34;&gt;Storyteller&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;tellStory&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;5&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token punctuation&#34;&gt;&amp;#125;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;&lt;p&gt;此时分别运行 Forge 和 Fabric 客户端，输出的信息应该有所不同.&lt;/p&gt;
&lt;p&gt;我们的文件结构应该是这样的: &lt;/p&gt;
&lt;p&gt;ExpectPlatform 方法: common\src\main\java\trou\arch\Storyteller.java&lt;/p&gt;
&lt;p&gt;Forge 平台的实现: forge\src\main\java\trou\arch**\forge\**StorytellerImpl.java&lt;/p&gt;
&lt;p&gt;Fabric 平台的实现: fabric\src\main\java\trou\arch**\fabric\**StorytellerImpl.java&lt;/p&gt;
&lt;p&gt;&lt;img loading=&#34;lazy&#34; data-src=&#34;https://s2.loli.net/2022/04/02/TQ4f6vJWEqDRglF.png&#34; alt=&#34;Forge Side&#34; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img loading=&#34;lazy&#34; data-src=&#34;https://s2.loli.net/2022/04/02/Xc6QYMF8Es3hIai.png&#34; alt=&#34;Fabric Side&#34; /&gt;&lt;/p&gt;
&lt;h3 id=&#34;idea的architectury插件&#34;&gt;&lt;a class=&#34;anchor&#34; href=&#34;#idea的architectury插件&#34;&gt;#&lt;/a&gt; IDEA 的 Architectury 插件&lt;/h3&gt;
&lt;p&gt;Architectury 提供了 IDEA 的插件来帮助我们补全 ExpectPlatform 的实现&lt;/p&gt;
&lt;p&gt;&lt;img loading=&#34;lazy&#34; data-src=&#34;https://s2.loli.net/2022/04/02/JBgFSlKcz12d7iI.png&#34; alt=&#34;Code Hint&#34; /&gt;&lt;/p&gt;
&lt;p&gt;创建 ExpectPlatform 方法后，可以自动在正确的位置创建 forge 和 fabric 的实现&lt;/p&gt;
&lt;h1 id=&#34;architecturytarget&#34;&gt;&lt;a class=&#34;anchor&#34; href=&#34;#architecturytarget&#34;&gt;#&lt;/a&gt; ArchitecturyTarget&lt;/h1&gt;
&lt;h3 id=&#34;architecturytarget-a-hrefarchitecturytarget-idarchitecturytargeta&#34;&gt;&lt;a class=&#34;anchor&#34; href=&#34;#architecturytarget-a-hrefarchitecturytarget-idarchitecturytargeta&#34;&gt;#&lt;/a&gt; ArchitecturyTarget &amp;lt;a href=&amp;quot;#architecturytarget&amp;quot; id=&amp;quot;architecturytarget&amp;quot;&amp;gt;&amp;lt;/a&amp;gt;&lt;/h3&gt;
&lt;p&gt;利用&lt;strong&gt; ArchitecturyTarget&lt;/strong&gt; 类的&lt;strong&gt; getCurrentTarget&lt;/strong&gt; 方法，可以直接获取当前运行的模组加载器平台.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;ArchitecturyTarget.getCurrentTarget () 需要在 common 包中被调用&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;当 forge 环境执行到该语句时，方法的返回值为 forge&lt;/li&gt;
&lt;li&gt;当 fabric 环境执行到该语句时，方法的返回值为 fabric&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;下面我们来用 ArchitecturyTarget 修改我们上一章节编写的 Storyteller 代码.&lt;/p&gt;
&lt;p&gt;修改 StoryTeller 类，去掉 ExpectPlatform 注解，直接基于当前运行的加载器环境进行判断&lt;/p&gt;
&lt;figure class=&#34;highlight java&#34;&gt;&lt;figcaption data-lang=&#34;java&#34;&gt;&lt;/figcaption&gt;&lt;table&gt;&lt;tr&gt;&lt;td data-num=&#34;1&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;class&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;Storyteller&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;&amp;#123;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;2&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;span class=&#34;token keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;static&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;final&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;String&lt;/span&gt; &lt;span class=&#34;token constant&#34;&gt;FORGE_STORY&lt;/span&gt; &lt;span class=&#34;token operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;token string&#34;&gt;&#34;I&#39;m forge, your old friend.&#34;&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;3&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;span class=&#34;token keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;static&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;final&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;String&lt;/span&gt; &lt;span class=&#34;token constant&#34;&gt;FABRIC_STORY&lt;/span&gt; &lt;span class=&#34;token operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;token string&#34;&gt;&#34;I&#39;m fabric, your new friend.&#34;&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;4&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;span class=&#34;token keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;static&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;void&lt;/span&gt; &lt;span class=&#34;token function&#34;&gt;tellStory&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;&amp;#123;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;5&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;        &lt;span class=&#34;token keyword&#34;&gt;final&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;String&lt;/span&gt; platform &lt;span class=&#34;token operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;ArchitecturyTarget&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;getCurrentTarget&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;6&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;        &lt;span class=&#34;token class-name&#34;&gt;System&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;out&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;println&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;platform&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;equals&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token string&#34;&gt;&#34;forge&#34;&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;token operator&#34;&gt;?&lt;/span&gt; &lt;span class=&#34;token constant&#34;&gt;FORGE_STORY&lt;/span&gt; &lt;span class=&#34;token operator&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;token constant&#34;&gt;FABRIC_STORY&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;7&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;span class=&#34;token punctuation&#34;&gt;&amp;#125;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;8&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token punctuation&#34;&gt;&amp;#125;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;&lt;p&gt;现在应该能得到与之前相同的效果。读者可以根据需要取舍使用 ExpectPlatform 和 ArchitecturyTarget&lt;/p&gt;
&lt;h1 id=&#34;platformonly&#34;&gt;&lt;a class=&#34;anchor&#34; href=&#34;#platformonly&#34;&gt;#&lt;/a&gt; PlatformOnly&lt;/h1&gt;
&lt;h3 id=&#34;platformonly-2&#34;&gt;&lt;a class=&#34;anchor&#34; href=&#34;#platformonly-2&#34;&gt;#&lt;/a&gt; PlatformOnly&lt;/h3&gt;
&lt;p&gt;PlatformOnly 是一个用于限定某一方法只能在某一平台执行的注解.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;@PlatformOnly (&amp;quot;forge&amp;quot;) 修饰的方法&lt;strong&gt;只能&lt;/strong&gt;在 Forge 模块调用 (编译后不会出现在 Fabric 代码中)&lt;/li&gt;
&lt;li&gt;@PlatformOnly (&amp;quot;fabric&amp;quot;) 修饰的方法&lt;strong&gt;只能&lt;/strong&gt;在 Fabric 模块调用 (编译后不会出现在 Forge 代码中)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;被 PlatformOnly 修饰的方法如果在 common 包直接调用会产生警告 (需要安装插件)&lt;/p&gt;
&lt;p&gt;&lt;img loading=&#34;lazy&#34; data-src=&#34;https://s2.loli.net/2022/04/02/Hp85GEtrSfOKueB.png&#34; alt=&#34;Warning&#34; /&gt;&lt;/p&gt;
&lt;p&gt;如果我们尝试在 Forge 模块中调用被 @PlatformOnly (&amp;quot;fabric&amp;quot;) 修饰的方法，游戏会抛出反射异常&lt;/p&gt;
&lt;p&gt;&lt;img loading=&#34;lazy&#34; data-src=&#34;https://s2.loli.net/2022/04/02/1DZfJ9xFXowS5mn.png&#34; alt=&#34;InvocationTargetException&#34; /&gt;&lt;/p&gt;
&lt;h1 id=&#34;environment-onlyin&#34;&gt;&lt;a class=&#34;anchor&#34; href=&#34;#environment-onlyin&#34;&gt;#&lt;/a&gt; Environment &amp;amp; OnlyIn&lt;/h1&gt;
&lt;h3 id=&#34;transform-to-environment&#34;&gt;&lt;a class=&#34;anchor&#34; href=&#34;#transform-to-environment&#34;&gt;#&lt;/a&gt; Transform to Environment&lt;/h3&gt;
&lt;p&gt;在 Forge 中，为了标识某一方法只在逻辑服务端执行，我们通常会添加 @OnlyIn (Dists.DEDICATED_SERVER)&lt;/p&gt;
&lt;p&gt;在 Fabric 中，为了标识某一方法只在逻辑服务端执行，我们通常会添加 @Environment (EnvType.SERVER)&lt;/p&gt;
&lt;p&gt;为了统一这两个相同的需求，Architectury 为我们添加了自动处理的映射关系.&lt;/p&gt;
&lt;p&gt;在实际代码中，我们只需要添加 Environment 注解即可，Architrctury 会对 Forge 平台进行映射处理&lt;/p&gt;
&lt;p&gt;&lt;img loading=&#34;lazy&#34; data-src=&#34;https://s2.loli.net/2022/04/02/rU8y6HdeiV4oaXP.png&#34; alt=&#34;LightOverlay&#34; /&gt;&lt;/p&gt;
</content>
        <category term="Minecraft" />
        <category term="模组开发" />
        <category term="ArchitecturyAPI" />
        <updated>2022-06-11T03:35:12.000Z</updated>
    </entry>
    <entry>
        <id>https://turou.fun/minecraft/arch-tutor/environment/</id>
        <title>搭建跨平台模组开发环境</title>
        <link rel="alternate" href="https://turou.fun/minecraft/arch-tutor/environment/"/>
        <content type="html">&lt;h3 id=&#34;构建开发环境&#34;&gt;&lt;a class=&#34;anchor&#34; href=&#34;#构建开发环境&#34;&gt;#&lt;/a&gt; 构建开发环境&lt;/h3&gt;
&lt;p&gt;类似于 fabric, architectury plugin 也提供了相对应的 example-mod 来方便我们搭建环境。&lt;/p&gt;
&lt;p&gt;首先来到 Architectury-Templates 的 Releases 页面，选择合适的版本&lt;/p&gt;
&lt;p&gt;目前 Architectury 仅支持 1.16 + 的版本&lt;/p&gt;
&lt;p&gt;Architectury 提供下面这几种 Templates 可供选择&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;1.18.2-forge.zip: 1.18.2 的基于&lt;strong&gt; Forge-Loom&lt;/strong&gt; 的&lt;strong&gt; Forge 开发环境&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;1.18.2-architectury.zip: 1.18.2 跨加载器的开发环境&lt;/li&gt;
&lt;li&gt;1.18.2-architectury-mixin.zip: 1.18.2 使用 Mixin 的跨加载器开发环境&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;相信阅读过初识 Architectury 章节的读者已经明白自己需要下载的版本了&lt;/p&gt;
&lt;p&gt;这里我们不着重介绍 Mixin, 感兴趣的读者可以阅读笔者先前的文章&lt;/p&gt;
&lt;p&gt;本文基于 Minecraft 1.18.2 版本，并且我们不需要 Mixin, 所以我们下载&lt;strong&gt; 1.18.2-architectury.zip&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;将下载到的文件进行解压，&lt;strong&gt;直接&lt;/strong&gt;使用 Intellij IDEA 来导入文件夹中的&lt;strong&gt; build.gradle&lt;/strong&gt; 文件&lt;/p&gt;
&lt;p&gt;类似于 Fabric 和 Forge 的环境构建，这里 Gradle 会帮助我们下载必要的文件，并且自动使用 Loom 进行反混淆，读者需要稍加等待&lt;/p&gt;
&lt;p&gt;build.gradle 中的一些常量现在移动到了&lt;strong&gt; gradle.properties&lt;/strong&gt; 中&lt;/p&gt;
&lt;p&gt;&amp;lt;img src=&amp;quot;&lt;a href=&#34;https://s2.loli.net/2022/04/01/iaMLz2vOkusrQoB.png&#34;&gt;https://s2.loli.net/2022/04/01/iaMLz2vOkusrQoB.png&lt;/a&gt;&amp;quot; alt=&amp;quot;image.png&amp;quot; data-size=&amp;quot;original&amp;quot;&amp;gt;&lt;/p&gt;
&lt;p&gt;构建完成后，Gradle 会自动为我们生成针对两个平台的运行配置&lt;/p&gt;
&lt;p&gt;&lt;img loading=&#34;lazy&#34; data-src=&#34;https://s2.loli.net/2022/04/01/FxjZpD4ufm3IQNl.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;
&lt;p&gt;在 Architectury 项目创建完成后，请尽量避免移动项目的位置或者给项目的目录改名。可能会产生找不到 architectury-transformer-agent.jar 的问题&lt;/p&gt;
&lt;p&gt;如果产生了这个问题，可以自行修改启动配置中 - javaagent 的路径&lt;/p&gt;
&lt;p&gt;恭喜你，你已经构建好了一个完整的开发环境&lt;/p&gt;
&lt;h3 id=&#34;简析目录结构&#34;&gt;&lt;a class=&#34;anchor&#34; href=&#34;#简析目录结构&#34;&gt;#&lt;/a&gt; 简析目录结构&lt;/h3&gt;
&lt;p&gt;重新审视我们构建好的开发环境，我们的目录结构类似这样&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;common
&lt;ul&gt;
&lt;li&gt;src/main/java/net.examplemod
&lt;ul&gt;
&lt;li&gt;ExampleMod.java&lt;/li&gt;
&lt;li&gt;ExampleExpectPlatform.java&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;forge
&lt;ul&gt;
&lt;li&gt;src/main/java/net.examplemod.forge
&lt;ul&gt;
&lt;li&gt;ExampleModForge.java&lt;/li&gt;
&lt;li&gt;ExampleExpectPlatformImpl.java&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;fabric
&lt;ul&gt;
&lt;li&gt;src/main/java/net.examplemod.fabric
&lt;ul&gt;
&lt;li&gt;ExampleModFabric.java&lt;/li&gt;
&lt;li&gt;ExampleExpectPlatformImpl.java&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;经过简单的观察，我们发现&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;实例代码在主类&lt;strong&gt; ExampleMod&lt;/strong&gt; 中进行了物品的注册和一些基本操作&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;ExampleModForge&lt;/strong&gt; 中添加了 @Mod 注解，并调用&lt;strong&gt; ExampleMod&lt;/strong&gt; 中的 init&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;ExampleModFabric&lt;/strong&gt; 类实现了 ModInitializer 方法，并调用&lt;strong&gt; ExampleMod&lt;/strong&gt; 中的 init&lt;/li&gt;
&lt;li&gt;实例代码在&lt;strong&gt; ExampleExpectPlatform&lt;/strong&gt; 中写了一个 **&amp;quot;没有方法体&amp;quot;** 的方法 getConfigDirectory&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;common&lt;/strong&gt; 模块中的某些代码调用了 getConfigDirectory&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;forge&lt;/strong&gt; 模块中的 ExampleExpectPlatformImpl 用&lt;strong&gt; forge 独有&lt;/strong&gt;的方式补全了方法体&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;fabric&lt;/strong&gt; 模块中的 ExampleExpectPlatformImpl 用&lt;strong&gt; fabric 独有&lt;/strong&gt;的方式补全了方法体&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;可以发现，我们模组的&lt;strong&gt;共性&lt;/strong&gt;部分在 common 模块中，而&lt;strong&gt;非共性&lt;/strong&gt;部分则分别在 forge 和 fabric 包中展现.&lt;/p&gt;
&lt;p&gt;针对一个需要考虑兼容性的方法，我们往往先定义 &amp;quot;接口&amp;quot;, 然后为接口写不同的实现 (Impl), 这是 Architechtury 以及大部分跨平台程序设计中所共有的思想&lt;/p&gt;
&lt;p&gt;实际上&lt;strong&gt; ExampleExpectPlatform&lt;/strong&gt; 在这里类似于接口.&lt;/p&gt;
&lt;h3 id=&#34;开始旅程&#34;&gt;&lt;a class=&#34;anchor&#34; href=&#34;#开始旅程&#34;&gt;#&lt;/a&gt; 开始旅程&lt;/h3&gt;
&lt;p&gt;实例代码已经为我们提供了一个清晰的思路，现在我们已经可以删除掉他们，开始我们自己的旅途了. &lt;/p&gt;
&lt;p&gt;在 common, forge, fabric 三个模块中分别建立 trou.arch (.forge/.fabric) 包，并创建对应的 Arch, ArchForge, ArchFabric 类.&lt;/p&gt;
&lt;figure class=&#34;highlight java&#34;&gt;&lt;figcaption data-lang=&#34;java&#34;&gt;&lt;/figcaption&gt;&lt;table&gt;&lt;tr&gt;&lt;td data-num=&#34;1&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;位于common&lt;span class=&#34;token operator&#34;&gt;:&lt;/span&gt; trou&lt;span class=&#34;token operator&#34;&gt;/&lt;/span&gt;arch&lt;span class=&#34;token operator&#34;&gt;/&lt;/span&gt;&lt;span class=&#34;token class-name&#34;&gt;Arch&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;java&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;2&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;static&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;final&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;String&lt;/span&gt; &lt;span class=&#34;token constant&#34;&gt;MOD_ID&lt;/span&gt; &lt;span class=&#34;token operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;token string&#34;&gt;&#34;arch&#34;&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;3&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;4&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;class&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;Arch&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;&amp;#123;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;5&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;span class=&#34;token keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;static&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;void&lt;/span&gt; &lt;span class=&#34;token function&#34;&gt;init&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;&amp;#123;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;6&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;        &lt;span class=&#34;token class-name&#34;&gt;System&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;out&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;println&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token string&#34;&gt;&#34;Hello Architectury!&#34;&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;7&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;span class=&#34;token punctuation&#34;&gt;&amp;#125;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;8&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token punctuation&#34;&gt;&amp;#125;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;&lt;figure class=&#34;highlight java&#34;&gt;&lt;figcaption data-lang=&#34;java&#34;&gt;&lt;/figcaption&gt;&lt;table&gt;&lt;tr&gt;&lt;td data-num=&#34;1&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;位于forge&lt;span class=&#34;token operator&#34;&gt;:&lt;/span&gt; trou&lt;span class=&#34;token operator&#34;&gt;/&lt;/span&gt;arch&lt;span class=&#34;token operator&#34;&gt;/&lt;/span&gt;forge&lt;span class=&#34;token operator&#34;&gt;/&lt;/span&gt;&lt;span class=&#34;token class-name&#34;&gt;ArchForge&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;java&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;2&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token annotation punctuation&#34;&gt;@Mod&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token class-name&#34;&gt;Arch&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token constant&#34;&gt;MOD_ID&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;3&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;class&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;ArchForge&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;&amp;#123;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;4&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;span class=&#34;token keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;ArchForge&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;&amp;#123;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;5&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;        &lt;span class=&#34;token class-name&#34;&gt;Arch&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;init&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;6&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;span class=&#34;token punctuation&#34;&gt;&amp;#125;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;7&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token punctuation&#34;&gt;&amp;#125;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;&lt;figure class=&#34;highlight java&#34;&gt;&lt;figcaption data-lang=&#34;java&#34;&gt;&lt;/figcaption&gt;&lt;table&gt;&lt;tr&gt;&lt;td data-num=&#34;1&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;位于fabric&lt;span class=&#34;token operator&#34;&gt;:&lt;/span&gt; trou&lt;span class=&#34;token operator&#34;&gt;/&lt;/span&gt;arch&lt;span class=&#34;token operator&#34;&gt;/&lt;/span&gt;fabric&lt;span class=&#34;token operator&#34;&gt;/&lt;/span&gt;&lt;span class=&#34;token class-name&#34;&gt;ArchFabric&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;java&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;2&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;class&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;ArchFabric&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;implements&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;ModInitializer&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;&amp;#123;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;3&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;span class=&#34;token annotation punctuation&#34;&gt;@Override&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;4&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;span class=&#34;token keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;void&lt;/span&gt; &lt;span class=&#34;token function&#34;&gt;onInitialize&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;&amp;#123;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;5&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;        &lt;span class=&#34;token class-name&#34;&gt;Arch&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;init&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;6&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;span class=&#34;token punctuation&#34;&gt;&amp;#125;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;7&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token punctuation&#34;&gt;&amp;#125;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;&lt;p&gt;根据读者以往开发 Forge 或 Fabric 的经验修改必要的文件名和参数变量，分别启动游戏，应该可以看到 Hello Architectury! 消息输出在控制台中&lt;/p&gt;
&lt;h3 id=&#34;可能出现的问题&#34;&gt;&lt;a class=&#34;anchor&#34; href=&#34;#可能出现的问题&#34;&gt;#&lt;/a&gt; 可能出现的问题&lt;/h3&gt;
&lt;p&gt;因为涉及到很多 transform 和黑科技，对规范性的要求比较高，读者可能会出现一些疏忽，下面是一些可能出现的常见问题 &lt;s&gt;(笔者搭建环境的时候出现的问题)&lt;/s&gt;&lt;/p&gt;
&lt;h4 id=&#34;开发中出现错误-其中有examplemod字样&#34;&gt;&lt;a class=&#34;anchor&#34; href=&#34;#开发中出现错误-其中有examplemod字样&#34;&gt;#&lt;/a&gt; 开发中出现错误，其中有 examplemod 字样&lt;/h4&gt;
&lt;p&gt;检查是否有遗漏的 examplemod 没有修改，请主要检查&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;fabric.mod.json&lt;/li&gt;
&lt;li&gt;gradle.properties&lt;/li&gt;
&lt;li&gt;mods.toml&lt;/li&gt;
&lt;li&gt;(accesswidener)&lt;/li&gt;
&lt;li&gt;(mixins.json)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img loading=&#34;lazy&#34; data-src=&#34;https://s2.loli.net/2022/04/02/uEsX9nicqLZWAb3.png&#34; alt=&#34;Architectury Templates&#34; /&gt;&lt;/p&gt;
&lt;h4 id=&#34;运行fabric调试-模组没有加载&#34;&gt;&lt;a class=&#34;anchor&#34; href=&#34;#运行fabric调试-模组没有加载&#34;&gt;#&lt;/a&gt; 运行 Fabric 调试，模组没有加载&lt;/h4&gt;
&lt;p&gt;请检查自己是否修改了 fabric.mod.json, 其中定义了 Mod 主类入口&lt;/p&gt;
&lt;p&gt;打开 fabric 模块中的 resources/fabric.mod.json, 修改 entrypoints&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;quot;entrypoints&amp;quot;: &amp;#123;
    &amp;quot;main&amp;quot;: [
      &amp;quot;trou.arch.fabric.ArchFabric&amp;quot;
    ]
  &amp;#125;,
&lt;/code&gt;&lt;/pre&gt;
&lt;h4 id=&#34;运行forge调试-抛出modules-generated错误&#34;&gt;&lt;a class=&#34;anchor&#34; href=&#34;#运行forge调试-抛出modules-generated错误&#34;&gt;#&lt;/a&gt; 运行 Forge 调试，抛出 Modules generated 错误&lt;/h4&gt;
&lt;p&gt;请检查自己的 Forge 主类是否放在了 forge 模块的对应&lt;strong&gt; forge 包&lt;/strong&gt;下&lt;/p&gt;
&lt;p&gt;我们项目的 ArchForge 应位于 forge\src\main\java\trou\arch**\forge\**ArchForge.java&lt;/p&gt;
&lt;h3 id=&#34;实验性内容-使用parchment&#34;&gt;&lt;a class=&#34;anchor&#34; href=&#34;#实验性内容-使用parchment&#34;&gt;#&lt;/a&gt; 实验性内容：使用&lt;a href=&#34;https://github.com/ParchmentMC/Parchment&#34;&gt;&lt;strong&gt; Parchment&lt;/strong&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;此项内容读者可以选择性阅读. Architectury 并未主动支持 Parchment. 以下内容均出自笔者尝试，不能保证是否出现问题，如果读者想要使用，出现了编译类问题，请首先尝试移除 Parchment&lt;/p&gt;
&lt;p&gt;众所周知，现在 Forge 和 Fabric 默认使用 Mojang 官方混淆表，但官方混淆表并没有放出参数的混淆信息，这导致高版本默认反编译的参数表是很难阅读的，Parchment 解决了这一问题，Parchment 提供了参数表的反混淆方式。这里笔者不过多赘述 Parchment 本身，国内也有相关的介绍和使用教程，读者可以自行翻阅查看.&lt;/p&gt;
&lt;p&gt;这里我们尝试在 Architectury 中引入 Parchment&lt;/p&gt;
&lt;p&gt;首先打开项目&lt;strong&gt;根目录&lt;/strong&gt;的 build.gradle, 在 subprojects 中按需&lt;strong&gt;添加&lt;/strong&gt;如下内容&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;subprojects &amp;#123;
    repositories &amp;#123;
        maven &amp;#123; url = &#39;https://maven.parchmentmc.org&#39; &amp;#125;
    &amp;#125;

    dependencies &amp;#123;
        minecraft &amp;quot;com.mojang:minecraft:$&amp;#123;rootProject.minecraft_version&amp;#125;&amp;quot;
        mappings loom.layered() &amp;#123;
            officialMojangMappings()
            parchment(&amp;quot;org.parchmentmc.data:parchment-1.18.2:2022.03.13@zip&amp;quot;)
        &amp;#125;
        // mappings loom.officialMojangMappings()
    &amp;#125;
&amp;#125;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;之后刷新 Gradle 项目，等待 Parchment 下载并自动部署后，再次查看参数表，已经反混淆过了.&lt;/p&gt;
</content>
        <category term="Minecraft" />
        <category term="模组开发" />
        <category term="ArchitecturyAPI" />
        <updated>2022-06-10T03:35:12.000Z</updated>
    </entry>
    <entry>
        <id>https://turou.fun/minecraft/arch-tutor/introduction/</id>
        <title>从零开始认识Architectury</title>
        <link rel="alternate" href="https://turou.fun/minecraft/arch-tutor/introduction/"/>
        <content type="html">&lt;h3 id=&#34;本文的想法来源&#34;&gt;&lt;a class=&#34;anchor&#34; href=&#34;#本文的想法来源&#34;&gt;#&lt;/a&gt; 本文的想法来源&lt;/h3&gt;
&lt;p&gt;前些天在 Curseforge 的 Library 区闲逛，发现了一直在前排榜单上的&lt;strong&gt; Architectury API&lt;/strong&gt;，笔者稍加了解以后决定起笔这篇文章。类似于&lt;strong&gt; Architectury&lt;/strong&gt; 这种的跨平台思路最近还是挺流行的，比如 flutter，uno 什么的，但是在 Minecraft 开发领域并不多见，查了一下 Curseforge 上依赖&lt;strong&gt; Architectury API&lt;/strong&gt; 的 Mod，发现还挺多的，可能也算比较流行了。&lt;/p&gt;
&lt;h3 id=&#34;本文的受众对象&#34;&gt;&lt;a class=&#34;anchor&#34; href=&#34;#本文的受众对象&#34;&gt;#&lt;/a&gt; 本文的受众对象&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;有跨加载器开发模组的需求 (如果没有这个需求的话就没必要阅读了)&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;对 Minecraft 及其生态链有一个较为进阶的了解&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;有利用 Fabric 或 Forge 开发 Mod 的经验 (本文不会讲解模组开发的基础部分)&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;有独立思维解决问题的能力&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;本文的组织方式&#34;&gt;&lt;a class=&#34;anchor&#34; href=&#34;#本文的组织方式&#34;&gt;#&lt;/a&gt; 本文的组织方式&lt;/h3&gt;
&lt;p&gt;本文不会详细记述如何开发一个完整的 Forge 或 Fabric 模组，只会抽出开发中的几个关键点进行诠释，并加以笔者感兴趣的实例进行补充讲解。&lt;/p&gt;
&lt;p&gt;在 Architectury Plugin 部分中，注重于介绍&lt;strong&gt; Architectury&lt;/strong&gt; 的特性和使用方式。所以，这部分&lt;strong&gt;不是&lt;/strong&gt;思路引领性的，&lt;strong&gt;而是&lt;/strong&gt;知识引领性的。&lt;/p&gt;
&lt;p&gt;而 Architectury API 部分中，注重于介绍&lt;strong&gt; Architectury&lt;/strong&gt; 的实际使用和开发经验。主要以实例的角度，这部分是思路引领性的，熟悉笔者写作习惯的读者应该更乐于去接受这种方式。&lt;/p&gt;
&lt;p&gt;对于希望快速上手的读者，可以阅读完前言并搭建完环境后，直接阅读 Architectury API 部分.&lt;/p&gt;
&lt;h3 id=&#34;本文基于&#34;&gt;&lt;a class=&#34;anchor&#34; href=&#34;#本文基于&#34;&gt;#&lt;/a&gt; 本文基于&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Minecraft: 1.18.2&lt;/li&gt;
&lt;li&gt;Architectury: 4.1.32&lt;/li&gt;
&lt;li&gt;Fabric Loader: 0.13.3&lt;/li&gt;
&lt;li&gt;Fabric API: 0.48.0+1.18.2&lt;/li&gt;
&lt;li&gt;Forge: 1.18.2-40.0.17&lt;/li&gt;
&lt;li&gt;(Parchment: 1.18.2-2022.03.13)&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;正文开始&#34;&gt;&lt;a class=&#34;anchor&#34; href=&#34;#正文开始&#34;&gt;#&lt;/a&gt; 正文开始&lt;/h3&gt;
&lt;p&gt;Architectury 是一套跨加载器的模组开发工具链，依靠这套工具链，读者可以快速且舒适的&lt;strong&gt;一次性&lt;/strong&gt;开发出模组的&lt;strong&gt; Forge&lt;/strong&gt; 和&lt;strong&gt; Fabric&lt;/strong&gt; 版本，而无需构建多套开发环境。&lt;/p&gt;
&lt;p&gt;Architectury 分为&lt;strong&gt; Architectury Plugin&lt;/strong&gt; 和&lt;strong&gt; Architectury API&lt;/strong&gt;。&lt;/p&gt;
&lt;p&gt;Architectury Plugin 是一个 Gradle 插件，意在快速而方便的帮助开发者构建出一个包含 Forge 模块、Fabric 模块和 Common 模块的项目。&lt;/p&gt;
&lt;p&gt;在开发过程中，开发者只需把重心放在 Common 模块的实现，而为了适配两个不同加载器的&lt;strong&gt;额外代码&lt;/strong&gt;则需要编写在 forge 和 fabric 模块中，最后构建时 Architectury 会帮助你进行混合。&lt;/p&gt;
&lt;p&gt;开发者的编码组织方式类似于下图:&lt;/p&gt;
&lt;p&gt;&lt;img loading=&#34;lazy&#34; data-src=&#34;https://s2.loli.net/2022/03/28/3KqQpujYFETZin2.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;
&lt;p&gt;起初开发者们认为这种开发方式的确带来了很大的便利。但因为 Fabric 和 Forge 本身存在较大的差异，开发整仍需花费大量时间在 Forge 模块和 Fabric 模块的开发上，而且开发者们仿佛都在&lt;strong&gt;重复地&lt;/strong&gt;做同一类事情​：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;注册物品: Common 调用注册的方法。Forge 里面实现一遍，Fabric 里面实现一遍&lt;/li&gt;
&lt;li&gt;监听事件: Common 调用监听事件的方法。Forge 里面实现一遍，Fabric 里面实现一遍&lt;/li&gt;
&lt;li&gt;网络发包: Common 调用发包的方法。Forge 里面实现一遍，Fabric 里面实现一遍&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;所以 Architectury API 横空出世。&lt;/p&gt;
&lt;p&gt;Architectury API 在 Forge 和 Fabric 上封装了一个抽象层，开发者实际开发中可以跟这些 API 层打交道，组织方式类似于下图:&lt;/p&gt;
&lt;p&gt;&lt;img loading=&#34;lazy&#34; data-src=&#34;https://s2.loli.net/2022/03/28/JMs24kZCXvUaAKh.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;
&lt;p&gt;所以实际上我们的开发实际上主要是围绕 Architectury API 进行的。&lt;/p&gt;
&lt;p&gt;在 Architectury 的工具链中，还有一个重要角色就是 Architectury Loom，相信之前尝试过利用 Fabric 开发的读者应该会想到 Fabric Loom, 负责构建开发环境时反编译 Minecraft 源码并且反混淆的工具。&lt;/p&gt;
&lt;p&gt;这里 Architectury 创造性的把这套东西搬到了 Forge, 替代了 Forge Gradle，感兴趣的读者可以自行了解。&lt;/p&gt;
&lt;p&gt;可以想到的，为了便于管理，在我们的开发环境搭建中也使用 Architectury Loom，构建环境时 Architectury 会自动为我们进行必要的操作，不需要我们手动干涉。&lt;/p&gt;
&lt;p&gt;Architectury Loom 也可以作为 Forge Loom 独立使用，如果看不惯 Forge Gradle 的读者也可以尝试独立使用它构建 Forge 环境，但是需要注意的是，这套框架还处于测试阶段，可能出现无法构建或者杂七杂八的问题，具体的信息可以在这里找到: &lt;a href=&#34;https://github.com/architectury/architectury-loom&#34;&gt;https://github.com/architectury/architectury-loom&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;相信读者已经基本了解了 Architectury，下面我们开始尝试基于 Architectury 开发模组。请注意，构建过程中需要&lt;strong&gt;通畅&lt;/strong&gt;的网络，请读者自行解决，不过鉴于 forge-cdn 和 fabric 最近的情况，读者应该不必担心。&lt;/p&gt;
</content>
        <category term="Minecraft" />
        <category term="模组开发" />
        <category term="ArchitecturyAPI" />
        <updated>2022-06-10T03:35:12.000Z</updated>
    </entry>
    <entry>
        <id>https://turou.fun/minecraft/multiblock-tutor/</id>
        <title>由实例上手快速开发多方块结构</title>
        <link rel="alternate" href="https://turou.fun/minecraft/multiblock-tutor/"/>
        <content type="html">&lt;h2 id=&#34;前言-真的很快很简单真的&#34;&gt;&lt;a class=&#34;anchor&#34; href=&#34;#前言-真的很快很简单真的&#34;&gt;#&lt;/a&gt; 前言 - 真的很快很简单，真的&lt;/h2&gt;
&lt;p&gt;许多开发者对 Minecraft 中如何实现多方块结构感兴趣，鉴于国内相关知识的缺失，开发者们很难将自己的想法付诸实践，这里笔者将带领读者力求简单快速的构建自己的多方块结构。&lt;/p&gt;
&lt;p&gt;鉴于本篇教程的基础性，我们并不想探究多方块结构中复杂的位置变换和数学计算，而仅仅是想用最快的方式上手并使用它们。所以在撰写过程中我们并不会深究某些方法的具体实现，而是将重点放在如何使用上，降低读者的阅读门槛。&lt;/p&gt;
&lt;p&gt;本文编写使用环境: Minecraft 1.16.5 Forge 36.2.23 ZeroCore2 2.1.9&lt;/p&gt;
&lt;h2 id=&#34;明确思路-你的下一个蓄水器何必是蓄水器&#34;&gt;&lt;a class=&#34;anchor&#34; href=&#34;#明确思路-你的下一个蓄水器何必是蓄水器&#34;&gt;#&lt;/a&gt; 明确思路 - 你的下一个蓄水器何必是蓄水器&lt;/h2&gt;
&lt;p&gt;审视我们之前见到过的多方块结构，笔者这里想把他们分成两类。首先，例如 RailCraft 的多方块蓄水器，这种多方块结构往往需要玩家搭建一个立方体结构 (这个结构的长宽高往往可以变化)，然后在立方体结构上面添加自己需要的交互方块 (例如蓄水器阀门) 用来与外界交互，笔者希望把这种多方块结构称为立体型多方块结构。&lt;/p&gt;
&lt;p&gt;我们似乎还能想到另外一些例子，例如 OpenBlocks 的蓄水器，可以单个放置使用，但玩家可以通过在旁边摆更多的蓄水器来扩展蓄水器的空间，这种结构往往具有一种扩展性，即围绕一个中心方块 (我们第一次放的蓄水器就可以看做中心方块) 进行扩展，以扩增原有的功能，笔者希望把这种多方块结构称为扩展性多方块结构。对于扩展型多方块结构，由&lt;a href=&#34;https://www.mcbbs.net/home.php?mod=space&amp;amp;uid=588878&#34;&gt; Ph - 苯&lt;/a&gt;开发的&lt;a href=&#34;https://www.mcbbs.net/forum.php?mod=viewthread&amp;amp;tid=1186201&#34;&gt;容积储存&lt;/a&gt;可以成为很好的学习例子。&lt;/p&gt;
&lt;p&gt;&lt;img loading=&#34;lazy&#34; data-src=&#34;https://s2.loli.net/2022/01/22/mSKjHT1RJvf46WA.png&#34; alt=&#34;Tanks&#34; /&gt;&lt;/p&gt;
&lt;p&gt;这里笔者将着重介绍立体型多方块结构，当然，有的读者除上述的两种多方块结构外，还想到了例如 Immersive Engineering 的各类多方块结构，他们的形状并不是非常规则的立方体，这里笔者尚且不介绍这种结构，感兴趣的读者可以尝试阅读 IE 的源码。&lt;/p&gt;
&lt;p&gt;对于立体型多方块结构，我们使用 ZeroCore2 中提供的 Multiblock API 进行实现&lt;/p&gt;
&lt;h2 id=&#34;立体型多方块结构-powerful-tank&#34;&gt;&lt;a class=&#34;anchor&#34; href=&#34;#立体型多方块结构-powerful-tank&#34;&gt;#&lt;/a&gt; 立体型多方块结构 - Powerful Tank&lt;/h2&gt;
&lt;p&gt;本节将带领读者开发一个多方块储罐，&lt;s&gt;这个储罐利用 Forge Energy 构建一个多维异次元空间，使用能量立场对流体的分子结构进行压缩&lt;/s&gt; 这个多方块储罐可以储存无限桶流体，但需要消耗 FE 能量，当能量不足时内部储存的所有流体将消失。&lt;/p&gt;
&lt;p&gt;首先我们创建项目并添加所需的依赖，为了使用 ZeroCore 中 Multiblock API，首先我们要引入 ZeroCore2。利用 ForgeGradle，我们无需手动下载。在 build.gradle 中的 repositories 和 dependencies 中加入以下条目，并且在 mods.toml 中添加依赖，之后点击 IDE 中的加载 Gradle 变更即可。&lt;/p&gt;
&lt;figure class=&#34;highlight java&#34;&gt;&lt;figcaption data-lang=&#34;java&#34;&gt;&lt;/figcaption&gt;&lt;table&gt;&lt;tr&gt;&lt;td data-num=&#34;1&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;位于&lt;span class=&#34;token operator&#34;&gt;:&lt;/span&gt; build&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;gradle&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;2&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;repositories &lt;span class=&#34;token punctuation&#34;&gt;&amp;#123;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;3&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    maven &lt;span class=&#34;token punctuation&#34;&gt;&amp;#123;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;4&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;        name &lt;span class=&#34;token operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;token string&#34;&gt;&#34;zerono&#34;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;5&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;        url &lt;span class=&#34;token operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;token string&#34;&gt;&#34;https://maven.zerono.it/&#34;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;6&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;span class=&#34;token punctuation&#34;&gt;&amp;#125;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;7&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token punctuation&#34;&gt;&amp;#125;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;8&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;9&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;dependencies &lt;span class=&#34;token punctuation&#34;&gt;&amp;#123;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;10&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;span class=&#34;token comment&#34;&gt;// 替换成当前的 Minecraft 版本和 ZeroCore2 版本&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;11&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    implementation fg&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;deobf&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token string&#34;&gt;&#34;it.zerono.mods.zerocore:ZeroCore2:1.16.5-2.1.9&#34;&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt; &lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;12&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token punctuation&#34;&gt;&amp;#125;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;&lt;figure class=&#34;highlight toml&#34;&gt;&lt;figcaption data-lang=&#34;TOML&#34;&gt;&lt;/figcaption&gt;&lt;table&gt;&lt;tr&gt;&lt;td data-num=&#34;1&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token comment&#34;&gt;# 位于: resources\META-INF\mods.toml&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;2&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token punctuation&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;token table class-name&#34;&gt;dependencies.powerful_tank&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;]&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;]&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;3&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;span class=&#34;token key property&#34;&gt;modId&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;token string&#34;&gt;&#34;zerocore&#34;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;4&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;span class=&#34;token key property&#34;&gt;mandatory&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;token boolean&#34;&gt;true&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;5&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;span class=&#34;token key property&#34;&gt;versionRange&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;token string&#34;&gt;&#34;[1.16.5-2.1.9,)&#34;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;6&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;span class=&#34;token key property&#34;&gt;ordering&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;token string&#34;&gt;&#34;AFTER&#34;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;7&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;span class=&#34;token key property&#34;&gt;side&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;token string&#34;&gt;&#34;BOTH&#34;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;&lt;p&gt;ZeroCore 是由 ZeroNoRyouki 开发的一套 API，其中包含了网络包、GUI、连接材质、矿物生成等功能，这里我们着重使用 ZeroCore 的多方块结构 API。需要注意的是，本文面向高版本的 ZeroCore2 开发。&lt;/p&gt;
&lt;p&gt;读者需要注意，本篇教程并不适用于 Minecraft 1.12.2 及以前的旧版 ZeroCore，有需要的读者可以阅读原作者发到自己博客的开发教程: &lt;a href=&#34;http://zerono.it/zerocore-multiblock-api-tutorial/&#34;&gt;Zero CORE multiblock API tutorial&lt;/a&gt;，该教程已严重过时，仅适用于 1.12.2 及更早版本的 ZeroCore。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;由于原作者尚未针对 ZeroCore2 编写相适应的 WIKI，本文是笔者根据 ZeroNoRyouki 自己的模组极限反应堆 (&lt;a href=&#34;https://www.curseforge.com/minecraft/mc-mods/extreme-reactors&#34;&gt;Extreme Reactors&lt;/a&gt;) 中相关内容总结理解而来，可能存在错误，欢迎批评指正。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;想要创建一个多方块结构，在代码层面我们需要有如下考量，笔者把它们分为虚实两部分：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;实：什么方块构成了这个多方块结构，这些方块需要提供什么功能。&lt;/li&gt;
&lt;li&gt;虚：这个多方块结构整体有何功能，如何编写逻辑。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;比如 Powerful Tank 这个例子中，对于实的部分，我们需要设计对应的能量和流体的接口方块，这些方块必然的需要创建对应的 TileEntity (或称 BlockEntity) 来提供数据处理的支持。对于虚的部分，我们可能需要设计一个主控类负责管理能源和流体，这个类所描述的事物是多方块结构的&lt;strong&gt;整体效果&lt;/strong&gt;，我们的设计中心应该在这个类上。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;虚：这个多方块结构整体有何功能，如何编写逻辑。比如 Powerful Tank 这个例子中，我们可能需要设计一个 MultiBlockTank 类，负责管理能源和流体 (储存多少流体，能量不足时清空流体)，这个类所描述的事物是多方块结构的&lt;strong&gt;整体效果&lt;/strong&gt;，我们的&lt;strong&gt;设计中心&lt;/strong&gt;应该在这个类上。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;在 ZeroCore2 中，主控类实际上被称作 MultiblockController, 而组成多方块结构的部件，被称为 MultiblockPart，需要注意的是，MultiblockPart 继承自 TileEntity，换言之，我们不应该在功能方块的 XxxBlock 类上做文章，而是在 XxxTileEntity 上做文章。&lt;/p&gt;
&lt;p&gt;可能有读者对 MultiblockController 的说法有误解，这里的 MultiblockController 与我们在游戏中常见的 XXX 控制器是没有关联的，对于 ZeroCore 来说 XXX 控制器也算是多方块的部件，如果读者阅读极限反应堆的源码可以发现，反应堆控制器的 TileEntity 也继承自 MultiblockPart。&lt;strong&gt;反应堆控制器中是不包含反应堆工作的任何逻辑的&lt;/strong&gt;，相信理解了笔者写的虚和实关系的读者已经明白了。由此也可以看出，我们创建的多方块结构也是完全可以不包含 XXX 控制器的，在我们的例子中，笔者就并没有设计储罐控制器方块。&lt;/p&gt;
&lt;hr /&gt;
&lt;p&gt;综合我们的考量，我们的代码结构应该是这样的&lt;/p&gt;
&lt;p&gt;&lt;img loading=&#34;lazy&#34; data-src=&#34;https://s2.loli.net/2022/01/25/xc1C8IbWTQaFkDr.png&#34; alt=&#34;Structure&#34; /&gt;&lt;/p&gt;
&lt;p&gt;首先，我们先创建代表多方块结构的主控类，在 multiblock 包中创建类 MultiblockTank, 这个类需要继承 AbstractCuboidMultiblockController。&lt;/p&gt;
&lt;p&gt;因为继承自抽象类，我们需要补全他的所有抽象方法。&lt;/p&gt;
&lt;figure class=&#34;highlight java&#34;&gt;&lt;figcaption data-lang=&#34;java&#34;&gt;&lt;/figcaption&gt;&lt;table&gt;&lt;tr&gt;&lt;td data-num=&#34;1&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;位于&lt;span class=&#34;token operator&#34;&gt;:&lt;/span&gt; powerful_tank&lt;span class=&#34;token operator&#34;&gt;/&lt;/span&gt;multiblock&lt;span class=&#34;token operator&#34;&gt;/&lt;/span&gt;&lt;span class=&#34;token class-name&#34;&gt;MultiblockTank&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;java&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;2&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;class&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;MultiblockTank&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;extends&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;AbstractCuboidMultiblockController&lt;/span&gt;&lt;span class=&#34;token generics&#34;&gt;&lt;span class=&#34;token punctuation&#34;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&#34;token class-name&#34;&gt;MultiblockTank&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;&gt;&lt;/span&gt;&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;&amp;#123;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;3&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;4&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token punctuation&#34;&gt;&amp;#125;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;&lt;blockquote&gt;
&lt;p&gt;读者可能发现，除了 Cuboid 外，我们还可以选择 Rectangular 的主控。对于立方体结构，我们选择 Cuboid，而平面的结构一般选择 Rectangular，感兴趣的读者可以自己研究 Rectangular 版本。需要注意的是，在 1.12.2 版本 Rectangular 和 Cuboid 的概念并不是很明确，如果阅读过 1.12.2 文档的读者在这里可能感到疑惑，请以笔者的版本为准。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;使用 IDE 自动填充所有抽象方法后，我们可以先关注这样几个方法&lt;/p&gt;
&lt;figure class=&#34;highlight java&#34;&gt;&lt;figcaption data-lang=&#34;java&#34;&gt;&lt;/figcaption&gt;&lt;table&gt;&lt;tr&gt;&lt;td data-num=&#34;1&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token comment&#34;&gt;// 位于: powerful_tank/multiblock/MultiblockTank.java&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;2&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token annotation punctuation&#34;&gt;@Override&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;3&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;span class=&#34;token comment&#34;&gt;// 这里定义了多方块结构中 Part 的最少数量，我们把储罐外壳也作为 Part，所以 3*3 中空结构正好需要 26 个 Part&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;4&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;span class=&#34;token keyword&#34;&gt;protected&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;int&lt;/span&gt; &lt;span class=&#34;token function&#34;&gt;getMinimumNumberOfPartsForAssembledMachine&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;&amp;#123;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;5&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;        &lt;span class=&#34;token keyword&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;token number&#34;&gt;26&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt; &lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;6&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;span class=&#34;token punctuation&#34;&gt;&amp;#125;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;7&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;8&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;span class=&#34;token comment&#34;&gt;// 多方块结构在 X,Y,Z 方向上的尺寸，ZeroCore 允许创建可变大小的结构，这里我们想使用 3*3 的固定大小的结构，所以所有方向的尺寸均为 3&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;9&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;span class=&#34;token annotation punctuation&#34;&gt;@Override&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;10&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;span class=&#34;token keyword&#34;&gt;protected&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;int&lt;/span&gt; &lt;span class=&#34;token function&#34;&gt;getMaximumXSize&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;&amp;#123;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;11&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;        &lt;span class=&#34;token keyword&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;token number&#34;&gt;3&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt; &lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;12&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;span class=&#34;token punctuation&#34;&gt;&amp;#125;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;13&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;14&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;span class=&#34;token annotation punctuation&#34;&gt;@Override&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;15&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;span class=&#34;token keyword&#34;&gt;protected&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;int&lt;/span&gt; &lt;span class=&#34;token function&#34;&gt;getMinimumXSize&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;&amp;#123;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;16&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;        &lt;span class=&#34;token keyword&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;token number&#34;&gt;3&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;17&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;span class=&#34;token punctuation&#34;&gt;&amp;#125;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;18&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;19&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;span class=&#34;token annotation punctuation&#34;&gt;@Override&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;20&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;span class=&#34;token keyword&#34;&gt;protected&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;int&lt;/span&gt; &lt;span class=&#34;token function&#34;&gt;getMaximumZSize&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;&amp;#123;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;21&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;        &lt;span class=&#34;token keyword&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;token number&#34;&gt;3&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;22&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;span class=&#34;token punctuation&#34;&gt;&amp;#125;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;23&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;24&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;span class=&#34;token annotation punctuation&#34;&gt;@Override&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;25&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;span class=&#34;token keyword&#34;&gt;protected&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;int&lt;/span&gt; &lt;span class=&#34;token function&#34;&gt;getMinimumZSize&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;&amp;#123;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;26&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;        &lt;span class=&#34;token keyword&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;token number&#34;&gt;3&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;27&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;span class=&#34;token punctuation&#34;&gt;&amp;#125;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;28&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;29&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;span class=&#34;token annotation punctuation&#34;&gt;@Override&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;30&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;span class=&#34;token keyword&#34;&gt;protected&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;int&lt;/span&gt; &lt;span class=&#34;token function&#34;&gt;getMaximumYSize&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;&amp;#123;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;31&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;        &lt;span class=&#34;token keyword&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;token number&#34;&gt;3&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;32&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;span class=&#34;token punctuation&#34;&gt;&amp;#125;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;33&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;34&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;span class=&#34;token annotation punctuation&#34;&gt;@Override&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;35&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;span class=&#34;token keyword&#34;&gt;protected&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;int&lt;/span&gt; &lt;span class=&#34;token function&#34;&gt;getMinimumYSize&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;&amp;#123;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;36&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;        &lt;span class=&#34;token keyword&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;token number&#34;&gt;3&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;37&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;span class=&#34;token punctuation&#34;&gt;&amp;#125;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;38&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;39&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;span class=&#34;token comment&#34;&gt;// 这里我们需要判断某个非 Part 方块方块是否合法。&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;40&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;span class=&#34;token annotation punctuation&#34;&gt;@Override&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;41&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;span class=&#34;token keyword&#34;&gt;protected&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;boolean&lt;/span&gt; &lt;span class=&#34;token function&#34;&gt;isBlockGoodForInterior&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token annotation punctuation&#34;&gt;@Nonnull&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;World&lt;/span&gt; world&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;int&lt;/span&gt; i&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;int&lt;/span&gt; i1&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;int&lt;/span&gt; i2&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;token annotation punctuation&#34;&gt;@Nonnull&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;IMultiblockValidator&lt;/span&gt; iMultiblockValidator&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;&amp;#123;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;42&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;        &lt;span class=&#34;token keyword&#34;&gt;return&lt;/span&gt; world&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;getBlockState&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token keyword&#34;&gt;new&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;BlockPos&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;i&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; i1&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; i2&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;getMaterial&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;token operator&#34;&gt;==&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;Material&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token constant&#34;&gt;AIR&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;43&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;span class=&#34;token punctuation&#34;&gt;&amp;#125;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;&lt;p&gt;与 isBlockGoodForInterior 相似的一系列方法参与了多方块结构的判断，ZeroCore 会在合适的时候调用他们来判断多方块结构是否成立。在后文我们还会看到关于多方块结构判断的更多细节。&lt;/p&gt;
&lt;p&gt;这里的 isBlockGoodForXXX 的一系列方法，会在 ZeroCore 发现&lt;strong&gt;非 Part 方块&lt;/strong&gt;以后执行，一般情况下我们不希望多方块结构中混入其他无关方块，所以所有的方法我们均返回 false。但是这里我们希望多方块结构&lt;strong&gt;中空&lt;/strong&gt;，所以我们重写了 isBlockGoodForInterior，并且要求多方块结构内部为 AIR。&lt;/p&gt;
&lt;p&gt;之后我们来创建对应的 Part，这里我们以外壳 GlassWallBlock 为例，读者可以采用相似的方法创建 GlassEnergyPortBlock，GlassFluidPortBlock 几个类。&lt;/p&gt;
&lt;p&gt;在 block 包中创建类 GlassWallBlock&lt;/p&gt;
&lt;figure class=&#34;highlight java&#34;&gt;&lt;figcaption data-lang=&#34;java&#34;&gt;&lt;/figcaption&gt;&lt;table&gt;&lt;tr&gt;&lt;td data-num=&#34;1&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token comment&#34;&gt;// 位于: powerful_tank/block/GlassWallBlock.java&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;2&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token comment&#34;&gt;// 为了实现透明效果，我们继承自 AbstractGlassBlock&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;3&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;class&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;GlassWallBlock&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;extends&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;AbstractGlassBlock&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;&amp;#123;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;4&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;span class=&#34;token keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;GlassWallBlock&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;&amp;#123;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;5&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;        &lt;span class=&#34;token comment&#34;&gt;// 为了实现透明效果，必须设置 noOcclusion&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;6&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;        &lt;span class=&#34;token keyword&#34;&gt;super&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token class-name&#34;&gt;Properties&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;of&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token class-name&#34;&gt;Material&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token constant&#34;&gt;GLASS&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;MaterialColor&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token constant&#34;&gt;NONE&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;sound&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token class-name&#34;&gt;SoundType&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token constant&#34;&gt;GLASS&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;noOcclusion&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;7&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;        &lt;span class=&#34;token keyword&#34;&gt;this&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;setRegistryName&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token string&#34;&gt;&#34;powerful_tank&#34;&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;token string&#34;&gt;&#34;wall_block&#34;&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;8&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;span class=&#34;token punctuation&#34;&gt;&amp;#125;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;9&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;10&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;span class=&#34;token annotation punctuation&#34;&gt;@Override&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;11&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;span class=&#34;token keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;boolean&lt;/span&gt; &lt;span class=&#34;token function&#34;&gt;hasTileEntity&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token class-name&#34;&gt;BlockState&lt;/span&gt; state&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;&amp;#123;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;12&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;        &lt;span class=&#34;token keyword&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;token boolean&#34;&gt;true&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;13&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;span class=&#34;token punctuation&#34;&gt;&amp;#125;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;14&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;15&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;span class=&#34;token annotation punctuation&#34;&gt;@Nullable&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;16&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;span class=&#34;token annotation punctuation&#34;&gt;@Override&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;17&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;span class=&#34;token keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;TileEntity&lt;/span&gt; &lt;span class=&#34;token function&#34;&gt;createTileEntity&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token class-name&#34;&gt;BlockState&lt;/span&gt; state&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;IBlockReader&lt;/span&gt; world&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;&amp;#123;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;18&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;        &lt;span class=&#34;token keyword&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;new&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;GlassWallTileEntity&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;19&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;span class=&#34;token punctuation&#34;&gt;&amp;#125;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;20&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token punctuation&#34;&gt;&amp;#125;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;&lt;p&gt;之后我们来创建对应的 GlassWallTileEntity，在包 tileEntity 中创建类 GlassWallTileEntity，他需要继承自 AbstractCuboidMultiblockPart，同时泛型类型为我们先前创建的 MultiblockTank&lt;/p&gt;
&lt;figure class=&#34;highlight java&#34;&gt;&lt;figcaption data-lang=&#34;java&#34;&gt;&lt;/figcaption&gt;&lt;table&gt;&lt;tr&gt;&lt;td data-num=&#34;1&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token comment&#34;&gt;// 位于: powerful_tank/tileentity/GlassWallTileEntity.java&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;2&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;class&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;GlassWallTileEntity&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;extends&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;AbstractCuboidMultiblockPart&lt;/span&gt;&lt;span class=&#34;token generics&#34;&gt;&lt;span class=&#34;token punctuation&#34;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&#34;token class-name&#34;&gt;MultiblockTank&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;&gt;&lt;/span&gt;&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;&amp;#123;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;3&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;span class=&#34;token keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;GlassWallTileEntity&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;&amp;#123;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;4&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;        &lt;span class=&#34;token comment&#34;&gt;// 待会我们要注册的 TILE_ENTITY_TYPE&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;5&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;        &lt;span class=&#34;token keyword&#34;&gt;super&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token class-name&#34;&gt;PowerfulTank&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;RegistryEvents&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token constant&#34;&gt;TILE_ENTITY_TYPE_WALL&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;6&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;span class=&#34;token punctuation&#34;&gt;&amp;#125;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;7&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;8&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;span class=&#34;token comment&#34;&gt;// 不允许 Part 被放置在多方块内部&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;9&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;span class=&#34;token annotation punctuation&#34;&gt;@Override&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;10&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;span class=&#34;token keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;boolean&lt;/span&gt; &lt;span class=&#34;token function&#34;&gt;isGoodForPosition&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token annotation punctuation&#34;&gt;@Nonnull&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;PartPosition&lt;/span&gt; partPosition&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;token annotation punctuation&#34;&gt;@Nonnull&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;IMultiblockValidator&lt;/span&gt; iMultiblockValidator&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;&amp;#123;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;11&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;        &lt;span class=&#34;token keyword&#34;&gt;return&lt;/span&gt; partPosition &lt;span class=&#34;token operator&#34;&gt;!=&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;PartPosition&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;Interior&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;12&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;span class=&#34;token punctuation&#34;&gt;&amp;#125;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;13&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;14&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;span class=&#34;token comment&#34;&gt;// 这里需要返回我们 MultiblockTank 类的实例&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;15&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;span class=&#34;token annotation punctuation&#34;&gt;@Nonnull&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;16&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;span class=&#34;token annotation punctuation&#34;&gt;@Override&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;17&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;span class=&#34;token keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;MultiblockTank&lt;/span&gt; &lt;span class=&#34;token function&#34;&gt;createController&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;&amp;#123;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;18&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;        &lt;span class=&#34;token keyword&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;new&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;MultiblockTank&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;getCurrentWorld&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;19&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;span class=&#34;token punctuation&#34;&gt;&amp;#125;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;20&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;21&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;span class=&#34;token comment&#34;&gt;// 这里需要返回我们 MultiblockTank 类的 Class&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;22&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;span class=&#34;token annotation punctuation&#34;&gt;@Nonnull&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;23&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;span class=&#34;token annotation punctuation&#34;&gt;@Override&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;24&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;span class=&#34;token keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;Class&lt;/span&gt;&lt;span class=&#34;token generics&#34;&gt;&lt;span class=&#34;token punctuation&#34;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&#34;token class-name&#34;&gt;MultiblockTank&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;&gt;&lt;/span&gt;&lt;/span&gt; &lt;span class=&#34;token function&#34;&gt;getControllerType&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;&amp;#123;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;25&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;        &lt;span class=&#34;token keyword&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;MultiblockTank&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token keyword&#34;&gt;class&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;26&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;span class=&#34;token punctuation&#34;&gt;&amp;#125;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;27&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token punctuation&#34;&gt;&amp;#125;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;&lt;p&gt;上文我们提到了非 Part 方块的成立条件判断，对于 Part 的判断则是在这里的 isGoodForPosition 方法中。PartPosition 是一个枚举类，读者可以自行根据自己需要的逻辑进行判断，这里我们只是不希望 Part 出现在结构内部，所以针对 Interior 进行了拒绝。&lt;/p&gt;
&lt;p&gt;我们在主类中添加对应的注册，注意这里我们需要对三个方块的 RenderType 进行特殊的注册，因为我们希望这三者的渲染方式为 CutoutMipped，感兴趣的读者可以阅读&lt;a href=&#34;https://boson.v2mcdev.com/block/rendertype.html&#34;&gt; Boson 的相关内容&lt;/a&gt;&lt;/p&gt;
&lt;figure class=&#34;highlight java&#34;&gt;&lt;figcaption data-lang=&#34;java&#34;&gt;&lt;/figcaption&gt;&lt;table&gt;&lt;tr&gt;&lt;td data-num=&#34;1&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token comment&#34;&gt;// 位于: powerful_tank/PowerfulTank.java&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;2&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token annotation punctuation&#34;&gt;@Mod&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token string&#34;&gt;&#34;powerful_tank&#34;&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;3&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;class&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;PowerfulTank&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;&amp;#123;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;4&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;span class=&#34;token comment&#34;&gt;// 这里我们让注册类作为主类的内部类&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;5&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;span class=&#34;token annotation punctuation&#34;&gt;@Mod.EventBusSubscriber&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;bus &lt;span class=&#34;token operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;Mod&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;EventBusSubscriber&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;Bus&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token constant&#34;&gt;MOD&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;6&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;span class=&#34;token keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;static&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;class&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;RegistryEvents&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;&amp;#123;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;7&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;        &lt;span class=&#34;token keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;static&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;final&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;GlassWallBlock&lt;/span&gt; &lt;span class=&#34;token constant&#34;&gt;BLOCK_WALL&lt;/span&gt; &lt;span class=&#34;token operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;new&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;GlassWallBlock&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;8&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;        &lt;span class=&#34;token keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;static&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;final&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;TileEntityType&lt;/span&gt;&lt;span class=&#34;token generics&#34;&gt;&lt;span class=&#34;token punctuation&#34;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&#34;token class-name&#34;&gt;GlassWallTileEntity&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;&gt;&lt;/span&gt;&lt;/span&gt; &lt;span class=&#34;token constant&#34;&gt;TILE_ENTITY_TYPE_WALL&lt;/span&gt; &lt;span class=&#34;token operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;TileEntityType&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;Builder&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;of&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token class-name&#34;&gt;GlassWallTileEntity&lt;/span&gt;&lt;span class=&#34;token operator&#34;&gt;::&lt;/span&gt;&lt;span class=&#34;token keyword&#34;&gt;new&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;token constant&#34;&gt;BLOCK_WALL&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;build&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token constant&#34;&gt;DSL&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;remainderType&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;9&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;10&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;        &lt;span class=&#34;token annotation punctuation&#34;&gt;@SubscribeEvent&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;11&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;        &lt;span class=&#34;token keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;static&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;void&lt;/span&gt; &lt;span class=&#34;token function&#34;&gt;onBlocksRegistry&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token class-name&#34;&gt;RegistryEvent&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;Register&lt;/span&gt;&lt;span class=&#34;token generics&#34;&gt;&lt;span class=&#34;token punctuation&#34;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&#34;token class-name&#34;&gt;Block&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;&gt;&lt;/span&gt;&lt;/span&gt; event&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;&amp;#123;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;12&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;            event&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;getRegistry&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;registerAll&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token constant&#34;&gt;BLOCK_WALL&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;13&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;        &lt;span class=&#34;token punctuation&#34;&gt;&amp;#125;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;14&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;15&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;        &lt;span class=&#34;token annotation punctuation&#34;&gt;@SubscribeEvent&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;16&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;        &lt;span class=&#34;token keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;static&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;void&lt;/span&gt; &lt;span class=&#34;token function&#34;&gt;onItemsRegistry&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token class-name&#34;&gt;RegistryEvent&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;Register&lt;/span&gt;&lt;span class=&#34;token generics&#34;&gt;&lt;span class=&#34;token punctuation&#34;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&#34;token class-name&#34;&gt;Item&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;&gt;&lt;/span&gt;&lt;/span&gt; event&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;&amp;#123;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;17&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;            event&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;getRegistry&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;register&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token keyword&#34;&gt;new&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;BlockItem&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token constant&#34;&gt;BLOCK_WALL&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;new&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;Item&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;Properties&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;tab&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token class-name&#34;&gt;ItemGroup&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token constant&#34;&gt;TAB_BUILDING_BLOCKS&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;setRegistryName&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token string&#34;&gt;&#34;powerful_tank&#34;&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;token string&#34;&gt;&#34;wall_block&#34;&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;18&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;        &lt;span class=&#34;token punctuation&#34;&gt;&amp;#125;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;19&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;20&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;        &lt;span class=&#34;token annotation punctuation&#34;&gt;@SubscribeEvent&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;21&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;        &lt;span class=&#34;token keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;static&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;void&lt;/span&gt; &lt;span class=&#34;token function&#34;&gt;onTileEntitiesRegistry&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token class-name&#34;&gt;RegistryEvent&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;Register&lt;/span&gt;&lt;span class=&#34;token generics&#34;&gt;&lt;span class=&#34;token punctuation&#34;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&#34;token class-name&#34;&gt;TileEntityType&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&#34;token operator&#34;&gt;?&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;&gt;&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;&gt;&lt;/span&gt;&lt;/span&gt; event&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;&amp;#123;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;22&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;            event&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;getRegistry&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;register&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token constant&#34;&gt;TILE_ENTITY_TYPE_WALL&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;setRegistryName&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token string&#34;&gt;&#34;powerful_tank:wall&#34;&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;23&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;        &lt;span class=&#34;token punctuation&#34;&gt;&amp;#125;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;24&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;25&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;        &lt;span class=&#34;token comment&#34;&gt;// 注意对于玻璃样式的材质，需要注册 RenderType&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;26&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;        &lt;span class=&#34;token annotation punctuation&#34;&gt;@SubscribeEvent&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;27&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;        &lt;span class=&#34;token keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;static&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;void&lt;/span&gt; &lt;span class=&#34;token function&#34;&gt;onRenderTypeRegistry&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token class-name&#34;&gt;FMLClientSetupEvent&lt;/span&gt; event&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;&amp;#123;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;28&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;            event&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;enqueueWork&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;token operator&#34;&gt;-&gt;&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;&amp;#123;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;29&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;                &lt;span class=&#34;token class-name&#34;&gt;RenderTypeLookup&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;setRenderLayer&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token constant&#34;&gt;BLOCK_WALL&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;RenderType&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;cutoutMipped&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;30&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;            &lt;span class=&#34;token punctuation&#34;&gt;&amp;#125;&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;31&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;        &lt;span class=&#34;token punctuation&#34;&gt;&amp;#125;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;32&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;span class=&#34;token punctuation&#34;&gt;&amp;#125;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;33&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token punctuation&#34;&gt;&amp;#125;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;&lt;p&gt;相似的，读者应该补全相对于的其他 Part 的类和注册 (EnergyPort 和 FluidPort)，读者完成后，项目结构应该类似是这样:&lt;/p&gt;
&lt;p&gt;&lt;img loading=&#34;lazy&#34; data-src=&#34;https://s2.loli.net/2022/01/25/xc1C8IbWTQaFkDr.png&#34; alt=&#34;Structure&#34; /&gt;&lt;/p&gt;
&lt;p&gt;之后我们来为能量接口和流体接口添加承载能量和流体的能力，即添加 Capability。&lt;/p&gt;
&lt;p&gt;对于 Capability 感兴趣的读者，这里笔者推荐阅读&lt;a href=&#34;https://boson.v2mcdev.com/capability/intro.html&#34;&gt; Boson 的相关部分&lt;/a&gt;。&lt;/p&gt;
&lt;p&gt;对于实现流体能力感兴趣的读者，这里笔者推荐阅读&lt;a href=&#34;https://harbinger.covertdragon.team/chapter-27/container/block.html&#34;&gt; Harbinger 的相关部分&lt;/a&gt;。&lt;/p&gt;
&lt;p&gt;对于实现能量能力感兴趣的读者，这里笔者推荐阅读&lt;a href=&#34;https://www.mcbbs.net/forum.php?mod=viewthread&amp;amp;tid=1034965&#34;&gt; Forge 能量系统简述&lt;/a&gt;。&lt;/p&gt;
&lt;p&gt;首先，在 GlassEnergyPortTileEntity 中加入以下内容，为他添加能量储存&lt;/p&gt;
&lt;figure class=&#34;highlight java&#34;&gt;&lt;figcaption data-lang=&#34;java&#34;&gt;&lt;/figcaption&gt;&lt;table&gt;&lt;tr&gt;&lt;td data-num=&#34;1&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token comment&#34;&gt;// 位于: powerful_tank/tileentity/GlassEnergyPortTileEntity.java&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;2&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token comment&#34;&gt;// 因为我们对能量的需求比较简单，我们这里没有自己手动实现 IEnergyHandler，而是直接利用了 Forge 实现好的 EnergyStorage 类&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;3&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;final&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;EnergyStorage&lt;/span&gt; energyStorage &lt;span class=&#34;token operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;new&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;EnergyStorage&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token number&#34;&gt;2_500_000&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;token number&#34;&gt;2_500_000&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;4&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;5&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token comment&#34;&gt;// 在 1.16.5 中，我们只需要重写 getCapability，而不需要重写 hasCapability 了&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;6&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token comment&#34;&gt;// 这里需要注意，我们应该重写的是包含两个参数 Capability&amp;lt;T &gt; 和 Direction 的方法，而不是仅有一个参数的方法，这里读者要注意&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;7&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token annotation punctuation&#34;&gt;@Nonnull&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;8&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token annotation punctuation&#34;&gt;@Override&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;9&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;token generics&#34;&gt;&lt;span class=&#34;token punctuation&#34;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&#34;token class-name&#34;&gt;T&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;&gt;&lt;/span&gt;&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;LazyOptional&lt;/span&gt;&lt;span class=&#34;token generics&#34;&gt;&lt;span class=&#34;token punctuation&#34;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&#34;token class-name&#34;&gt;T&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;&gt;&lt;/span&gt;&lt;/span&gt; &lt;span class=&#34;token function&#34;&gt;getCapability&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token annotation punctuation&#34;&gt;@Nonnull&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;Capability&lt;/span&gt;&lt;span class=&#34;token generics&#34;&gt;&lt;span class=&#34;token punctuation&#34;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&#34;token class-name&#34;&gt;T&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;&gt;&lt;/span&gt;&lt;/span&gt; cap&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;token annotation punctuation&#34;&gt;@Nullable&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;Direction&lt;/span&gt; side&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;&amp;#123;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;10&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;span class=&#34;token keyword&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;cap &lt;span class=&#34;token operator&#34;&gt;==&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;CapabilityEnergy&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token constant&#34;&gt;ENERGY&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;&amp;#123;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;11&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;        &lt;span class=&#34;token keyword&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;LazyOptional&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;of&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;token operator&#34;&gt;-&gt;&lt;/span&gt; energyStorage&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;cast&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;12&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;span class=&#34;token punctuation&#34;&gt;&amp;#125;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;13&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;span class=&#34;token keyword&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;super&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;getCapability&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;cap&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; side&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;14&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token punctuation&#34;&gt;&amp;#125;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;15&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;16&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token comment&#34;&gt;// 读者不要忘记储存和读取 TileEntity 内部的数据，这里我们把储存的能量储存在 energy 标签中&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;17&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token annotation punctuation&#34;&gt;@Override&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;18&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;void&lt;/span&gt; &lt;span class=&#34;token function&#34;&gt;syncDataFrom&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token annotation punctuation&#34;&gt;@Nonnull&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;CompoundNBT&lt;/span&gt; data&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;token annotation punctuation&#34;&gt;@Nonnull&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;SyncReason&lt;/span&gt; syncReason&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;&amp;#123;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;19&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;span class=&#34;token keyword&#34;&gt;super&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;syncDataFrom&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;data&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; syncReason&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;20&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    energyStorage&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;receiveEnergy&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;data&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;getInt&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token string&#34;&gt;&#34;energy&#34;&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;token boolean&#34;&gt;false&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;21&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token punctuation&#34;&gt;&amp;#125;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;22&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;23&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token annotation punctuation&#34;&gt;@Nonnull&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;24&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token annotation punctuation&#34;&gt;@Override&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;25&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;CompoundNBT&lt;/span&gt; &lt;span class=&#34;token function&#34;&gt;syncDataTo&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token class-name&#34;&gt;CompoundNBT&lt;/span&gt; data&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;token annotation punctuation&#34;&gt;@Nonnull&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;SyncReason&lt;/span&gt; syncReason&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;&amp;#123;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;26&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    data&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;putInt&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token string&#34;&gt;&#34;energy&#34;&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; energyStorage&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;getEnergyStored&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;27&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;span class=&#34;token keyword&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;super&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;syncDataTo&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;data&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; syncReason&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;28&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token punctuation&#34;&gt;&amp;#125;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;29&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;30&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token comment&#34;&gt;// 这个方法并不是必要的，但是方便我们一会查看容器内的能量和流体储存&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;31&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;String&lt;/span&gt; &lt;span class=&#34;token function&#34;&gt;getEnergyText&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;&amp;#123;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;32&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;        &lt;span class=&#34;token keyword&#34;&gt;int&lt;/span&gt; cost &lt;span class=&#34;token operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;token number&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;33&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;        &lt;span class=&#34;token comment&#34;&gt;// 注意处理多方块结构的存在性问题&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;34&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;        &lt;span class=&#34;token keyword&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;getMultiblockController&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;isPresent&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;token operator&#34;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&#34;token function&#34;&gt;isMachineAssembled&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;&amp;#123;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;35&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;            &lt;span class=&#34;token comment&#34;&gt;// 一会我们来补充这个方法&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;36&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;            cost &lt;span class=&#34;token operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;token function&#34;&gt;getMultiblockController&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;get&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;getCost&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;37&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;        &lt;span class=&#34;token punctuation&#34;&gt;&amp;#125;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;38&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;        &lt;span class=&#34;token keyword&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;String&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;format&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token string&#34;&gt;&#34;Energy: %d / %d FE, Cost: %d FE/t&#34;&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; energyStorage&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;getEnergyStored&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; energyStorage&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;getMaxEnergyStored&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; cost&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;39&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;span class=&#34;token punctuation&#34;&gt;&amp;#125;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;&lt;p&gt;此时进入游戏，我们的能量接口已经支持了各个遵循 Forge 标准的模组的能量管线。&lt;/p&gt;
&lt;p&gt;&lt;img loading=&#34;lazy&#34; data-src=&#34;https://s2.loli.net/2022/01/26/lxaA5ur9z4BdIco.png&#34; alt=&#34;Energy Port&#34; /&gt;&lt;/p&gt;
&lt;p&gt;同样的，这里我们为 GlassFluidPortTileEntity 添加流体支持&lt;/p&gt;
&lt;figure class=&#34;highlight java&#34;&gt;&lt;figcaption data-lang=&#34;java&#34;&gt;&lt;/figcaption&gt;&lt;table&gt;&lt;tr&gt;&lt;td data-num=&#34;1&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token comment&#34;&gt;// 位于: powerful_tank/tileentity/GlassFluidPortTileEntity.java&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;2&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token comment&#34;&gt;// 这里我们也没有手动实现 IFluidHandler，而是使用了 Forge 实现好的 FluidTank，并且做了小更改&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;3&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;final&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;FluidTank&lt;/span&gt; fluidTank &lt;span class=&#34;token operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;new&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;FluidTank&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token class-name&#34;&gt;Integer&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token constant&#34;&gt;MAX_VALUE&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;&amp;#123;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;4&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;span class=&#34;token annotation punctuation&#34;&gt;@Override&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;5&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;span class=&#34;token keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;int&lt;/span&gt; &lt;span class=&#34;token function&#34;&gt;fill&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token class-name&#34;&gt;FluidStack&lt;/span&gt; resource&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;FluidAction&lt;/span&gt; action&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;&amp;#123;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;6&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;        &lt;span class=&#34;token comment&#34;&gt;// 不构成多方块就想填入流体？&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;7&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;        &lt;span class=&#34;token keyword&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;super&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;fill&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token class-name&#34;&gt;GlassFluidPortTileEntity&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token keyword&#34;&gt;this&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;isMachineAssembled&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;token operator&#34;&gt;?&lt;/span&gt; resource &lt;span class=&#34;token operator&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;FluidStack&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token constant&#34;&gt;EMPTY&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; action&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;8&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;span class=&#34;token punctuation&#34;&gt;&amp;#125;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;9&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;10&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;span class=&#34;token annotation punctuation&#34;&gt;@Nonnull&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;11&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;span class=&#34;token annotation punctuation&#34;&gt;@Override&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;12&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;span class=&#34;token keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;FluidStack&lt;/span&gt; &lt;span class=&#34;token function&#34;&gt;drain&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token keyword&#34;&gt;int&lt;/span&gt; maxDrain&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;FluidAction&lt;/span&gt; action&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;&amp;#123;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;13&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;        &lt;span class=&#34;token comment&#34;&gt;// 不构成多方块就想取出流体？&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;14&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;        &lt;span class=&#34;token keyword&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;super&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;drain&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token class-name&#34;&gt;GlassFluidPortTileEntity&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token keyword&#34;&gt;this&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;isMachineAssembled&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;token operator&#34;&gt;?&lt;/span&gt; maxDrain &lt;span class=&#34;token operator&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;token number&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; action&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;15&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;span class=&#34;token punctuation&#34;&gt;&amp;#125;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;16&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token punctuation&#34;&gt;&amp;#125;&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;17&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;18&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token annotation punctuation&#34;&gt;@Nonnull&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;19&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token annotation punctuation&#34;&gt;@Override&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;20&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;token generics&#34;&gt;&lt;span class=&#34;token punctuation&#34;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&#34;token class-name&#34;&gt;T&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;&gt;&lt;/span&gt;&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;LazyOptional&lt;/span&gt;&lt;span class=&#34;token generics&#34;&gt;&lt;span class=&#34;token punctuation&#34;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&#34;token class-name&#34;&gt;T&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;&gt;&lt;/span&gt;&lt;/span&gt; &lt;span class=&#34;token function&#34;&gt;getCapability&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token annotation punctuation&#34;&gt;@Nonnull&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;Capability&lt;/span&gt;&lt;span class=&#34;token generics&#34;&gt;&lt;span class=&#34;token punctuation&#34;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&#34;token class-name&#34;&gt;T&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;&gt;&lt;/span&gt;&lt;/span&gt; cap&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;token annotation punctuation&#34;&gt;@Nullable&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;Direction&lt;/span&gt; side&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;&amp;#123;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;21&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;span class=&#34;token keyword&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;cap &lt;span class=&#34;token operator&#34;&gt;==&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;CapabilityFluidHandler&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token constant&#34;&gt;FLUID_HANDLER_CAPABILITY&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;&amp;#123;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;22&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;        &lt;span class=&#34;token keyword&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;LazyOptional&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;of&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;token operator&#34;&gt;-&gt;&lt;/span&gt; fluidTank&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;cast&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;23&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;span class=&#34;token punctuation&#34;&gt;&amp;#125;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;24&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;span class=&#34;token keyword&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;super&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;getCapability&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;cap&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; side&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;25&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token punctuation&#34;&gt;&amp;#125;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;26&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;27&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token annotation punctuation&#34;&gt;@Override&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;28&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;void&lt;/span&gt; &lt;span class=&#34;token function&#34;&gt;syncDataFrom&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token annotation punctuation&#34;&gt;@Nonnull&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;CompoundNBT&lt;/span&gt; data&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;token annotation punctuation&#34;&gt;@Nonnull&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;SyncReason&lt;/span&gt; syncReason&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;&amp;#123;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;29&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;span class=&#34;token keyword&#34;&gt;super&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;syncDataFrom&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;data&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; syncReason&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;30&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    fluidTank&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;readFromNBT&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;data&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;31&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token punctuation&#34;&gt;&amp;#125;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;32&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;33&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token annotation punctuation&#34;&gt;@Nonnull&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;34&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token annotation punctuation&#34;&gt;@Override&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;35&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;CompoundNBT&lt;/span&gt; &lt;span class=&#34;token function&#34;&gt;syncDataTo&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token annotation punctuation&#34;&gt;@Nonnull&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;CompoundNBT&lt;/span&gt; data&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;token annotation punctuation&#34;&gt;@Nonnull&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;SyncReason&lt;/span&gt; syncReason&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;&amp;#123;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;36&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    fluidTank&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;writeToNBT&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;data&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;37&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;span class=&#34;token keyword&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;super&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;syncDataTo&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;data&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; syncReason&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;38&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token punctuation&#34;&gt;&amp;#125;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;39&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;40&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token comment&#34;&gt;// 这个方法并不是必要的，但是方便我们一会查看容器内的能量和流体储存&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;41&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;String&lt;/span&gt; &lt;span class=&#34;token function&#34;&gt;getFluidText&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;&amp;#123;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;42&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;span class=&#34;token class-name&#34;&gt;FluidStack&lt;/span&gt; fluidStack &lt;span class=&#34;token operator&#34;&gt;=&lt;/span&gt; fluidTank&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;getFluid&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;43&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;span class=&#34;token keyword&#34;&gt;int&lt;/span&gt; cost &lt;span class=&#34;token operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;token number&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;44&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;span class=&#34;token keyword&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;getMultiblockController&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;isPresent&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;token operator&#34;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&#34;token function&#34;&gt;isMachineAssembled&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;&amp;#123;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;45&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;        cost &lt;span class=&#34;token operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;token function&#34;&gt;getMultiblockController&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;get&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;getCost&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;46&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;span class=&#34;token punctuation&#34;&gt;&amp;#125;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;47&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;span class=&#34;token keyword&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;String&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;format&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token string&#34;&gt;&#34;Fluid(%s): %d / %s mb, Cost: %d FE/t&#34;&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;I18n&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;get&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;fluidStack&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;getTranslationKey&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; fluidStack&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;getAmount&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;token string&#34;&gt;&#34;INFINITE&#34;&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; cost&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;48&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token punctuation&#34;&gt;&amp;#125;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;&lt;p&gt;为了限制流体只在多方块成立时可以存取，我们这里重写了 FluidTank 的两个方法。&lt;/p&gt;
&lt;p&gt;对于多方块结构的 Part，我们可以调用 isMachineAssembled 来得知多方块结构是否完整，同时也可以调用 getMultiblockController 来获得到多方块结构自身。这里可能需要读者处理一下 Optional 问题。&lt;/p&gt;
&lt;p&gt;此时进入游戏，我们的流体接口也可以接上其他模组的流体管道了，但此时流体应该还流不进去，因为我们没有构成完整的多方块结构。&lt;/p&gt;
&lt;p&gt;&lt;img loading=&#34;lazy&#34; data-src=&#34;https://s2.loli.net/2022/01/26/HeDWI8KzTpaqdXx.png&#34; alt=&#34;Fluid Port&#34; /&gt;&lt;/p&gt;
&lt;p&gt;最后我们来补全 MultiblockTank 的逻辑。在 MultiblockTank 中添加下面的内容&lt;/p&gt;
&lt;figure class=&#34;highlight java&#34;&gt;&lt;figcaption data-lang=&#34;java&#34;&gt;&lt;/figcaption&gt;&lt;table&gt;&lt;tr&gt;&lt;td data-num=&#34;1&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token comment&#34;&gt;// 位于: powerful_tank/multiblock/MultiblockTank.java&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;2&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token keyword&#34;&gt;private&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;EnergyStorage&lt;/span&gt; energyStorage&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;3&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token keyword&#34;&gt;private&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;FluidTank&lt;/span&gt; fluidTank&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;4&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;5&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token comment&#34;&gt;// 为了让多方块结构找到两个接口，我们添加了这样一个私有方法，我们可以利用_connectedParts 成员来获得所有连接的 Part&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;6&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token keyword&#34;&gt;private&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;void&lt;/span&gt; &lt;span class=&#34;token function&#34;&gt;findPorts&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;&amp;#123;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;7&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;span class=&#34;token keyword&#34;&gt;for&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token class-name&#34;&gt;IMultiblockPart&lt;/span&gt;&lt;span class=&#34;token generics&#34;&gt;&lt;span class=&#34;token punctuation&#34;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&#34;token class-name&#34;&gt;MultiblockTank&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;&gt;&lt;/span&gt;&lt;/span&gt; connectedPart &lt;span class=&#34;token operator&#34;&gt;:&lt;/span&gt; _connectedParts&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;&amp;#123;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;8&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;        energyStorage &lt;span class=&#34;token operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;null&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;9&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;        fluidTank &lt;span class=&#34;token operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;null&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;10&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;        &lt;span class=&#34;token comment&#34;&gt;// 获得位于各个 Part 的实例，这种手法很常见&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;11&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;        &lt;span class=&#34;token keyword&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;connectedPart &lt;span class=&#34;token keyword&#34;&gt;instanceof&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;GlassEnergyPortTileEntity&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;12&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;            energyStorage &lt;span class=&#34;token operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token class-name&#34;&gt;GlassEnergyPortTileEntity&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt; connectedPart&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;energyStorage&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;13&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;        &lt;span class=&#34;token keyword&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;connectedPart &lt;span class=&#34;token keyword&#34;&gt;instanceof&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;GlassFluidPortTileEntity&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;14&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;            fluidTank &lt;span class=&#34;token operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token class-name&#34;&gt;GlassFluidPortTileEntity&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt; connectedPart&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;fluidTank&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;15&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;span class=&#34;token punctuation&#34;&gt;&amp;#125;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;16&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token punctuation&#34;&gt;&amp;#125;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;17&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;18&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token comment&#34;&gt;// 当多方块结构的区块被卸载后重新加载时，会调用这个方法&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;19&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token annotation punctuation&#34;&gt;@Override&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;20&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token keyword&#34;&gt;protected&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;void&lt;/span&gt; &lt;span class=&#34;token function&#34;&gt;onMachineRestored&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;&amp;#123;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;21&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;span class=&#34;token comment&#34;&gt;// 我们希望这个时候重新搜索一次 Part&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;22&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;span class=&#34;token function&#34;&gt;findPorts&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;23&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token punctuation&#34;&gt;&amp;#125;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;24&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;25&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token comment&#34;&gt;// 当多方块结构形成时，会调用这个方法&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;26&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token annotation punctuation&#34;&gt;@Override&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;27&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token keyword&#34;&gt;protected&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;void&lt;/span&gt; &lt;span class=&#34;token function&#34;&gt;onMachineAssembled&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;&amp;#123;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;28&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;span class=&#34;token comment&#34;&gt;// 这应该是第一次寻找 Part&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;29&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;span class=&#34;token function&#34;&gt;findPorts&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;30&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token punctuation&#34;&gt;&amp;#125;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;31&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;32&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token comment&#34;&gt;// 最终判定多方块结构是否成立的方法，下文会予以介绍&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;33&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token annotation punctuation&#34;&gt;@Override&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;34&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token keyword&#34;&gt;protected&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;boolean&lt;/span&gt; &lt;span class=&#34;token function&#34;&gt;isMachineWhole&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token annotation punctuation&#34;&gt;@Nonnull&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;IMultiblockValidator&lt;/span&gt; validatorCallback&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;&amp;#123;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;35&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;span class=&#34;token function&#34;&gt;findPorts&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;36&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;span class=&#34;token keyword&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;energyStorage &lt;span class=&#34;token operator&#34;&gt;!=&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;null&lt;/span&gt; &lt;span class=&#34;token operator&#34;&gt;&amp;amp;&amp;amp;&lt;/span&gt; fluidTank &lt;span class=&#34;token operator&#34;&gt;!=&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;null&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;37&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;        &lt;span class=&#34;token keyword&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;super&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;isMachineWhole&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;validatorCallback&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;38&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;span class=&#34;token keyword&#34;&gt;else&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;39&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;        &lt;span class=&#34;token comment&#34;&gt;//validatorCallback.setLastError(······);&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;40&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;        &lt;span class=&#34;token keyword&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;token boolean&#34;&gt;false&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;41&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token punctuation&#34;&gt;&amp;#125;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;&lt;p&gt;这里笔者详细介绍一下 ZeroCore 是如何判断多方块结构是否成立的。读者如果查看父类的 isMachineWhole 方法大概可以猜出，这个方法对于 Part 类方块，会利用我们之前提供的 isGoodForPosition 方法来辨别该方块是否合法，而对于其他方块，则会调用 isBlockGoodForXXX 的一系列方法来判断是否合法。&lt;/p&gt;
&lt;p&gt;我们要做的，便是重写 isMachineWhole，添加我们自己所需的判断条件。例如我们上文调用 findPorts 方法，只有两个接口都存在，才会进一步判断多方块结构是否成立。&lt;/p&gt;
&lt;p&gt;这里细心的读者可能对 validatorCallback 抱有疑问，我们上文多次见到这个参数，且都是在与结构成立判断的相关方法中存在，我们可以猜测这个参数可能与结构判断有关。&lt;/p&gt;
&lt;p&gt;其实，validatorCallback 是负责处理多方块结构不成立时的错误信息的，当结构出现不符合的情况时，我们希望给予一个错误信息来让玩家知道如何正确摆放多方块机器。在阅读父类 isMachineWhole 方法中，我们发现 ZeroCore 已经针对一些常见的问题处理了错误文本，我们只需关心自己的逻辑判断中的问题，并给予提示就可以了。&lt;/p&gt;
&lt;p&gt;这里读者需要补全 setLastError 并且给予错误字符串，这里的字符串是支持 lang 提供的国际化的，读者可以自行设置。如果希望显示最后一个错误，可以调用 validatorCallback.getLastError () 方法，下文我们会看到相关的例子。&lt;/p&gt;
&lt;p&gt;之后，我们需要添加核心逻辑，相信读者应该对 ITickableTileEntity 略知一二，他会在每一个游戏刻执行 TileEntity 内的代码，正是这个功能给 Minecraft 中无数科技模组的机器提供了可能。相似的，多方块结构也有类似的方法。&lt;/p&gt;
&lt;p&gt;对于多方块结构，updateServer 方法会在逻辑服务端每一个游戏刻被执行一次，所以请用对待 tick 方法的态度对待他，尽量减小对服务器性能的影响。&lt;/p&gt;
&lt;figure class=&#34;highlight java&#34;&gt;&lt;figcaption data-lang=&#34;java&#34;&gt;&lt;/figcaption&gt;&lt;table&gt;&lt;tr&gt;&lt;td data-num=&#34;1&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token comment&#34;&gt;// 位于: powerful_tank/multiblock/MultiblockTank.java&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;2&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token annotation punctuation&#34;&gt;@Override&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;3&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token keyword&#34;&gt;protected&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;boolean&lt;/span&gt; &lt;span class=&#34;token function&#34;&gt;updateServer&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;&amp;#123;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;4&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;span class=&#34;token keyword&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;energyStorage &lt;span class=&#34;token operator&#34;&gt;!=&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;null&lt;/span&gt; &lt;span class=&#34;token operator&#34;&gt;&amp;amp;&amp;amp;&lt;/span&gt; fluidTank &lt;span class=&#34;token operator&#34;&gt;!=&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;null&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;&amp;#123;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;5&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;        &lt;span class=&#34;token comment&#34;&gt;// 每一个 tick 都扣除指定的能量，energyStorage 实例我们上文已经获取到了&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;6&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;        &lt;span class=&#34;token keyword&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;energyStorage&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;getEnergyStored&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;token operator&#34;&gt;&gt;&lt;/span&gt; &lt;span class=&#34;token number&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt; energyStorage&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;extractEnergy&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;getCost&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;token boolean&#34;&gt;false&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;7&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;        &lt;span class=&#34;token comment&#34;&gt;// 能量归零且有流体存在&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;8&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;        &lt;span class=&#34;token keyword&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;energyStorage&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;getEnergyStored&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;token operator&#34;&gt;==&lt;/span&gt; &lt;span class=&#34;token number&#34;&gt;0&lt;/span&gt; &lt;span class=&#34;token operator&#34;&gt;&amp;amp;&amp;amp;&lt;/span&gt; fluidTank&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;getFluidAmount&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;token operator&#34;&gt;!=&lt;/span&gt; &lt;span class=&#34;token number&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;&amp;#123;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;9&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;            fluidTank&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;setFluid&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token class-name&#34;&gt;FluidStack&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token constant&#34;&gt;EMPTY&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;10&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;        &lt;span class=&#34;token punctuation&#34;&gt;&amp;#125;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;11&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;        &lt;span class=&#34;token keyword&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;token boolean&#34;&gt;true&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;12&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;span class=&#34;token punctuation&#34;&gt;&amp;#125;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;13&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;span class=&#34;token keyword&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;token boolean&#34;&gt;false&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;14&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;15&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;span class=&#34;token comment&#34;&gt;// 这里，如果&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;16&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token punctuation&#34;&gt;&amp;#125;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;17&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;18&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token comment&#34;&gt;// 根据内部保存的流体数量计算耗能，这里对于 1000 桶流体大概消耗 251RF/t&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;19&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;int&lt;/span&gt; &lt;span class=&#34;token function&#34;&gt;getCost&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;&amp;#123;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;20&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;span class=&#34;token keyword&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token keyword&#34;&gt;int&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;Math&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;pow&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;fluidTank&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;getFluidAmount&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;token operator&#34;&gt;/&lt;/span&gt; &lt;span class=&#34;token number&#34;&gt;1000.0&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;token number&#34;&gt;0.8&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;21&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token punctuation&#34;&gt;&amp;#125;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;&lt;p&gt;读者可能注意到 updateServer 具有返回值，这个返回值意味着是否需要数据同步。我们操作了储存在世界中的 TileEntity 中相关数据的值，所以我们在值发生改变的时候返回 true。类似于 TileEntity 中的 markDirty 方法&lt;/p&gt;
&lt;p&gt;现在进入游戏，我们的多方块结构已经能够正常运作了，相信阅读到了这里，读者已经对如何开发一个简单的多方块机器有了自己的理解。我们这里还会补全一些外围方法，来让机器更人性化。首先，我们希望多方块结构在没有构成时告知我们错误的原因。我们编辑 GlassWallBlock 类并且重写 use 方法&lt;/p&gt;
&lt;figure class=&#34;highlight java&#34;&gt;&lt;figcaption data-lang=&#34;java&#34;&gt;&lt;/figcaption&gt;&lt;table&gt;&lt;tr&gt;&lt;td data-num=&#34;1&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token comment&#34;&gt;// 位于: powerful_tank/block/GlassWallBlock.java&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;2&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token annotation punctuation&#34;&gt;@Nonnull&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;3&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token annotation punctuation&#34;&gt;@Override&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;4&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;ActionResultType&lt;/span&gt; &lt;span class=&#34;token function&#34;&gt;use&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token annotation punctuation&#34;&gt;@Nonnull&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;BlockState&lt;/span&gt; pState&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;World&lt;/span&gt; pLevel&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;token annotation punctuation&#34;&gt;@Nonnull&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;BlockPos&lt;/span&gt; pPos&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;token annotation punctuation&#34;&gt;@Nonnull&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;PlayerEntity&lt;/span&gt; pPlayer&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;token annotation punctuation&#34;&gt;@Nonnull&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;Hand&lt;/span&gt; pHand&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;token annotation punctuation&#34;&gt;@Nonnull&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;BlockRayTraceResult&lt;/span&gt; pHit&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;&amp;#123;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;5&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;span class=&#34;token keyword&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token operator&#34;&gt;!&lt;/span&gt;pLevel&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;isClientSide &lt;span class=&#34;token operator&#34;&gt;&amp;amp;&amp;amp;&lt;/span&gt; pHand &lt;span class=&#34;token operator&#34;&gt;==&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;Hand&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token constant&#34;&gt;MAIN_HAND&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;&amp;#123;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;6&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;        &lt;span class=&#34;token class-name&#34;&gt;GlassWallTileEntity&lt;/span&gt; te &lt;span class=&#34;token operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token class-name&#34;&gt;GlassWallTileEntity&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt; pLevel&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;getBlockEntity&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;pPos&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;7&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;        &lt;span class=&#34;token keyword&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;te &lt;span class=&#34;token operator&#34;&gt;!=&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;null&lt;/span&gt; &lt;span class=&#34;token operator&#34;&gt;&amp;amp;&amp;amp;&lt;/span&gt; te&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;getMultiblockController&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;isPresent&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;token operator&#34;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&#34;token operator&#34;&gt;!&lt;/span&gt;te&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;isMachineAssembled&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;token operator&#34;&gt;&amp;amp;&amp;amp;&lt;/span&gt; pPlayer&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;isShiftKeyDown&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;&amp;#123;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;8&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;            &lt;span class=&#34;token comment&#34;&gt;// 对于可能存在的错误信息，我们使用 Optional 来处理，调用 validationError.getChatMessage () 就可以得到本地化后的信息，方便发给玩家。&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;9&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;            &lt;span class=&#34;token class-name&#34;&gt;Optional&lt;/span&gt;&lt;span class=&#34;token generics&#34;&gt;&lt;span class=&#34;token punctuation&#34;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&#34;token class-name&#34;&gt;ValidationError&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;&gt;&lt;/span&gt;&lt;/span&gt; errorOptional &lt;span class=&#34;token operator&#34;&gt;=&lt;/span&gt; te&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;getMultiblockController&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;get&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;getLastError&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;10&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;            errorOptional&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;ifPresent&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;validationError &lt;span class=&#34;token operator&#34;&gt;-&gt;&lt;/span&gt; pPlayer&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;sendMessage&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;validationError&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;getChatMessage&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;PowerfulTank&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token constant&#34;&gt;POWERFUL_TANK_UUID&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;11&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;            &lt;span class=&#34;token keyword&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;ActionResultType&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token constant&#34;&gt;SUCCESS&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;12&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;        &lt;span class=&#34;token punctuation&#34;&gt;&amp;#125;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;13&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;span class=&#34;token punctuation&#34;&gt;&amp;#125;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;14&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;span class=&#34;token keyword&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;super&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;use&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;pState&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; pLevel&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; pPos&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; pPlayer&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; pHand&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; pHit&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;&lt;p&gt;&lt;img loading=&#34;lazy&#34; data-src=&#34;https://s2.loli.net/2022/01/26/1ojJCAZdyGvtTO3.png&#34; alt=&#34;Tank Error&#34; /&gt;&lt;/p&gt;
&lt;p&gt;玩家可能还想要知道此时储存的能量和流体数量，我们也添加相关方法，编辑 GlassEnergyPortBlock 和 GlassFluidPortBlock 类&lt;/p&gt;
&lt;figure class=&#34;highlight java&#34;&gt;&lt;figcaption data-lang=&#34;java&#34;&gt;&lt;/figcaption&gt;&lt;table&gt;&lt;tr&gt;&lt;td data-num=&#34;1&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token comment&#34;&gt;// 位于: powerful_tank/block/GlassEnergyPortBlock.java&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;2&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token annotation punctuation&#34;&gt;@Nonnull&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;3&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token annotation punctuation&#34;&gt;@Override&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;4&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;ActionResultType&lt;/span&gt; &lt;span class=&#34;token function&#34;&gt;use&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token annotation punctuation&#34;&gt;@Nonnull&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;BlockState&lt;/span&gt; pState&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;World&lt;/span&gt; pLevel&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;token annotation punctuation&#34;&gt;@Nonnull&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;BlockPos&lt;/span&gt; pPos&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;token annotation punctuation&#34;&gt;@Nonnull&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;PlayerEntity&lt;/span&gt; pPlayer&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;token annotation punctuation&#34;&gt;@Nonnull&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;Hand&lt;/span&gt; pHand&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;token annotation punctuation&#34;&gt;@Nonnull&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;BlockRayTraceResult&lt;/span&gt; pHit&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;&amp;#123;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;5&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;span class=&#34;token keyword&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token operator&#34;&gt;!&lt;/span&gt;pLevel&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;isClientSide &lt;span class=&#34;token operator&#34;&gt;&amp;amp;&amp;amp;&lt;/span&gt; pHand &lt;span class=&#34;token operator&#34;&gt;==&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;Hand&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token constant&#34;&gt;MAIN_HAND&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;&amp;#123;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;6&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;        &lt;span class=&#34;token class-name&#34;&gt;GlassEnergyPortTileEntity&lt;/span&gt; te &lt;span class=&#34;token operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token class-name&#34;&gt;GlassEnergyPortTileEntity&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt; pLevel&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;getBlockEntity&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;pPos&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;7&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;        &lt;span class=&#34;token comment&#34;&gt;// 只有在多方块结构形成时，才希望读取内部储存的能量&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;8&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;        &lt;span class=&#34;token keyword&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;te &lt;span class=&#34;token operator&#34;&gt;!=&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;null&lt;/span&gt; &lt;span class=&#34;token operator&#34;&gt;&amp;amp;&amp;amp;&lt;/span&gt; te&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;isMachineAssembled&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;&amp;#123;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;9&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;            &lt;span class=&#34;token comment&#34;&gt;// 这里调用了我们之前写过的 getEnergyText 方法&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;10&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;            pPlayer&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;sendMessage&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token keyword&#34;&gt;new&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;StringTextComponent&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;te&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;getEnergyText&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;PowerfulTank&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token constant&#34;&gt;POWERFUL_TANK_UUID&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;11&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;            &lt;span class=&#34;token keyword&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;ActionResultType&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token constant&#34;&gt;SUCCESS&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;12&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;        &lt;span class=&#34;token punctuation&#34;&gt;&amp;#125;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;13&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;span class=&#34;token punctuation&#34;&gt;&amp;#125;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;14&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;span class=&#34;token keyword&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;super&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;use&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;pState&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; pLevel&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; pPos&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; pPlayer&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; pHand&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; pHit&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;15&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token punctuation&#34;&gt;&amp;#125;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;&lt;figure class=&#34;highlight java&#34;&gt;&lt;figcaption data-lang=&#34;java&#34;&gt;&lt;/figcaption&gt;&lt;table&gt;&lt;tr&gt;&lt;td data-num=&#34;1&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token comment&#34;&gt;// 位于: powerful_tank/block/GlassFluidPortBlock.java&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;2&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token annotation punctuation&#34;&gt;@Nonnull&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;3&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token annotation punctuation&#34;&gt;@Override&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;4&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;ActionResultType&lt;/span&gt; &lt;span class=&#34;token function&#34;&gt;use&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token annotation punctuation&#34;&gt;@Nonnull&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;BlockState&lt;/span&gt; pState&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;World&lt;/span&gt; pLevel&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;token annotation punctuation&#34;&gt;@Nonnull&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;BlockPos&lt;/span&gt; pPos&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;token annotation punctuation&#34;&gt;@Nonnull&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;PlayerEntity&lt;/span&gt; pPlayer&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;token annotation punctuation&#34;&gt;@Nonnull&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;Hand&lt;/span&gt; pHand&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;token annotation punctuation&#34;&gt;@Nonnull&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;BlockRayTraceResult&lt;/span&gt; pHit&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;&amp;#123;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;5&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;span class=&#34;token keyword&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token operator&#34;&gt;!&lt;/span&gt;pLevel&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;isClientSide &lt;span class=&#34;token operator&#34;&gt;&amp;amp;&amp;amp;&lt;/span&gt; pHand &lt;span class=&#34;token operator&#34;&gt;==&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;Hand&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token constant&#34;&gt;MAIN_HAND&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;&amp;#123;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;6&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;        &lt;span class=&#34;token class-name&#34;&gt;GlassFluidPortTileEntity&lt;/span&gt; te &lt;span class=&#34;token operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token class-name&#34;&gt;GlassFluidPortTileEntity&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt; pLevel&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;getBlockEntity&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;pPos&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;7&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;        &lt;span class=&#34;token comment&#34;&gt;// 只有在多方块结构形成时，才希望读取内部储存的流体&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;8&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;        &lt;span class=&#34;token keyword&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;te &lt;span class=&#34;token operator&#34;&gt;!=&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;null&lt;/span&gt; &lt;span class=&#34;token operator&#34;&gt;&amp;amp;&amp;amp;&lt;/span&gt; te&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;isMachineAssembled&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;&amp;#123;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;9&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;            &lt;span class=&#34;token comment&#34;&gt;// 这里调用了我们之前写过的 getFluidText 方法&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;10&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;            pPlayer&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;sendMessage&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token keyword&#34;&gt;new&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;StringTextComponent&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;te&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;getFluidText&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;PowerfulTank&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token constant&#34;&gt;POWERFUL_TANK_UUID&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;11&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;            &lt;span class=&#34;token keyword&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;ActionResultType&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token constant&#34;&gt;SUCCESS&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;12&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;        &lt;span class=&#34;token punctuation&#34;&gt;&amp;#125;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;13&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;span class=&#34;token punctuation&#34;&gt;&amp;#125;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;14&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;span class=&#34;token keyword&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;super&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;use&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;pState&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; pLevel&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; pPos&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; pPlayer&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; pHand&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; pHit&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;15&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token punctuation&#34;&gt;&amp;#125;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;&lt;p&gt;&lt;img loading=&#34;lazy&#34; data-src=&#34;https://s2.loli.net/2022/01/26/ERlQo1hHd75akKn.png&#34; alt=&#34;Tank Overall&#34; /&gt;&lt;/p&gt;
&lt;p&gt;至此，我们已经完成了一个基于 ZeroCore 的立体型多方块结构。&lt;/p&gt;
&lt;hr /&gt;
&lt;p&gt;这里笔者还希望补充一些附加思考，读者可以选择性阅读：&lt;/p&gt;
&lt;p&gt;善于逻辑的读者可能看出，这里笔者的逻辑实现方式略显 Dirty，读者可能认为，FluidTank 这一成员不应是流体接口所拥有的，而是整个多方块结构 MultiblockTank 所应该拥有的。即拥有储存流体能力的并不应该是流体接口，而是整个多方块储罐。&lt;/p&gt;
&lt;p&gt;对于这一问题的答案，读者毋庸置疑是正确的，这一成员确实应该移至 MultiblockTank 中，这里笔者的确处理的并不是很妥当。但如若读者仅是单纯将它移动至 MultiblockTank，并且对 Capability 的处理是从 MultiblockTank 中获取 (可能需要是否为空的判断)，这样的行为也是不正确的。因为 Capability 的本意是某一方块具有某一能力，这一能力不应该从别处引用。如果读者这样尝试，会得到一个结果，当重新进入存档时，所有连接的管道均会断开，因为读取存档的时候，各个 Mod 获取我们流体接口的 Capability 是在多方块结构形成之前，这时我们无法获取到对应的 MultiblockTank 实例，也就无法获取到对应的 FluidTank (即 Capability 本身)。&lt;/p&gt;
&lt;p&gt;那对于这一问题如何处理呢，如果感兴趣的读者阅读极限反应堆代码可以了解到，原作者是使用一种 Forwarder 的想法来处理的，即其他模组访问到的不是 FluidTank 本身，而是一个 Forwarder，这个 Forwarder 负责处理对于多方块机器的连接。从逻辑角度考虑，对于我们的流体接口，他所含有的能力应该是 &lt;strong&gt;连接至多方块结构中的 FluidTank&lt;/strong&gt; 的能力，而不是具有 FluidTank 的能力，在逻辑层面编写一个 Forwarder 也是合理的，对此感兴趣的读者可以自行阅读，并且尝试改进笔者的代码。&lt;a href=&#34;https://github.com/ZeroNoRyouki/ExtremeReactors2/blob/master/src/main/java/it/zerono/mods/extremereactors/gamecontent/multiblock/reprocessor/part/ReprocessorFluidPortEntity.java&#34;&gt;其中一个例子&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;img loading=&#34;lazy&#34; data-src=&#34;https://s2.loli.net/2022/01/26/rehv8tI62gFfLc1.png&#34; alt=&#34;image.png&#34; /&gt;&lt;/p&gt;
&lt;p&gt;需要注意的是如果改进以后，FluidTank 的数据持久化就需要由我们的 MultiblockTank 来完成，可喜的是，MultiblockTank 也提供了对应的 syncDataFrom 和 syncDataTo 方法，这有助于我们储存多方块结构整体的数据，也就是这里我们需要储存的 FluidTank。&lt;/p&gt;
&lt;hr /&gt;
&lt;p&gt;这里笔者还希望添加一些上文例子中没有包含到的一些方法的补充：&lt;/p&gt;
&lt;p&gt;对于 MultiBlockController，还有一些相关的方法读者可能会用到&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;onPartAdded 当 Part 并入 Multiblock 的时候会被执行，如果读者用一个列表来储存很多 Part 以作备用，这里可以向列表中添加 Part&lt;/li&gt;
&lt;li&gt;onPartRemoved 同理，这里可以从列表中移除某些 Part&lt;/li&gt;
&lt;li&gt;onMachinePaused 因为一些特殊原因 (比如区块卸载) 导致多方块结构无法正常使用时，会调用这个方法&lt;/li&gt;
&lt;li&gt;onMachineRestored 区块重新加载时会调用这个方法&lt;/li&gt;
&lt;li&gt;onMachineDisassembled 当多方块结构被破坏时会调用这个方法 (例如爆炸或者被玩家挖掉)&lt;/li&gt;
&lt;li&gt;onAssimilate 当有另外一个结构与当前结构合并时，会调用这个方法，参数中会给出另外一个结构的 Controller, 开发者应该在这里接管对方的所有数据 (例如把对面的能量输入到自己里面)&lt;/li&gt;
&lt;li&gt;onAssimilated 与上一个相似的，如果自己被合并了，会调用这个方法，通常是清理一些之前的数据。这两个功能一般很少会用到，可以参考极限反应堆的相关代码&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;对于 MultiblockPart，读者可以自行查阅。&lt;/p&gt;
&lt;h2 id=&#34;小结与后记&#34;&gt;&lt;a class=&#34;anchor&#34; href=&#34;#小结与后记&#34;&gt;#&lt;/a&gt; 小结与后记&lt;/h2&gt;
&lt;p&gt;经过本节的学习，读者应该学会了如何利用 ZeroCore 快速建立一个属于自己的多方块结构，同时也了解了多方块的各个部件间是如何与主控类建立联系的，这种思路将始终贯穿各种方式开发的各种多方块结构中，相信读者阅读本文后，对进一步阅读并理解 IE 和容积储存等 Mod 多方块结构的设计思路有很大帮助。同时这里笔者也针对读者可能产生的想法进行了解读，这些问题和相应的解决方案也是笔者行文时产生并解决的。这里笔者始终希望读者明白，在模组开发的过程中，你所遇到的需求和解决方案并不总是前人总结好的经验，更多的是在阅读前人代码中自行理解和解读出来的。如何让读者尝试去解读，让读者有自己为自己写指南的能力，这正是笔者行文的初衷，也是之前举二反三系列的初衷。&lt;/p&gt;
&lt;p&gt;至此，读者可以自行根据需求开发自己的多方块结构，或者依靠本节的经验进一步深挖多方块结构的魅力。&lt;/p&gt;
&lt;p&gt;本文代码已开源：&lt;a href=&#34;https://github.com/TROU2004/PowerfulTank&#34;&gt;Github - Powerful Tank&lt;/a&gt;&lt;/p&gt;
&lt;h2 id=&#34;参考资料&#34;&gt;&lt;a class=&#34;anchor&#34; href=&#34;#参考资料&#34;&gt;#&lt;/a&gt; 参考资料&lt;/h2&gt;
&lt;p&gt;ExtremeReactors2: &lt;a href=&#34;https://github.com/ZeroNoRyouki/ExtremeReactors2&#34;&gt;https://github.com/ZeroNoRyouki/ExtremeReactors2&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Forge 能量系统简述: &lt;a href=&#34;https://www.mcbbs.net/forum.php?mod=viewthread&amp;amp;tid=1034965&#34;&gt;https://www.mcbbs.net/forum.php?mod=viewthread&amp;amp;tid=1034965&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Boson 1.16 Modding Tutorial: &lt;a href=&#34;https://boson.v2mcdev.com/&#34;&gt;https://boson.v2mcdev.com/&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Harbinger: &lt;a href=&#34;https://harbinger.covertdragon.team/&#34;&gt;https://harbinger.covertdragon.team/&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;CapacityStorage: &lt;a href=&#34;https://github.com/Phoupraw/CapacityStorage&#34;&gt;https://github.com/Phoupraw/CapacityStorage&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;ZERONOMODS (过时): &lt;a href=&#34;http://zerono.it/zerocore-multiblock-api-tutorial/&#34;&gt;http://zerono.it/zerocore-multiblock-api-tutorial/&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;ZeroTest (过时): &lt;a href=&#34;https://github.com/ZeroNoRyouki/ZeroTest&#34;&gt;https://github.com/ZeroNoRyouki/ZeroTest&lt;/a&gt;&lt;/p&gt;
</content>
        <category term="Minecraft" />
        <category term="模组开发" />
        <updated>2022-01-26T03:19:59.000Z</updated>
    </entry>
    <entry>
        <id>https://turou.fun/about/</id>
        <title>关于我和本站</title>
        <link rel="alternate" href="https://turou.fun/about/"/>
        <content type="html">&lt;h2 id=&#34;关于我&#34;&gt;&lt;a class=&#34;anchor&#34; href=&#34;#关于我&#34;&gt;#&lt;/a&gt; 关于我&lt;/h2&gt;
&lt;p&gt;软件工程专业大三在读，目前主要在开发 UsagiCard 兔卡系列 NFC 卡片。曾接触 Unity 开发，对 VR/AR/MR 领域开发略有涉及。目前打算是精进前后端技术的同时继续挖掘自己熟悉的领域市场的空缺&lt;/p&gt;
&lt;p&gt;对全栈或者 UnityVR 相关领域感兴趣的可以来找我聊。其他的领域当然也欢迎，Galgame 什么的 (逃.&lt;/p&gt;
&lt;h2 id=&#34;关于本站&#34;&gt;&lt;a class=&#34;anchor&#34; href=&#34;#关于本站&#34;&gt;#&lt;/a&gt; 关于本站&lt;/h2&gt;
&lt;p&gt;之前断断续续写过很多文章，再加上买了准备持久用下去的域名，因此开设了这个博客。旨在整理之前在各个平台发过的文章做一个归档，同时也可以写些自己的感受.&lt;/p&gt;
&lt;p&gt;之后可能会逐步将个人 OneNote 中的一些内容搬到博客上来.&lt;/p&gt;
&lt;h2 id=&#34;我的联系方式&#34;&gt;&lt;a class=&#34;anchor&#34; href=&#34;#我的联系方式&#34;&gt;#&lt;/a&gt; 我的联系方式&lt;/h2&gt;
&lt;p&gt;你可以在 Github 等社交平台直接联系到我，也可以使用下面这些即时聊天方式.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;QQ: 1822974018&lt;/li&gt;
&lt;li&gt;Wechat: trou2004&lt;/li&gt;
&lt;li&gt;Discord: truerou&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;同时非常欢迎与我交换友链，或者共同探讨感兴趣的话题.&lt;/p&gt;
&lt;p&gt;你也可以加入催更群，鞭策我不要咕咕，群号: 439076839&lt;/p&gt;
</content>
        <updated>2021-12-31T16:00:00.000Z</updated>
    </entry>
    <entry>
        <id>https://turou.fun/friends/</id>
        <title>友情链接</title>
        <link rel="alternate" href="https://turou.fun/friends/"/>
        <content type="html">&lt;h2 id=&#34;友情链接&#34;&gt;&lt;a class=&#34;anchor&#34; href=&#34;#友情链接&#34;&gt;#&lt;/a&gt; 友情链接&lt;/h2&gt;
&lt;p&gt;&lt;div class=&#34;links&#34;&gt;&lt;div class=&#34;item&#34; title=&#34;Kaitaku&#34; style=&#34;--block-color:#00BFFF;&#34;&gt;&lt;a href=&#34;https://www.kaitaku.xyz&#34; class=&#34;image&#34; data-background-image=&#34;https://www.kaitaku.xyz/assets/avatar.webp&#34;&gt;&lt;/a&gt;
        &lt;div class=&#34;info&#34;&gt;
        &lt;a href=&#34;https://www.kaitaku.xyz&#34; class=&#34;title&#34;&gt;望月阁&lt;/a&gt;
        &lt;p class=&#34;desc&#34;&gt;一个萌新的学习笔记&lt;/p&gt;
        &lt;/div&gt;&lt;/div&gt;&lt;div class=&#34;item&#34; title=&#34;0o酱&#34; style=&#34;--block-color:#F47F2C;&#34;&gt;&lt;a href=&#34;https://blog.im0o.top/&#34; class=&#34;image&#34; data-background-image=&#34;https://image.im0o.top/files/202112021204213.jpg&#34;&gt;&lt;/a&gt;
        &lt;div class=&#34;info&#34;&gt;
        &lt;a href=&#34;https://blog.im0o.top/&#34; class=&#34;title&#34;&gt;Hello I&#39;m 0o酱&lt;/a&gt;
        &lt;p class=&#34;desc&#34;&gt;光阴如梦，昨日随风&lt;/p&gt;
        &lt;/div&gt;&lt;/div&gt;&lt;div class=&#34;item&#34; title=&#34;FrZ&#34; style=&#34;--block-color:#A50D00;&#34;&gt;&lt;a href=&#34;https://blog.frzmeow.cc/&#34; class=&#34;image&#34; data-background-image=&#34;https://blog.frzmeow.cc/avatar.jpg&#34;&gt;&lt;/a&gt;
        &lt;div class=&#34;info&#34;&gt;
        &lt;a href=&#34;https://blog.frzmeow.cc/&#34; class=&#34;title&#34;&gt;FrZ&#39;s Blog&lt;/a&gt;
        &lt;p class=&#34;desc&#34;&gt;我瞎写的来看什么？&lt;/p&gt;
        &lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/p&gt;
</content>
        <updated>2021-12-31T16:00:00.000Z</updated>
    </entry>
    <entry>
        <id>https://turou.fun/rambling/gal-character-varities/</id>
        <title>浅谈人物形象的变化在视觉小说中的表现形式</title>
        <link rel="alternate" href="https://turou.fun/rambling/gal-character-varities/"/>
        <content type="html">&lt;p&gt;首先笔者介绍一下本文的写作来由，对于当今的文学摄取者，我们已经接受了足够多的经典文学形式，对于任何一名当代高中生来说，谈起散文、小说、戏剧即使不能十分精通，至少也能做到 “夸夸其谈”，但随着社会的发展，许多新兴的文学形式也有所显露并且也广泛被人们接受，但对于我们高中生来说却少有耳闻或者抱有误解，这里笔者的选题为 “视觉小说” 也正是这个原因。视觉小说是一种非常棒的文化载体，既能通过剧本精致的文笔展现文学的魅力，又有精美直观的画面，还有能强化故事氛围的音乐和加深人物塑造的语音。这正是其突破经典小说，逐渐登上当代文学舞台的部分原因。&lt;/p&gt;
&lt;p&gt;经典小说的核心就是人物形象推动情节发展，而视觉小说的本质也同样是小说，也有相同的特点，二者在某种程度上具有一定的相似性与同质性。这里笔者站在视觉小说这一当今流行的文学表现手法的角度，从存在性、实践性、优越性三个方面具体谈一谈人物形象的变化过程这一主题。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;注：视觉小说广义上的定义是在电子萤幕上阅读的小说。使用图片和音乐来让观众从中获取资讯，笔者这里的视觉小说主要包含冒险游戏的领域，这种类型的游戏通常游戏的成分不多，玩游戏和在电脑萤幕上看小说很相似。视觉小说有时会被归类于冒险游戏之中，二者并没有很明确的区别。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id=&#34;人物形象变化的存在性&#34;&gt;&lt;a class=&#34;anchor&#34; href=&#34;#人物形象变化的存在性&#34;&gt;#&lt;/a&gt; 人物形象变化的存在性&lt;/h2&gt;
&lt;p&gt;人物形象的变化广泛存在于各类文学作品中，如果对于小说中的某一人物，他出场时的形象一直在全篇小说的情节发展中体现并且直接导致结局的产生，我们认为这一人物的形象没有发生变化。如果小说中的人物在出场时是一个形象，在故事情节的不断推进中，人物的形象因某种原因发生了转变，并且这个转变直接或间接导致了结局的产生，我们认为这一人物的形象发生了变化。&lt;/p&gt;
&lt;p&gt;当然，这种变化可能是有形的或者无形的。对于人物形象有形的变化，例如《euphoria》中的主人公，在世界的伦理观崩塌之后，人性的恶的一面得到解放，人物形象发生了巨大的转变，这种转变来源于有形，表现于有形。与此相似，来源于有形的变化还包括例如亲友亡故、受到挫折，或者自己生活的环境发生巨大的改变等等。许多小说作者利用这种方式来实现人物形象的转变，这也是最为常见，应用非常广泛的手法。对于读者来说，这种人物形象的变化会被读者立即察觉，与小说中的人物产生共情，受到感染。&lt;/p&gt;
&lt;p&gt;而无形的变化常常体现在人物的心理、语言、动作与往常不同中，读者一般会在一种轻微的违和感中体会到人物形象发生的转变，而这种转变通常也没有确切的关键性事件触发，甚至读者直到阅读至小说的结尾才后知后觉，甚至没有察觉。这种手法经常应用于推理、悬疑小说中，表面上是增添了故事的悬疑色彩或者推理要素，但通常知道真相的读者往往发现，转变的人物形象可能才是导致这个结局发生的关键性因素，或者说转变的人物形象可能才是推理过程中中最为重要的一个 “证据”，这种巧妙的搭配方式不会让读者直接受到感染，更多的是在结局时读者纵观全局时感受到的一种深邃感和真相大白的满足感，这也使之成为了提高小说深度的一种方式之一，当然对于作家和读者的要求比较高。&lt;/p&gt;
&lt;p&gt;这里还需要讨论的就是一些不属于人物形象变化的情况，首先，人物的情感变化本身并不能归为人物的形象变化范畴，举个例子就是天气和气候的关系，只有情感变化从瞬时的 “天气” 范畴推进到规律性的 “气候” 范畴，才能说是人物形象发生了改变，但是这种情况并不多见，因为情感的变化往往很难对一个人的人格造成根本性的影响，通常情况下情感的变化一般用来推动情节发展，而不是成为情节的决定性因素。&lt;/p&gt;
&lt;p&gt;人类的一些本能也不属于人物形象变化，假如人处在危急情况时，可能会暴露出求生的本能（例如海事惯例），或者说面临绝境时的应激反应、走投无路的选择，可能被误理解为人物形象的变化。那在这种情况下怎样算是人物形象的变化呢，例如《骆驼祥子》中，最后走投无路的祥子就发生了剧烈的人物形象变化，这里同时也将故事情节导向高潮，这里的祥子属于我们上文提到过的来源于有形，表现于有形的变化。很明显祥子也是面临绝境，有人评价祥子是在极度绝望中扭曲了灵魂堕落成了走兽，他与我们提到的例外中的情形不同，这并不是一种应激反应，而是祥子的本身造就而成，祥子因家庭原因，心理和性格有缺陷，这也是悲剧产生的一种原因，也是人物形象能够变化的根本原因，这是与我们所提到的例外不同的。&lt;/p&gt;
&lt;p&gt;再分析祥子的人物形象转变，祥子转变为了行尸走肉，而这种行尸走肉导向了故事的结局，一个彻彻底底的悲剧，这同时印证了我们上文提到过的，人物形象的变化通常会成为造成故事结局的关键性因素。&lt;/p&gt;
&lt;h2 id=&#34;人物形象变化的实践性&#34;&gt;&lt;a class=&#34;anchor&#34; href=&#34;#人物形象变化的实践性&#34;&gt;#&lt;/a&gt; 人物形象变化的实践性&lt;/h2&gt;
&lt;p&gt;直至今天，这种人物形象变化存在于各类作品中，并且被广泛实践和应用，但人物形象的变化这一手法是无法连续应用的，很明显，假使一个小说的核心人物的性格常常发生巨大的变化，读者会对作品中的人物的精神健康表示深深的担忧，所以大多文学作品中常常用这种方式将情节引向高潮，进而谢幕，而几乎不反复改变一个核心人物的形象。&lt;/p&gt;
&lt;p&gt;这里笔者希望介绍一部作品，它正打破了我们的这种想法 — 人物形象变化的手法无法连续应用。这部作品把这一手法应用到全局，即《纸上的魔法使》。&lt;/p&gt;
&lt;p&gt;这里笔者简单介绍一下这部作品为何成为人物形象变化的实践性的最好例证。人物形象无法连续变化，但如果将人物形象付托于故事，那么会有怎样的变化呢，这里读者不妨联想一下现实生活中的 “剧本”，剧本本身记录着的是他人的故事，而饰演剧本中角色的演员则被赋予了剧本中角色的人物形象，这可否理解成是 “演员的人物形象发生了变化呢”，这里《纸上的魔法使》巧妙的利用了这一点。小说中出现的人物会成为剧本中的角色，而将剧本演绎的就是 “魔法书”，当魔法书翻开，主人公们就会在魔法书的引领下不由自主成为剧中人，演绎魔法书中的故事。&lt;/p&gt;
&lt;p&gt;人物的形象会在故事的范围内产生变化，而这种变化是怎样的呢，人物的心理、语言、动作与往常有略微的不同，读者轻微的违和感中体会到人物形象发生的转变，这正是我们此前介绍过的无形的变化。这部作品作为一个悬疑推理作品，正是以这种方式一点一点将人物的内心暴露出来的，这正是我们下文要介绍的人物形象变化的优越性的一部分，即多面性。&lt;/p&gt;
&lt;p&gt;魔法书面向的是单独个体的视点，而魔法书引领主人公们的人物形象变化固然会引起读者视点的变化，故事情节正是在这种谎言与故意设计的局限性里面不断推进的。&lt;/p&gt;
&lt;p&gt;这里笔者引用《叙事的艺术》中对于这个作品中一幕的描写，女主人公夜子的形象发生了略微的转变，这是一个契机。夜子因自己白色的皮肤被排挤，对自己抱有自卑的心理，她是怎样发生变化的呢？&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;第二章中，夜子的妈妈翻开了魔法书，魔法书上演绎了角色享受快乐校园生活的故事。夜子极度排斥学校生活，把故事当作是夜子的视点，那么这个故事就暗喻了她内心深处想跟朋友们一起去学校共渡青春的想法，夜子难道没有这种想法吗？ 当然有。而事实上这个故事确是她妈妈掀开的，那么她妈妈的目的是什么呢？是希望自己的孩子跟朋友们一起共渡青春吗？当然也有，但是最后的最后，揭开谜底，才会发现，原来这本书的存在本身就是虚构的，并不存在，只是夜子的妈妈为了让夜子走出图书馆的一点点狡猾的努力。你看随着视点的变化，整个故事的内涵层层递进，把几个角色的内心都表达出来了，而且不是生硬的表达， 是跟读者一起探索一起感受的表达，十分的高级。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;正是在每一本魔法书引导的人物形象变化中，小说最后的结局渐渐清晰。这是无形的变化累加的结果，希望读者也能在今后的写作中对这种手法加以利用。&lt;/p&gt;
&lt;h2 id=&#34;人物形象变化的优越性&#34;&gt;&lt;a class=&#34;anchor&#34; href=&#34;#人物形象变化的优越性&#34;&gt;#&lt;/a&gt; 人物形象变化的优越性&lt;/h2&gt;
&lt;p&gt;人物形象变化在各类作品中频繁出现，显然证明了这种手法具有一定的优越性。&lt;/p&gt;
&lt;p&gt;在推动情节发展的方面，这种方式比起单纯的矛盾推动情节，显得更加多元化和戏剧性，读者往往将自己的情感寄托于小说中的某个人物，当人物的性格发生某种程度的变化时，读者必然会因此产生情感的连通效应，使矛盾更加突出，情节发展更加跌宕。&lt;/p&gt;
&lt;p&gt;在设置悬念方面，正如我们先前所说，在悬疑推理小说中利用人物形象变化暗示情节、甚至导引结局的使用已经并不少见，首先这种手段更适合铺设多线索主题，明线是故事情节，而暗线可以铺设为人物形象的变化。而且同时也可以与上文介绍过的《纸上的魔法使》类似，用人物形象变化来描绘角色的内心世界，这种手法显然也是及其高明的。&lt;/p&gt;
&lt;p&gt;在结构设计方面，人物形象变化的设计常常容易落入俗套，“女主角车祸，男主角崩溃” 这种韩剧式的设计已经让我们审美疲劳，这种情况下更需要创新式的手法的提出，例如在人物形象变化的设计上套用双层剧本、或者我们上文介绍过的多次变化已经是这种手法的优秀发展的例子，人物形象变化的优越性本质上需要极强的结构设计能力和情节把握能力，这考验着作家的综合能力，为了使这种优越性在新一代小说、剧本中有所发扬，创新是必不可少的，也是笔者希望阅读完这篇文章的读者有所启迪和实践的。&lt;/p&gt;
&lt;h2 id=&#34;后记&#34;&gt;&lt;a class=&#34;anchor&#34; href=&#34;#后记&#34;&gt;#&lt;/a&gt; 后记&lt;/h2&gt;
&lt;p&gt;笔者认为兴趣驱动是完成并且完成好每一项任务中必不可少的部分，也是最为难得的部分，本文介绍的两部作品《euphoria》《纸上的魔法使》都是在假期时间阅读的两部视觉小说，这次的这项作业有机会让笔者以文学和较为专业的角度评价自己兴趣所在的部分，感觉十分的开心，意义当然是有的，笔者也不是第一次写这种综述类型的文章，在遣词造句上也略有经验，这也是笔者能够用这种较为正式和议论性的语言写出这篇文章的原因。有的时候，将自己所感用语言文字表达出来真的很难，在此基础上加以评论也不是一件易事，但笔者认为本文的确已经将自己对于人物形象变化的理解诠释的淋漓尽致了，当然站在视觉小说的角度的理解可能过于片面和理想化，希望能够有所指正。&lt;/p&gt;
&lt;h2 id=&#34;参考资料&#34;&gt;&lt;a class=&#34;anchor&#34; href=&#34;#参考资料&#34;&gt;#&lt;/a&gt; 参考资料&lt;/h2&gt;
&lt;p&gt;叙事的艺术: &lt;a href=&#34;http://bangumi.tv/blog/274100&#34;&gt;http://bangumi.tv/blog/274100&lt;/a&gt;&lt;br /&gt;
 百度百科，视觉小说条目: &lt;a href=&#34;https://baike.baidu.com/item/%E8%A7%86%E8%A7%89%E5%B0%8F%E8%AF%B4/7283954&#34;&gt;https://baike.baidu.com/item/ 视觉小说 / 7283954&lt;/a&gt;&lt;br /&gt;
 知乎回答，如何评价纸上的魔法使: &lt;a href=&#34;https://www.zhihu.com/question/54143243&#34;&gt;https://www.zhihu.com/question/54143243&lt;/a&gt;&lt;br /&gt;
euphoria 剧情及人物解析: &lt;a href=&#34;https://www.bilibili.com/read/cv6293955&#34;&gt;https://www.bilibili.com/read/cv6293955&lt;/a&gt;&lt;br /&gt;
 骆驼祥子中祥子的形象: &lt;a href=&#34;https://m.xzbu.com/1/view-14800008.htm&#34;&gt;https://m.xzbu.com/1/view-14800008.htm&lt;/a&gt;&lt;br /&gt;
 如何推动故事情节发展: &lt;a href=&#34;https://zhidao.baidu.com/question/686481533223841812.html&#34;&gt;https://zhidao.baidu.com/question/686481533223841812.html&lt;/a&gt;&lt;br /&gt;
 百度百科，文献综述格式: &lt;a href=&#34;https://baike.baidu.com/item/%E6%96%87%E7%8C%AE%E7%BB%BC%E8%BF%B0%E6%A0%BC%E5%BC%8F/16340733&#34;&gt;https://baike.baidu.com/item/ 文献综述格式 / 16340733&lt;/a&gt;&lt;/p&gt;
</content>
        <category term="Galgame" />
        <category term="杂谈" />
        <updated>2021-08-20T03:19:59.000Z</updated>
    </entry>
    <entry>
        <id>https://turou.fun/minecraft/legacy-render-tutor/</id>
        <title>从实例的角度出发浅谈TileEntity渲染</title>
        <link rel="alternate" href="https://turou.fun/minecraft/legacy-render-tutor/"/>
        <content type="html">&lt;p&gt;许多模组开发者不单单希望自己创造出来的一个个方块有着非凡的功能，也希望他们能拥有美观的外表，笔者曾经也有过这种想法，而面对国内众多的开发教程，大多对 TESR 闭而不淡或浅尝辄止，而这种现象也是很好理解并体谅的。许多教程的编写者具备 OpenGL 的知识，但这种理论和原理并不是三言两语可以解释清楚的，也不适合在以 Minecraft 模组开发为核心的教程中插入过多的 OpenGL 理论知识，这可能会打击读者的信心，或使读者丧失兴趣&lt;/p&gt;
&lt;p&gt;笔者曾经也是众多读者中的一个，直至现在，也仅仅是满足上文条件中的 ** 了解 OpenGL 的读音和拼写 **. 所以本文的写作缘由也因此而生，笔者希望基于 Minecraft 模组开发，介绍简单的渲染知识，实现 TESR 简单的一些效果。而在这个过程中希望读者可以和笔者一起，从实例的角度出发，共同探究 TESR 的渲染方式，同时对 OpenGL 的渲染有简单的了解，启发读者的兴趣.&lt;/p&gt;
&lt;h2 id=&#34;本文的受众对象&#34;&gt;&lt;a class=&#34;anchor&#34; href=&#34;#本文的受众对象&#34;&gt;#&lt;/a&gt; 本文的受众对象&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;对于 Minecraft 和 Minecraft Forge 开发有较为进阶的了解&lt;/li&gt;
&lt;li&gt;对实例代码有一定的理解和分析能力&lt;/li&gt;
&lt;li&gt;简要了解 Minecraft 模型和材质&lt;/li&gt;
&lt;li&gt;了解 OpenGL 的读音和拼写&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;介绍tesr&#34;&gt;&lt;a class=&#34;anchor&#34; href=&#34;#介绍tesr&#34;&gt;#&lt;/a&gt; 介绍 TESR&lt;/h2&gt;
&lt;p&gt;先说说为什么要引入 TESR. Minecraft 1.8 以后，相信读者也对此有所了解，Mojang 引入了一套模型系统，之后我们的 Mod 开发过程中经常会有这样的操作：将贴图放在一个特定的目录 (texture), 用模型 (model) 来定义贴图的呈现方式，再以代码的形式将物品绑定到模型上。我们一直以来以这样的方式为我们的物品和方块添加贴图.&lt;/p&gt;
&lt;p&gt;但如若我们要根据游戏内的具体情况 (比如物品内部的 NBTTag, 或者方块的 TileEntity 数据), 对物品或方块的渲染有所干预呢，这时候静态的模型文件 (.json) 就不管用了，我们需要引入一种特殊的渲染方式。对于物品来说，读者可以参见笔者之前的文章，利用 ItemMeshDefinition 来实现。而对于方块来说，我们可以使用 TileEntitySpecialRenderer 来实现&lt;/p&gt;
&lt;p&gt;TileEntitySpecialRenderer (下文简称为 TESR) 顾名思义，是基于 TileEntity 数据的一种特殊渲染器。其特殊之处，便是我们可以随心所欲的定制化这种渲染的方式，当 TileEntity 刷新时，Minecraft 会调用我们事先设计好的渲染方式来渲染整个方块，而我们要做的，便是利用手中的一个完整的 TileEntity 数据来完成中间渲染的所有操作。而这种对于模型的特殊操作，是靠近底层的&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;这些模型有一个共同特点：它们都在某种意义上靠近底层。   -Harbinger (11.4 TESR)&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;而这种底层，即 Minecraft 的渲染器 ——OpenGL&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;By default OpenGL (via  &lt;code&gt;GlStateManager&lt;/code&gt; ) is used to handle rendering in a TESR. See the OpenGL documentation to learn more. -FORGE docs&lt;/p&gt;
&lt;p&gt;通常来说，需要 OpenGl (通过 GlStateManager 固定管线) 来处理 TESR 的渲染，这部分需要查阅 OpenGL 的文档来了解&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id=&#34;一-谈谈管线&#34;&gt;&lt;a class=&#34;anchor&#34; href=&#34;#一-谈谈管线&#34;&gt;#&lt;/a&gt; 一、谈谈管线&lt;/h2&gt;
&lt;p&gt;相信读者对 TESR 的简单了解中，大概清晰了思路，我们需要调用 OpenGL 的底层方法来完成一个方块的渲染工序。作为我们的显卡 GPU, 所有渲染是以管线的方式进行的，说起管线，我们常将其比作流水线。我们将一堆数据塞进去，经过一些流水线处理，处理出一帧帧画面。在 Minecraft 世界中，也就是我们玩家充当观察者角色所能看到的一个个方块，实体.&lt;/p&gt;
&lt;p&gt;对于我们开发者而言，我们的任务很清晰，我们只需编排好渲染方块的流水线就可以了。而这些，都包含在这个方块对应 TESR 类的一个 render 方法中。我们只需事先准备好一段固定的 render 方法。来让 Minecraft 渲染器在需要渲染的时候不断调用 render () 就可以了，这也被称为是固定管线.&lt;/p&gt;
&lt;p&gt;固定管线与可编程渲染管线相对应，固定管线可以从字面理解，管线中渲染的方式是完全固定的，所以我们可能会看到这样的伪代码:&lt;/p&gt;
&lt;figure class=&#34;highlight java&#34;&gt;&lt;figcaption data-lang=&#34;java&#34;&gt;&lt;/figcaption&gt;&lt;table&gt;&lt;tr&gt;&lt;td data-num=&#34;1&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token keyword&#34;&gt;class&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;MyTESR&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;&amp;#123;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;2&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;span class=&#34;token function&#34;&gt;render&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;&amp;#123;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;3&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;        &lt;span class=&#34;token class-name&#34;&gt;GL&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;RenderALight&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;x1&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; y1&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; z1&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;4&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;        &lt;span class=&#34;token class-name&#34;&gt;GL&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;RenderBLight&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;x2&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; y2&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; z2&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;5&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;        &lt;span class=&#34;token class-name&#34;&gt;GL&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;RenderCShadow&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;x3&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; y3&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; z3&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;6&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;span class=&#34;token punctuation&#34;&gt;&amp;#125;&lt;/span&gt; &lt;span class=&#34;token comment&#34;&gt;// 很容易看出我们是按照一段固定的逻辑渲染 ALight BLight CShadow 的&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;7&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token punctuation&#34;&gt;&amp;#125;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;8&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;9&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token keyword&#34;&gt;class&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;Minecraft&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;&amp;#123;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;10&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;span class=&#34;token class-name&#34;&gt;RenderTask&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;&amp;#123;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;11&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;        &lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token comment&#34;&gt;//Render Trees&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;12&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;        myTESR&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;render&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;13&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;        &lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token comment&#34;&gt;//Render Waters&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;14&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;        &lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token comment&#34;&gt;//Render Items&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;15&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;span class=&#34;token punctuation&#34;&gt;&amp;#125;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;16&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token punctuation&#34;&gt;&amp;#125;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;&lt;p&gt;我们在今后的渲染中，需要填充 render 方法的内容。而其他事情，就尽管交给 Minecraft 和 Forge 吧.&lt;/p&gt;
&lt;p&gt;但是这里还要插入一个注意事项：我们操作的所谓 &amp;quot;OpenGL&amp;quot;(例如下文中的 GL11) 是一个全局的操作 (状态机), 所以我们对全局量进行一些操作后，记得复原他的状态。例如下文的伪代码:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;......
开发者: 喂, OpenGL, 我要渲染了, 先保存你原来的状态.
OpenGL: 了解, 原来状态已保存, 你可以开始渲染工作了
开发者: xxoo渲染就交给你了
OpenGL: xxoo渲染已完成(我的里面已经变成了你的形状, 已经处理不了别的任务了)
开发者: 喂, OpenGL, 请读取你之前的状态继续工作吧
OpenGL: 好的(满血复活, 继续别的工作)
......
这就是OpenGL上下文
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;到这里为止，单纯的理论知识部分就已经结束啦，下面跟随读者一起从实例的角度出发，探究这种工序的具体编写方法吧.&lt;/p&gt;
&lt;h2 id=&#34;二-从简单实例入手&#34;&gt;&lt;a class=&#34;anchor&#34; href=&#34;#二-从简单实例入手&#34;&gt;#&lt;/a&gt; 二、从简单实例入手&lt;/h2&gt;
&lt;p&gt;先看下面一个例子，我们需要用到 TESR 吗？&lt;/p&gt;
&lt;p&gt;我们有一个饼干罐，当我们右键点击的时候可以向里面存入曲奇饼，并且罐子内部会逐渐充满曲奇饼的贴图，我们也可以空手右键取出曲奇饼，罐子内部会相应减少曲奇饼的数量.&lt;/p&gt;
&lt;p&gt;&lt;img loading=&#34;lazy&#34; data-src=&#34;https://i.loli.net/2021/01/31/MQr3ojCsBul1HTU.png&#34; alt=&#34;cookie.png&#34; /&gt;&lt;/p&gt;
&lt;p&gt;答案是肯定的，饼干罐一定有自己的 TileEntity 储存内部的饼干数量，并且会有一个 TESR 指导渲染工作，于是我们可以找到如下代码&lt;/p&gt;
&lt;figure class=&#34;highlight java&#34;&gt;&lt;figcaption data-lang=&#34;java&#34;&gt;&lt;/figcaption&gt;&lt;table&gt;&lt;tr&gt;&lt;td data-num=&#34;1&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;class&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;CookieRenderer&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;extends&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;TileEntitySpecialRenderer&lt;/span&gt;&lt;span class=&#34;token generics&#34;&gt;&lt;span class=&#34;token punctuation&#34;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&#34;token class-name&#34;&gt;TileEntityCookieJar&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;&gt;&lt;/span&gt;&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;&amp;#123;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;2&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;span class=&#34;token annotation punctuation&#34;&gt;@Override&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;3&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;span class=&#34;token keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;void&lt;/span&gt; &lt;span class=&#34;token function&#34;&gt;render&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token class-name&#34;&gt;TileEntityCookieJar&lt;/span&gt; cookieJar&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;double&lt;/span&gt; x&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;double&lt;/span&gt; y&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;double&lt;/span&gt; z&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;float&lt;/span&gt; partialTicks&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;int&lt;/span&gt; destroyStage&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;float&lt;/span&gt; alpha&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;&amp;#123;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;4&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;        &lt;span class=&#34;token comment&#34;&gt;//Render work&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;5&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;span class=&#34;token punctuation&#34;&gt;&amp;#125;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;6&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token punctuation&#34;&gt;&amp;#125;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;7&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token comment&#34;&gt;/*&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;8&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;仓库地址: https://github.com/MrCrayfish/MrCrayfishFurnitureMod/&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;9&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;节选自: /1.12.2/src/main/java/com/mrcrayfish/furniture/render/tileentity/CookieRenderer.java&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;10&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;*/&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;&lt;p&gt;不出我们所料，果然有一个 render 方法负责渲染整个方块，但细心的读者可能发现，里面的渲染工作似乎与我们之前所描述的过程有一些区别，我们可以看到代码中是围绕曲奇饼的 EntityItem 进行渲染，但我们并没有发现这个玻璃罐子的渲染。这里其实是因为 TileEntitySpecialRender 中强调了 Special 一词，这也就表面我们只需渲染其特殊的一部分就可以了，静态的罐子并不特殊，他是以普通的模型 -&amp;gt; 材质的方式来定义的，所以不在 TESR 渲染的范围内.&lt;/p&gt;
&lt;p&gt;接下来我们来具体看我们省略的 Render work 部分.&lt;/p&gt;
&lt;figure class=&#34;highlight java&#34;&gt;&lt;figcaption data-lang=&#34;java&#34;&gt;&lt;/figcaption&gt;&lt;table&gt;&lt;tr&gt;&lt;td data-num=&#34;1&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token constant&#34;&gt;GL11&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;glPushMatrix&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt; &lt;span class=&#34;token comment&#34;&gt;// 保存变换前的位置和角度&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;2&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;span class=&#34;token comment&#34;&gt;//Some Code&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;3&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token constant&#34;&gt;GL11&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;glPopMatrix&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt; &lt;span class=&#34;token comment&#34;&gt;// 读取变换前的位置和角度 (恢复原状)&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;&lt;p&gt;根据上文我们提到的 OpenGL 状态机和 OpenGL 上下文的特点，相信读者不难想出为什么这两行代码是成对出现的。其实，这两句代码就是我们上文提到的一个保存和读取的功能，而这里，我们保存和读取的是矩阵 (这里可以看做是 Minecraft 世界中的坐标). 我们先保存之前的坐标状态，然后在中间的渲染步骤中随意的变更，操作坐标，最后读取回原来的状态，渲染顺利进行的同时不会破坏 OpenGL 原有的工作.&lt;/p&gt;
&lt;figure class=&#34;highlight java&#34;&gt;&lt;figcaption data-lang=&#34;java&#34;&gt;&lt;/figcaption&gt;&lt;table&gt;&lt;tr&gt;&lt;td data-num=&#34;1&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token constant&#34;&gt;GL11&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;glDisable&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token constant&#34;&gt;GL11&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token constant&#34;&gt;GL_LIGHTING&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;2&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;span class=&#34;token comment&#34;&gt;//Some Code&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;3&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token constant&#34;&gt;GL11&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;glEnable&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token constant&#34;&gt;GL11&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token constant&#34;&gt;GL_LIGHTING&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;&lt;p&gt;这两句代码也是成对出现的，按照字面意思，是先关闭了 OpenGL 的光线，又打开了 OpenGL 的光线，这也就意味着我们的渲染过程中不需要光线 (希望 GL_LIGHTING 处于关闭状态), 而完成后为了恢复状态，再一次开启了 GL_LIGHTING, 这也符合我们所介绍的状态机的设定。而这里为什么我们的渲染过程要关闭 GL_LIGHTING 呢，这是因为我们的渲染过程中没有光源，我们不希望 OpenGL 帮我们进行光源的演算和渲染，如果没有光源强行渲染，会出现很黑的情况 (没有光源当然很黑嘛). 所以我们索性直接关掉光线演算，避免这个问题，详情请查看鸣谢部分的具体解读.&lt;/p&gt;
&lt;p&gt;好，状态保存的问题已经解决了，接下来就该轮到我们大搞特搞地来渲染具体物品的环节了，而这时我们看到了 translate 语句.&lt;/p&gt;
&lt;figure class=&#34;highlight java&#34;&gt;&lt;figcaption data-lang=&#34;java&#34;&gt;&lt;/figcaption&gt;&lt;table&gt;&lt;tr&gt;&lt;td data-num=&#34;1&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token constant&#34;&gt;GL11&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;glTranslatef&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token keyword&#34;&gt;float&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt; x &lt;span class=&#34;token operator&#34;&gt;+&lt;/span&gt; &lt;span class=&#34;token number&#34;&gt;0.5F&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token keyword&#34;&gt;float&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt; y &lt;span class=&#34;token operator&#34;&gt;+&lt;/span&gt; &lt;span class=&#34;token number&#34;&gt;0.05F&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token keyword&#34;&gt;float&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt; z &lt;span class=&#34;token operator&#34;&gt;+&lt;/span&gt; &lt;span class=&#34;token number&#34;&gt;0.18F&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt; &lt;span class=&#34;token comment&#34;&gt;// 设置 &#34;原点&#34;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;2&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;3&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token class-name&#34;&gt;GlStateManager&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;translate&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token number&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;token operator&#34;&gt;-&lt;/span&gt;&lt;span class=&#34;token number&#34;&gt;0.1&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;token number&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt; &lt;span class=&#34;token comment&#34;&gt;// 沿 y 轴负方向移动 0.1 个单位&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;&lt;p&gt;&lt;img loading=&#34;lazy&#34; data-src=&#34;https://i.loli.net/2021/01/31/9qphezAWxMIcK1D.png&#34; alt=&#34;20190415170233683.png&#34; /&gt;&lt;/p&gt;
&lt;p&gt;相信读者看懂这张图之后，translate 的用途问题不攻自破了，其实它就相当于一个绝对坐标转换为相对坐标的操作，方便我们接下来对这个方块的具体位置进行一定的操作做铺垫工作。而 translate 提供的参数，将作为新坐标的原点位置，而为了让这个原点不停留在方块边缘，对方块坐标进行加减运算的用途不言而喻.&lt;/p&gt;
&lt;p&gt;当开发者需要对当前操作坐标进行旋转变换时，可能需要多次进行 translate 操作，相信读者可以在之后的工作中逐步理解这个概念。而这里，我们猜测 MrCrayfish 是将渲染原点定在了靠近罐子中心底部的位置，以便进行接下来的操作.&lt;/p&gt;
&lt;figure class=&#34;highlight java&#34;&gt;&lt;figcaption data-lang=&#34;java&#34;&gt;&lt;/figcaption&gt;&lt;table&gt;&lt;tr&gt;&lt;td data-num=&#34;1&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token constant&#34;&gt;GL11&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;glRotatef&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token number&#34;&gt;180&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;token number&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;token number&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;token number&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt; &lt;span class=&#34;token comment&#34;&gt;// 沿着 y 轴和 z 轴方向旋转 180 度&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;&lt;p&gt;之后，我们对操作的矩阵进行了旋转变换，目的是为了在下一步的渲染中让曲奇饼 &amp;quot;平躺&amp;quot; 在罐子的底部，而不是竖直立在罐子里面.&lt;/p&gt;
&lt;figure class=&#34;highlight java&#34;&gt;&lt;figcaption data-lang=&#34;java&#34;&gt;&lt;/figcaption&gt;&lt;table&gt;&lt;tr&gt;&lt;td data-num=&#34;1&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token constant&#34;&gt;GL11&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;glScalef&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token number&#34;&gt;0.9F&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;token number&#34;&gt;0.9F&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;token number&#34;&gt;0.9F&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt; &lt;span class=&#34;token comment&#34;&gt;//xyz 轴各缩放 0.9 倍&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;&lt;p&gt;最后，我们对操作的矩阵进行了简单的缩放，让曲奇饼缩放到 0.9 倍的大小，相信读者可以在今后的操作中逐步明白并理解，这里笔者还想插入一些注意事项。读者可以选择性阅读，或者遇到相关问题后再回来阅读&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;glTranslate ，把当前矩阵和一个表示移动物体的矩阵相乘。三个参数分别表示了在三个坐标上的位移值。&lt;/p&gt;
&lt;p&gt;glRotate ，把当前矩阵和一个表示旋转物体的矩阵相乘。物体将绕着 (0 , 0 , 0) 到 ( x , y , z ) 的直线以逆时针旋转，参数提供了旋转的角度。&lt;/p&gt;
&lt;p&gt;glScale ，把当前矩阵和一个表示缩放物体的矩阵相乘。 x , y , z 分别表示在该方向上的缩放比例。&lt;/p&gt;
&lt;p&gt;注意，“先移动后旋转” 和 “先旋转后移动” 得到的结果很可能不同。对于未曾接触过数学中矩阵运算的读者，这里笔者不以矩阵为数学基础展开，相信依靠读者丰富的想象力和 Minecraft 培养出来的空间能力，可以在自己摸索的过程中逐步理解这些概念.&lt;/p&gt;
&lt;p&gt;&lt;img loading=&#34;lazy&#34; data-src=&#34;https://i.loli.net/2021/01/31/aCZUmfqk8jF5wAp.png&#34; alt=&#34;20170419132833166.jpg&#34; /&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;到这里，坐标的预处理工作已经完成了，也就是我们现在坐标的位置已经位于饼干罐中心靠近底部的位置了，我们只需在当前位置渲染出一个 Minecraft 原版曲奇的贴图，就完成了，而 MrCrayfish 也是这样做的，用一对 for 循环嵌套，逐个渲染出物品的位置，完成我们的工作&lt;/p&gt;
&lt;figure class=&#34;highlight java&#34;&gt;&lt;figcaption data-lang=&#34;java&#34;&gt;&lt;/figcaption&gt;&lt;table&gt;&lt;tr&gt;&lt;td data-num=&#34;1&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token keyword&#34;&gt;for&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token keyword&#34;&gt;int&lt;/span&gt; i &lt;span class=&#34;token operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;token number&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt; i &lt;span class=&#34;token operator&#34;&gt;&amp;lt;&lt;/span&gt; metadata&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt; i&lt;span class=&#34;token operator&#34;&gt;++&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;&amp;#123;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;2&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;span class=&#34;token class-name&#34;&gt;Minecraft&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;getMinecraft&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;getRenderManager&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;renderEntity&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;entityItem&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;token number&#34;&gt;0.0D&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;token number&#34;&gt;0.0D&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;token number&#34;&gt;0.1D&lt;/span&gt; &lt;span class=&#34;token operator&#34;&gt;*&lt;/span&gt; i&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;token number&#34;&gt;0.0F&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;token number&#34;&gt;0.0F&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;token boolean&#34;&gt;false&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;3&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token punctuation&#34;&gt;&amp;#125;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;&lt;p&gt;我们直接调用 Minecraft 内部 RenderManager, 使其渲染一个 entity 就可以了。而且笔者也相信，此时的读者已经不会问出 &amp;quot;为什么调用这个方法不需要提供饼干罐的具体坐标呀&amp;quot; 这种问题了.&lt;/p&gt;
&lt;p&gt;当我们阅读后，我们发现实际代码与我们的预想有一些偏差，即饼干罐内部的数量并不是 TileEntity 类内部的一个成员。而是以方块状态 (metadata) 的形式储存的，对此抱有疑问的读者可以自行查阅 Harbinger 相关部分，这里读者就不多作介绍了.&lt;/p&gt;
&lt;p&gt;至此，我们对游戏内调用 OpenGL 渲染的过程有了初步了解，相信读者也可以自己根据需求，渲染出自己想要的效果了。这里笔者补充一点。在我们编写矩阵变换时，通常很难把握物品的具体位置，使用尺子测量这种操作也是完全没有必要的，毕竟我们不是进行精密的数学运算，这里笔者推荐使用自己 IDE 的调试功能，利用代码热重载来完成变换操作，这一点读者可以自行查询自己 IDE 的热重载方式。对于 IDEA 来说，请使用调试按钮和 Debugging Action 中的重载按钮来重载.&lt;/p&gt;
&lt;hr /&gt;
&lt;p&gt;根据以上的学习，笔者自己也尝试写了一个 TESR, 读者可以用自己学到的知识简单解析一下&lt;/p&gt;
&lt;figure class=&#34;highlight java&#34;&gt;&lt;figcaption data-lang=&#34;java&#34;&gt;&lt;/figcaption&gt;&lt;table&gt;&lt;tr&gt;&lt;td data-num=&#34;1&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token annotation punctuation&#34;&gt;@Override&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;2&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;void&lt;/span&gt; &lt;span class=&#34;token function&#34;&gt;render&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token class-name&#34;&gt;TestBlockTile&lt;/span&gt; te&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;double&lt;/span&gt; x&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;double&lt;/span&gt; y&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;double&lt;/span&gt; z&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;float&lt;/span&gt; partialTicks&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;int&lt;/span&gt; destroyStage&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;float&lt;/span&gt; alpha&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;&amp;#123;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;3&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;span class=&#34;token class-name&#34;&gt;GlStateManager&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;PushMatrix&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;4&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;span class=&#34;token class-name&#34;&gt;GlStateManager&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;disableLighting&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;5&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;span class=&#34;token class-name&#34;&gt;GlStateManager&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;translate&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;x&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; y&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; z&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;6&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;span class=&#34;token class-name&#34;&gt;GlStateManager&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;translate&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token number&#34;&gt;0.5&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;token number&#34;&gt;0.2&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;token number&#34;&gt;0.4&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;7&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;span class=&#34;token class-name&#34;&gt;GlStateManager&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;scale&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token number&#34;&gt;0.6&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;token number&#34;&gt;0.6&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;token number&#34;&gt;0.6&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;8&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;span class=&#34;token class-name&#34;&gt;Minecraft&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;getMinecraft&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;getRenderManager&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;renderEntity&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token keyword&#34;&gt;new&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;EntityPig&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;te&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;getWorld&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;token number&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;token number&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;token number&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;token number&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;token number&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;token boolean&#34;&gt;false&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;9&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;span class=&#34;token class-name&#34;&gt;GlStateManager&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;enableLighting&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;10&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;span class=&#34;token class-name&#34;&gt;GlStateManager&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;popMatrix&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;11&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token punctuation&#34;&gt;&amp;#125;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;&lt;p&gt;&lt;img loading=&#34;lazy&#34; data-src=&#34;https://i.loli.net/2021/01/31/2JEMwQeFZRKpLA1.png&#34; alt=&#34;Snipaste_2021-01-31_19-50-21.png&#34; /&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;一些提示:&lt;/p&gt;
&lt;p&gt;​    对于 GLStateManager 和 GL11: 读者在阅读代码的时候经常会看到不同的开发者可能会使用这两个不同的类来调用 OpenGL, GL11 是 org.lwjgl.opengl 包下的，这意味着我们如果调用 GL11, 是直接利用 lwjgl 提供的方法来进行渲染的，而 GLStateManager 是 net.minecraft.client.renderer 包下的，这是 Minecraft 为我们封装的渲染方法。这里读者推荐使用 GLStateManager, 一是为了尽量使用 Minecraft 提供的规范，二是 GL11 相比 GLStateManager 更底层，对于 TESR 来说调用 Minecraft 的方法就足够了&lt;/p&gt;
&lt;p&gt;​    GLStateManager 和 GL11 中的方法名可能略有不同，这里希望读者可以自行区分。例如:&lt;/p&gt;
&lt;p&gt;​    GL11.glPushMatrix (); 和 GlStateManager.pushMatrix ();&lt;/p&gt;
&lt;p&gt;​    GL11.disable (GL11.GL_LIGHTING); 和 GlStateManager.disableLighting ();&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id=&#34;三-我还想要更多-更多&#34;&gt;&lt;a class=&#34;anchor&#34; href=&#34;#三-我还想要更多-更多&#34;&gt;#&lt;/a&gt; 三、我还想要更多，更多&lt;/h2&gt;
&lt;p&gt;之后我们来看一看如何用 OpenGL 在 Minecraft 世界中渲染出文字图片和平面图形。相信聪明的读者会尝试在 getRenderManager () 的 Manager 中寻找类似名为 renderText 或 renderTexture 的方法，不过 IDE 给我们的自动补全则给了我们当头一棒，RenderManager 中并没有类似的方法。这里我们深入思考一下，文字和平面图形并不是 Minecraft 中独创的内容，而游戏中会动的长方体 (生物或物品实体) 则是游戏内独创的要素，所以游戏给我们提供相应的接口是理所当然的。那对于文字图片来说，游戏有没有给我们封装相应的方法呢？&lt;/p&gt;
&lt;p&gt;这里我们从 EnderUtilities 的储物桶中可以得到相关的代码实现，储物桶上方有物品数量的悬浮提示，储物桶有红色的容量指示条，储物桶还有一些需要渲染的图片 (例如上锁的储物桶有小锁头的图片) 或许这些功能的实现可以解答我们的疑问，并且教会我们一些知识&lt;/p&gt;
&lt;p&gt;&lt;img loading=&#34;lazy&#34; data-src=&#34;https://i.loli.net/2021/02/06/AYwOITRqxinLkrW.png&#34; alt=&#34;Snipaste_2021-02-06_10-07-31.png&#34; /&gt;&lt;/p&gt;
&lt;h3 id=&#34;文字渲染-fontrenderer&#34;&gt;&lt;a class=&#34;anchor&#34; href=&#34;#文字渲染-fontrenderer&#34;&gt;#&lt;/a&gt; 文字渲染 —— fontRenderer&lt;/h3&gt;
&lt;figure class=&#34;highlight java&#34;&gt;&lt;figcaption data-lang=&#34;java&#34;&gt;&lt;/figcaption&gt;&lt;table&gt;&lt;tr&gt;&lt;td data-num=&#34;1&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token keyword&#34;&gt;private&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;void&lt;/span&gt; &lt;span class=&#34;token function&#34;&gt;renderText&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token class-name&#34;&gt;String&lt;/span&gt; text&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;double&lt;/span&gt; x&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;double&lt;/span&gt; y&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;double&lt;/span&gt; z&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;EnumFacing&lt;/span&gt; side&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;EnumFacing&lt;/span&gt; barrelFront&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;2&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;span class=&#34;token punctuation&#34;&gt;&amp;#123;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;3&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;        &lt;span class=&#34;token class-name&#34;&gt;FontRenderer&lt;/span&gt; fontRenderer &lt;span class=&#34;token operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;Minecraft&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;getMinecraft&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;fontRenderer&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;4&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;        &lt;span class=&#34;token keyword&#34;&gt;int&lt;/span&gt; strLenHalved &lt;span class=&#34;token operator&#34;&gt;=&lt;/span&gt; fontRenderer&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;getStringWidth&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;text&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;token operator&#34;&gt;/&lt;/span&gt; &lt;span class=&#34;token number&#34;&gt;2&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;5&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;6&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;        &lt;span class=&#34;token class-name&#34;&gt;GlStateManager&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;pushMatrix&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;7&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;        &lt;span class=&#34;token comment&#34;&gt;//... 省略了 translate 过程...&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;8&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;        &lt;span class=&#34;token class-name&#34;&gt;GlStateManager&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;scale&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token operator&#34;&gt;-&lt;/span&gt;&lt;span class=&#34;token number&#34;&gt;0.01F&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;token operator&#34;&gt;-&lt;/span&gt;&lt;span class=&#34;token number&#34;&gt;0.01F&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;token number&#34;&gt;0.01F&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;9&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;        &lt;span class=&#34;token class-name&#34;&gt;GlStateManager&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;color&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token number&#34;&gt;1.0F&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;token number&#34;&gt;1.0F&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;token number&#34;&gt;1.0F&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;token number&#34;&gt;1.0F&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt; &lt;span class=&#34;token comment&#34;&gt;// 将画笔置为黑色，便于进行绘画 (这里没有进行绘画)&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;10&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;11&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;        &lt;span class=&#34;token class-name&#34;&gt;GlStateManager&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;disableLighting&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;12&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;        &lt;span class=&#34;token class-name&#34;&gt;GlStateManager&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;enablePolygonOffset&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;13&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;        &lt;span class=&#34;token class-name&#34;&gt;GlStateManager&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;depthMask&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token boolean&#34;&gt;false&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;14&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;        &lt;span class=&#34;token class-name&#34;&gt;GlStateManager&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;enableBlend&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt; &lt;span class=&#34;token comment&#34;&gt;// 开启混合器 (使 GL 支持 Alpha 透明通道)&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;15&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;        &lt;span class=&#34;token class-name&#34;&gt;GlStateManager&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;doPolygonOffset&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token operator&#34;&gt;-&lt;/span&gt;&lt;span class=&#34;token number&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;token operator&#34;&gt;-&lt;/span&gt;&lt;span class=&#34;token number&#34;&gt;20&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;16&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;17&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;        fontRenderer&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;drawString&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;text&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;token operator&#34;&gt;-&lt;/span&gt;strLenHalved&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;token number&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;token number&#34;&gt;0xFFFFFFFF&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;18&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;19&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;        &lt;span class=&#34;token class-name&#34;&gt;GlStateManager&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;disableBlend&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;20&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;        &lt;span class=&#34;token class-name&#34;&gt;GlStateManager&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;depthMask&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token boolean&#34;&gt;true&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;21&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;        &lt;span class=&#34;token class-name&#34;&gt;GlStateManager&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;disablePolygonOffset&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;22&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;        &lt;span class=&#34;token class-name&#34;&gt;GlStateManager&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;enableLighting&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;23&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;24&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;        &lt;span class=&#34;token class-name&#34;&gt;GlStateManager&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;popMatrix&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;25&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;span class=&#34;token punctuation&#34;&gt;&amp;#125;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;26&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token comment&#34;&gt;/*&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;27&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;仓库地址: https://github.com/maruohon/enderutilities/&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;28&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;节选自: /MC_1.11.x/src/main/java/fi/dy/masa/enderutilities/client/renderer/tileentity/TESRBarrel.java&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;29&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;*/&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;&lt;p&gt;这里笔者将 translate 部分语句省略了，作为储物桶来说，有一个面是玩家的操作面，所有的文字和图标应该渲染在这个方向上，作者的 translate 语句正是将操作方向转移到操作面上。感兴趣的读者可以自行阅读相关代码，这里我们一起来看看一些我们没有介绍过的 GL 语句以及字体渲染的方法&lt;/p&gt;
&lt;figure class=&#34;highlight java&#34;&gt;&lt;figcaption data-lang=&#34;java&#34;&gt;&lt;/figcaption&gt;&lt;table&gt;&lt;tr&gt;&lt;td data-num=&#34;1&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token class-name&#34;&gt;GlStateManager&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;enablePolygonOffset&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;2&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token class-name&#34;&gt;GlStateManager&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;doPolygonOffset&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token operator&#34;&gt;-&lt;/span&gt;&lt;span class=&#34;token number&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;token operator&#34;&gt;-&lt;/span&gt;&lt;span class=&#34;token number&#34;&gt;20&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;3&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token class-name&#34;&gt;GlStateManager&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;disablePolygonOffset&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;&lt;p&gt;PolygonOffset 解决了一个贴图打架的问题，即 z-fighting, 如果我们不指定贴图的层叠关系，贴图们会挤在同一层中发生闪烁.&lt;/p&gt;
&lt;p&gt;所以当我们想要将两个贴图相互重叠时，我们的想法是让 A 叠在 B 上 (或者说让 B 在后面，稍微跟 A 隔开一点距离，防止打架), 所以我们得将这种层叠关系表示出来。这就是 PolygonOffset&lt;/p&gt;
&lt;p&gt;&lt;img loading=&#34;lazy&#34; data-src=&#34;https://i.loli.net/2021/02/06/2N8ZkPg1EHwxej4.jpg&#34; alt=&#34;z-fighting.jpg&#34; /&gt;&lt;/p&gt;
&lt;p&gt;doPolygonOffset 的两个参数为 factor 和 units, 这两个量会影响 AB 两个面隔开的距离，如果想要非常好地使用 PolygonOffset，你需要做一些数学上的研究。不过一般而言，只需把 1.0 和 0.0 这样简单的值赋给 glPolygonOffset 即可满足需要。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;GlStateManager.depthMask(false);
GlStateManager.depthMask(true);
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;depthMask 也与我们刚刚介绍过的 z-fighting 有关，depthMask 也就是深度测试 (深度缓冲), 参考一些文章后，有这样的解释&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;开启深度测试后 OpenGL 就不会再去绘制模型被遮挡的部分，这样实现的显示画面更为真实，但是由于深度缓冲区精度的限制，对于深度相差非常小的情况（例如在同一平面上进行两次绘制），&lt;strong&gt;OpenGL 就不能正确判定两者的深度值，会导致深度测试的结果不可预测&lt;/strong&gt;，显示出来的现象时交错闪烁的前后两个画面，这种情况称为 z-fighting。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;这也不难推知我们为什么要关闭 depthMask 并且手动处理 PolygonOffset 了，因为我们的需求是在方块的一个面上绘制内容，即 &amp;quot;在同一平面上进行两次绘制&amp;quot;, 所以我们这种操作也不难理解了.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;FontRenderer fontRenderer = Minecraft.getMinecraft().fontRenderer;
int strLenHalved = fontRenderer.getStringWidth(text) / 2;
fontRenderer.drawString(text, -strLenHalved, 0, 0xFFFFFFFF);
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;相信读者已经可以回答我们这一节开头提出的问题了，Minecraft 的确为我们封装了字体渲染的相关内容，即 Minecraft.getMinecraft ().fontRenderer, 我们只需调用 drawString () 方法并传入字符串，x, y 轴位置和十六进制颜色值就可以了。至于为什么这里计算了字符串一半的长度，相信聪明的读者已经想到了什么... 这里进一步提问，我们前文省略的 translate 语句，是将画笔的位置移动到了哪里呢？ 对了，是移动到了贴面顶端的中心位置.&lt;/p&gt;
&lt;h3 id=&#34;图片渲染-bindtexture和tessellator&#34;&gt;&lt;a class=&#34;anchor&#34; href=&#34;#图片渲染-bindtexture和tessellator&#34;&gt;#&lt;/a&gt; 图片渲染 —— bindTexture 和 Tessellator&lt;/h3&gt;
&lt;p&gt;事情到了这里，我们终于不可避免的要涉及到 Tessellator 了，这里我们需要先引入一些理论知识，才能继续向下理解.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;注: Tessellator 与图形学中的细分曲面 (Tessellation) 无关，Tessellator 是 OpenGL 立即模式的一种封装&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;我们之前的操作都直接调用了 OpenGL 去绘制我们在屏幕上看到的事物，但直接与 OpenGL 交互在某种层面来说是很麻烦的，所以我们引入了 Tessellator 来简化这个绘制过程同时提高效率.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Tessellator 代替了我们直接调用 GL 的一个个方法调用，Tessellator 收集了关于渲染细节的一个数组，并且规定这个数组哪些部分代表什么 (以便于渲染器正确组织数组中的数据), 并且批量提交给 GPU 进行绘制，最后这些细节会被传进 VBO, 然后用 glDrawArray 批量画出来.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;figure class=&#34;highlight java&#34;&gt;&lt;figcaption data-lang=&#34;java&#34;&gt;&lt;/figcaption&gt;&lt;table&gt;&lt;tr&gt;&lt;td data-num=&#34;1&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token class-name&#34;&gt;Minecraft&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;getMinecraft&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;getTextureManager&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;bindTexture&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token constant&#34;&gt;TEXTURE_LOCK&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;2&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token class-name&#34;&gt;Tessellator&lt;/span&gt; tessellator &lt;span class=&#34;token operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;Tessellator&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;getInstance&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt; &lt;span class=&#34;token comment&#34;&gt;// 获取 Tessellator 的一般方式&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;3&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token class-name&#34;&gt;VertexBuffer&lt;/span&gt; buffer &lt;span class=&#34;token operator&#34;&gt;=&lt;/span&gt; tessellator&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;getBuffer&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt; &lt;span class=&#34;token comment&#34;&gt;// 获取记录顶点信息的 &#34;数组&#34;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;4&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;5&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;buffer&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;begin&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token constant&#34;&gt;GL11&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token constant&#34;&gt;GL_QUADS&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;DefaultVertexFormats&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token constant&#34;&gt;POSITION_TEX&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt; &lt;span class=&#34;token comment&#34;&gt;// 指定数组的组织方式 (位置 + UV 方式), 以及要画的图像的顶点数 (矩形四个顶点)&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;6&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;7&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;buffer&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;pos&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;  &lt;span class=&#34;token number&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt;   &lt;span class=&#34;token number&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;token number&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;tex&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token number&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;token number&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;endVertex&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt; &lt;span class=&#34;token comment&#34;&gt;// 提供矩形的四个顶点，并绑定 UV&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;8&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;buffer&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;pos&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;  &lt;span class=&#34;token number&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;token number&#34;&gt;0.15&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;token number&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;tex&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token number&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;token number&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;endVertex&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt; &lt;span class=&#34;token comment&#34;&gt;// 提供矩形的四个顶点，并绑定 UV&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;9&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;buffer&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;pos&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token number&#34;&gt;0.15&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;token number&#34;&gt;0.15&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;token number&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;tex&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token number&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;token number&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;endVertex&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt; &lt;span class=&#34;token comment&#34;&gt;// 提供矩形的四个顶点，并绑定 UV&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;10&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;buffer&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;pos&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token number&#34;&gt;0.15&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt;   &lt;span class=&#34;token number&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;token number&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;tex&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token number&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;token number&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;endVertex&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt; &lt;span class=&#34;token comment&#34;&gt;// 提供矩形的四个顶点，并绑定 UV&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;11&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;12&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;tessellator&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;draw&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt; &lt;span class=&#34;token comment&#34;&gt;// 将数组和渲染方式提交到 GPU&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;&lt;p&gt;这里我们调用了 Minecraft.getMinecraft ().getTextureManager () 的 bindTexture 方法，将锁的图片载入到 GL 状态机中，之后我们利用 Tessellator 来将矩形的四个顶点与对应 UV 的四个角落绑定，就完成了操作，如下图&lt;/p&gt;
&lt;p&gt;&lt;img loading=&#34;lazy&#34; data-src=&#34;https://i.loli.net/2021/02/06/XeRuVsivqlWPcaK.png&#34; alt=&#34;Snipaste_2021-02-06_12-28-05.png&#34; /&gt;&lt;/p&gt;
&lt;p&gt;这里读者应该对 Tessellator 矩形绑定材质的渲染方式有了初步的了解。下面我们来进一步巩固一下我们的理解&lt;/p&gt;
&lt;figure class=&#34;highlight java&#34;&gt;&lt;figcaption data-lang=&#34;java&#34;&gt;&lt;/figcaption&gt;&lt;table&gt;&lt;tr&gt;&lt;td data-num=&#34;1&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;buffer&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;begin&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token constant&#34;&gt;GL11&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token constant&#34;&gt;GL_QUADS&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;DefaultVertexFormats&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token constant&#34;&gt;POSITION_COLOR&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt; &lt;span class=&#34;token comment&#34;&gt;// 指定数组的组织方式 (位置 + 颜色值), 以及要画的图像的顶点数 (矩形四个顶点)&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;2&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;3&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token keyword&#34;&gt;int&lt;/span&gt; r_b &lt;span class=&#34;token operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;token number&#34;&gt;0x03&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;4&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token keyword&#34;&gt;int&lt;/span&gt; g_b &lt;span class=&#34;token operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;token number&#34;&gt;0x03&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;5&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token keyword&#34;&gt;int&lt;/span&gt; b_b &lt;span class=&#34;token operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;token number&#34;&gt;0x20&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;6&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;7&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;buffer&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;pos&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;  &lt;span class=&#34;token number&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt;    &lt;span class=&#34;token number&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;token number&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;color&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;r_b&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; g_b&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; b_b&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;token number&#34;&gt;255&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;endVertex&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;8&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;buffer&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;pos&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;  &lt;span class=&#34;token number&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;token number&#34;&gt;0.08&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;token number&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;color&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;r_b&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; g_b&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; b_b&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;token number&#34;&gt;255&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;endVertex&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;9&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;buffer&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;pos&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token number&#34;&gt;0.6&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;token number&#34;&gt;0.08&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;token number&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;color&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;r_b&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; g_b&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; b_b&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;token number&#34;&gt;255&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;endVertex&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;10&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;buffer&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;pos&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token number&#34;&gt;0.6&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt;    &lt;span class=&#34;token number&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;token number&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;color&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;r_b&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; g_b&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; b_b&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;token number&#34;&gt;255&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;endVertex&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt; &lt;span class=&#34;token comment&#34;&gt;// 提供矩形的四个顶点，传入相应的颜色值&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;11&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;12&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token keyword&#34;&gt;int&lt;/span&gt; r_f &lt;span class=&#34;token operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;token number&#34;&gt;0x20&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;13&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token keyword&#34;&gt;int&lt;/span&gt; g_f &lt;span class=&#34;token operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;token number&#34;&gt;0x90&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;14&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token keyword&#34;&gt;int&lt;/span&gt; b_f &lt;span class=&#34;token operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;token number&#34;&gt;0xF0&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;15&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token keyword&#34;&gt;float&lt;/span&gt; e &lt;span class=&#34;token operator&#34;&gt;=&lt;/span&gt; fullness &lt;span class=&#34;token operator&#34;&gt;*&lt;/span&gt; &lt;span class=&#34;token number&#34;&gt;0.57f&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;16&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;17&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;buffer&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;pos&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token number&#34;&gt;0.585&lt;/span&gt;    &lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;token number&#34;&gt;0.065&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;token operator&#34;&gt;-&lt;/span&gt;&lt;span class=&#34;token number&#34;&gt;0.001&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;color&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;r_f&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; g_f&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; b_f&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;token number&#34;&gt;255&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;endVertex&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;18&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;buffer&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;pos&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token number&#34;&gt;0.585&lt;/span&gt;    &lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;token number&#34;&gt;0.015&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;token operator&#34;&gt;-&lt;/span&gt;&lt;span class=&#34;token number&#34;&gt;0.001&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;color&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;r_f&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; g_f&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; b_f&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;token number&#34;&gt;255&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;endVertex&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;19&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;buffer&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;pos&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token number&#34;&gt;0.585&lt;/span&gt; &lt;span class=&#34;token operator&#34;&gt;-&lt;/span&gt; e&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;token number&#34;&gt;0.015&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;token operator&#34;&gt;-&lt;/span&gt;&lt;span class=&#34;token number&#34;&gt;0.001&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;color&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;r_f&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; g_f&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; b_f&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;token number&#34;&gt;255&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;endVertex&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;20&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;buffer&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;pos&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token number&#34;&gt;0.585&lt;/span&gt; &lt;span class=&#34;token operator&#34;&gt;-&lt;/span&gt; e&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;token number&#34;&gt;0.065&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;token operator&#34;&gt;-&lt;/span&gt;&lt;span class=&#34;token number&#34;&gt;0.001&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;color&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;r_f&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; g_f&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; b_f&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;token number&#34;&gt;255&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;endVertex&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;span class=&#34;token comment&#34;&gt;// 提供矩形的四个顶点，传入相应的颜色值，顶点的位置随着储物桶的内部容量而更改.&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;21&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;22&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;tessellator&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;draw&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt; &lt;span class=&#34;token comment&#34;&gt;// 将数组和渲染方式提交到 GPU&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;&lt;p&gt;这样，我们就成功在储物桶上绘制好了两个矩形。相信到这里，读者已经对 Tessellator 的使用方式和基本原理有了初步的理解和认识。读懂其他 TESR 的渲染过程也不会一头雾水了吧.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;注：其实除了 Tessellator, 还有一种更为原始的操作方式，这里笔者就不详细说明了。笔者希望读者能使用 Tessellator 这种便利而高效的方式，对于 glBegin () glEnd () 这种原始操作，笔者这里放出一个例子，感兴趣的读者可以自行查阅，相信理解 Tessellator 的读者不难体会其中的原理:&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;https://github.com/MrCrayfish/MrCrayfishFurnitureMod/blob/1.12.2/src/main/java/com/mrcrayfish/furniture/render/tileentity/CupRenderer.java&#34;&gt;https://github.com/MrCrayfish/MrCrayfishFurnitureMod/blob/1.12.2/src/main/java/com/mrcrayfish/furniture/render/tileentity/CupRenderer.java&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id=&#34;四-小结&#34;&gt;&lt;a class=&#34;anchor&#34; href=&#34;#四-小结&#34;&gt;#&lt;/a&gt; 四、小结&lt;/h2&gt;
&lt;h3 id=&#34;小结&#34;&gt;&lt;a class=&#34;anchor&#34; href=&#34;#小结&#34;&gt;#&lt;/a&gt; 小结&lt;/h3&gt;
&lt;p&gt;在本文中，我们以 OpenGL 初学者角度从简单的实例入手，由浅入深地对 OpenGL 在 Minecraft 中的使用方式进行了简要的介绍，对 Tessellator 的使用方式和原理进行了简要说明，相信读者已经可以自己上手写出成型的 TESR, 也会对之后 GUI 界面的绘制有很大的帮助。笔者水平有限，希望多多包涵。如果你有任何的反馈和建议欢迎在本帖下方留言.&lt;/p&gt;
&lt;h3 id=&#34;鸣谢&#34;&gt;&lt;a class=&#34;anchor&#34; href=&#34;#鸣谢&#34;&gt;#&lt;/a&gt; 鸣谢&lt;/h3&gt;
&lt;p&gt;Harbinger 提供知识框架: &lt;a href=&#34;harbinger.covertdragon.team&#34;&gt;harbinger.covertdragon.team&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;御坂御坂 001 个人 Blog 提供基础知识补充&lt;/strong&gt;: &lt;a href=&#34;www.cnblogs.com/xiegaosen/p/10809022.html&#34;&gt;www.cnblogs.com/xiegaosen/p/10809022.html&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;MrCrayfish 家具 Mod 提供部分代码分析: &lt;a href=&#34;github.com/MrCrayfish/MrCrayfishFurnitureMod&#34;&gt;github.com/MrCrayfish/MrCrayfishFurnitureMod&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;McJty 的模组教程提供部分知识基础: &lt;a href=&#34;wiki.mcjty.eu/modding/index.php&#34;&gt;wiki.mcjty.eu/modding/index.php&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;GL_LIGHTING 关闭再打开的原因解答: &lt;a href=&#34;http://forum.lwjgl.org/index.php?topic=4753.0&#34;&gt;http://forum.lwjgl.org/index.php?topic=4753.0&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;矩阵变换相关理解: &lt;a href=&#34;https://blog.csdn.net/hebbely/article/details/70225045&#34;&gt;https://blog.csdn.net/hebbely/article/details/70225045&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;一些 Tessellator 的灵感 &lt;a href=&#34;https://forums.minecraftforge.net/topic/66168-1122-using-minecrafts-tessellator-and-bufferbuilder/&#34;&gt;https://forums.minecraftforge.net/topic/66168-1122-using-minecrafts-tessellator-and-bufferbuilder/&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;土球球解答了笔者的一些疑惑&lt;/strong&gt;: &lt;a href=&#34;https://www.mcbbs.net/home.php?mod=space&amp;amp;uid=1480882&#34;&gt;https://www.mcbbs.net/home.php?mod=space&amp;amp;uid=1480882&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;PolygonOffset 和 z-fighting: &lt;a href=&#34;https://www.cnblogs.com/bitzhuwei/p/polygon-offset-for-stitching-andz-fighting.html&#34;&gt;https://www.cnblogs.com/bitzhuwei/p/polygon-offset-for-stitching-andz-fighting.html&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Tessellator&lt;/strong&gt;: &lt;a href=&#34;http://greyminecraftcoder.blogspot.com/2013/08/the-tessellator.html&#34;&gt;http://greyminecraftcoder.blogspot.com/2013/08/the-tessellator.html&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;EnderUtilities 提供部分代码分析: &lt;a href=&#34;https://github.com/maruohon/enderutilities/&#34;&gt;https://github.com/maruohon/enderutilities/&lt;/a&gt;&lt;/p&gt;
</content>
        <category term="Minecraft" />
        <category term="模组开发" />
        <updated>2021-07-11T03:19:59.000Z</updated>
    </entry>
    <entry>
        <id>https://turou.fun/minecraft/mixin-tutor/</id>
        <title>由现象到本质的Minecraft源码注入艺术</title>
        <link rel="alternate" href="https://turou.fun/minecraft/mixin-tutor/"/>
        <content type="html">&lt;h2 id=&#34;地狱放水-吟唱魔法-储物工作台-人生苦短-我要mixin&#34;&gt;&lt;a class=&#34;anchor&#34; href=&#34;#地狱放水-吟唱魔法-储物工作台-人生苦短-我要mixin&#34;&gt;#&lt;/a&gt; 地狱放水？吟唱魔法？储物工作台？人生苦短，我要 Mixin&lt;/h2&gt;
&lt;hr /&gt;
&lt;p&gt;Mixin 以其易用性与强大性，逐渐取代了传统意义上的 CoreMod 编写流程，这使得将自己的代码注入到 Minecraft 的内部变得十分容易且便利。这里笔者将从三个从浅入深的例子入手，带领读者快速入门 Mixin, 欣赏 Minecraft 源码的注入艺术&lt;/p&gt;
&lt;p&gt;本文推荐使用开发环境:&lt;/p&gt;
&lt;p&gt;开发工具: IntelliJ IDEA with Minecraft Devlopment Plugin&lt;/p&gt;
&lt;p&gt;开发环境: Fabric Loader 0.11.1 with fabric-api 0.30.0&lt;/p&gt;
&lt;p&gt;Mapping 版本: Yarn-1.16.5 build.4:v2&lt;/p&gt;
&lt;p&gt;游戏版本: Minecraft 1.16.5 with Fabric&lt;/p&gt;
&lt;p&gt;本文的受众对象:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;对 Minecraft 有着基本认识和了解的读者&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;曾经有进行过 Fabric 或 Forge 开发的读者&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;有独立阅读相关文档，解决问题能力的读者&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;对 Mixin 的工作原理，使用方式&lt;strong&gt;简单了解&lt;/strong&gt;的读者&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;hr /&gt;
&lt;h2 id=&#34;一-揭开minecraft的面纱-水桶美学&#34;&gt;&lt;a class=&#34;anchor&#34; href=&#34;#一-揭开minecraft的面纱-水桶美学&#34;&gt;#&lt;/a&gt; 一、揭开 Minecraft 的面纱 - 水桶美学&lt;/h2&gt;
&lt;h3 id=&#34;1-慧眼观察&#34;&gt;&lt;a class=&#34;anchor&#34; href=&#34;#1-慧眼观察&#34;&gt;#&lt;/a&gt; 1. 慧眼观察&lt;/h3&gt;
&lt;p&gt;本节，我们将以一个十分经典的需求：在地狱放水，打开读者的思路，培养读者阅读源码的能力，并且对 Mixin 建立基本的认识.&lt;/p&gt;
&lt;p&gt;相信读者在以往的开发过程中常常会不经意间用到 IDEA 的内置反编译功能，当我们对某一方法或类抱有疑问时，我们常常会 Shift 左击来查看某个原版方法或类的定义。如果我们试图对我们查看到的源码进行更改时，IDEA 会提示我们，该文件是只读的，我们深切的知道，已经编译到 class 的文件是不能被直接修改的.&lt;/p&gt;
&lt;p&gt;但正是 Mixin 的出现，使这种更改成为了可能，我们可以利用一些表达方式来在 class 文件被 Java 虚拟机执行前修改它，所以我们的工作就需要先利用 IDEA 的反编译功能定位代码，之后用 Mixin 进行修改.&lt;/p&gt;
&lt;p&gt;说到这里，相信聪明的读者已经对我们的定位工作有了头绪，很容易想到，地狱不能放水的原因就出在我们用水桶在地狱放置的一瞬间，所以我们只需在水桶交互的实现方法中找到这部分就可以了.&lt;/p&gt;
&lt;p&gt;这里我们使用 IDEA 的查找功能，双击 Shift, 我们可以打开一个快速搜索框&lt;/p&gt;
&lt;p&gt;这里我们搜索水桶 Bucket, 我们可以找到原版 Item 包下的 BucketItem 类，这里要注意的是，我们要选中 Include non-project items. 不然的话 Minecraft 包内的方法是没办法查找到的&lt;/p&gt;
&lt;p&gt;&lt;img loading=&#34;lazy&#34; data-src=&#34;https://i.loli.net/2021/04/07/QpLDCKqPjFhVNcB.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;
&lt;p&gt;这里我们直接双击，让 IDEA 用内置的反编译器来查看 BucketItem 类的源码&lt;/p&gt;
&lt;p&gt;相信细心的读者很快就能找到对应的条件判断语句，我们在 placeFluid 方法中找到了我们想要操作的目标，我们发现，这与我们印象中地狱放水的现象相似 (先播放桶倒水的声音，之后在生成烟的粒子效果)&lt;/p&gt;
&lt;p&gt;&lt;img loading=&#34;lazy&#34; data-src=&#34;https://i.loli.net/2021/04/07/SmsMcXEVBdwjKq4.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;
&lt;p&gt;这里 Dimension, Ultrawarm 等字样引起了我们的注意，我们进一步向下查阅发现，这个 isUltrawarm 方法返回了成员变量 ultrawarm.&lt;/p&gt;
&lt;p&gt;&lt;img loading=&#34;lazy&#34; data-src=&#34;https://i.loli.net/2021/04/09/hfHTpryCaibUqez.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;
&lt;p&gt;不难猜想，这个成员应该是 DimensionType 类初始化时传入的，而代表地狱的 DimensionType 创建时参数可能传入了 true, 继续向下阅读，我们找到了具体的实例化的代码，这证实了我们的猜想&lt;/p&gt;
&lt;p&gt;&lt;img loading=&#34;lazy&#34; data-src=&#34;https://i.loli.net/2021/04/09/48zRFVMT6Li7aJj.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;
&lt;p&gt;所以最简单的解决方案就清晰了，我们需要修改的正是这个 isUltrawarm 方法。使其一律返回 false, 这样就允许了所有维度水的放置.&lt;/p&gt;
&lt;h4 id=&#34;2-动手实践&#34;&gt;&lt;a class=&#34;anchor&#34; href=&#34;#2-动手实践&#34;&gt;#&lt;/a&gt; 2. 动手实践&lt;/h4&gt;
&lt;p&gt;相信对 Mixin 有简单了解的读者已经动手写出自己的代码了，这里笔者仅简单提几点注意事项，以供对 Mixin 了解不深的读者参考，相信你们会在之后的学习中逐渐理解 Mixin 的实质&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;所有 Mixin 的相关 &amp;quot;类&amp;quot; 必须放在一个独立的包下，并且在对应 json 中明确声明&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Mixin 类需要使用 @Mixin 来修饰，参数提供想要注入的类&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;被 @Mixin 修饰的类，在实质的角度上来说不是类，而是指导代码注入过程的一个说明&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;@Overwrite 表示用下列代码完全替换目标方法的内容&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;这里笔者放出自己的代码，并简要说明一下定位与修改的过程.&lt;/p&gt;
&lt;figure class=&#34;highlight java&#34;&gt;&lt;figcaption data-lang=&#34;java&#34;&gt;&lt;/figcaption&gt;&lt;table&gt;&lt;tr&gt;&lt;td data-num=&#34;1&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token annotation punctuation&#34;&gt;@Mixin&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token class-name&#34;&gt;DimensionType&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token keyword&#34;&gt;class&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;token comment&#34;&gt;// 只有被 @Mixin 修饰的类才会被识别并注入&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;2&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;class&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;DimensionTypeMixin&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;&amp;#123;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;3&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;span class=&#34;token comment&#34;&gt;/**&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;4&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;     * @author TuRou&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;5&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;     * Make water can place at all dimensions.&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;6&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;     */&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;7&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;span class=&#34;token annotation punctuation&#34;&gt;@Overwrite&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;token comment&#34;&gt;// 将目标方法完全替换为下方的代码&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;8&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;span class=&#34;token keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;boolean&lt;/span&gt; &lt;span class=&#34;token function&#34;&gt;isUltrawarm&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;&amp;#123;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;9&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;        &lt;span class=&#34;token keyword&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;token boolean&#34;&gt;false&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt; &lt;span class=&#34;token comment&#34;&gt;// 所有维度都返回 false, 包括地狱&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;10&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;span class=&#34;token punctuation&#34;&gt;&amp;#125;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;11&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token punctuation&#34;&gt;&amp;#125;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;&lt;p&gt;只需要短短的五行代码，我们便达成了我们的目的，简单地修改一个 Minecraft 的内部方法。之后将 Mixin 类添加到对应 json 文件中就可以了&lt;/p&gt;
&lt;p&gt;&lt;img loading=&#34;lazy&#34; data-src=&#34;https://i.loli.net/2021/04/07/G4bQY6fZJ3oW9kI.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;
&lt;p&gt;package 指向所有 Mixin 文件的存放包，mixins 是存放所有 Mixin 类的列表。最后不要忘记在主 json 中加入 mixins.json 的引用，之后我们就可以进游戏测试了。在之后部分的文章中，将不会再提及这部分内容，如果读者发现自己的 Mixin 没有被正确加载，不妨检查一下有没有将其添加到 mixins 中.&lt;/p&gt;
&lt;p&gt;&lt;img loading=&#34;lazy&#34; data-src=&#34;https://i.loli.net/2021/04/09/U2dbxGQTF1HC7th.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;
&lt;hr /&gt;
&lt;h2 id=&#34;二-方块与魔法的世界-吟唱施法&#34;&gt;&lt;a class=&#34;anchor&#34; href=&#34;#二-方块与魔法的世界-吟唱施法&#34;&gt;#&lt;/a&gt; 二、方块与魔法的世界 - 吟唱施法&lt;/h2&gt;
&lt;p&gt;本部分将以聊天栏吟唱施法为实例，使读者对定位注入的位置，以及源代码的感知进入一个更深层次的境界，同时启发读者产生更多的想法和点子。我们将从聊天栏消息的注入出发，深入浅出揭秘 Minecraft 聊天的处理方式，这里也包含一小部分 Fabric 网络通信的方式，读者可以选择性阅读.&lt;/p&gt;
&lt;h3 id=&#34;1-明确需求&#34;&gt;&lt;a class=&#34;anchor&#34; href=&#34;#1-明确需求&#34;&gt;#&lt;/a&gt; 1. 明确需求&lt;/h3&gt;
&lt;p&gt;作为一名出色的魔法术士，我们应有流畅的魔法吟唱的能力，在 Minecraft 世界中，我们可以用聊天的形式呈现，在聊天栏中说出对应魔咒，我们就可以触发相应的魔法，产生一些特殊的效果。我们希望自己的魔咒不在公屏上显示，甚至我们输入魔咒的时候会发出悦耳的声音，并且也不允许玩家使用方向上键来直接输入历史魔法... 这样，我们可以做出一个趋于完善的吟唱施法系统。这里我们重新梳理一下需求&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;拦截聊天栏魔咒消息的发送，判断施法是否成立&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;在输入魔咒时发出悦耳的声音&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;禁止魔咒消息储存至消息历史&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;如此这样，我们把目光主要定位在了&lt;strong&gt;聊天&lt;/strong&gt;的相关实现上，我们想要寻找的类，大概率包含 Chat, Message 字样。之后，我们就可以开始着手分析 Minecraft 关于聊天相关实现的源码了.&lt;/p&gt;
&lt;h4 id=&#34;2-渐入佳境&#34;&gt;&lt;a class=&#34;anchor&#34; href=&#34;#2-渐入佳境&#34;&gt;#&lt;/a&gt; 2. 渐入佳境&lt;/h4&gt;
&lt;p&gt;搜索后我们发现，我们找到了三个可疑目标，分别是 ChatHud, ChatMessages, ChatScreen&lt;/p&gt;
&lt;p&gt;&lt;img loading=&#34;lazy&#34; data-src=&#34;https://i.loli.net/2021/04/10/qrYTpGudO8S1if7.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;
&lt;p&gt;经过简单的分析我们发现:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;ChatMessages 主要负责聊天信息中文字的渲染，这对我们的工作没有帮助&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;ChatScreen 中绘制了我们聊天栏的 GUI, 聊天信息的发送可能在这里被处理&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;ChatHud 着重于聊天栏的文本部分，我们聊天信息的历史可能保存在这里&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;为了印证我们的猜测与分析，我们尝试依靠我们的直觉来寻找我们需要的代码。让我们回想在游戏中我们发送聊天信息的操作流程。我们首先按 T 打开聊天栏，在文本框中输入内容，最后&lt;strong&gt;单击回车发送消息&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;细心的读者可能已经发现，这个按 T 打开的聊天栏正是 ChatScreen 所描述的界面，而文本框就是 ChatScreen 类中发现的一个名为 chatField 的属性。而点击回车这个动作，在 keyPressed 方法中有所体现.&lt;/p&gt;
&lt;figure class=&#34;highlight java&#34;&gt;&lt;figcaption data-lang=&#34;java&#34;&gt;&lt;/figcaption&gt;&lt;table&gt;&lt;tr&gt;&lt;td data-num=&#34;1&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token class-name&#34;&gt;String&lt;/span&gt; string &lt;span class=&#34;token operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;this&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;chatField&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;getText&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;trim&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;2&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token keyword&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token operator&#34;&gt;!&lt;/span&gt;string&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;isEmpty&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;&amp;#123;&lt;/span&gt; &lt;span class=&#34;token comment&#34;&gt;// 如果不为空&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;3&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;span class=&#34;token keyword&#34;&gt;this&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;sendMessage&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;string&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;4&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token punctuation&#34;&gt;&amp;#125;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;5&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;6&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token keyword&#34;&gt;this&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;client&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;openScreen&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token class-name&#34;&gt;Screen&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token keyword&#34;&gt;null&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt; &lt;span class=&#34;token comment&#34;&gt;// 关闭界面&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;7&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token keyword&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;token boolean&#34;&gt;true&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;8&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;位于&lt;span class=&#34;token operator&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;&lt;span class=&#34;token namespace&#34;&gt;net&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;minecraft&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;client&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;gui&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;screen&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;/span&gt;ChatScreen&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;&lt;p&gt;不难猜想，我们需要深入探究 sendMessage 是以怎样的方式发送玩家的聊天信息的，我们查询相关实现，发现了如下的代码.&lt;/p&gt;
&lt;figure class=&#34;highlight java&#34;&gt;&lt;figcaption data-lang=&#34;java&#34;&gt;&lt;/figcaption&gt;&lt;table&gt;&lt;tr&gt;&lt;td data-num=&#34;1&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;void&lt;/span&gt; &lt;span class=&#34;token function&#34;&gt;sendMessage&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token class-name&#34;&gt;String&lt;/span&gt; message&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;boolean&lt;/span&gt; toHud&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;&amp;#123;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;2&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;span class=&#34;token keyword&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;toHud&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;&amp;#123;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;3&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;        &lt;span class=&#34;token keyword&#34;&gt;this&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;client&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;inGameHud&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;getChatHud&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;addToMessageHistory&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;message&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;4&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;span class=&#34;token punctuation&#34;&gt;&amp;#125;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;5&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;6&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;span class=&#34;token keyword&#34;&gt;this&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;client&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;player&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;sendChatMessage&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;message&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;7&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token punctuation&#34;&gt;&amp;#125;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;8&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;位于&lt;span class=&#34;token operator&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;&lt;span class=&#34;token namespace&#34;&gt;net&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;minecraft&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;client&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;gui&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;screen&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;/span&gt;Screen&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;&lt;p&gt;这里我们同时有了两个发现，发送消息之前，会调用 ChatHud 类中的 addToMessageHistory 方法来将该聊天添加到聊天历史中，发送消息时调用 ClientPlayerEntity 的 sendChatMessage 方法来以客户端玩家的身份将消息发送至 server-side. 这里不难猜想是发送了一个 C2S 的网络包&lt;/p&gt;
&lt;figure class=&#34;highlight java&#34;&gt;&lt;figcaption data-lang=&#34;java&#34;&gt;&lt;/figcaption&gt;&lt;table&gt;&lt;tr&gt;&lt;td data-num=&#34;1&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;void&lt;/span&gt; &lt;span class=&#34;token function&#34;&gt;sendChatMessage&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token class-name&#34;&gt;String&lt;/span&gt; message&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;&amp;#123;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;2&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;span class=&#34;token keyword&#34;&gt;this&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;networkHandler&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;sendPacket&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token keyword&#34;&gt;new&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;ChatMessageC2SPacket&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;message&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;3&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token punctuation&#34;&gt;&amp;#125;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;4&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;位于&lt;span class=&#34;token operator&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;&lt;span class=&#34;token namespace&#34;&gt;net&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;minecraft&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;client&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;network&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;/span&gt;ClientPlayerEntity&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;&lt;p&gt;接下来我们的工作便清晰了，注入这两个方法并按照我们的需求进行修改&lt;/p&gt;
&lt;h4 id=&#34;3-唯手熟尔&#34;&gt;&lt;a class=&#34;anchor&#34; href=&#34;#3-唯手熟尔&#34;&gt;#&lt;/a&gt; 3. 唯手熟尔&lt;/h4&gt;
&lt;p&gt;在我们的 mixin 包中新建 ClientPlayerEntityMixin 类，写入如下内容&lt;/p&gt;
&lt;figure class=&#34;highlight java&#34;&gt;&lt;figcaption data-lang=&#34;java&#34;&gt;&lt;/figcaption&gt;&lt;table&gt;&lt;tr&gt;&lt;td data-num=&#34;1&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token annotation punctuation&#34;&gt;@Mixin&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token class-name&#34;&gt;ClientPlayerEntity&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token keyword&#34;&gt;class&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;2&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;class&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;ClientPlayerEntityMixin&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;&amp;#123;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;3&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;span class=&#34;token annotation punctuation&#34;&gt;@Inject&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;method &lt;span class=&#34;token operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;token string&#34;&gt;&#34;sendChatMessage&#34;&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;token comment&#34;&gt;// 要注入的方法 sendChatMessage&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;4&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;            at &lt;span class=&#34;token operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;token annotation punctuation&#34;&gt;@At&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token string&#34;&gt;&#34;HEAD&#34;&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;token comment&#34;&gt;// 表明插入的位置在方法的头部&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;5&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;            cancellable &lt;span class=&#34;token operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;token boolean&#34;&gt;true&lt;/span&gt; &lt;span class=&#34;token comment&#34;&gt;// 表明我们可以中途取消 (return) 这个方法&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;6&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;7&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;span class=&#34;token keyword&#34;&gt;private&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;void&lt;/span&gt; &lt;span class=&#34;token function&#34;&gt;sendChatMessage&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token class-name&#34;&gt;String&lt;/span&gt; message&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;CallbackInfo&lt;/span&gt; ci&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;&amp;#123;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;8&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;        &lt;span class=&#34;token keyword&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;message&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;toLowerCase&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;startsWith&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token string&#34;&gt;&#34;maho shoot&#34;&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;&amp;#123;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;9&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;            &lt;span class=&#34;token class-name&#34;&gt;ClientPlayNetworking&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;send&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token class-name&#34;&gt;ChantConstant&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token constant&#34;&gt;CHANT_IDENTIFIER&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;PacketByteBufs&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;create&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;writeString&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;message&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;10&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;            &lt;span class=&#34;token comment&#34;&gt;// 将施法网络包发送，这里会在下面部分进行讲解&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;11&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;            ci&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;cancel&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;12&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;        &lt;span class=&#34;token punctuation&#34;&gt;&amp;#125;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;13&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;span class=&#34;token punctuation&#34;&gt;&amp;#125;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;14&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token punctuation&#34;&gt;&amp;#125;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;&lt;p&gt;这个类被 @Mixin 修饰，意味着它是一个用来补充 ClientPlayerEntity 内容的 &amp;quot;类&amp;quot;, 与上文不同的是，这里我们对想要修改的方法添加了 @Inject 注解，而不是 @Overwrite 注解，相信读者已经通过字面意思猜到了二者之间的区别&lt;/p&gt;
&lt;p&gt;@Inject 注解表示将下面的代码&lt;strong&gt;插入&lt;/strong&gt;到目标方法内，我们在目标方法 (&amp;quot;sendChatMessage&amp;quot;) 的头部 (&amp;quot;HEAD&amp;quot;) 插入一个可取消的 (cancellable = true) 的方法.&lt;/p&gt;
&lt;p&gt;方法体内部，我们判断 message 内容是否以 &amp;quot;maho shoot&amp;quot; 开头，如果是的话，就把施法的动作发送给 server-side (这里涉及到一个 C2S 的网络包，会在下文讲解), 以便于我们在逻辑服务端部分处理施法的具体效果。最后，我们调用 ci.cancel (); 方法，代表我们希望在这里调用 return () 结束这个方法，不再继续向下执行，因为我们不想让施法的聊天消息再发出了.&lt;/p&gt;
&lt;p&gt;所以在游戏运行时，Minecraft 会把我们的 sendChatMessage 方法当成是这样的:&lt;/p&gt;
&lt;figure class=&#34;highlight java&#34;&gt;&lt;figcaption data-lang=&#34;java&#34;&gt;&lt;/figcaption&gt;&lt;table&gt;&lt;tr&gt;&lt;td data-num=&#34;1&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;void&lt;/span&gt; &lt;span class=&#34;token function&#34;&gt;sendChatMessage&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token class-name&#34;&gt;String&lt;/span&gt; message&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;&amp;#123;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;2&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;span class=&#34;token keyword&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;message&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;toLowerCase&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;startsWith&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token string&#34;&gt;&#34;maho shoot&#34;&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;&amp;#123;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;3&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;        &lt;span class=&#34;token class-name&#34;&gt;ClientPlayNetworking&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;send&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token class-name&#34;&gt;ChantConstant&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token constant&#34;&gt;CHANT_IDENTIFIER&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;PacketByteBufs&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;create&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;writeString&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;message&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;4&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;        &lt;span class=&#34;token keyword&#34;&gt;return&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;5&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;span class=&#34;token punctuation&#34;&gt;&amp;#125;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;6&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;span class=&#34;token keyword&#34;&gt;this&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;networkHandler&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;sendPacket&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token keyword&#34;&gt;new&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;ChatMessageC2SPacket&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;message&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;7&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token punctuation&#34;&gt;&amp;#125;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;&lt;p&gt;这符合我们的预期效果。接下来我们要做的事情就是在 server-side 处理我们这个网络包，这已经超出我们本节教程的范围，会在下文简略介绍。接下来我们来处理 messageHistory.&lt;/p&gt;
&lt;p&gt;在我们的 mixin 包中新建 ChatHudMixin 类，写入如下内容&lt;/p&gt;
&lt;figure class=&#34;highlight java&#34;&gt;&lt;figcaption data-lang=&#34;java&#34;&gt;&lt;/figcaption&gt;&lt;table&gt;&lt;tr&gt;&lt;td data-num=&#34;1&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token annotation punctuation&#34;&gt;@Mixin&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token class-name&#34;&gt;ChatHud&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token keyword&#34;&gt;class&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;2&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;class&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;ChatHudMixin&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;&amp;#123;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;3&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;span class=&#34;token annotation punctuation&#34;&gt;@Inject&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;method &lt;span class=&#34;token operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;token string&#34;&gt;&#34;addToMessageHistory&#34;&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;4&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;            at &lt;span class=&#34;token operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;token annotation punctuation&#34;&gt;@At&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token string&#34;&gt;&#34;HEAD&#34;&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;5&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;            cancellable &lt;span class=&#34;token operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;token boolean&#34;&gt;true&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;6&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;7&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;span class=&#34;token keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;void&lt;/span&gt; &lt;span class=&#34;token function&#34;&gt;addToMessageHistory&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token class-name&#34;&gt;String&lt;/span&gt; message&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;CallbackInfo&lt;/span&gt; ci&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;&amp;#123;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;8&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;        &lt;span class=&#34;token keyword&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;message&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;toLowerCase&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;startsWith&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token string&#34;&gt;&#34;maho shoot&#34;&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;&amp;#123;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;9&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;            ci&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;cancel&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;10&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;        &lt;span class=&#34;token punctuation&#34;&gt;&amp;#125;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;11&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;span class=&#34;token punctuation&#34;&gt;&amp;#125;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;12&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token punctuation&#34;&gt;&amp;#125;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;&lt;p&gt;相信这里笔者不用加以解释，读者已经充分理解并明白了这段代码的含义和作用.&lt;/p&gt;
&lt;p&gt;最后，我们来完成第二个需求，在输入魔咒时发出悦耳的声音。先前认真阅读过 ChatScreen 类的读者可能会发现这样一个方法&lt;/p&gt;
&lt;figure class=&#34;highlight java&#34;&gt;&lt;figcaption data-lang=&#34;java&#34;&gt;&lt;/figcaption&gt;&lt;table&gt;&lt;tr&gt;&lt;td data-num=&#34;1&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token keyword&#34;&gt;private&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;void&lt;/span&gt; &lt;span class=&#34;token function&#34;&gt;onChatFieldUpdate&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token class-name&#34;&gt;String&lt;/span&gt; chatText&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;&amp;#123;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;2&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;span class=&#34;token class-name&#34;&gt;String&lt;/span&gt; string &lt;span class=&#34;token operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;this&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;chatField&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;getText&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;3&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;span class=&#34;token keyword&#34;&gt;this&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;commandSuggestor&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;setWindowActive&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token operator&#34;&gt;!&lt;/span&gt;string&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;equals&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token keyword&#34;&gt;this&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;originalChatText&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;4&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;span class=&#34;token keyword&#34;&gt;this&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;commandSuggestor&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;refresh&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;5&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token punctuation&#34;&gt;&amp;#125;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;6&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;位于&lt;span class=&#34;token operator&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;&lt;span class=&#34;token namespace&#34;&gt;net&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;minecraft&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;client&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;gui&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;screen&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;/span&gt;ChatScreen&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;&lt;p&gt;看起来这个类原本是为了在输入指令的时候进行自动补全的，这里刚好迎合了我们的需求，有了之前的经验，现在我们很清晰地明白，我们将使用 Inject 注解在这个方法中填上有关播放音效的代码.&lt;/p&gt;
&lt;p&gt;在我们的 mixin 包中新建 ChatScreenMixin 类，写入如下内容&lt;/p&gt;
&lt;figure class=&#34;highlight java&#34;&gt;&lt;figcaption data-lang=&#34;java&#34;&gt;&lt;/figcaption&gt;&lt;table&gt;&lt;tr&gt;&lt;td data-num=&#34;1&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token annotation punctuation&#34;&gt;@Inject&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;method &lt;span class=&#34;token operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;token string&#34;&gt;&#34;onChatFieldUpdate&#34;&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;2&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;        at &lt;span class=&#34;token operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;token annotation punctuation&#34;&gt;@At&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token string&#34;&gt;&#34;RETURN&#34;&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;token comment&#34;&gt;// 这里我们尝试在方法结束前插入这段代码&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;3&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;4&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;void&lt;/span&gt; &lt;span class=&#34;token function&#34;&gt;onChatFieldUpdate&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token class-name&#34;&gt;String&lt;/span&gt; message&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;CallbackInfo&lt;/span&gt; ci&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;&amp;#123;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;5&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;span class=&#34;token class-name&#34;&gt;ClientPlayerEntity&lt;/span&gt; player &lt;span class=&#34;token operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;MinecraftClient&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;getInstance&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;player&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;6&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;span class=&#34;token keyword&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;message&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;toLowerCase&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;startsWith&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token string&#34;&gt;&#34;maho shoot &#34;&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;token operator&#34;&gt;&amp;amp;&amp;amp;&lt;/span&gt; player &lt;span class=&#34;token operator&#34;&gt;!=&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;null&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;&amp;#123;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;7&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;        player&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;playSound&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token class-name&#34;&gt;SoundEvents&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token constant&#34;&gt;ENTITY_EXPERIENCE_ORB_PICKUP&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;token number&#34;&gt;0.1f&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;token number&#34;&gt;0.8f&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;8&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;span class=&#34;token punctuation&#34;&gt;&amp;#125;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;9&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token punctuation&#34;&gt;&amp;#125;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;&lt;p&gt;方法体内部，我们用 MinecraftClient.getInstance ().player 获取到的玩家对象是运行在逻辑客户端上的玩家，因为我们仅仅是想播放声音，这种操作是可取的.&lt;/p&gt;
&lt;p&gt;之后，我们判断聊天信息是否是以施法的前缀 &amp;quot;maho shoot&amp;quot; 开头的，如果是这样的话，就播放经验球捡起的声音。需要注意的是，这个声音因为是在客户端玩家对象上播放的，固然只有施法的玩家自己能听见，若想要让服务器中附近的所有玩家听到，也要用我们上文类似的方法发送一个 C2S 网络包，并让播放音效的语句在逻辑服务端执行.&lt;/p&gt;
&lt;p&gt;最后，将我们新建的两个 Mixin 类添加到 mixins.json 中，我们就可以打开游戏测试了.&lt;/p&gt;
&lt;h4 id=&#34;4-请问您今天要来点额外知识吗&#34;&gt;&lt;a class=&#34;anchor&#34; href=&#34;#4-请问您今天要来点额外知识吗&#34;&gt;#&lt;/a&gt; 4. 请问您今天要来点额外知识吗？&lt;/h4&gt;
&lt;h5 id=&#34;clientserverplaynetworking&#34;&gt;&lt;a class=&#34;anchor&#34; href=&#34;#clientserverplaynetworking&#34;&gt;#&lt;/a&gt; Client/ServerPlayNetworking&lt;/h5&gt;
&lt;p&gt;上文我们调用了 &lt;code&gt;ClientPlayNetworking.send(ChantConstant.CHANT_IDENTIFIER, PacketByteBufs.create().writeString(message));&lt;/code&gt;  方法，将施法的网络包发给了 server-side. 这里我们提供了两个参数，一个是我们要发送的字节集数据，另一个是网络包的标识符。这里我们的标识符在一个名为 ChantConstant 的类中定义 &lt;code&gt;public static final Identifier CHANT_IDENTIFIER = new Identifier(&amp;quot;shinto&amp;quot;, &amp;quot;chant&amp;quot;);&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;接下来我们来到服务端侧，来看看处理施法的过程&lt;/p&gt;
&lt;p&gt;新建 network 包，创建 ServerPacketHandler 类，写入以下内容&lt;/p&gt;
&lt;figure class=&#34;highlight java&#34;&gt;&lt;figcaption data-lang=&#34;java&#34;&gt;&lt;/figcaption&gt;&lt;table&gt;&lt;tr&gt;&lt;td data-num=&#34;1&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;class&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;ServerPacketHandler&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;&amp;#123;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;2&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;span class=&#34;token keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;static&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;void&lt;/span&gt; &lt;span class=&#34;token function&#34;&gt;init&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;&amp;#123;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;3&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;        &lt;span class=&#34;token class-name&#34;&gt;ServerPlayNetworking&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;registerGlobalReceiver&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token class-name&#34;&gt;ChantConstant&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token constant&#34;&gt;CHANT_IDENTIFIER&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;server&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; player&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; handler&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; buf&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; responseSender&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;token operator&#34;&gt;-&gt;&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;&amp;#123;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;4&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;            &lt;span class=&#34;token class-name&#34;&gt;String&lt;/span&gt; str &lt;span class=&#34;token operator&#34;&gt;=&lt;/span&gt; buf&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;readString&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token number&#34;&gt;32767&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;5&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;            server&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;execute&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;token operator&#34;&gt;-&gt;&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;&amp;#123;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;6&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;                &lt;span class=&#34;token keyword&#34;&gt;new&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;StatementResolver&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;resolve&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;str&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; player&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;7&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;            &lt;span class=&#34;token punctuation&#34;&gt;&amp;#125;&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;8&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;        &lt;span class=&#34;token punctuation&#34;&gt;&amp;#125;&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;9&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;span class=&#34;token punctuation&#34;&gt;&amp;#125;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;10&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token punctuation&#34;&gt;&amp;#125;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;&lt;p&gt;这里我们调用 ServerPlayNetworking 类来注册一个监听器，等到网络包一到达，我们读取其中的字符串，并且用服务端线程执行施法的相关代码，这里笔者施法的解析放在了 StatementResolver 类中，对笔者魔法 Mod 感兴趣的读者可以前来阅读&lt;/p&gt;
&lt;h5 id=&#34;inject注解的locals参数&#34;&gt;&lt;a class=&#34;anchor&#34; href=&#34;#inject注解的locals参数&#34;&gt;#&lt;/a&gt; @Inject 注解的 locals 参数&lt;/h5&gt;
&lt;p&gt;当我们注入一个方法时，可能会有需要访问方法内的局部变量的时候，虽然本教程尚未涉及，这里笔者也希望给予补充。这种情况下，我们通常会给 @Inject 注解添加 locals 参数，这样我们就可以捕获局部变量表了.&lt;/p&gt;
&lt;p&gt;比如说我们可以让 @Inject 注解变成这个样子&lt;/p&gt;
&lt;figure class=&#34;highlight java&#34;&gt;&lt;figcaption data-lang=&#34;java&#34;&gt;&lt;/figcaption&gt;&lt;table&gt;&lt;tr&gt;&lt;td data-num=&#34;1&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token annotation punctuation&#34;&gt;@Inject&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;method &lt;span class=&#34;token operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;token string&#34;&gt;&#34;myFunction&#34;&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;2&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt; at &lt;span class=&#34;token operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;token annotation punctuation&#34;&gt;@At&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token string&#34;&gt;&#34;HEAD&#34;&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; &lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;3&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;cancellable &lt;span class=&#34;token operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;token boolean&#34;&gt;true&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; &lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;4&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;locals &lt;span class=&#34;token operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;LocalCapture&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token constant&#34;&gt;CAPTURE_FAILHARD&lt;/span&gt; &lt;span class=&#34;token comment&#34;&gt;// 我们需要捕捉这个方法的局部变量表&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;5&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt; &lt;span class=&#34;token comment&#34;&gt;//FAILHARD 表示一旦捕捉失败，游戏不会继续加载，发生崩溃&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;6&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt; &lt;span class=&#34;token comment&#34;&gt;//FAILSOFT 表示一旦捕捉失败，游戏继续加载，放弃注入这个方法&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;7&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;&lt;p&gt;那么我们为什么要用这种复杂的方式呢？简要来说，局部变量表具有不确定性，Mixin 只能根据字节码推测局部变量，这导致当其他 Mod 一旦与你有共同的需求，都&lt;strong&gt;注入了同一个方法&lt;/strong&gt;并且&lt;strong&gt;给这个方法添加了局部变量&lt;/strong&gt;，事情可能就会复杂起来，因为此时局部变量表对于两个 Mod 双方在游戏实际跑起来之前都是不确定的。所以双方都需要做好一旦方法注入失败之后的打算.&lt;/p&gt;
&lt;p&gt;所以，我们的局部变量列表并不是所见即所得的，而且我们也不能通过变量的确切名字来访问变量 (因为 Mixin 也不知道这个名字究竟指哪个变量，Mixin 只有一个标记着数据类型的有序列表), 所以推荐读者第一次注入该方法时将 &lt;code&gt;locals&lt;/code&gt;  设为 &lt;code&gt;LocalCapture.PRINT&lt;/code&gt; ，以便确定具体的局部变量表，如果知道可能产生冲突的 Mod, 可以将那个 Mod 加入进开发环境，再重新 PRINT 并编写重载后的代码，将可能冲突的情况避免。mixin 会告诉你此时的方法应该怎么写，读者可以加以尝试&lt;/p&gt;
&lt;p&gt;这里笔者推荐读者阅读&lt;a href=&#34;https://xfl03.gitbook.io/coremodtutor/5/5.3&#34;&gt; CoreModTutor 中的相关内容&lt;/a&gt;，结合 Minecraft 原版中的具体例子进行理解，这里笔者就不过多赘述了.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;注：这里笔者特别感谢 xfl03 的 CoreModTutor 中对 Mixin 系统而详细的介绍，笔者的知识储备部分来源于此，当读者希望对核心概念有更深理解时，可以阅读 xfl03 的 [CoreModTutor](&lt;a href=&#34;https://xfl03.gitbook.io/coremodtutor/5&#34;&gt;5 Mixin - CoreModTutor (gitbook.io)&lt;/a&gt;)&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr /&gt;
&lt;h2 id=&#34;三-关于工作台上现在可以保持物品不掉下来这档事&#34;&gt;&lt;a class=&#34;anchor&#34; href=&#34;#三-关于工作台上现在可以保持物品不掉下来这档事&#34;&gt;#&lt;/a&gt; 三、关于工作台上现在可以保持物品不掉下来这档事&lt;/h2&gt;
&lt;p&gt;本部分将为读者展现一个非常常规的需求：将物品保持在工作台中不掉落。在探究过程中，我们将继续沿用我们上面现象到本质的探究思路，从工作台本身出发，一步步探求其背后的代码实现以及注入方案，培养读者探究一个未知问题的能力.&lt;/p&gt;
&lt;h3 id=&#34;1-切入要点&#34;&gt;&lt;a class=&#34;anchor&#34; href=&#34;#1-切入要点&#34;&gt;#&lt;/a&gt; 1. 切入要点&lt;/h3&gt;
&lt;p&gt;同样的，我们利用 idea 的搜索功能搜索 Crafting 关键字，并且筛选我们可能需要用到的类，我们最后留下了下面这些看起来与我们需求有关的类&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;CraftingTablework Block 类的子类，定义了工作台这个方块&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;CraftingScreen Screen 类的子类，定义了合成界面是如何渲染的&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;CraftingScreenHandler 处理合成界面的类，可能与创建合成 GUI 有关&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;CraftingInventory 处理合成界面九宫格上的物品的类&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;经过对代码简单的阅读，我们先排除掉了 CraftingScreen 类，因为 CraftingScreen 类中大多是关于界面渲染有关的方法和语句，这显然与我们的需求无关.&lt;/p&gt;
&lt;p&gt;之后为了理清各个类之间的关系，我们开始按照我们的思路分析问题。设想一下，我们在游戏中使用工作台的第一个动作，就是对工作台右键单击，我们可以以此为切入点，看看右击工作台会发生什么。在 CraftingTableBlock 类的 onUse 方法中，调用了 createScreenHandlerFactory 来打开合成的 GUI 界面.&lt;/p&gt;
&lt;figure class=&#34;highlight java&#34;&gt;&lt;figcaption data-lang=&#34;java&#34;&gt;&lt;/figcaption&gt;&lt;table&gt;&lt;tr&gt;&lt;td data-num=&#34;1&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;NamedScreenHandlerFactory&lt;/span&gt; &lt;span class=&#34;token function&#34;&gt;createScreenHandlerFactory&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token class-name&#34;&gt;BlockState&lt;/span&gt; state&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;World&lt;/span&gt; world&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;BlockPos&lt;/span&gt; pos&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;&amp;#123;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;2&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;span class=&#34;token keyword&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;new&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;SimpleNamedScreenHandlerFactory&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;i&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; playerInventory&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; playerEntity&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;token operator&#34;&gt;-&gt;&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;&amp;#123;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;3&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;        &lt;span class=&#34;token keyword&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;new&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;CraftingScreenHandler&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;i&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; playerInventory&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;ScreenHandlerContext&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;create&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;world&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; pos&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;4&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;span class=&#34;token punctuation&#34;&gt;&amp;#125;&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;token constant&#34;&gt;TITLE&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;5&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;span class=&#34;token comment&#34;&gt;// 在玩家的 GUI 中显示新创建的 CraftingScreenHandler 界面&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;6&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token punctuation&#34;&gt;&amp;#125;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;7&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;位于&lt;span class=&#34;token operator&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;&lt;span class=&#34;token namespace&#34;&gt;net&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;minecraft&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;block&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;/span&gt;CraftingTableBlock&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;&lt;p&gt;可以看到，这里右键单击时，是实例化了一个 CraftingScreenHandler 类，在这个类的构造方法中，我们可以找到下面这样的语句&lt;/p&gt;
&lt;figure class=&#34;highlight java&#34;&gt;&lt;figcaption data-lang=&#34;java&#34;&gt;&lt;/figcaption&gt;&lt;table&gt;&lt;tr&gt;&lt;td data-num=&#34;1&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;CraftingScreenHandler&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token keyword&#34;&gt;int&lt;/span&gt; syncId&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;PlayerInventory&lt;/span&gt; playerInventory&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;ScreenHandlerContext&lt;/span&gt; context&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;&amp;#123;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;2&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;span class=&#34;token keyword&#34;&gt;super&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token class-name&#34;&gt;ScreenHandlerType&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token constant&#34;&gt;CRAFTING&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; syncId&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;3&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;span class=&#34;token keyword&#34;&gt;this&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;input &lt;span class=&#34;token operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;new&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;CraftingInventory&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token keyword&#34;&gt;this&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;token number&#34;&gt;3&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;token number&#34;&gt;3&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;4&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;span class=&#34;token comment&#34;&gt;// 在构造方法中创建一个 3*3 的 CraftingInventory, 用来存放待合成品&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;5&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;6&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token punctuation&#34;&gt;&amp;#125;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;7&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;位于&lt;span class=&#34;token operator&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;&lt;span class=&#34;token namespace&#34;&gt;net&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;minecraft&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;screen&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;/span&gt;CraftingScreenHandler&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;&lt;p&gt;当我们继续向下查询 CraftingInventory 类，我们发现了下面这样的代码&lt;/p&gt;
&lt;figure class=&#34;highlight java&#34;&gt;&lt;figcaption data-lang=&#34;java&#34;&gt;&lt;/figcaption&gt;&lt;table&gt;&lt;tr&gt;&lt;td data-num=&#34;1&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token keyword&#34;&gt;private&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;final&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;DefaultedList&lt;/span&gt;&lt;span class=&#34;token generics&#34;&gt;&lt;span class=&#34;token punctuation&#34;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&#34;token class-name&#34;&gt;ItemStack&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;&gt;&lt;/span&gt;&lt;/span&gt; stacks&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;2&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;CraftingInventory&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token class-name&#34;&gt;ScreenHandler&lt;/span&gt; handler&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;int&lt;/span&gt; width&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;int&lt;/span&gt; height&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;&amp;#123;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;3&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;span class=&#34;token keyword&#34;&gt;this&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;stacks &lt;span class=&#34;token operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;DefaultedList&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;ofSize&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;width &lt;span class=&#34;token operator&#34;&gt;*&lt;/span&gt; height&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;ItemStack&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token constant&#34;&gt;EMPTY&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;4&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;span class=&#34;token comment&#34;&gt;//CraftingInventory 中维护一个有序列表，来存放一些 Itemstack&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;5&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;6&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token punctuation&#34;&gt;&amp;#125;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;7&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;位于&lt;span class=&#34;token operator&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;&lt;span class=&#34;token namespace&#34;&gt;net&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;minecraft&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;inventory&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;/span&gt;CraftingInventory&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;&lt;p&gt;相信聪明的读者已经从这些代码中明确了三个类之间的关系.&lt;/p&gt;
&lt;p&gt;我们期望 CraftingScreenHandler 类中能有一个类似于 setInputItems () 和 getInputItems () 的方法，如果这样的话，我们只需要在关闭界面的时候把 get 到的物品列表储存在方块内，并且在打开界面的时候把物品列表 set 进去就可以了，我们清楚的知道，mixin 应该能帮助我们做到这一点.&lt;/p&gt;
&lt;p&gt;我们的思路已经基本明确，现在的首要任务便是利用 mixin 为我们添加进去这一组 getset&lt;/p&gt;
&lt;h4 id=&#34;2-做出尝试&#34;&gt;&lt;a class=&#34;anchor&#34; href=&#34;#2-做出尝试&#34;&gt;#&lt;/a&gt; 2. 做出尝试&lt;/h4&gt;
&lt;p&gt;在我们的 mixin 包中创建 CraftingScreenHandlerMixin 类，按照我们的直觉，我们的代码可能是这样的&lt;/p&gt;
&lt;figure class=&#34;highlight java&#34;&gt;&lt;figcaption data-lang=&#34;java&#34;&gt;&lt;/figcaption&gt;&lt;table&gt;&lt;tr&gt;&lt;td data-num=&#34;1&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token annotation punctuation&#34;&gt;@Mixin&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token class-name&#34;&gt;CraftingScreenHandler&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token keyword&#34;&gt;class&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;2&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;class&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;CraftingScreenHandlerMixin&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;&amp;#123;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;3&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;span class=&#34;token keyword&#34;&gt;private&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;DefaultedList&lt;/span&gt;&lt;span class=&#34;token generics&#34;&gt;&lt;span class=&#34;token punctuation&#34;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&#34;token class-name&#34;&gt;ItemStack&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;&gt;&lt;/span&gt;&lt;/span&gt; &lt;span class=&#34;token function&#34;&gt;getInputItems&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;&amp;#123;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;4&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;        &lt;span class=&#34;token keyword&#34;&gt;return&lt;/span&gt; input&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;stacks&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;5&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;span class=&#34;token punctuation&#34;&gt;&amp;#125;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;6&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;7&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;span class=&#34;token keyword&#34;&gt;private&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;void&lt;/span&gt; &lt;span class=&#34;token function&#34;&gt;setInputItems&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token class-name&#34;&gt;DefaultedList&lt;/span&gt;&lt;span class=&#34;token generics&#34;&gt;&lt;span class=&#34;token punctuation&#34;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&#34;token class-name&#34;&gt;ItemStack&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;&gt;&lt;/span&gt;&lt;/span&gt; itemStacks&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;&amp;#123;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;8&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;        &lt;span class=&#34;token keyword&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;itemStacks &lt;span class=&#34;token operator&#34;&gt;!=&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;null&lt;/span&gt; &lt;span class=&#34;token operator&#34;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&#34;token operator&#34;&gt;!&lt;/span&gt;itemStacks&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;isEmpty&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;&amp;#123;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;9&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;            &lt;span class=&#34;token keyword&#34;&gt;for&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token keyword&#34;&gt;int&lt;/span&gt; i &lt;span class=&#34;token operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;token number&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt; i &lt;span class=&#34;token operator&#34;&gt;&amp;lt;&lt;/span&gt; itemStacks&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;size&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt; i&lt;span class=&#34;token operator&#34;&gt;++&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;&amp;#123;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;10&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;                input&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;stacks&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;set&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;i&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; itemStacks&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;get&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;i&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;11&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;            &lt;span class=&#34;token punctuation&#34;&gt;&amp;#125;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;12&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;            &lt;span class=&#34;token function&#34;&gt;onContentChanged&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token keyword&#34;&gt;null&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt; &lt;span class=&#34;token comment&#34;&gt;// 为了让合成产物更新，改变完物品列表后应该手动进行刷新&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;13&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;        &lt;span class=&#34;token punctuation&#34;&gt;&amp;#125;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;14&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;span class=&#34;token punctuation&#34;&gt;&amp;#125;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;15&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token punctuation&#34;&gt;&amp;#125;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;&lt;p&gt;idea 此时为我们报出了许多错误，其中最为明显的就是找不到 input 这个变量，我们清楚地记得，input 这个成员是在 CraftingScreenHandler 中明确定义了的，为什么这里却找不到了呢...&lt;/p&gt;
&lt;p&gt;了解 Java 的读者此时可能已经意识到了事情的不对，虽然我们目标类中有 input 这个成员，但是对于 Java 编译器，CraftingScreenHandlerMixin 中确确实实没有声明 input, 这里 mixin 为我们提供了一种方法，@Shadow 注解&lt;/p&gt;
&lt;p&gt;@Shadow 注解意味着被标记的成员或方法在目标类中出现。当我们需要引用这个成员或方法的时候，我们可以用 @Shadow 注解来欺骗 Java 编译器，所以我们在代码中添加下面几行&lt;/p&gt;
&lt;figure class=&#34;highlight java&#34;&gt;&lt;figcaption data-lang=&#34;java&#34;&gt;&lt;/figcaption&gt;&lt;table&gt;&lt;tr&gt;&lt;td data-num=&#34;1&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token annotation punctuation&#34;&gt;@Shadow&lt;/span&gt; &lt;span class=&#34;token annotation punctuation&#34;&gt;@Final&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;private&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;CraftingInventory&lt;/span&gt; input&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;2&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token comment&#34;&gt;// 这里还添加了 @Final 注解，这个很好理解，目标类中原本被 final 修饰的成员推荐在 mixin 类中添加 @Final 注解，这样当你不小心更改了这个成员的值的时候 Mixin 会提醒你的&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;3&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token annotation punctuation&#34;&gt;@Shadow&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;abstract&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;void&lt;/span&gt; &lt;span class=&#34;token function&#34;&gt;onContentChanged&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token class-name&#34;&gt;Inventory&lt;/span&gt; inventory&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;4&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token comment&#34;&gt;// 这里 @Shadow 修饰的方法 onContentChanged 被定义为了抽象方法，这也符合实际情况，因为 onContentChanged 的具体实现是存放在 CraftingScreenHandler 类中的&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;5&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token comment&#34;&gt;// 这里我们的 Mixin 类可能需要随之更改为抽象类，不过这没什么影响&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;&lt;p&gt;然后，我们发现代码中仍旧有红色的报错，它告诉我们 stacks 是 CraftingInventory 的私有属性，我们无法访问。强大的 mixin 当然能简单地破除访问限制，这里我们需要新建一个接口，来读取 stacks.&lt;/p&gt;
&lt;p&gt;在 mixin 包下创建一个类 IMixinCraftingInventory, 写入如下代码&lt;/p&gt;
&lt;figure class=&#34;highlight java&#34;&gt;&lt;figcaption data-lang=&#34;java&#34;&gt;&lt;/figcaption&gt;&lt;table&gt;&lt;tr&gt;&lt;td data-num=&#34;1&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token annotation punctuation&#34;&gt;@Mixin&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token class-name&#34;&gt;CraftingInventory&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token keyword&#34;&gt;class&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;2&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;interface&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;IMixinCraftingInventory&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;&amp;#123;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;3&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;span class=&#34;token annotation punctuation&#34;&gt;@Accessor&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;4&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;span class=&#34;token class-name&#34;&gt;DefaultedList&lt;/span&gt;&lt;span class=&#34;token generics&#34;&gt;&lt;span class=&#34;token punctuation&#34;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&#34;token class-name&#34;&gt;ItemStack&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;&gt;&lt;/span&gt;&lt;/span&gt; &lt;span class=&#34;token function&#34;&gt;getStacks&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;5&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token punctuation&#34;&gt;&amp;#125;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;&lt;p&gt;@Accessor 注解会帮助我们获取我们想要的字段，而直接忽略访问限制。需要注意的是方法名需要以 get 开头，并且返回值就是想要获取的字段的类型，我们可以使用如下的方式来访问这个字段&lt;/p&gt;
&lt;figure class=&#34;highlight java&#34;&gt;&lt;figcaption data-lang=&#34;java&#34;&gt;&lt;/figcaption&gt;&lt;table&gt;&lt;tr&gt;&lt;td data-num=&#34;1&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token class-name&#34;&gt;IMixinCraftingInventory&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt; input&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;getStacks&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;&lt;p&gt;所以最终，我们的 CraftingScreenHandlerMixin 类应该变成这样子&lt;/p&gt;
&lt;figure class=&#34;highlight java&#34;&gt;&lt;figcaption data-lang=&#34;java&#34;&gt;&lt;/figcaption&gt;&lt;table&gt;&lt;tr&gt;&lt;td data-num=&#34;1&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token annotation punctuation&#34;&gt;@Mixin&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token class-name&#34;&gt;CraftingScreenHandler&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token keyword&#34;&gt;class&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;2&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;abstract&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;class&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;CraftingScreenHandlerMixin&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;&amp;#123;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;3&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;span class=&#34;token annotation punctuation&#34;&gt;@Shadow&lt;/span&gt; &lt;span class=&#34;token annotation punctuation&#34;&gt;@Final&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;private&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;CraftingInventory&lt;/span&gt; input&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;4&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;span class=&#34;token annotation punctuation&#34;&gt;@Shadow&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;abstract&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;void&lt;/span&gt; &lt;span class=&#34;token function&#34;&gt;onContentChanged&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token class-name&#34;&gt;Inventory&lt;/span&gt; inventory&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;5&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;span class=&#34;token keyword&#34;&gt;private&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;final&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;DefaultedList&lt;/span&gt;&lt;span class=&#34;token generics&#34;&gt;&lt;span class=&#34;token punctuation&#34;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&#34;token class-name&#34;&gt;ItemStack&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;&gt;&lt;/span&gt;&lt;/span&gt; itemList &lt;span class=&#34;token operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token class-name&#34;&gt;IMixinCraftingInventory&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt; input&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;getStacks&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;6&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;7&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;span class=&#34;token keyword&#34;&gt;private&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;DefaultedList&lt;/span&gt;&lt;span class=&#34;token generics&#34;&gt;&lt;span class=&#34;token punctuation&#34;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&#34;token class-name&#34;&gt;ItemStack&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;&gt;&lt;/span&gt;&lt;/span&gt; &lt;span class=&#34;token function&#34;&gt;getInputItems&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;&amp;#123;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;8&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;        &lt;span class=&#34;token keyword&#34;&gt;return&lt;/span&gt; itemList&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;9&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;span class=&#34;token punctuation&#34;&gt;&amp;#125;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;10&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;11&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;span class=&#34;token keyword&#34;&gt;private&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;void&lt;/span&gt; &lt;span class=&#34;token function&#34;&gt;setInputItems&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token class-name&#34;&gt;DefaultedList&lt;/span&gt;&lt;span class=&#34;token generics&#34;&gt;&lt;span class=&#34;token punctuation&#34;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&#34;token class-name&#34;&gt;ItemStack&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;&gt;&lt;/span&gt;&lt;/span&gt; itemStacks&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;&amp;#123;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;12&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;        &lt;span class=&#34;token keyword&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;itemStacks &lt;span class=&#34;token operator&#34;&gt;!=&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;null&lt;/span&gt; &lt;span class=&#34;token operator&#34;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&#34;token operator&#34;&gt;!&lt;/span&gt;itemStacks&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;isEmpty&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;&amp;#123;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;13&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;            &lt;span class=&#34;token keyword&#34;&gt;for&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token keyword&#34;&gt;int&lt;/span&gt; i &lt;span class=&#34;token operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;token number&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt; i &lt;span class=&#34;token operator&#34;&gt;&amp;lt;&lt;/span&gt; itemStacks&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;size&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt; i&lt;span class=&#34;token operator&#34;&gt;++&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;&amp;#123;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;14&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;                itemList&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;set&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;i&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; itemStacks&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;get&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;i&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;15&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;            &lt;span class=&#34;token punctuation&#34;&gt;&amp;#125;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;16&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;            &lt;span class=&#34;token function&#34;&gt;onContentChanged&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token keyword&#34;&gt;null&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;17&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;        &lt;span class=&#34;token punctuation&#34;&gt;&amp;#125;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;18&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;span class=&#34;token punctuation&#34;&gt;&amp;#125;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;19&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token punctuation&#34;&gt;&amp;#125;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;&lt;p&gt;至此，我们彻底完成了我们需要的这两个方法&lt;/p&gt;
&lt;h4 id=&#34;3-最后一步&#34;&gt;&lt;a class=&#34;anchor&#34; href=&#34;#3-最后一步&#34;&gt;#&lt;/a&gt; 3. 最后一步&lt;/h4&gt;
&lt;p&gt;之后，我们就需要适时地保存或者读取我们的物品列表了。相信聪明的读者现在立马脑中会想到一件事情，我们起初查询 crafting 关键字的时候，没有发现工作台有储存数据的能力，即我们没有找到类似 CraftingTableBlockEntity 的字眼，这意味着我们需要自己创建一个 BlockEntity 来储存需要的数据.&lt;/p&gt;
&lt;p&gt;新建 blockentity 包，创建 CraftingTableBlockEntity 类并写入以下内容&lt;/p&gt;
&lt;figure class=&#34;highlight java&#34;&gt;&lt;figcaption data-lang=&#34;java&#34;&gt;&lt;/figcaption&gt;&lt;table&gt;&lt;tr&gt;&lt;td data-num=&#34;1&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;class&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;CraftingTableBlockEntity&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;extends&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;BlockEntity&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;&amp;#123;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;2&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;span class=&#34;token keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;DefaultedList&lt;/span&gt;&lt;span class=&#34;token generics&#34;&gt;&lt;span class=&#34;token punctuation&#34;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&#34;token class-name&#34;&gt;ItemStack&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;&gt;&lt;/span&gt;&lt;/span&gt; innerItemStacks &lt;span class=&#34;token operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;DefaultedList&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;ofSize&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token number&#34;&gt;9&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;ItemStack&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token constant&#34;&gt;EMPTY&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;3&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;4&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;span class=&#34;token keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;CraftingTableBlockEntity&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;&amp;#123;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;5&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;        &lt;span class=&#34;token keyword&#34;&gt;super&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token class-name&#34;&gt;MyMod&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token constant&#34;&gt;CRAFTINGTABLE_BLOCKENTITY&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;6&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;span class=&#34;token punctuation&#34;&gt;&amp;#125;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;7&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;8&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;span class=&#34;token keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;CompoundTag&lt;/span&gt; &lt;span class=&#34;token function&#34;&gt;toTag&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token class-name&#34;&gt;CompoundTag&lt;/span&gt; tag&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;&amp;#123;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;9&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;        &lt;span class=&#34;token keyword&#34;&gt;super&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;toTag&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;tag&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;10&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;        &lt;span class=&#34;token class-name&#34;&gt;Inventories&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;toTag&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;tag&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; innerItemStacks&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;11&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;        &lt;span class=&#34;token keyword&#34;&gt;return&lt;/span&gt; tag&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;12&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;span class=&#34;token punctuation&#34;&gt;&amp;#125;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;13&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;14&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;span class=&#34;token annotation punctuation&#34;&gt;@Override&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;15&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;span class=&#34;token keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;void&lt;/span&gt; &lt;span class=&#34;token function&#34;&gt;fromTag&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token class-name&#34;&gt;BlockState&lt;/span&gt; state&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;CompoundTag&lt;/span&gt; tag&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;&amp;#123;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;16&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;        &lt;span class=&#34;token keyword&#34;&gt;super&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;fromTag&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;state&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; tag&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;17&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;        &lt;span class=&#34;token keyword&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;innerItemStacks &lt;span class=&#34;token operator&#34;&gt;!=&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;null&lt;/span&gt; &lt;span class=&#34;token operator&#34;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&#34;token operator&#34;&gt;!&lt;/span&gt;innerItemStacks&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;isEmpty&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;&amp;#123;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;18&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;            &lt;span class=&#34;token class-name&#34;&gt;Inventories&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;fromTag&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;tag&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; innerItemStacks&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;19&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;        &lt;span class=&#34;token punctuation&#34;&gt;&amp;#125;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;20&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;span class=&#34;token punctuation&#34;&gt;&amp;#125;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;21&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token punctuation&#34;&gt;&amp;#125;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;&lt;p&gt;我们更改 Mod 主类，注册 BlockEntity&lt;/p&gt;
&lt;figure class=&#34;highlight java&#34;&gt;&lt;figcaption data-lang=&#34;java&#34;&gt;&lt;/figcaption&gt;&lt;table&gt;&lt;tr&gt;&lt;td data-num=&#34;1&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;class&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;MyMod&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;implements&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;ModInitializer&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;&amp;#123;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;2&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;span class=&#34;token keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;static&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;BlockEntityType&lt;/span&gt;&lt;span class=&#34;token generics&#34;&gt;&lt;span class=&#34;token punctuation&#34;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&#34;token class-name&#34;&gt;CraftingTableBlockEntity&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;&gt;&lt;/span&gt;&lt;/span&gt; &lt;span class=&#34;token constant&#34;&gt;CRAFTINGTABLE_BLOCKENTITY&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;3&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;4&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;span class=&#34;token annotation punctuation&#34;&gt;@Override&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;5&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;span class=&#34;token keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;void&lt;/span&gt; &lt;span class=&#34;token function&#34;&gt;onInitialize&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;&amp;#123;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;6&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;        &lt;span class=&#34;token constant&#34;&gt;CRAFTINGTABLE_BLOCKENTITY&lt;/span&gt; &lt;span class=&#34;token operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;Registry&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;register&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token class-name&#34;&gt;Registry&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token constant&#34;&gt;BLOCK_ENTITY_TYPE&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;token string&#34;&gt;&#34;mymod:crafting_table&#34;&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;BlockEntityType&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;Builder&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;create&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token class-name&#34;&gt;CraftingTableBlockEntity&lt;/span&gt;&lt;span class=&#34;token operator&#34;&gt;::&lt;/span&gt;&lt;span class=&#34;token keyword&#34;&gt;new&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;Blocks&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token constant&#34;&gt;CRAFTING_TABLE&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;build&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token keyword&#34;&gt;null&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;7&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;span class=&#34;token punctuation&#34;&gt;&amp;#125;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;8&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token punctuation&#34;&gt;&amp;#125;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;&lt;p&gt;到这里，我们的工作台已经有了存取物品的能力，下面我们添加相关代码。如果仔细阅读过 CraftingScreenHandler 类的读者可能会注意到一个名为 close 的方法&lt;/p&gt;
&lt;figure class=&#34;highlight java&#34;&gt;&lt;figcaption data-lang=&#34;java&#34;&gt;&lt;/figcaption&gt;&lt;table&gt;&lt;tr&gt;&lt;td data-num=&#34;1&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;void&lt;/span&gt; &lt;span class=&#34;token function&#34;&gt;close&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token class-name&#34;&gt;PlayerEntity&lt;/span&gt; player&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;&amp;#123;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;2&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;span class=&#34;token keyword&#34;&gt;super&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;close&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;player&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;3&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;span class=&#34;token keyword&#34;&gt;this&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;context&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;run&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;world&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; blockPos&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;token operator&#34;&gt;-&gt;&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;&amp;#123;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;4&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;        &lt;span class=&#34;token keyword&#34;&gt;this&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;dropInventory&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;player&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; world&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;this&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;input&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;5&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;span class=&#34;token punctuation&#34;&gt;&amp;#125;&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;6&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token punctuation&#34;&gt;&amp;#125;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;7&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;位于&lt;span class=&#34;token operator&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;&lt;span class=&#34;token namespace&#34;&gt;net&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;minecraft&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;screen&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;/span&gt;CraftingScreenHandler&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;&lt;p&gt;很明显，原版的逻辑是关闭界面后掉落所有物品。这里我们将更改 close 方法，使关闭界面时工作台内物品得以保存。我们在 CraftingScreenHandlerMixin 中添加下面的代码&lt;/p&gt;
&lt;figure class=&#34;highlight java&#34;&gt;&lt;figcaption data-lang=&#34;java&#34;&gt;&lt;/figcaption&gt;&lt;table&gt;&lt;tr&gt;&lt;td data-num=&#34;1&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token annotation punctuation&#34;&gt;@Shadow&lt;/span&gt; &lt;span class=&#34;token annotation punctuation&#34;&gt;@Final&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;private&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;ScreenHandlerContext&lt;/span&gt; context&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;2&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token comment&#34;&gt;/**&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;3&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt; * @author TuRou&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;4&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt; * Filled the BlockEntity with the data in the crafting grid.&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;5&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt; */&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;6&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token annotation punctuation&#34;&gt;@Overwrite&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;7&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;void&lt;/span&gt; &lt;span class=&#34;token function&#34;&gt;close&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token class-name&#34;&gt;PlayerEntity&lt;/span&gt; player&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;&amp;#123;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;8&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    context&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;run&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;world&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; pos&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;token operator&#34;&gt;-&gt;&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;&amp;#123;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;9&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;        &lt;span class=&#34;token class-name&#34;&gt;BlockEntity&lt;/span&gt; blockEntity &lt;span class=&#34;token operator&#34;&gt;=&lt;/span&gt; world&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;getBlockEntity&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;pos&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;10&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;        &lt;span class=&#34;token keyword&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;blockEntity &lt;span class=&#34;token operator&#34;&gt;!=&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;null&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;&amp;#123;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;11&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;            &lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token class-name&#34;&gt;CraftingTableBlockEntity&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt; blockEntity&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;innerItemStacks &lt;span class=&#34;token operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;token function&#34;&gt;getInputItems&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;12&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;        &lt;span class=&#34;token punctuation&#34;&gt;&amp;#125;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;13&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;span class=&#34;token punctuation&#34;&gt;&amp;#125;&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;14&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token punctuation&#34;&gt;&amp;#125;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;&lt;p&gt;之后我们需要让合成 GUI 被打开的时候从 BlockEntity 中读取并填充列表，依据我们之前的分析，GUI 打开的语句在在 CraftingTable 类中，我们在 mixin 包中新建类 CraftingTableMixin, 并先写入以下内容&lt;/p&gt;
&lt;figure class=&#34;highlight java&#34;&gt;&lt;figcaption data-lang=&#34;java&#34;&gt;&lt;/figcaption&gt;&lt;table&gt;&lt;tr&gt;&lt;td data-num=&#34;1&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token annotation punctuation&#34;&gt;@Mixin&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token class-name&#34;&gt;CraftingTableBlock&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token keyword&#34;&gt;class&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;2&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;class&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;CraftingTableMixin&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;extends&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;Block&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;implements&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;BlockEntityProvider&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;&amp;#123;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;3&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;span class=&#34;token annotation punctuation&#34;&gt;@Nullable&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;4&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;span class=&#34;token annotation punctuation&#34;&gt;@Override&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;5&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;span class=&#34;token keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;BlockEntity&lt;/span&gt; &lt;span class=&#34;token function&#34;&gt;createBlockEntity&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token class-name&#34;&gt;BlockView&lt;/span&gt; world&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;&amp;#123;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;6&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;        &lt;span class=&#34;token keyword&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;new&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;CraftingTableBlockEntity&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;7&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;span class=&#34;token punctuation&#34;&gt;&amp;#125;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;8&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;9&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;span class=&#34;token annotation punctuation&#34;&gt;@Override&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;10&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;span class=&#34;token keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;void&lt;/span&gt; &lt;span class=&#34;token function&#34;&gt;afterBreak&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token class-name&#34;&gt;World&lt;/span&gt; world&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;PlayerEntity&lt;/span&gt; player&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;BlockPos&lt;/span&gt; pos&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;BlockState&lt;/span&gt; state&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;token annotation punctuation&#34;&gt;@Nullable&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;BlockEntity&lt;/span&gt; blockEntity&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;ItemStack&lt;/span&gt; stack&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;&amp;#123;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;11&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;        &lt;span class=&#34;token keyword&#34;&gt;super&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;afterBreak&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;world&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; player&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; pos&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; state&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; blockEntity&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; stack&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;12&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;        &lt;span class=&#34;token keyword&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;blockEntity &lt;span class=&#34;token operator&#34;&gt;!=&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;null&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;&amp;#123;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;13&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;            &lt;span class=&#34;token keyword&#34;&gt;for&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token class-name&#34;&gt;ItemStack&lt;/span&gt; innerItemStack &lt;span class=&#34;token operator&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token class-name&#34;&gt;CraftingTableBlockEntity&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt; blockEntity&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;innerItemStacks&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;&amp;#123;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;14&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;                world&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;spawnEntity&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token keyword&#34;&gt;new&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;ItemEntity&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;world&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; pos&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;getX&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; pos&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;getY&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; pos&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;getZ&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; innerItemStack&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;15&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;            &lt;span class=&#34;token punctuation&#34;&gt;&amp;#125;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;16&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;        &lt;span class=&#34;token punctuation&#34;&gt;&amp;#125;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;17&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;span class=&#34;token punctuation&#34;&gt;&amp;#125;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;18&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token punctuation&#34;&gt;&amp;#125;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;&lt;p&gt;我们给合成台添加 BlockEntity, 并要求在方块被破坏的时候掉出所有的物品。之后我们来重写 createScreenHandlerFactory 方法，在 CraftingTableMixin 中添加以下内容&lt;/p&gt;
&lt;figure class=&#34;highlight java&#34;&gt;&lt;figcaption data-lang=&#34;java&#34;&gt;&lt;/figcaption&gt;&lt;table&gt;&lt;tr&gt;&lt;td data-num=&#34;1&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token comment&#34;&gt;/**&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;2&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt; * @author TuRou&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;3&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt; * Handle Crafting Progress.&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;4&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt; */&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;5&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token annotation punctuation&#34;&gt;@Overwrite&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;6&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;span class=&#34;token keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;NamedScreenHandlerFactory&lt;/span&gt; &lt;span class=&#34;token function&#34;&gt;createScreenHandlerFactory&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token class-name&#34;&gt;BlockState&lt;/span&gt; state&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;World&lt;/span&gt; world&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;BlockPos&lt;/span&gt; pos&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;&amp;#123;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;7&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;        &lt;span class=&#34;token class-name&#34;&gt;BlockEntity&lt;/span&gt; blockEntity &lt;span class=&#34;token operator&#34;&gt;=&lt;/span&gt; world&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;getBlockEntity&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;pos&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;8&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;        &lt;span class=&#34;token keyword&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;blockEntity &lt;span class=&#34;token operator&#34;&gt;==&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;null&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;&amp;#123;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;9&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;            blockEntity &lt;span class=&#34;token operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;new&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;CraftingTableBlockEntity&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;10&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;            world&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;setBlockEntity&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;pos&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; blockEntity&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;11&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;        &lt;span class=&#34;token punctuation&#34;&gt;&amp;#125;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;12&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;        &lt;span class=&#34;token class-name&#34;&gt;DefaultedList&lt;/span&gt;&lt;span class=&#34;token generics&#34;&gt;&lt;span class=&#34;token punctuation&#34;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&#34;token class-name&#34;&gt;ItemStack&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;&gt;&lt;/span&gt;&lt;/span&gt; itemStacks &lt;span class=&#34;token operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token class-name&#34;&gt;CraftingTableBlockEntity&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt; blockEntity&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;innerItemStacks&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;13&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;        &lt;span class=&#34;token keyword&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;new&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;SimpleNamedScreenHandlerFactory&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;i&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; playerInventory&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; playerEntity&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;token operator&#34;&gt;-&gt;&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;&amp;#123;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;14&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;            &lt;span class=&#34;token class-name&#34;&gt;CraftingScreenHandler&lt;/span&gt; screenHandler &lt;span class=&#34;token operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;new&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;CraftingScreenHandler&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;i&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; playerInventory&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;ScreenHandlerContext&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;create&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;world&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; pos&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;15&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;            &lt;span class=&#34;token keyword&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token operator&#34;&gt;!&lt;/span&gt;itemStacks&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;isEmpty&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt; screenHandler&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;setInputItems&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;itemStacks&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;16&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;            &lt;span class=&#34;token keyword&#34;&gt;return&lt;/span&gt; screenHandler&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;17&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;        &lt;span class=&#34;token punctuation&#34;&gt;&amp;#125;&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;new&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;TranslatableText&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token string&#34;&gt;&#34;container.crafting&#34;&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;18&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;span class=&#34;token punctuation&#34;&gt;&amp;#125;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;&lt;p&gt;这时 idea 给了我们红色报错，提示我们 setInputItems 方法不存在，这里相信读者已经清楚发生了什么事情，读者可能尝试解决问题，给 screenHandler 添加了类型强转:  &lt;code&gt;(CraftingScreenHandlerMixin) (Object) screenHandler&lt;/code&gt;  但当此时读者启动游戏，游戏却给我们抛了一个 ClassNotFoundException&lt;/p&gt;
&lt;p&gt;这里的正确做法是使用一个接口来过度，在在&lt;strong&gt;非 mixin 包&lt;/strong&gt;中新建类 ICraftingScreenHandler, 并且写入以下内容&lt;/p&gt;
&lt;figure class=&#34;highlight java&#34;&gt;&lt;figcaption data-lang=&#34;java&#34;&gt;&lt;/figcaption&gt;&lt;table&gt;&lt;tr&gt;&lt;td data-num=&#34;1&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;interface&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;ICraftingScreenHandler&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;&amp;#123;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;2&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;span class=&#34;token keyword&#34;&gt;void&lt;/span&gt; &lt;span class=&#34;token function&#34;&gt;setInputItems&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token class-name&#34;&gt;DefaultedList&lt;/span&gt;&lt;span class=&#34;token generics&#34;&gt;&lt;span class=&#34;token punctuation&#34;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&#34;token class-name&#34;&gt;ItemStack&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;&gt;&lt;/span&gt;&lt;/span&gt; itemStacks&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;3&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token punctuation&#34;&gt;&amp;#125;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;&lt;p&gt;之后我们用 &lt;code&gt;((ICraftingScreenHandler) screenHandler).setInputItems()&lt;/code&gt;  的方式，就可以顺利调用 setInputItems 方法了&lt;/p&gt;
&lt;p&gt;最后，将我们涉及到的所有类添加到 mixins.json 中，启动游戏，相信读者已经测试到了自己想要的效果.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;注：我们上文强调，ICraftingScreenHandler 必须放在非 mixin 包中，很明显地，这个类是为了欺骗 Java 虚拟机，使其相信 screenHandler 一定实现了 ICraftingScreenHandler 接口，而事实上，这个接口没有被目标类使用 implements 显式实现，但接口里面的方法 (setInputItems) 是的的确确存在的 (我们利用 Mixin 加进去的), 我们敢于使用这种方式来蒙骗 Java 虚拟机从而调用到目标方法。所以显然，ICraftingScreenHandler 并不属于我们整个 Mixin 流程中的一部分，它是没有必要在游戏加载之前提前被注入的.&lt;br /&gt;
 读者这时可能会想到我们上文声明的 IMixinCraftingInventory, 这个接口是实打实需要注入然后被目标类实现的，可是回忆一下，我们并没有在目标类中实现这个接口中的方法...... 其实是 Mixin 帮我们根据接口的内容自动生成的 (因为我们的需求很固定，就是访问目标类的成员变量), 固然，这个接口是需要在游戏加载前被注入的，所以它应放在 mixin 包内&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr /&gt;
&lt;h2 id=&#34;总结&#34;&gt;&lt;a class=&#34;anchor&#34; href=&#34;#总结&#34;&gt;#&lt;/a&gt; 总结&lt;/h2&gt;
&lt;p&gt;如果读者跟随笔者的思路，对这三个实例进行了由浅入深的分析，相信读者已经对 mixin 有了新的认识和理解，并且有了自己解决问题的能力。相信读者已经体会到了 Minecraft 源码注入的精巧之处和强大之处，希望各位开发者能活用 mixin 技术，创造出属于自己的技术.&lt;/p&gt;
&lt;p&gt;本文的创作大力感谢 xfl03 的 [CoreModTutor](&lt;a href=&#34;https://xfl03.gitbook.io/coremodtutor/5&#34;&gt;5 Mixin - CoreModTutor (gitbook.io)&lt;/a&gt;).&lt;/p&gt;
&lt;p&gt;感谢&lt;a href=&#34;https://www.mcbbs.net/home.php?mod=space&amp;amp;uid=897946&#34;&gt; dengyu&lt;/a&gt; 和&lt;a href=&#34;https://www.mcbbs.net/home.php?mod=space&amp;amp;uid=2853776&#34;&gt;洞穴夜莺&lt;/a&gt;给笔者提出改进意见&lt;/p&gt;
&lt;p&gt;本文部分源代码和思路摘自笔者曾经开发的 Mod: [Shinto](&lt;a href=&#34;https://github.com/TROU2004/Shinto&#34;&gt;TROU2004/Shinto (github.com)&lt;/a&gt;), [Benchworks](&lt;a href=&#34;https://github.com/TROU2004/Benchworks&#34;&gt;TROU2004/Benchworks (github.com)&lt;/a&gt;)&lt;/p&gt;
</content>
        <category term="Minecraft" />
        <category term="模组开发" />
        <updated>2021-06-07T03:19:59.000Z</updated>
    </entry>
    <entry>
        <id>https://turou.fun/minecraft/fabric-tech-mods/</id>
        <title>关于Fabric的科技Mod相关问题和解决方案</title>
        <link rel="alternate" href="https://turou.fun/minecraft/fabric-tech-mods/"/>
        <content type="html">&lt;h2 id=&#34;前言&#34;&gt;&lt;a class=&#34;anchor&#34; href=&#34;#前言&#34;&gt;#&lt;/a&gt; 前言&lt;/h2&gt;
&lt;p&gt;最近和朋友做了一个 1.16.4 的 Fabric 包来玩，玩的过程中出了点小问题，这里分享一下问题和解决方案，避免朋友们采坑 (虽然可能很少有人碰高版本). 这里先吹一波 Fabric, 开包速度比 Forge 快到不知道哪里去了，除了 Mod 少点别的确实舒服，开发看起来也舒服一些。这些可能都得归功于一堆优化 Mod 和 Sponge 团队开发的那个造福人类的 mixin (话说现在 Forge 也或多或少在用着 mixin?)&lt;/p&gt;
&lt;h2 id=&#34;关于fabric下ae的渲染问题&#34;&gt;&lt;a class=&#34;anchor&#34; href=&#34;#关于fabric下ae的渲染问题&#34;&gt;#&lt;/a&gt; 关于 Fabric 下 AE 的渲染问题&lt;/h2&gt;
&lt;p&gt;首先说说 Fabric 下的 AE2 的问题，AE 不知道去年年末吹的什么风，发出来了几个 Fabric 的测试版然后又回到 Forge 了，这也让我们有机会在 1.16.4Fabric 下体验 AE 的方便自动化。但可能有会有朋友跟我一样把 Sodium (一个神奇的多线程渲染优化 Mod) 和 AE2 共存以后，所有的 AE 非实体方块 (导线终端什么的) 变成紫黑块。这个问题就是因为 Sodium 优化程度太深，把 Fabric Rendering API 重写了很多地方，导致了 3D 模型的一些渲染问题，如果我们不想抛弃这两个 Mod 的话，有一个简单 (好像不是很简单) 的修复方式.&lt;/p&gt;
&lt;p&gt;这个 Indium Mod (Github: &lt;a href=&#34;https://github.com/comp500/Indium&#34;&gt;https://github.com/comp500/Indium&lt;/a&gt;) 修复了这个渲染问题，但是由于我们得手动编译它，所以这里我把它的最新 build 编译好放在下面的云盘了.&lt;/p&gt;
&lt;p&gt;Indium 得搭配这个快照版的 Sodium (Github: &lt;a href=&#34;https://github.com/jellysquid3/sodium-fabric&#34;&gt;https://github.com/jellysquid3/sodium-fabric&lt;/a&gt;), 这里也得手动编译，所以也放在下面的云盘了.&lt;/p&gt;
&lt;p&gt;这样的话 AE 和 Sodium 就可以愉快共存了.&lt;/p&gt;
&lt;h2 id=&#34;关于techreborn的一些平衡可能不太平衡&#34;&gt;&lt;a class=&#34;anchor&#34; href=&#34;#关于techreborn的一些平衡可能不太平衡&#34;&gt;#&lt;/a&gt; 关于 TechReborn 的一些平衡 (可能不太平衡)&lt;/h2&gt;
&lt;p&gt;TechReborn 把之前高效的太阳能发电削的超级弱，然后其他发电方式也很弱，发电很困难，机器耗电还没怎么变，这也启发了我改改数据，这点玩意 config 里面就能改.&lt;/p&gt;
&lt;p&gt;之后就是 TechReborn 鸡肋的电力传输，之前摆了一堆太阳能还是带不动 128E/t 的高炉，很是奇怪。后来查了一下 wiki.&lt;/p&gt;
&lt;p&gt;最后得出的结论就是.... 电线接的越长运输效率就越低，单人试了一下 512E/t 接了 20 格左右就只剩 300 多 E/t 了，而且这玩意不是线损，解决方式很困难。只能是每隔 10-15 格就放一个储电盒来重置这个效率。然后高电压的储电盒又很贵很贵 (MFE 才 128E/t 的输入输出，吐了), 所以最后的解决方式就是... 到 Github 上下载了一份源码，在里面加了个能源中继器，材质和用法跟储电盒是一样的，但输入和输入都改成了无限 (Integar.MaxValue), 合成也很便宜，只能隔几格就放这种中继器了。另外我又在这个版本上面改了一些东西，导线的传输上限都 * 2, 机器的输入上限都 * 2, 速度升级略微 buff 了一点。储电盒储电量全部扩大 10 倍 (原来那破储电盒都没有我喷气背包储电量大). 修改过的 Mod 这边也放在下面的网盘了.&lt;/p&gt;
&lt;h2 id=&#34;涉及的文件和下载地址&#34;&gt;&lt;a class=&#34;anchor&#34; href=&#34;#涉及的文件和下载地址&#34;&gt;#&lt;/a&gt; 涉及的文件和下载地址&lt;/h2&gt;
&lt;p&gt;网盘: &lt;a href=&#34;https://wwa.lanzous.com/b06yj7ehi&#34;&gt;https://wwa.lanzous.com/b06yj7ehi&lt;/a&gt; 密码:f77g 这里自己玩的 Modpack 也一起附上了，希望各位朋友高版本 Mod 之旅愉快 qwq&lt;/p&gt;
</content>
        <category term="Minecraft" />
        <category term="模组开发" />
        <updated>2021-02-10T03:27:12.000Z</updated>
    </entry>
    <entry>
        <id>https://turou.fun/minecraft/analogy/rainbow-gadgets/</id>
        <title>举二反三深入模组开发 彩虹桥法杖 + 建筑小助手 = ?</title>
        <link rel="alternate" href="https://turou.fun/minecraft/analogy/rainbow-gadgets/"/>
        <content type="html">&lt;blockquote&gt;
&lt;p&gt;本节将分析彩虹桥方块的动画效果，建筑小助手是如何触及极远的区域的，最后我们会造一个便利的建筑方块&lt;/p&gt;
&lt;p&gt;知识速览:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;ITickable TileEntity&lt;/li&gt;
&lt;li&gt;BlockRayTraceResult&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;
&lt;hr /&gt;
&lt;h3 id=&#34;天之苍苍其正色耶&#34;&gt;&lt;a class=&#34;anchor&#34; href=&#34;#天之苍苍其正色耶&#34;&gt;#&lt;/a&gt; 天之苍苍，其正色耶？&lt;/h3&gt;
&lt;p&gt;说起 Botania 的彩虹桥法杖，相信读者并不陌生，其美丽的特效出自浪漫的 Vazkii 之手，观察彩虹桥方块我们可以发现，其模型应该是一个不断变色的方块，并且不时会散发出有植物气息的粒子效果，彩虹桥方块会在其产生后一段时间内自动消失，不难想象应该是实现了 ITickable 接口的 TileEntity.&lt;/p&gt;
&lt;p&gt;&lt;img loading=&#34;lazy&#34; data-src=&#34;https://i.loli.net/2020/04/13/4Nu2kcxbGsMnEPz.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;
&lt;p&gt;从彩虹桥方块中我们可以发现如下三个特性，我们一个一个来看&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;透明方块的动态材质&lt;/li&gt;
&lt;li&gt;在方块周围生成指定的粒子效果&lt;/li&gt;
&lt;li&gt;过一段时间后自我消失&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id=&#34;透明方块的动态材质&#34;&gt;&lt;a class=&#34;anchor&#34; href=&#34;#透明方块的动态材质&#34;&gt;#&lt;/a&gt; 透明方块的动态材质&lt;/h4&gt;
&lt;p&gt;关于动态材质相信读者都不会太陌生，因为模组的模型加载说到底还是跟随了原版的机制，所以我们固然可以想到动态资源包中常用的 mcmeta 格式以及瀑布般的长条材质，其实 Minecraft 中重复简单的方块动画都可以用 mcmeta 配合一个包含动画过程关键帧的图片格式轻松实现，其实彩虹桥方块的动画也是这样的，在 Botania 的 resources 目录中我们可以找到 bifrost.json, 这就是彩虹桥方块的模型&lt;/p&gt;
&lt;figure class=&#34;highlight json&#34;&gt;&lt;figcaption data-lang=&#34;JSON&#34;&gt;&lt;/figcaption&gt;&lt;table&gt;&lt;tr&gt;&lt;td data-num=&#34;1&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token punctuation&#34;&gt;&amp;#123;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;2&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;  &lt;span class=&#34;token property&#34;&gt;&#34;parent&#34;&lt;/span&gt;&lt;span class=&#34;token operator&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;token string&#34;&gt;&#34;minecraft:block/cube_all&#34;&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;3&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;  &lt;span class=&#34;token property&#34;&gt;&#34;textures&#34;&lt;/span&gt;&lt;span class=&#34;token operator&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;&amp;#123;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;4&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;span class=&#34;token property&#34;&gt;&#34;all&#34;&lt;/span&gt;&lt;span class=&#34;token operator&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;token string&#34;&gt;&#34;botania:blocks/bifrost&#34;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;5&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;  &lt;span class=&#34;token punctuation&#34;&gt;&amp;#125;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;6&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token punctuation&#34;&gt;&amp;#125;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;7&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token comment&#34;&gt;/*&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;8&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;Botania 开源地址: https://github.com/Vazkii/Botania&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;9&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;节选自 (1.12-final 分支): /resources/assets/botania/models/block/bifrost.json&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;10&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;*/&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;&lt;p&gt;显然彩虹桥方块的模型与普通方块是相同的，换句话说我们只需要关注动画材质就可以了，根据 Model 里面声明的路径我们去找对应的贴图以及 mcmeta 文件.&lt;/p&gt;
&lt;p&gt;&lt;img loading=&#34;lazy&#34; data-src=&#34;https://i.loli.net/2020/04/13/9Tpkw1CDHErVZJe.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;
&lt;p&gt;这里就不多说什么了，如果我们也想要一个动态模型只需如图一样创建贴图和 mcmeta 标记文件就可以了&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;注: mcmeta 的文件名应为贴图名 +.mcmeta&lt;/p&gt;
&lt;p&gt;frametime: 动画播放的帧率&lt;/p&gt;
&lt;p&gt;interpolate: 是否开启差值补帧 (使动画渐变更流畅)&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;之后我们来看透明的实现，在 Bifrost 的方块声明中我们可以找到如下代码&lt;/p&gt;
&lt;figure class=&#34;highlight java&#34;&gt;&lt;figcaption data-lang=&#34;java&#34;&gt;&lt;/figcaption&gt;&lt;table&gt;&lt;tr&gt;&lt;td data-num=&#34;1&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;class&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;BlockBifrostPerm&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;extends&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;BlockMod&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;implements&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;ILexiconable&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;&amp;#123;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;2&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;3&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;	&lt;span class=&#34;token keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;BlockBifrostPerm&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token class-name&#34;&gt;String&lt;/span&gt; name&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;&amp;#123;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;4&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;		&lt;span class=&#34;token keyword&#34;&gt;super&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token class-name&#34;&gt;Material&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token constant&#34;&gt;GLASS&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; name&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;5&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;		&lt;span class=&#34;token function&#34;&gt;setLightOpacity&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token number&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;6&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;		&lt;span class=&#34;token function&#34;&gt;setLightLevel&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token number&#34;&gt;1F&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;7&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;		&lt;span class=&#34;token function&#34;&gt;setSoundType&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token class-name&#34;&gt;SoundType&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token constant&#34;&gt;GLASS&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;8&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;	&lt;span class=&#34;token punctuation&#34;&gt;&amp;#125;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;9&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;10&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;	&lt;span class=&#34;token annotation punctuation&#34;&gt;@Override&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;11&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;	&lt;span class=&#34;token keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;boolean&lt;/span&gt; &lt;span class=&#34;token function&#34;&gt;isOpaqueCube&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token class-name&#34;&gt;IBlockState&lt;/span&gt; state&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;&amp;#123;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;12&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;		&lt;span class=&#34;token keyword&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;token boolean&#34;&gt;false&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;13&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;	&lt;span class=&#34;token punctuation&#34;&gt;&amp;#125;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;14&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;15&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;	&lt;span class=&#34;token annotation punctuation&#34;&gt;@Override&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;16&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;	&lt;span class=&#34;token keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;boolean&lt;/span&gt; &lt;span class=&#34;token function&#34;&gt;isFullCube&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token class-name&#34;&gt;IBlockState&lt;/span&gt; state&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;&amp;#123;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;17&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;		&lt;span class=&#34;token keyword&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;token boolean&#34;&gt;false&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;18&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;	&lt;span class=&#34;token punctuation&#34;&gt;&amp;#125;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;19&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;20&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;	&lt;span class=&#34;token annotation punctuation&#34;&gt;@Override&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;21&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;	&lt;span class=&#34;token keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;boolean&lt;/span&gt; &lt;span class=&#34;token function&#34;&gt;shouldSideBeRendered&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token class-name&#34;&gt;IBlockState&lt;/span&gt; state&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;token annotation punctuation&#34;&gt;@Nonnull&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;IBlockAccess&lt;/span&gt; world&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;token annotation punctuation&#34;&gt;@Nonnull&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;BlockPos&lt;/span&gt; pos&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;EnumFacing&lt;/span&gt; side&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;&amp;#123;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;22&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;		&lt;span class=&#34;token keyword&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;world&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;getBlockState&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;pos&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;offset&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;side&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;getBlock&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;token operator&#34;&gt;==&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;this&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;&amp;#123;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;23&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;			&lt;span class=&#34;token keyword&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;token boolean&#34;&gt;false&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;24&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;		&lt;span class=&#34;token punctuation&#34;&gt;&amp;#125;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;25&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;		&lt;span class=&#34;token keyword&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;super&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;shouldSideBeRendered&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;state&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; world&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; pos&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; side&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;26&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;	&lt;span class=&#34;token punctuation&#34;&gt;&amp;#125;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;27&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;28&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;	&lt;span class=&#34;token annotation punctuation&#34;&gt;@SideOnly&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token class-name&#34;&gt;Side&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token constant&#34;&gt;CLIENT&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;29&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;	&lt;span class=&#34;token annotation punctuation&#34;&gt;@Nonnull&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;30&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;	&lt;span class=&#34;token annotation punctuation&#34;&gt;@Override&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;31&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;	&lt;span class=&#34;token keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;BlockRenderLayer&lt;/span&gt; &lt;span class=&#34;token function&#34;&gt;getRenderLayer&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;&amp;#123;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;32&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;		&lt;span class=&#34;token keyword&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;BlockRenderLayer&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token constant&#34;&gt;TRANSLUCENT&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;33&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;		&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;34&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;	&lt;span class=&#34;token punctuation&#34;&gt;&amp;#125;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;35&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token punctuation&#34;&gt;&amp;#125;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;36&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token comment&#34;&gt;/*&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;37&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;Botania 开源地址: https://github.com/Vazkii/Botania&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;38&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;节选自 (1.12-final 分支): /common/block/BlockBifrostPerm.java&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;39&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;*/&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;&lt;p&gt;显然我们的彩虹桥方块需要像玻璃一样可以透光，在 Minecraft1.15.2 中我们可以让方块直接继承自 AbstractGlassBlock 从而直接得到类似玻璃的透光能力，但在 1.12.2 我们还不能这样做，于是我们可以覆盖 isOpaqueCube isFullCube getRenderLayer 等一系列方法，使方块可以透光，为了拥有更好的显示效果，Vazkii 还覆盖了 shouldSideBeRendered 方法，重叠的面将不会渲染，这里笔者就不多说了，读者可以自行阅读上文该方法的代码&lt;/p&gt;
&lt;h4 id=&#34;在方块周围生成指定的粒子效果&#34;&gt;&lt;a class=&#34;anchor&#34; href=&#34;#在方块周围生成指定的粒子效果&#34;&gt;#&lt;/a&gt; 在方块周围生成指定的粒子效果&lt;/h4&gt;
&lt;p&gt;有心的读者如果去 Github 阅读了 BlockBifrostPerm 的源码可以发现我们列举的代码中缺少了如下部分&lt;/p&gt;
&lt;figure class=&#34;highlight java&#34;&gt;&lt;figcaption data-lang=&#34;java&#34;&gt;&lt;/figcaption&gt;&lt;table&gt;&lt;tr&gt;&lt;td data-num=&#34;1&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token annotation punctuation&#34;&gt;@Override&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;2&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;void&lt;/span&gt; &lt;span class=&#34;token function&#34;&gt;randomDisplayTick&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token class-name&#34;&gt;IBlockState&lt;/span&gt; state&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;World&lt;/span&gt; world&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;BlockPos&lt;/span&gt; pos&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;Random&lt;/span&gt; rand&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;&amp;#123;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;3&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;span class=&#34;token keyword&#34;&gt;if&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;rand&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;nextBoolean&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;4&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;        &lt;span class=&#34;token class-name&#34;&gt;Botania&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;proxy&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;sparkleFX&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;pos&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;getX&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;token operator&#34;&gt;+&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;Math&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;random&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; pos&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;getY&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;token operator&#34;&gt;+&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;Math&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;random&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; pos&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;getZ&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;token operator&#34;&gt;+&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;Math&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;random&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token keyword&#34;&gt;float&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;Math&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;random&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token keyword&#34;&gt;float&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;Math&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;random&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token keyword&#34;&gt;float&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;Math&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;random&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;token number&#34;&gt;0.45F&lt;/span&gt; &lt;span class=&#34;token operator&#34;&gt;+&lt;/span&gt; &lt;span class=&#34;token number&#34;&gt;0.2F&lt;/span&gt; &lt;span class=&#34;token operator&#34;&gt;*&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token keyword&#34;&gt;float&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;Math&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;random&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;token number&#34;&gt;6&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;5&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token punctuation&#34;&gt;&amp;#125;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;6&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token comment&#34;&gt;/*&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;7&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;Botania 开源地址: https://github.com/Vazkii/Botania&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;8&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;节选自 (1.12-final 分支): /common/block/BlockBifrostPerm.java&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;9&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;*/&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;&lt;p&gt;显然这段代码的含义便是粒子效果的渲染，randomDisplayTick 会在贴图刷新的随机游戏刻执行，可以看到 Vazkii 在代码的开头使用 if (rand.nextBoolean ()) 使下面代码执行的概率变为 50%, 防止生成太多粒子造成卡顿. Vazkii 自己实现了一套 proxy 来运行 sparkleFX () 来保持客户端渲染，并且让粒子可以运动，其中大多使用 GL 直接渲染，这里的实现略微有点复杂我们就不展开了，读者如果希望粒子效果包含运动的效果，可以自行阅读 Botania 源码，其代码大多位于 fx 包中&lt;/p&gt;
&lt;p&gt;若要渲染普通的粒子效果，使用 world.spawnParticle (), 并且保证位于 ClientSide 执行&lt;/p&gt;
&lt;h4 id=&#34;过一段时间后自我消失&#34;&gt;&lt;a class=&#34;anchor&#34; href=&#34;#过一段时间后自我消失&#34;&gt;#&lt;/a&gt; 过一段时间后自我消失&lt;/h4&gt;
&lt;p&gt;在 BlockBifrost 类中，我们可以发现彩虹桥方块绑定了 TileEntity, 其类为 TileBifrost, 用于储存数据，我们来看一下 TileBifrost 类&lt;/p&gt;
&lt;figure class=&#34;highlight java&#34;&gt;&lt;figcaption data-lang=&#34;java&#34;&gt;&lt;/figcaption&gt;&lt;table&gt;&lt;tr&gt;&lt;td data-num=&#34;1&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;class&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;TileBifrost&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;extends&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;TileMod&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;implements&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;ITickable&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;&amp;#123;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;2&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;3&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;	&lt;span class=&#34;token keyword&#34;&gt;private&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;static&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;final&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;String&lt;/span&gt; &lt;span class=&#34;token constant&#34;&gt;TAG_TICKS&lt;/span&gt; &lt;span class=&#34;token operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;token string&#34;&gt;&#34;ticks&#34;&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;4&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;5&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;	&lt;span class=&#34;token keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;int&lt;/span&gt; ticks &lt;span class=&#34;token operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;token number&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;6&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;7&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;	&lt;span class=&#34;token annotation punctuation&#34;&gt;@Override&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;8&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;	&lt;span class=&#34;token keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;void&lt;/span&gt; &lt;span class=&#34;token function&#34;&gt;update&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;&amp;#123;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;9&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;		&lt;span class=&#34;token keyword&#34;&gt;if&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token operator&#34;&gt;!&lt;/span&gt;world&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;isRemote&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;&amp;#123;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;10&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;			&lt;span class=&#34;token keyword&#34;&gt;if&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;ticks &lt;span class=&#34;token operator&#34;&gt;&amp;lt;=&lt;/span&gt; &lt;span class=&#34;token number&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;&amp;#123;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;11&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;				world&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;setBlockToAir&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;pos&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;12&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;			&lt;span class=&#34;token punctuation&#34;&gt;&amp;#125;&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;else&lt;/span&gt; ticks&lt;span class=&#34;token operator&#34;&gt;--&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;13&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;		&lt;span class=&#34;token punctuation&#34;&gt;&amp;#125;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;14&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;	&lt;span class=&#34;token punctuation&#34;&gt;&amp;#125;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;15&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;16&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;	&lt;span class=&#34;token annotation punctuation&#34;&gt;@Nonnull&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;17&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;	&lt;span class=&#34;token annotation punctuation&#34;&gt;@Override&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;18&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;	&lt;span class=&#34;token keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;NBTTagCompound&lt;/span&gt; &lt;span class=&#34;token function&#34;&gt;writeToNBT&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token class-name&#34;&gt;NBTTagCompound&lt;/span&gt; par1nbtTagCompound&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;&amp;#123;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;19&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;		&lt;span class=&#34;token class-name&#34;&gt;NBTTagCompound&lt;/span&gt; ret &lt;span class=&#34;token operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;super&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;writeToNBT&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;par1nbtTagCompound&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;20&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;		ret&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;setInteger&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token constant&#34;&gt;TAG_TICKS&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; ticks&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;21&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;		&lt;span class=&#34;token keyword&#34;&gt;return&lt;/span&gt; ret&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;22&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;	&lt;span class=&#34;token punctuation&#34;&gt;&amp;#125;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;23&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;24&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;	&lt;span class=&#34;token annotation punctuation&#34;&gt;@Override&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;25&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;	&lt;span class=&#34;token keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;void&lt;/span&gt; &lt;span class=&#34;token function&#34;&gt;readFromNBT&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token class-name&#34;&gt;NBTTagCompound&lt;/span&gt; par1nbtTagCompound&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;&amp;#123;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;26&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;		&lt;span class=&#34;token keyword&#34;&gt;super&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;readFromNBT&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;par1nbtTagCompound&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;27&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;		ticks &lt;span class=&#34;token operator&#34;&gt;=&lt;/span&gt; par1nbtTagCompound&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;getInteger&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token constant&#34;&gt;TAG_TICKS&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;28&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;	&lt;span class=&#34;token punctuation&#34;&gt;&amp;#125;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;29&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;30&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token punctuation&#34;&gt;&amp;#125;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;31&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token comment&#34;&gt;/*&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;32&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;Botania 开源地址: https://github.com/Vazkii/Botania&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;33&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;节选自 (1.12-final 分支): /common/block/tile/TileBifrost.java&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;34&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;*/&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;&lt;p&gt;显然 TileBifrost 实现了 ITickable, 这意味着这个方块具备了刷新数据的能力，阅读 update 方法我们发现，其实质就是自减内部储存的 tick 值，如果减没了，那么就使方块消失，writeToNBT 和 readFromNBT 的用途不言而喻，常写 TileEntity 的读者一定对其有所了解，Minecraft 会在合适的时候 (一般是保存世界和读取世界的时候) 调用这两个方法，以便于在进入和退出存档之前读写方块里面的数据&lt;/p&gt;
&lt;p&gt;善于思考的读者此时可能发问了，这个 TileEntity 的 tick 初始值应该是什么呢，在上述对彩虹桥方块的分析过程中我们并没有看到对于 tick 初始值的声明.... 不妨再来想想彩虹桥方块是怎么被放置在世界中的呢.... 对了，就是通过彩虹桥法杖，所以不难想象设置 tick 的初始值的代码应该写在法杖中，读者可自行验证猜想，代码位于 &lt;code&gt;/common/item/rod/ItemRainbowRod.java&lt;/code&gt;  的 onItemRightClick 方法中&lt;/p&gt;
&lt;hr /&gt;
&lt;h3 id=&#34;其远而无所至极邪&#34;&gt;&lt;a class=&#34;anchor&#34; href=&#34;#其远而无所至极邪&#34;&gt;#&lt;/a&gt; 其远而无所至极邪？&lt;/h3&gt;
&lt;p&gt;建筑小助手是 direwolf20 作为模组开发者的处女作，其中各种小助手可以隔数十格远放置方块，手中握着小助手就可以追踪到目光所及之处的方块，通过查阅建筑小助手 GadgetBuilding 类的代码，我们可以找到如下方法&lt;/p&gt;
&lt;figure class=&#34;highlight java&#34;&gt;&lt;figcaption data-lang=&#34;java&#34;&gt;&lt;/figcaption&gt;&lt;table&gt;&lt;tr&gt;&lt;td data-num=&#34;1&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token keyword&#34;&gt;private&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;void&lt;/span&gt; &lt;span class=&#34;token function&#34;&gt;build&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token class-name&#34;&gt;ServerPlayerEntity&lt;/span&gt; player&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;ItemStack&lt;/span&gt; stack&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;&amp;#123;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;2&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;	······&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;3&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;span class=&#34;token class-name&#34;&gt;BlockRayTraceResult&lt;/span&gt; lookingAt &lt;span class=&#34;token operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;VectorHelper&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;getLookingAt&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;player&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; stack&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;4&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;span class=&#34;token keyword&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;world&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;isAirBlock&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;lookingAt&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;getPos&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;token comment&#34;&gt;//If we aren&#39;t looking at anything, exit&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;5&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;span class=&#34;token keyword&#34;&gt;return&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;6&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;	&lt;span class=&#34;token class-name&#34;&gt;Direction&lt;/span&gt; sideHit &lt;span class=&#34;token operator&#34;&gt;=&lt;/span&gt; lookingAt&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;getFace&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;7&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;	······&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;8&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;	&lt;span class=&#34;token function&#34;&gt;placeBlock&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;world&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; player&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; index&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; builder&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; coordinate&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; blockData&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;9&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;	······&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;10&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token punctuation&#34;&gt;&amp;#125;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;11&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token comment&#34;&gt;/*&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;12&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;BuildingGadgets 开源地址: https://github.com/Direwolf20-MC/BuildingGadgets&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;13&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;节选自 (master 分支): /common/items/gadgets/GadgetBuilding.java&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;14&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;*/&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;&lt;p&gt;可以猜想，在 GadgetBuilding 类的 build 方法中，实现了建筑小助手追踪视线并放置方块的特性，细心的读者可能发现，build 方法里面还具有针对 Collection 和 Undo 操作功能实现，的这里由于篇幅所限，不能为读者介绍到更为详细的部分，感兴趣的读者可以自行阅读查看&lt;/p&gt;
&lt;p&gt;根据节选的代码片段，相信读者可以发现，追踪方块的实现依赖与 VectorHelper.getLookingAt () 这个方法，他的返回值为一个 BlockRayTraceResult, 对这个光追结果进行 getPos (), 就可以获取到目光所及之处了，所以我们更进一步，来看看 VectorHelper 的具体实现&lt;/p&gt;
&lt;figure class=&#34;highlight java&#34;&gt;&lt;figcaption data-lang=&#34;java&#34;&gt;&lt;/figcaption&gt;&lt;table&gt;&lt;tr&gt;&lt;td data-num=&#34;1&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;class&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;VectorHelper&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;&amp;#123;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;2&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;span class=&#34;token keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;static&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;BlockRayTraceResult&lt;/span&gt; &lt;span class=&#34;token function&#34;&gt;getLookingAt&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token class-name&#34;&gt;PlayerEntity&lt;/span&gt; player&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;RayTraceContext&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;FluidMode&lt;/span&gt; rayTraceFluid&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;&amp;#123;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;3&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;        &lt;span class=&#34;token keyword&#34;&gt;double&lt;/span&gt; rayTraceRange &lt;span class=&#34;token operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;Config&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token constant&#34;&gt;GENERAL&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;rayTraceRange&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;get&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;4&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;        &lt;span class=&#34;token class-name&#34;&gt;RayTraceResult&lt;/span&gt; result &lt;span class=&#34;token operator&#34;&gt;=&lt;/span&gt; player&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;pick&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;rayTraceRange&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;token number&#34;&gt;0f&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; rayTraceFluid &lt;span class=&#34;token operator&#34;&gt;!=&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;RayTraceContext&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;FluidMode&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token constant&#34;&gt;NONE&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;5&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;6&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;        &lt;span class=&#34;token keyword&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token class-name&#34;&gt;BlockRayTraceResult&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt; result&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;7&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;span class=&#34;token punctuation&#34;&gt;&amp;#125;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;8&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token punctuation&#34;&gt;&amp;#125;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;9&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token comment&#34;&gt;/*&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;10&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;BuildingGadgets 开源地址: https://github.com/Direwolf20-MC/BuildingGadgets&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;11&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;节选自 (master 分支): /common/util/helpers/VectorHelper.java&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;12&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;*/&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;&lt;p&gt;VectorHelper 中包含了对于 getLookingAt () 的多个重写，显然，我们的目光重点应该放在 player.pick () 方法上，相信聪明的读者已经意识到了什么，player.pick () 就是原版为我们提供好的 RayTrace 的方法，我们只需要提供 Range (第一个参数), 和流体追踪模式 (第三个参数) 就可以了&lt;/p&gt;
&lt;hr /&gt;
&lt;h3 id=&#34;海运则将徙于南冥&#34;&gt;&lt;a class=&#34;anchor&#34; href=&#34;#海运则将徙于南冥&#34;&gt;#&lt;/a&gt; 海运则将徙于南冥&lt;/h3&gt;
&lt;p&gt;之后我们就要着手来将这两个点子融合，我们再来回顾一下我们的想法，既利用彩虹桥方块的发光效果，以及建筑小助手 getLookingAt () 方法来制作一个便利的建筑方块，&lt;/p&gt;
&lt;p&gt;非常抱歉的是，下面的内容因为 MCBBS 关站而遗失了，如果你曾经无意间保存了下面内容的副本，请联系我将这部分补全.&lt;/p&gt;
</content>
        <category term="Minecraft" />
        <category term="模组开发" />
        <updated>2020-07-31T03:19:59.000Z</updated>
    </entry>
    <entry>
        <id>https://turou.fun/minecraft/analogy/hexang-bento/</id>
        <title>举二反三深入模组开发 息壤 + 午餐盒 = ?</title>
        <link rel="alternate" href="https://turou.fun/minecraft/analogy/hexang-bento/"/>
        <content type="html">&lt;blockquote&gt;
&lt;p&gt;本节将带领你通过息壤和午餐盒的代码实现来制作一把可变换贴图万能工具&lt;/p&gt;
&lt;p&gt;知识速览:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;ToolHandler.isRightMaterial&lt;/li&gt;
&lt;li&gt;ItemMeshDefinition&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;
&lt;hr /&gt;
&lt;h3 id=&#34;列一元二次方程-神秘工匠的息壤实现&#34;&gt;&lt;a class=&#34;anchor&#34; href=&#34;#列一元二次方程-神秘工匠的息壤实现&#34;&gt;#&lt;/a&gt; 列一元二次方程 - 神秘工匠的息壤实现&lt;/h3&gt;
&lt;p&gt;息壤是在神秘工匠灵道篇目颇为冷门的物品之一，这里先简单介绍一下，他会在你使用错误的觉醒灵宝系列工具挖方块时自动替换成背包中正确的工具，引用 mcmod 里面举的例子大概就是这样的：一旦你试图使用觉醒灵宝镐挖土，而背包中有觉醒灵宝铲时会自动替换两工具的位置，以便让你更快挖掘脚下泥土。&lt;/p&gt;
&lt;p&gt;站在开发的角度我们可以理解，息壤的实现主要有两部分，一是&lt;strong&gt;判定交互的方块应该用何种工具挖掘&lt;/strong&gt;，二是&lt;strong&gt;在物品栏寻找对应的工具替换手中的错误工具&lt;/strong&gt;，我们先抛开第二个问题不谈，来看看如何实现第一个部分，我们可以在 TT 的开源项目关于息壤的代码中找到如下的部分&lt;/p&gt;
&lt;figure class=&#34;highlight java&#34;&gt;&lt;figcaption data-lang=&#34;java&#34;&gt;&lt;/figcaption&gt;&lt;table&gt;&lt;tr&gt;&lt;td data-num=&#34;1&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token keyword&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;block &lt;span class=&#34;token operator&#34;&gt;!=&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;null&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;&amp;#123;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;2&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;	&lt;span class=&#34;token class-name&#34;&gt;Material&lt;/span&gt; mat &lt;span class=&#34;token operator&#34;&gt;=&lt;/span&gt; block&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;getMaterial&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;3&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;	&lt;span class=&#34;token keyword&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token class-name&#34;&gt;ToolHandler&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;isRightMaterial&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;mat&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;ToolHandler&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;materialsPick&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;4&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;		typeToFind &lt;span class=&#34;token operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;token string&#34;&gt;&#34;pick&#34;&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;5&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;	&lt;span class=&#34;token keyword&#34;&gt;else&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token class-name&#34;&gt;ToolHandler&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;isRightMaterial&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;mat&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;ToolHandler&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;materialsShovel&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;6&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;		typeToFind &lt;span class=&#34;token operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;token string&#34;&gt;&#34;shovel&#34;&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;7&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;	&lt;span class=&#34;token keyword&#34;&gt;else&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token class-name&#34;&gt;ToolHandler&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;isRightMaterial&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;mat&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;ToolHandler&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;materialsAxe&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;8&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;		typeToFind &lt;span class=&#34;token operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;token string&#34;&gt;&#34;axe&#34;&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;9&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token punctuation&#34;&gt;&amp;#125;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;10&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token comment&#34;&gt;/*&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;11&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;神秘工匠开源地址: https://github.com/Thaumic-Tinkerer/ThaumicTinkerer/&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;12&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;节选自 (1.7.10 分支): /common/item/kami/ItemProtoclay.java&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;13&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;*/&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;&lt;p&gt;不难看出 TT 的作者自己实现了一个实用的方法 **&amp;quot;isRightMaterial&amp;quot;&lt;strong&gt;, 来判断方块 A 是否应该用工具 B 来采掘，继续深入，我们来看看&lt;/strong&gt; ToolHandler.isRightMaterial ()** 方法的相关细节&lt;/p&gt;
&lt;figure class=&#34;highlight java&#34;&gt;&lt;figcaption data-lang=&#34;java&#34;&gt;&lt;/figcaption&gt;&lt;table&gt;&lt;tr&gt;&lt;td data-num=&#34;1&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;static&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;Material&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;]&lt;/span&gt; materialsPick &lt;span class=&#34;token operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;new&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;Material&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;]&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;&amp;#123;&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;Material&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;rock&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;Material&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;iron&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;Material&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;ice&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;Material&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;glass&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;Material&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;piston&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;Material&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;anvil &lt;span class=&#34;token punctuation&#34;&gt;&amp;#125;&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;2&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;3&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;static&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;Material&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;]&lt;/span&gt; materialsShovel &lt;span class=&#34;token operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;new&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;Material&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;]&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;&amp;#123;&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;Material&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;grass&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;Material&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;ground&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;Material&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;sand&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;Material&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;snow&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;Material&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;craftedSnow&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;Material&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;clay &lt;span class=&#34;token punctuation&#34;&gt;&amp;#125;&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;4&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;	&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;5&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;static&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;Material&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;]&lt;/span&gt; materialsAxe &lt;span class=&#34;token operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;new&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;Material&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;]&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;&amp;#123;&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;Material&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;coral&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;Material&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;leaves&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;Material&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;plants&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;Material&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;wood &lt;span class=&#34;token punctuation&#34;&gt;&amp;#125;&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;6&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;	&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;7&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;static&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;boolean&lt;/span&gt; &lt;span class=&#34;token function&#34;&gt;isRightMaterial&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token class-name&#34;&gt;Material&lt;/span&gt; material&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;Material&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;]&lt;/span&gt; materialsListing&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;&amp;#123;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;8&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;	&lt;span class=&#34;token keyword&#34;&gt;for&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token class-name&#34;&gt;Material&lt;/span&gt; mat &lt;span class=&#34;token operator&#34;&gt;:&lt;/span&gt; materialsListing&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;&amp;#123;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;9&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;		&lt;span class=&#34;token keyword&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;material &lt;span class=&#34;token operator&#34;&gt;==&lt;/span&gt; mat&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;10&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;		&lt;span class=&#34;token keyword&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;token boolean&#34;&gt;true&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;11&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;	&lt;span class=&#34;token punctuation&#34;&gt;&amp;#125;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;12&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;	&lt;span class=&#34;token keyword&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;token boolean&#34;&gt;false&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;13&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token punctuation&#34;&gt;&amp;#125;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;14&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token comment&#34;&gt;/*&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;15&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;神秘工匠开源地址: https://github.com/Thaumic-Tinkerer/ThaumicTinkerer/&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;16&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;节选自 (1.7.10 分支): /common/item/kami/tool/ToolHandler.java&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;17&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;*/&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;&lt;p&gt;看到这里，相信读者已经略知一二了，既 isRightMaterial 实则遍历了一遍&lt;strong&gt;你提供的 Material []&lt;/strong&gt;, 如果你提供的 Material 是预先填充好的 Material [] 中的成员，则返回了 true. 如果作为读者的你觉得这有点暴力，那不妨去看看 ItemPickaxe 等对应工具类的源码 (雾), 这里就不展示了&lt;/p&gt;
&lt;hr /&gt;
&lt;h3 id=&#34;列一元二次方程-生活调味料的午餐盒实现&#34;&gt;&lt;a class=&#34;anchor&#34; href=&#34;#列一元二次方程-生活调味料的午餐盒实现&#34;&gt;#&lt;/a&gt; 列一元二次方程 - 生活调味料的午餐盒实现&lt;/h3&gt;
&lt;p&gt;接下来我们看看生活调味料中难得可见的两个物品中的其中一个 —— 午餐盒。午餐盒的投食逻辑实现这里就不多说了，这并不是我们要讨论的重点，我们着重来看一看午餐盒的贴图 (Models) 的变化，如果读者使用过午餐盒的话大概会有印象，午餐盒可以打开和关上，如果里面存有食物的话会有果蔬的贴图，像下面这样:&lt;/p&gt;
&lt;p&gt;&lt;img loading=&#34;lazy&#34; data-src=&#34;https://i.loli.net/2020/04/06/MokSeIBa3Qgq5Ks.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;
&lt;p&gt;关于贴图更换的逻辑，我们不难猜想，实际更换贴图的思路伪代码应该是这样的&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;if (找找NBT里面午餐盒是开着的吗) &amp;#123;
	if (那午餐盒里面有东西吗) &amp;#123;
		渲染带马铃薯面包苹果的贴图()
	&amp;#125; else &amp;#123;
		渲染开着的空盒子的贴图()
	&amp;#125;
&amp;#125; else &amp;#123;
	渲染关上的贴图()
&amp;#125;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;思而不学则殆，下面我们来看看午餐盒改变贴图的代码&lt;/p&gt;
&lt;figure class=&#34;highlight java&#34;&gt;&lt;figcaption data-lang=&#34;java&#34;&gt;&lt;/figcaption&gt;&lt;table&gt;&lt;tr&gt;&lt;td data-num=&#34;1&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token annotation punctuation&#34;&gt;@SideOnly&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token class-name&#34;&gt;Side&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token constant&#34;&gt;CLIENT&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;2&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;void&lt;/span&gt; &lt;span class=&#34;token function&#34;&gt;registerModels&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;3&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token punctuation&#34;&gt;&amp;#123;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;4&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;span class=&#34;token keyword&#34;&gt;final&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;ModelResourceLocation&lt;/span&gt; closed &lt;span class=&#34;token operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;new&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;ModelResourceLocation&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;getRegistryName&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;token string&#34;&gt;&#34;inventory&#34;&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;5&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;span class=&#34;token keyword&#34;&gt;final&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;ModelResourceLocation&lt;/span&gt; openEmpty &lt;span class=&#34;token operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;new&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;ModelResourceLocation&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;getRegistryName&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;token operator&#34;&gt;+&lt;/span&gt; &lt;span class=&#34;token string&#34;&gt;&#34;_open_empty&#34;&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;token string&#34;&gt;&#34;inventory&#34;&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;6&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;span class=&#34;token keyword&#34;&gt;final&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;ModelResourceLocation&lt;/span&gt; openFull &lt;span class=&#34;token operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;new&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;ModelResourceLocation&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;getRegistryName&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;token operator&#34;&gt;+&lt;/span&gt; &lt;span class=&#34;token string&#34;&gt;&#34;_open_full&#34;&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;token string&#34;&gt;&#34;inventory&#34;&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;7&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;8&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;span class=&#34;token class-name&#34;&gt;ModelLoader&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;registerItemVariants&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token keyword&#34;&gt;this&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; closed&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; openEmpty&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; openFull&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;9&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;10&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;span class=&#34;token class-name&#34;&gt;ModelLoader&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;setCustomMeshDefinition&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token keyword&#34;&gt;this&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;new&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;ItemMeshDefinition&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;11&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;span class=&#34;token punctuation&#34;&gt;&amp;#123;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;12&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;        &lt;span class=&#34;token annotation punctuation&#34;&gt;@Override&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;13&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;        &lt;span class=&#34;token keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;ModelResourceLocation&lt;/span&gt; &lt;span class=&#34;token function&#34;&gt;getModelLocation&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token class-name&#34;&gt;ItemStack&lt;/span&gt; itemStack&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;14&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;        &lt;span class=&#34;token punctuation&#34;&gt;&amp;#123;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;15&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;            &lt;span class=&#34;token keyword&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;isOpen&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;itemStack&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;16&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;            &lt;span class=&#34;token punctuation&#34;&gt;&amp;#123;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;17&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;                &lt;span class=&#34;token keyword&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;token function&#34;&gt;isEmpty&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;itemStack&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;token operator&#34;&gt;?&lt;/span&gt; openEmpty &lt;span class=&#34;token operator&#34;&gt;:&lt;/span&gt; openFull&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;18&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;            &lt;span class=&#34;token punctuation&#34;&gt;&amp;#125;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;19&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;            &lt;span class=&#34;token keyword&#34;&gt;return&lt;/span&gt; closed&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;20&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;        &lt;span class=&#34;token punctuation&#34;&gt;&amp;#125;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;21&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;span class=&#34;token punctuation&#34;&gt;&amp;#125;&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;22&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token punctuation&#34;&gt;&amp;#125;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;23&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token comment&#34;&gt;/*&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;24&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;生活调味料开源地址: https://github.com/squeek502/SpiceOfLife/&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;25&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;节选自 (1.12 分支): /items/ItemFoodContainer.java&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;26&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;*/&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;&lt;p&gt;前三行代码很普通的声明了三个 ModelResourceLocation, 其地址很普通的对应了三个 Model 的 json 文件，这与创建三个物品为他们分配贴图的时候的 ModelResourceLocation 没什么大区别，读者像普通给物品放贴图一样的方式来声明就可以了&lt;/p&gt;
&lt;p&gt;接下来，我们要让 Minecraft 知道，我们准备要给物品使用的贴图分别都有什么，以便于让 Minecraft 事先为我们准备，我们需要用 ModelLoader.registerItemVariants (Item, ModelResourceLocation&lt;strong&gt;s......&lt;/strong&gt;) 来提供，我们可以看到上文代码中把刚刚声明的三个资源位置传了进去&lt;/p&gt;
&lt;p&gt;最后，我们要给物品添加贴图，为了让 Minecraft 根据我们的需要自由的变换材质，我们应该给物品添加一个&lt;strong&gt; CustomMeshDefinition&lt;/strong&gt;, 何为&lt;strong&gt; MeshDefinition&lt;/strong&gt; 呢，我们引用 Harbinger 中的定义:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;code&gt;ItemMeshDefinition&lt;/code&gt;  是一个原版类，允许某个物品只根据  &lt;code&gt;ItemStack&lt;/code&gt;  提供的信息返回不同的  &lt;code&gt;ModelResourceLocation&lt;/code&gt; 。一个  &lt;code&gt;ItemStack&lt;/code&gt;  里有什么？物品类型、数量、metadata（或损害值）甚至是 NBT 数据。理论上应该足够满足各种奇怪的需求了。&lt;/p&gt;
&lt;p&gt;地址: &lt;a href=&#34;https://harbinger.covertdragon.team/chapter-11/baked/custom-mesh.html&#34;&gt;https://harbinger.covertdragon.team/chapter-11/baked/custom-mesh.html&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;相信读到这里，读者已经明白使用 CustomMeshDefinition 的意图了，它可以直接访问 ItemStack 对象，符合我们此时根据午餐盒这个 ItemStack 是否装满的状态来决定贴图的需求，其后对 ItemMeshDefinition 接口的实现的具体代码结合我们上文的伪代码来看，含义也就不言而喻了&lt;/p&gt;
&lt;hr /&gt;
&lt;h3 id=&#34;联立一元二次方程组求解-动态渲染的万能工具&#34;&gt;&lt;a class=&#34;anchor&#34; href=&#34;#联立一元二次方程组求解-动态渲染的万能工具&#34;&gt;#&lt;/a&gt; 联立一元二次方程组求解 - 动态渲染的 &amp;quot;万能工具&amp;quot;&lt;/h3&gt;
&lt;h4 id=&#34;造物品&#34;&gt;&lt;a class=&#34;anchor&#34; href=&#34;#造物品&#34;&gt;#&lt;/a&gt; 造物品&lt;/h4&gt;
&lt;p&gt;读者不妨想一想结合上文两个例子的分析我们能开发出什么来...........&lt;/p&gt;
&lt;p&gt;我认为你可能已经猜出来答案了，因为他就写在标题上 (雾), 所以我们要做万能工具，超凡脱俗的万能工具，举世闻名的万能工具，是会动的长方体...... 哦扯远了......&lt;/p&gt;
&lt;p&gt;传统的万能工具只是一个贴图，我们这次通过 ItemMeshDefinition 来让贴图随着挖掘方块的改变而改变，而决定应该用什么工具的方式，就是使用 ToolHandler.isRightMaterial () 类似的用法了，我们可以直接套用原版工具的材质，这样&lt;s&gt;不花一分钱&lt;/s&gt;就能搞到好看的贴图了&lt;/p&gt;
&lt;p&gt;俗话说万事开头难，但写一个工具的开头还是很简单的，我们可以简单创建一个继承自 ItemTool 的工具并且使其包含所有方块，不过这个解决方案不够浪漫，所以我们直接继承自 ItemPickaxe, 然后为其添加斧和铲的支持，对于原版来说，我们直接允许我们的万能工具挖掘所有的方块都用 efficiency 的速度就万事大吉了&lt;/p&gt;
&lt;figure class=&#34;highlight java&#34;&gt;&lt;figcaption data-lang=&#34;java&#34;&gt;&lt;/figcaption&gt;&lt;table&gt;&lt;tr&gt;&lt;td data-num=&#34;1&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;class&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;UniversalIronTool&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;extends&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;ItemPickaxe&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;&amp;#123;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;2&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;span class=&#34;token keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;UniversalIronTool&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;&amp;#123;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;3&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;        &lt;span class=&#34;token keyword&#34;&gt;super&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token class-name&#34;&gt;ToolMaterial&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token constant&#34;&gt;IRON&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;4&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;        &lt;span class=&#34;token keyword&#34;&gt;this&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;setCreativeTab&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token class-name&#34;&gt;CreativeTabs&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token constant&#34;&gt;TOOLS&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;5&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;        &lt;span class=&#34;token keyword&#34;&gt;this&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;setRegistryName&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token string&#34;&gt;&#34;universal_irontool&#34;&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;6&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;        &lt;span class=&#34;token keyword&#34;&gt;this&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;setTranslationKey&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token string&#34;&gt;&#34;forgedev.universalIronTool&#34;&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;7&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;span class=&#34;token punctuation&#34;&gt;&amp;#125;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;8&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;9&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;span class=&#34;token annotation punctuation&#34;&gt;@Override&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;10&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;span class=&#34;token keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;float&lt;/span&gt; &lt;span class=&#34;token function&#34;&gt;getDestroySpeed&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token class-name&#34;&gt;ItemStack&lt;/span&gt; stack&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;IBlockState&lt;/span&gt; state&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;&amp;#123;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;11&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;        &lt;span class=&#34;token keyword&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;this&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;efficiency&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;12&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;span class=&#34;token punctuation&#34;&gt;&amp;#125;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;13&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token punctuation&#34;&gt;&amp;#125;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;&lt;p&gt;之后我们要监听注册事件并且调用 registry 来注册这个物品，笔者这里就不多赘述了，不要忘记就可以了，如果此时进入游戏我们已经能得到一个紫黑块的 &amp;quot;万能工具了&amp;quot;&lt;/p&gt;
&lt;h4 id=&#34;填材质&#34;&gt;&lt;a class=&#34;anchor&#34; href=&#34;#填材质&#34;&gt;#&lt;/a&gt; 填材质&lt;/h4&gt;
&lt;p&gt;之后我们就要着手给我们的工具添加材质了，也是本章的重中之重，首先我们一如既往的监听&lt;strong&gt; ModelRegistryEvent&lt;/strong&gt;&lt;/p&gt;
&lt;figure class=&#34;highlight java&#34;&gt;&lt;figcaption data-lang=&#34;java&#34;&gt;&lt;/figcaption&gt;&lt;table&gt;&lt;tr&gt;&lt;td data-num=&#34;1&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token annotation punctuation&#34;&gt;@SubscribeEvent&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;2&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;span class=&#34;token annotation punctuation&#34;&gt;@SideOnly&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token class-name&#34;&gt;Side&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token constant&#34;&gt;CLIENT&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;3&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;span class=&#34;token keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;static&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;void&lt;/span&gt; &lt;span class=&#34;token function&#34;&gt;onModelRegistry&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token class-name&#34;&gt;ModelRegistryEvent&lt;/span&gt; event&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;&amp;#123;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;4&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;5&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;span class=&#34;token punctuation&#34;&gt;&amp;#125;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;&lt;p&gt;有了午餐盒的经验，我们已经对自定义模型的添加有了初步认识，我们这里先小小的复习一下：首先我们应该告诉 Minecraft 去准备我们需要的模型。像下面这样，我们声明需要的模型，并且拜托 Minecraft 先为我们加载着&lt;/p&gt;
&lt;figure class=&#34;highlight java&#34;&gt;&lt;figcaption data-lang=&#34;java&#34;&gt;&lt;/figcaption&gt;&lt;table&gt;&lt;tr&gt;&lt;td data-num=&#34;1&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token annotation punctuation&#34;&gt;@SubscribeEvent&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;2&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;span class=&#34;token annotation punctuation&#34;&gt;@SideOnly&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token class-name&#34;&gt;Side&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token constant&#34;&gt;CLIENT&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;3&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;span class=&#34;token keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;static&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;void&lt;/span&gt; &lt;span class=&#34;token function&#34;&gt;onModelRegistry&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token class-name&#34;&gt;ModelRegistryEvent&lt;/span&gt; event&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;&amp;#123;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;4&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    	&lt;span class=&#34;token comment&#34;&gt;// 我们这里使用了原版的木棍，铁镐，铁斧，铁铲的材质，读者也可以更换&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;5&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    	&lt;span class=&#34;token keyword&#34;&gt;final&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;ModelResourceLocation&lt;/span&gt; stick &lt;span class=&#34;token operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;new&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;ModelResourceLocation&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token string&#34;&gt;&#34;stick&#34;&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;6&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;        &lt;span class=&#34;token keyword&#34;&gt;final&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;ModelResourceLocation&lt;/span&gt; pickaxe &lt;span class=&#34;token operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;new&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;ModelResourceLocation&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token string&#34;&gt;&#34;iron_pickaxe&#34;&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;7&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;        &lt;span class=&#34;token keyword&#34;&gt;final&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;ModelResourceLocation&lt;/span&gt; axe &lt;span class=&#34;token operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;new&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;ModelResourceLocation&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token string&#34;&gt;&#34;iron_axe&#34;&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;8&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;        &lt;span class=&#34;token keyword&#34;&gt;final&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;ModelResourceLocation&lt;/span&gt; shovel &lt;span class=&#34;token operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;new&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;ModelResourceLocation&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token string&#34;&gt;&#34;iron_shovel&#34;&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;9&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;        &lt;span class=&#34;token comment&#34;&gt;// 这里 universalIronTool 是刚刚创建的继承自 ItemPickaxe 类的实例&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;10&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;        &lt;span class=&#34;token class-name&#34;&gt;ModelLoader&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;registerItemVariants&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;universalIronTool&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; pickaxe&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; axe&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; shovel&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; stick&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;11&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;span class=&#34;token punctuation&#34;&gt;&amp;#125;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;&lt;p&gt;之后我们就要基于 ItemStack 能搞到的所有信息来为我们选择正确的材质了，毫无疑问我们要基于 ItemStack 里面存储的 NBT 来判断，所以我们的&lt;strong&gt;初步想法&lt;/strong&gt;是:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;步骤一、在玩家开始挖掘方块的一刹那把对应工具类型的字符串&lt;strong&gt;存到 ItemStack 的 NBT&lt;/strong&gt; 里面&lt;/li&gt;
&lt;li&gt;步骤二、在自定义渲染模型中再次读取 NBT, 根据&lt;strong&gt;字符串的内容&lt;/strong&gt;决定该返回&lt;strong&gt;什么工具的 ModelResourceLocation&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h5 id=&#34;践行想法步骤二午餐盒的应用&#34;&gt;&lt;a class=&#34;anchor&#34; href=&#34;#践行想法步骤二午餐盒的应用&#34;&gt;#&lt;/a&gt; 践行想法步骤二 (午餐盒的应用)&lt;/h5&gt;
&lt;p&gt;有了想法我们就可以开始实践了，我们先把渲染趁热打铁写完，我们在上文的代码结束处追加&lt;/p&gt;
&lt;figure class=&#34;highlight java&#34;&gt;&lt;figcaption data-lang=&#34;java&#34;&gt;&lt;/figcaption&gt;&lt;table&gt;&lt;tr&gt;&lt;td data-num=&#34;1&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token comment&#34;&gt;// 这里 universalIronTool 是刚刚创建的继承自 ItemPickaxe 类的实例&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;2&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token class-name&#34;&gt;ModelLoader&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;setCustomMeshDefinition&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;universalIronTool&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; itemStack &lt;span class=&#34;token operator&#34;&gt;-&gt;&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;&amp;#123;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;3&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;span class=&#34;token keyword&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;itemStack&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;hasTagCompound&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;&amp;#123;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;4&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;        &lt;span class=&#34;token class-name&#34;&gt;NBTTagCompound&lt;/span&gt; nbtTag &lt;span class=&#34;token operator&#34;&gt;=&lt;/span&gt; itemStack&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;getTagCompound&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;5&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;        &lt;span class=&#34;token keyword&#34;&gt;switch&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;nbtTag&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;getString&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token string&#34;&gt;&#34;toolType&#34;&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;&amp;#123;&lt;/span&gt; &lt;span class=&#34;token comment&#34;&gt;// 我们打算往 NBT 里面存一个名为 &#34;toolType&#34; 的字符串值&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;6&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;            &lt;span class=&#34;token keyword&#34;&gt;case&lt;/span&gt; &lt;span class=&#34;token string&#34;&gt;&#34;pickaxe&#34;&lt;/span&gt;&lt;span class=&#34;token operator&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;token keyword&#34;&gt;return&lt;/span&gt; pickaxe&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt; &lt;span class=&#34;token comment&#34;&gt;// 如果这个值为 pickaxe, 则返回铁镐的 ModelResourceLocation&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;7&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;            &lt;span class=&#34;token keyword&#34;&gt;case&lt;/span&gt; &lt;span class=&#34;token string&#34;&gt;&#34;axe&#34;&lt;/span&gt;&lt;span class=&#34;token operator&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;token keyword&#34;&gt;return&lt;/span&gt; axe&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;8&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;            &lt;span class=&#34;token keyword&#34;&gt;case&lt;/span&gt; &lt;span class=&#34;token string&#34;&gt;&#34;shovel&#34;&lt;/span&gt;&lt;span class=&#34;token operator&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;token keyword&#34;&gt;return&lt;/span&gt; shovel&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;9&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;            &lt;span class=&#34;token keyword&#34;&gt;default&lt;/span&gt;&lt;span class=&#34;token operator&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;token keyword&#34;&gt;return&lt;/span&gt; stick&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt; &lt;span class=&#34;token comment&#34;&gt;// 如果值为 none 或者什么其他的不合法的值，则让我们的万能工具变成木棍的样子&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;10&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;        &lt;span class=&#34;token punctuation&#34;&gt;&amp;#125;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;11&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;span class=&#34;token punctuation&#34;&gt;&amp;#125;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;12&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;span class=&#34;token keyword&#34;&gt;return&lt;/span&gt; stick&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt; &lt;span class=&#34;token comment&#34;&gt;// 如果干脆物品就没有 NBT, 那也是渲染木棍的贴图&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;13&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token punctuation&#34;&gt;&amp;#125;&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;&lt;p&gt;现在 Minecraft 已经会根据 NBT 的内容不同来为我们正确挑选材质了，不过接下来也是重点，我们要完成想法的步骤一 (不要问我为什么先二后一)&lt;/p&gt;
&lt;h5 id=&#34;践行想法步骤一息壤的应用&#34;&gt;&lt;a class=&#34;anchor&#34; href=&#34;#践行想法步骤一息壤的应用&#34;&gt;#&lt;/a&gt; 践行想法步骤一 (息壤的应用)&lt;/h5&gt;
&lt;p&gt;玩家挖掘方块的一刹那，相信读者头脑中自然就产生了监听&lt;strong&gt; PlayerInteractEvent&lt;/strong&gt; 事件的念头，那么恭喜你，猜对了，我们正是要监听玩家与方块交互的这个事件，我们创建 EventHandler 类并监听它&lt;/p&gt;
&lt;figure class=&#34;highlight java&#34;&gt;&lt;figcaption data-lang=&#34;java&#34;&gt;&lt;/figcaption&gt;&lt;table&gt;&lt;tr&gt;&lt;td data-num=&#34;1&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token annotation punctuation&#34;&gt;@Mod.EventBusSubscriber&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;modid &lt;span class=&#34;token operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;token string&#34;&gt;&#34;forgedev&#34;&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;2&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;class&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;EventHandler&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;&amp;#123;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;3&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;span class=&#34;token annotation punctuation&#34;&gt;@SubscribeEvent&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;4&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;span class=&#34;token keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;static&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;void&lt;/span&gt; &lt;span class=&#34;token function&#34;&gt;onInteract&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token class-name&#34;&gt;PlayerInteractEvent&lt;/span&gt; event&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;&amp;#123;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;5&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;        &lt;span class=&#34;token keyword&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;event&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;getSide&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;isClient&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;return&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;6&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;        &lt;span class=&#34;token keyword&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;event&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;getHand&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;equals&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token class-name&#34;&gt;EnumHand&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token constant&#34;&gt;OFF_HAND&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;return&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;7&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;        &lt;span class=&#34;token keyword&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;event&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;getItemStack&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;getItem&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;instanceof&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;UniversalIronTool&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;&amp;#123;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;8&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;            &lt;span class=&#34;token class-name&#34;&gt;Material&lt;/span&gt; curMaterial &lt;span class=&#34;token operator&#34;&gt;=&lt;/span&gt; event&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;getWorld&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;getBlockState&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;event&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;getPos&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;getMaterial&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;9&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;            &lt;span class=&#34;token class-name&#34;&gt;NBTTagCompound&lt;/span&gt; nbtTag &lt;span class=&#34;token operator&#34;&gt;=&lt;/span&gt; event&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;getItemStack&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;hasTagCompound&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;token operator&#34;&gt;?&lt;/span&gt; event&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;getItemStack&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;getTagCompound&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;token operator&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;new&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;NBTTagCompound&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;10&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;            nbtTag&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;setString&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token string&#34;&gt;&#34;toolType&#34;&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;ToolUtils&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;getRightTool&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;curMaterial&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;11&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;            event&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;getItemStack&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token function&#34;&gt;setTagCompound&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;nbtTag&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;12&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;        &lt;span class=&#34;token punctuation&#34;&gt;&amp;#125;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;13&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;span class=&#34;token punctuation&#34;&gt;&amp;#125;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;14&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token punctuation&#34;&gt;&amp;#125;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;&lt;p&gt;相信代码的含义读者也一目了然，我们给 ItemStack 创建了一个 NBT, 并且调用 **ToolUtils.getRightTool ()** 方法来获取指定方块应该用什么工具来采掘..........Wait a second....... 别着急，我们现在来创建 ToolUtils 类&lt;/p&gt;
&lt;figure class=&#34;highlight java&#34;&gt;&lt;figcaption data-lang=&#34;java&#34;&gt;&lt;/figcaption&gt;&lt;table&gt;&lt;tr&gt;&lt;td data-num=&#34;1&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;class&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;ToolUtils&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;&amp;#123;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;2&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;span class=&#34;token keyword&#34;&gt;private&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;static&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;Material&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;]&lt;/span&gt; materialsPickaxe &lt;span class=&#34;token operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;new&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;Material&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;]&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;&amp;#123;&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;Material&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token constant&#34;&gt;ROCK&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;Material&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token constant&#34;&gt;IRON&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;Material&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token constant&#34;&gt;ICE&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;Material&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token constant&#34;&gt;GLASS&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;Material&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token constant&#34;&gt;PISTON&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;Material&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token constant&#34;&gt;ANVIL&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;&amp;#125;&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;3&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;span class=&#34;token keyword&#34;&gt;private&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;static&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;Material&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;]&lt;/span&gt; materialsShovel &lt;span class=&#34;token operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;new&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;Material&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;]&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;&amp;#123;&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;Material&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token constant&#34;&gt;GRASS&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;Material&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token constant&#34;&gt;GROUND&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;Material&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token constant&#34;&gt;SAND&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;Material&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token constant&#34;&gt;SNOW&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;Material&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token constant&#34;&gt;CRAFTED_SNOW&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;Material&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token constant&#34;&gt;CLAY&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;&amp;#125;&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;4&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;span class=&#34;token keyword&#34;&gt;private&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;static&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;Material&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;]&lt;/span&gt; materialsAxe &lt;span class=&#34;token operator&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;new&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;Material&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;]&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;&amp;#123;&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;Material&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token constant&#34;&gt;CORAL&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;Material&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token constant&#34;&gt;LEAVES&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;Material&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token constant&#34;&gt;PLANTS&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;Material&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;token constant&#34;&gt;WOOD&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;&amp;#125;&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;5&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;6&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;span class=&#34;token keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;static&lt;/span&gt; &lt;span class=&#34;token class-name&#34;&gt;String&lt;/span&gt; &lt;span class=&#34;token function&#34;&gt;getRightTool&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token class-name&#34;&gt;Material&lt;/span&gt; material&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;&amp;#123;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;7&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;        &lt;span class=&#34;token keyword&#34;&gt;for&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token class-name&#34;&gt;Material&lt;/span&gt; mat &lt;span class=&#34;token operator&#34;&gt;:&lt;/span&gt; materialsAxe&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;&amp;#123;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;8&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;            &lt;span class=&#34;token keyword&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;material &lt;span class=&#34;token operator&#34;&gt;==&lt;/span&gt; mat&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;token string&#34;&gt;&#34;axe&#34;&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;9&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;        &lt;span class=&#34;token punctuation&#34;&gt;&amp;#125;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;10&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;        &lt;span class=&#34;token keyword&#34;&gt;for&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token class-name&#34;&gt;Material&lt;/span&gt; mat &lt;span class=&#34;token operator&#34;&gt;:&lt;/span&gt; materialsPickaxe&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;&amp;#123;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;11&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;            &lt;span class=&#34;token keyword&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;material &lt;span class=&#34;token operator&#34;&gt;==&lt;/span&gt; mat&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;token string&#34;&gt;&#34;pickaxe&#34;&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;12&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;        &lt;span class=&#34;token punctuation&#34;&gt;&amp;#125;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;13&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;        &lt;span class=&#34;token keyword&#34;&gt;for&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;token class-name&#34;&gt;Material&lt;/span&gt; mat &lt;span class=&#34;token operator&#34;&gt;:&lt;/span&gt; materialsShovel&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;&amp;#123;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;14&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;            &lt;span class=&#34;token keyword&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;token punctuation&#34;&gt;(&lt;/span&gt;material &lt;span class=&#34;token operator&#34;&gt;==&lt;/span&gt; mat&lt;span class=&#34;token punctuation&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;token keyword&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;token string&#34;&gt;&#34;shovel&#34;&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;15&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;        &lt;span class=&#34;token punctuation&#34;&gt;&amp;#125;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;16&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;        &lt;span class=&#34;token keyword&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;token string&#34;&gt;&#34;none&#34;&lt;/span&gt;&lt;span class=&#34;token punctuation&#34;&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;17&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;    &lt;span class=&#34;token punctuation&#34;&gt;&amp;#125;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td data-num=&#34;18&#34;&gt;&lt;/td&gt;&lt;td&gt;&lt;pre&gt;&lt;span class=&#34;token punctuation&#34;&gt;&amp;#125;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;&lt;p&gt;类似于神秘工匠中息壤的写法，我们也创建了三个对应工具应挖掘的 Material 的集合，并且在 getRightTool 中遍历集合，找到其正确对应的集合并且返回 &amp;quot;axe&amp;quot;, &amp;quot;pickaxe&amp;quot; 或者是 &amp;quot;shovel&amp;quot;, 如果该 material 不属于任何工具将会返回 &amp;quot;none&amp;quot;&lt;/p&gt;
&lt;h4 id=&#34;看效果&#34;&gt;&lt;a class=&#34;anchor&#34; href=&#34;#看效果&#34;&gt;#&lt;/a&gt; 看效果&lt;/h4&gt;
&lt;p&gt;至此，我们已经创建了我们会动的万能工具了，你可以检查一遍自己是否已经监听了所有应该监听的事件，注册了所有应该注册的事件处理器，之后我们就可以进入游戏查看效果了，不出意外的话你会得到下面这样拔群的效果&lt;/p&gt;
&lt;p&gt;&lt;img loading=&#34;lazy&#34; data-src=&#34;https://ftp.bmp.ovh/imgs/2020/04/0f18c95f3ec36699.gif&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;
&lt;hr /&gt;
&lt;h3 id=&#34;小结&#34;&gt;&lt;a class=&#34;anchor&#34; href=&#34;#小结&#34;&gt;#&lt;/a&gt; 小结&lt;/h3&gt;
&lt;p&gt;本节内容的小结:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;了解了神秘工匠息壤工具决策的内部逻辑&lt;/li&gt;
&lt;li&gt;明白了如何使用&lt;strong&gt; MeshDefinition&lt;/strong&gt; 来自定义更改物品模型&lt;/li&gt;
&lt;li&gt;在 1 和 2 的基础上举 &amp;quot;万能工具&amp;quot; 例子，使读者对 1, 2 有&lt;strong&gt;更深了解&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;明确了&lt;strong&gt;举二反三深入模组开发&lt;/strong&gt;的基本形式&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;课后小思考:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;我们能否通过多态和封装让我们的万能工具直接支持原版所有的材料呢？&lt;/li&gt;
&lt;li&gt;思考能否通过&lt;strong&gt; MeshDefinition&lt;/strong&gt; 来&lt;strong&gt;动态生成&lt;/strong&gt;材质呢&lt;/li&gt;
&lt;/ul&gt;
</content>
        <category term="Minecraft" />
        <category term="模组开发" />
        <updated>2020-04-13T03:19:59.000Z</updated>
    </entry>
    <entry>
        <id>https://turou.fun/minecraft/cs-project/apis/</id>
        <title>CustomStuff3 API概述</title>
        <link rel="alternate" href="https://turou.fun/minecraft/cs-project/apis/"/>
        <content type="html">&lt;p&gt;&lt;img loading=&#34;lazy&#34; data-src=&#34;https://s2.loli.net/2023/01/10/k8xFHuVPMw9pe14.png&#34; alt=&#34;635589679383149678.png&#34; /&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;font-size3-face微软雅黑font-size3-face微软雅黑声明本api文档是帮助理解cs3的js内容编写troufont&#34;&gt;&lt;a class=&#34;anchor&#34; href=&#34;#font-size3-face微软雅黑font-size3-face微软雅黑声明本api文档是帮助理解cs3的js内容编写troufont&#34;&gt;#&lt;/a&gt; &amp;lt;font size=3 face=&amp;quot;微软雅黑&amp;quot;&amp;gt;&lt;br /&gt;
&lt;strong&gt;&amp;lt;font size=3 face=&amp;quot;微软雅黑&amp;quot;&amp;gt; 声明：本 API 文档是帮助理解 CS3 的 JS 内容，编写：TROU&amp;lt;/font&amp;gt;&lt;/strong&gt;&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;&amp;lt;font size=3 face=&amp;quot;微软雅黑&amp;quot;&amp;gt; 序言：&amp;lt;/font&amp;gt;&lt;/strong&gt;&lt;br /&gt;
 距离上次发教程也过了很久了，我考虑了一下，我决定先放出 API 及事件部分，可能还是需要这部分的人比较多，官方的游戏内帮助写的也不是很清楚，我也顺便完善以下，给第一次使用 JS 的朋友们加一些注释和举例，以便于大家理解，这章会比较长，我会都写在这一个文件里面，以便于查看，有些难以理解的或者太过复杂的内容我将会使用 gif 或视频来讲解，最后说一句话，不要局限在我的例子里面，灵感是属于你自己的，我只是帮助你理解而已，废话我就不说了，直接开始 API 部分&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;&amp;lt;font size=3 face=&amp;quot;微软雅黑&amp;quot;&amp;gt; 格式：&amp;lt;/font&amp;gt;&lt;/strong&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;这是我的注释&lt;br /&gt;
关于语法的易错点也会写在里面&lt;/p&gt;
&lt;/blockquote&gt;
&lt;pre&gt;&lt;code&gt;这是代码
关于代码的注释也会使用&amp;#123;&amp;#125;写在里面
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;普通文字&lt;/p&gt;
&lt;p 这是代码注释=&#34;&#34;&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;重要的文字&lt;/strong&gt;&lt;/p&gt;
&lt;hr /&gt;
&lt;p&gt;&lt;strong&gt;&amp;lt;font size=5 face=&amp;quot;微软雅黑&amp;quot;&amp;gt;Part1：World&amp;lt;/font&amp;gt;&lt;/strong&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;对于当前存档中的世界的操作及信息&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;可使用 world 操作的触发动作:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;onBlockDestroyed 当使用这个物品破坏方块的时候
onBlockStartBreak 当方块被破坏的时候(破坏前一瞬间)
onCreated 当你制作这个东西的时候(合成/冶炼)
onDroppedByPlayer 当玩家捡起这个东西的时候
onEaten 被吃掉的时候
onHitEntity 击打实体的时候
onLeftClickLiving 左击生物的时候
onLeftClickPlayer 左击玩家的时候
onRightClick 右击的时候
onStoppedUsing 停止使用的时候
onUpdate 更新的时候(只要你身上有这个东西就会循环执行)
onUse 被使用的时候
onUseOnEntity 在实体上使用的时候
onUseOnPlayer 在玩家上使用的时候
onUsing 正在使用的时候
onActivated 活跃的时候(空手右击)
onAdded 增加的时候
onBonemeal 使用骨粉的时候
onBreak 被破坏的时候
onClicked 右击的时候
onCollided 生物穿过的时候(循环执行)
onFallenUpon 在上面跳跃的时候
onNeighborChange 周围方块改变的时候(检测方块更新)
onPlaced 被放置的时候
onPlaceBy 依靠于什么方块放置的时候
onPlaceByPlayer 被玩家放置的时候
onRandomDisplayTick 随机发生的事件(通常用于粒子效果的显示)
onRedstoneSignal 接收到红石信号的的时候
onUpdate 更新的时候(只要放下就会循环执行)
onWalking 在上面走的时候
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;&amp;lt;font size=4 face=&amp;quot;微软雅黑&amp;quot;&amp;gt;World 的所有方法:&amp;lt;/font&amp;gt;&lt;/strong&gt;&lt;/p&gt;
&lt;h3 id=&#34;countentities-取实体数量&#34;&gt;&lt;a class=&#34;anchor&#34; href=&#34;#countentities-取实体数量&#34;&gt;#&lt;/a&gt; countEntities (取实体数量)&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;调用方式&lt;/strong&gt;:world.countEntities (x, y, z, radius, entities)&lt;br /&gt;
&lt;strong&gt; 返回值类型&lt;/strong&gt;:int (范围内实体的数量)&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;返回在指定位置 (x, y, z) 的指定范围 (radius) 内的指定实体 (entities) 的数量&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;strong&gt;参数规则&lt;/strong&gt;:&lt;/p&gt;
&lt;p&gt;x (int) 欲取出范围中心点的 X 轴&lt;br /&gt;
 y (int) 欲取出范围中心点的 Y 轴&lt;br /&gt;
 z (int) 欲取出范围中心点的 Z 轴&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;x, y, z 这三个参数也可以使用某些触发动作自带的 position 来定义&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;radius (float) 欲检测中心点的范围，注意使用浮点型&lt;br /&gt;
 entities (string) 欲检测实体的名称，多个名称使用英文逗号 “,” 来隔开&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;既可以输入实体 ID，也可以输入实体名称 (建议使用 CS3 自带的实体名字获取来确定，因为 Minecraft Wiki 关于实体部分的内容已经过期，无法使用，且 Minecraft 实体种类过多，这里不做介绍，如果感兴趣可以在后面找到实体部分的取实体名称)，这里也可以填写实体的种类，我列举一下实体的种类，注意要使用 &amp;quot;&amp;quot; 来括起来，因为这里要求填写的是字符串&lt;/p&gt;
&lt;/blockquote&gt;
&lt;pre&gt;&lt;code&gt;hostile 敌对生物
animal 动物
mob 生物
player 玩家
item 掉落的物品 
all 全部
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;注意这里的一个实体使用 2 来表示，比如这个范围内有 1 个实体，那么数值就是 2，有 2 个实体，数值为 4，同样的物品每 64 个算为一个实体&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;例子&lt;/strong&gt;:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;我在 onRightClick 中写的代码&lt;/p&gt;
&lt;p&gt;这里会用到一个另外的比较简单的代码，我也会简单介绍，后文详细介绍&lt;/p&gt;
&lt;p&gt;关于代码的注释会使用 {} 括起来&lt;/p&gt;
&lt;/blockquote&gt;
&lt;pre&gt;&lt;code&gt;带注释的代码:
var 实体
&amp;#123;声明实体这个变量&amp;#125;
实体 = world.countEntities(-209, 4, 48, 5.0, &amp;quot;items, player&amp;quot;)
&amp;#123;取-209, 4, 48这个地方5格以内物品和玩家的数量并储存在“实体”这个变量里面&amp;#125;
world.sendMessageToAllPlayers(实体.toString())
&amp;#123;这个语句我们没有接触过，我简单讲解一下，这个语句的意思是把实体里面的数值先转换为字符串，在给全体玩家发送过去(显示在聊天栏)，比如我输入world.sendMessageToAllPlayers(&amp;quot;啊啊啊啊&amp;quot;)，这样全体玩家的聊天栏都会出现&amp;quot;啦啦啦啦&amp;quot;这四个字，会在以后详细介绍&amp;#125;

不带注释的代码:
var 实体
实体 = world.countEntities(-209, 4, 48, 5.0, &amp;quot;items, player&amp;quot;)
world.sendMessageToAllPlayers(实体.toString())

效果:
&amp;#123;因为我输入的是检测玩家和物品，一个玩家+两种物品，也就是2+2+2=6&amp;#125;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img loading=&#34;lazy&#34; data-src=&#34;https://miao.su/images/2017/08/11/TIM2017081109465522843.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;
&lt;p&gt;###createExplosion (创建爆炸)&lt;br /&gt;
&lt;strong&gt; 调用方式&lt;/strong&gt;:world.createExplosion (x, y, z, strength, flaming)&lt;br /&gt;
&lt;strong&gt; 返回值类型&lt;/strong&gt;：没有返回值&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;在你指定的位置创建一个指定强度的爆炸&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;strong&gt;参数规则&lt;/strong&gt;:&lt;/p&gt;
&lt;p&gt;x (float) 欲爆炸位置的 X 轴&lt;br /&gt;
 y (float) 欲爆炸位置的 Y 轴&lt;br /&gt;
 z (float) 欲爆炸位置的 Z 轴&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;x, y, z 这三个参数也可以使用某些触发动作自带的 position 来定义&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;strength (float) 爆炸的强度&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;TNT的爆炸强度是4.0
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;flaming (bool) 是否让爆炸的附近着火 (是填 true，否填 false)&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;例子&lt;/strong&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;带注释的代码:
world.createExplosion(-207, 4, 48, 8.0, true)
&amp;#123;在-207, 4, 48的位置创建一个会着火的8.0级别的爆炸&amp;#125;

不带注释的代码:
world.createExplosion(-207, 4, 48, 8.0, true)

效果:
&amp;#123;因为我是超平坦，爆炸效果可能不是特别明显&amp;#125;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img loading=&#34;lazy&#34; data-src=&#34;https://miao.su/images/2017/08/11/TIM20170811095913308de.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;
&lt;p&gt;###createThunderbolt 创建一道雷电&lt;br /&gt;
&lt;strong&gt;调用方式&lt;/strong&gt;:world.createThunderbolt (x, y, z)&lt;br /&gt;
&lt;strong&gt; 返回值类型&lt;/strong&gt;：没有返回值&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;在指定的位置创建一道闪电&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;strong&gt;参数规则&lt;/strong&gt;:&lt;/p&gt;
&lt;p&gt;x (float) 欲雷击位置的 X 轴&lt;br /&gt;
 y (float) 欲雷击位置的 Y 轴&lt;br /&gt;
 z (float) 欲雷击位置的 Z 轴&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;x, y, z 这三个参数也可以使用某些触发动作自带的 position 来定义&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;strong&gt;例子&lt;/strong&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;带注释的代码:
world.createThunderbolt(-226, 4, 70)
&amp;#123;在-226, 4, 70的位置召唤一道闪电&amp;#125;

不带注释的代码:
world.createThunderbolt(-226, 4, 70)

效果:
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img loading=&#34;lazy&#34; data-src=&#34;https://miao.su/images/2017/08/11/TIM2017081110062590867.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;
&lt;p&gt;###enumEntities (枚举实体)&lt;br /&gt;
&lt;strong&gt; 调用方式&lt;/strong&gt;:world.enumEntities (x, y, z, radius, entities)&lt;br /&gt;
&lt;strong&gt; 返回值类型&lt;/strong&gt;:entity (枚举的结果)&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;枚举一个范围内的所有实体&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;strong&gt;参数规则&lt;/strong&gt;:&lt;/p&gt;
&lt;p&gt;x (int) 欲枚举实体范围中心点的 X 轴&lt;br /&gt;
 y (int) 欲枚举实体范围中心点的 Y 轴&lt;br /&gt;
 z (int) 欲枚举实体范围中心点的 Z 轴&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;x, y, z 这三个参数也可以使用某些触发动作自带的 position 来定义&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;radius (float) 欲枚举实体中心点的范围，注意使用浮点型&lt;br /&gt;
 entities (string) 欲枚举实体的名称，多个名称使用英文逗号 “,” 来隔开&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;既可以输入实体 ID，也可以输入实体名称 (建议使用 CS3 自带的实体名字获取来确定，因为 Minecraft Wiki 关于实体部分的内容已经过期，无法使用，且 Minecraft 实体种类过多，这里不做介绍，如果感兴趣可以在后面找到实体部分的取实体名称)，这里也可以填写实体的种类，我列举一下实体的种类，注意要使用 &amp;quot;&amp;quot; 来括起来，因为这里要求填写的是字符串&lt;/p&gt;
&lt;/blockquote&gt;
&lt;pre&gt;&lt;code&gt;hostile 敌对生物
animal 动物
mob 生物
player 玩家
item 掉落的物品 
all 全部
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;例子&lt;/strong&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;带注释的代码:
world.enumEntities(-226, 4, 70, 8.0, &amp;quot;animal&amp;quot;)
&amp;#123;枚举-226, 4, 70为中心点8格范围内的动物&amp;#125;

不带注释的代码:
world.enumEntities(-226, 4, 70, 8.0, &amp;quot;animal&amp;quot;)

效果:
这个命令比较特殊，他的返回值是entity实体，但是我无法对这个实体进行操作，而且这个命令不常用，尽量不要使用
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;###getBiome (获取生物群系)&lt;br /&gt;
&lt;strong&gt; 调用方式&lt;/strong&gt;:world.getBiome (x, y, z)&lt;br /&gt;
&lt;strong&gt; 返回值类型&lt;/strong&gt;:string (生物群系的英文名称)&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;获取指定位置的生物群系并返回&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;strong&gt;参数规则&lt;/strong&gt;:&lt;/p&gt;
&lt;p&gt;x (float) 欲取得生物群系的方块的位置的 X 轴&lt;br /&gt;
 y (float) 欲取得生物群系的方块的位置的 Y 轴&lt;br /&gt;
 z (float) 欲取得生物群系的方块的位置的 Z 轴&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;x, y, z 这三个参数也可以使用某些触发动作自带的 position 来定义&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;strong&gt;例子&lt;/strong&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;带注释的代码:
world.sendMessageToAllPlayers(world.getBiome(-226, 4, 70))
&amp;#123;先获取-226, 4, 70这个位置的生物群系，在显示在所有玩家的聊天栏&amp;#125;

不带注释的代码:
world.sendMessageToAllPlayers(world.getBiome(-226, 4, 70))
效果:
&amp;#123;因为-226, 4, 70这个位置是草原，也就是Plains&amp;#125;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;{关于生物群系的对应文本，可以在&lt;a href=&#34;https://minecraft-zh.gamepedia.com/%E7%94%9F%E7%89%A9%E7%BE%A4%E7%B3%BB#.E7.94.9F.E7.89.A9.E7.BE.A4.E7.B3.BBID&#34;&gt;这里&lt;/a&gt;找到}&lt;/p&gt;
&lt;p&gt;&lt;img loading=&#34;lazy&#34; data-src=&#34;https://miao.su/images/2017/08/11/TIM20170811113255fec1b.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;
&lt;p&gt;###getBlockLightLevel (获取方块的光照等级)&lt;br /&gt;
&lt;strong&gt; 调用方式&lt;/strong&gt;:world.getBlockLightLevel (x, y, z)&lt;br /&gt;
&lt;strong&gt; 返回值类型&lt;/strong&gt;:int (指定方块的光照等级)&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;获取指定位置的方块的光照等级并返回&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;strong&gt;参数规则&lt;/strong&gt;:&lt;/p&gt;
&lt;p&gt;x (int) 欲取得光照等级的方块的位置的 X 轴&lt;br /&gt;
 y (int) 欲取得光照等级的方块的位置的 Y 轴&lt;br /&gt;
 z (int) 欲取得光照等级的方块的位置的 Z 轴&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;x, y, z 这三个参数也可以使用某些触发动作自带的 position 来定义&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;strong&gt;例子&lt;/strong&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;带注释的代码:
var 光照 = world.getBlockLightLevel(-232, 4, 170)
&amp;#123;先初始化“光照”这个变量，并同时给“光照赋值”为取-232, 4, 170的光照等级的结果&amp;#125;
world.sendMessageToAllPlayers(光照)
&amp;#123;把获取到的光照等级转换为字符串显示在所有玩家的聊天栏&amp;#125;

不带注释的代码:
var 光照 = world.getBlockLightLevel(-232, 4, 170)
world.sendMessageToAllPlayers(光照.toString())
效果:
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img loading=&#34;lazy&#34; data-src=&#34;https://miao.su/images/2017/08/11/TIM20170811114209fe48d.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;
</content>
        <category term="Minecraft" />
        <category term="模组开发" />
        <category term="CustomStuff3" />
        <updated>2017-12-01T02:46:56.000Z</updated>
    </entry>
    <entry>
        <id>https://turou.fun/minecraft/cs-project/events/</id>
        <title>CustomStuff3事件概述</title>
        <link rel="alternate" href="https://turou.fun/minecraft/cs-project/events/"/>
        <content type="html">&lt;p&gt;&lt;img loading=&#34;lazy&#34; data-src=&#34;https://s2.loli.net/2023/01/10/k8xFHuVPMw9pe14.png&#34; alt=&#34;635589679383149678.png&#34; /&gt;&lt;/p&gt;
&lt;h2 id=&#34;font-size6-face微软雅黑简单事件font&#34;&gt;&lt;a class=&#34;anchor&#34; href=&#34;#font-size6-face微软雅黑简单事件font&#34;&gt;#&lt;/a&gt; &lt;strong&gt;&amp;lt;font size=6 face=&amp;quot;微软雅黑&amp;quot;&amp;gt; 简单事件 &amp;lt;/font&amp;gt;&lt;/strong&gt;&lt;/h2&gt;
&lt;h2 id=&#34;font-size5-face微软雅黑序言font&#34;&gt;&lt;a class=&#34;anchor&#34; href=&#34;#font-size5-face微软雅黑序言font&#34;&gt;#&lt;/a&gt; &lt;strong&gt;&amp;lt;font size=5 face=&amp;quot;微软雅黑&amp;quot;&amp;gt; 序言 &amp;lt;/font&amp;gt;&lt;/strong&gt;&lt;/h2&gt;
&lt;p&gt;&amp;lt;font size=3 face=&amp;quot;微软雅黑&amp;quot;&amp;gt;&lt;br /&gt;
 这章我来介绍一下 CS3 事件部分的基础内容，比较高级的实例及全部 API 的说明会放在下一章进行&lt;/p&gt;
&lt;h2 id=&#34;font-size5-face微软雅黑添加事件font&#34;&gt;&lt;a class=&#34;anchor&#34; href=&#34;#font-size5-face微软雅黑添加事件font&#34;&gt;#&lt;/a&gt; &lt;strong&gt;&amp;lt;font size=5 face=&amp;quot;微软雅黑&amp;quot;&amp;gt; 添加事件 &amp;lt;/font&amp;gt;&lt;/strong&gt;&lt;/h2&gt;
&lt;p&gt;你可以进入你创建的方块 / 物品的编辑界面，你可以看到一些 on 开头的属性，在这里，我把它叫做触发动作 (我自己的命名)&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;所有的触发动作所对应的意思:
onBlockDestroyed 当使用这个物品破坏方块的时候
onBlockStartBreak 当方块被破坏的时候(破坏前一瞬间)
onCreated 当你制作这个东西的时候(合成/冶炼)
onDroppedByPlayer 当玩家捡起这个东西的时候
onEaten 被吃掉的时候
onHitEntity 击打实体的时候
onLeftClickLiving 左击生物的时候
onLeftClickPlayer 左击玩家的时候
onRightClick 右击的时候
onStoppedUsing 停止使用的时候
onUpdate 更新的时候(只要你身上有这个东西就会循环执行)
onUse 被使用的时候
onUseOnEntity 在实体上使用的时候
onUseOnPlayer 在玩家上使用的时候
onUsing 正在使用的时候
onActivated 活跃的时候(空手右击)
onAdded 增加的时候
onBonemeal 使用骨粉的时候
onBreak 被破坏的时候
onClicked 右击的时候
onCollided 生物穿过的时候(循环执行)
onFallenUpon 在上面跳跃的时候
onNeighborChange 周围方块改变的时候(检测方块更新)
onPlaced 被放置的时候
onPlaceBy 依靠于什么方块放置的时候
onPlaceByPlayer 被玩家放置的时候
onRandomDisplayTick 随机发生的事件(通常用于粒子效果的显示)
onRedstoneSignal 接收到红石信号的的时候
onUpdate 更新的时候(只要放下就会循环执行)
onWalking 在上面走的时候
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;这些就是全部的触发动作，我们可以在触发动作里面编写一些代码，这些代码叫做这个触发动作的事件，那么事件都可以写些什么呢，我会在下一章的 API 帮助文档中详细介绍到，这也是 CS3 的重要组成部分&lt;/p&gt;
&lt;p&gt;那么我们如何给一个触发动作添加我们想要的事件呢？我们只需点击对应的触发事件的按键就可以了 (如果你找不到，你可以去方块 / 物品里面看看，有一些触发动作是方块 / 物品独有的)，比如说这里我们点击&lt;strong&gt; onRightClick&lt;/strong&gt;，也就是右击的时候，如图&lt;/p&gt;
&lt;p&gt;&lt;img loading=&#34;lazy&#34; data-src=&#34;https://miao.su/images/2017/08/16/TIM20170816134415cea3c.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;
&lt;p&gt;进入编辑界面，我们可以看到一个黑框，我们可以在这里编辑事件的代码，左下角是 Save (保存), 右下角有 Info (API 新建) 以及 Cancel (不保存退出)，当我们编辑好了事件的代码，我们就可以直接保存了，如果有语法错误，CS3 也会随即提示你，如果你有不知道怎么解决的问题，也可以直接在 MCBBS 或百度贴吧问我&lt;/p&gt;
&lt;p&gt;&lt;img loading=&#34;lazy&#34; data-src=&#34;https://miao.su/images/2017/08/16/TIM201708161347584c049.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;
&lt;p&gt;你们可能很想知道，我里面输入的这句语句是什么意思，会产生什么样的效果，你们可以先照着我的输入一遍，我在下面会讲解这个语句的作用。当你输入完成后，点击 Save，并拿出你创建的物品，点击鼠标右键&lt;/p&gt;
&lt;p&gt;&lt;img loading=&#34;lazy&#34; data-src=&#34;https://miao.su/images/2017/08/16/TIM20170816135024d5a67.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;
&lt;p&gt;我们刚刚的语句就发生了作用，在你输入的过程中，要注意双引号，如果你看了前面的 JavaScript 那课，你可能对双引号并不陌生，对，双引号引起的部分叫做字符串 (string)，也就是一段文本，当然你也可以把 HelloWorld! 改成任何中文 / 日文 / 英文等文字，如果你安装了 Inputfix，就可以直接输入中文了，同样也支持颜色代码 § 的使用，具体的颜色可以前往第一章第二课查看&lt;/p&gt;
&lt;p&gt;&lt;img loading=&#34;lazy&#34; data-src=&#34;https://miao.su/images/2017/08/16/TIM201708161355594374c.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img loading=&#34;lazy&#34; data-src=&#34;https://miao.su/images/2017/08/16/TIM20170816135611fe258.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;有些人可能想问了，这个语句到底是什么意思，他的官方解释是什么呢，你可以打开Info界面，点击player, 并且找到sendMessage条目，左键点击，你就可以找到你的答案
什么！你不懂英语？
不懂我来给你翻译下啦
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img loading=&#34;lazy&#34; data-src=&#34;https://miao.su/images/2017/08/16/TIM2017081614005091e98.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;这个命令很简单，就是给玩家发送一条信息，括号中要填写的是message(信息)
你这时可能会想问，我们没学过message这个类型啊？看到下面
- message (string) ······
这里了吗，这里就详细说明了message需要填写一个字符串，而且后面还给予了提示，十分人性化，且易理解
关于所有的语句的意思及使用方法，都会放在下一章来介绍
&lt;/code&gt;&lt;/pre&gt;
&lt;hr /&gt;
&lt;p&gt;你们学会了吗，这就是本章的全部内容，如果你们想知道更多语句，就给我金粒人气吧，这样我可能会更新的快一点 233 谢谢大家的支持！&lt;/p&gt;
&lt;hr /&gt;
&lt;p&gt;顺便我在这里说一下下一章节可能会使用到的一些关于编程的语言&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;我们以 player.sendMessage() 为例
sendMessage是player的一个方法
player.sendMessage()是一个函数，我习惯直接称作语句
()括号里面内容叫做参数
比如player.sendMessage(&amp;quot;你好&amp;quot;)
&amp;quot;你好&amp;quot;是player.sendMessage的一个参数
有些语句可能有多个函数，中间使用英文逗号, 隔开
比如world.sendMessageToPlayer(&amp;quot;TROU2004&amp;quot;, &amp;quot;你是TROU2004&amp;quot;)
这个语句就有2个参数
第一个参数是&amp;quot;TROU2004&amp;quot;
第二个参数是&amp;quot;你是TROU2004&amp;quot;
这个语句需要2个参数才能正常使用
有些语句不需要参数，但是()括号是不能省略的
有些语句有返回值，有些没有
比如 1+2
他的返回值是3
在下一章API文档中我也会具体告诉你们所有语句的返回值类型
另外在JS中也可以使用小括号来更改运算顺序
var a = 1+2*8
这时变量a是整数型(int)，值是17
var b = (1+2)*8
这时变量a是整数型(int)，值是24
&lt;/code&gt;&lt;/pre&gt;
&lt;hr /&gt;
&lt;p&gt;大家看到这里辛苦了，递杯 JAVA 给你们 233&lt;/p&gt;
</content>
        <category term="Minecraft" />
        <category term="模组开发" />
        <category term="CustomStuff3" />
        <updated>2017-11-30T02:46:56.000Z</updated>
    </entry>
    <entry>
        <id>https://turou.fun/minecraft/cs-project/scripts/</id>
        <title>了解CustomStuff3脚本</title>
        <link rel="alternate" href="https://turou.fun/minecraft/cs-project/scripts/"/>
        <content type="html">&lt;p&gt;&lt;img loading=&#34;lazy&#34; data-src=&#34;https://s2.loli.net/2023/01/10/k8xFHuVPMw9pe14.png&#34; alt=&#34;635589679383149678.png&#34; /&gt;&lt;/p&gt;
&lt;h2 id=&#34;font-size5-face微软雅黑序言font&#34;&gt;&lt;a class=&#34;anchor&#34; href=&#34;#font-size5-face微软雅黑序言font&#34;&gt;#&lt;/a&gt; &lt;strong&gt;&amp;lt;font size=5 face=&amp;quot;微软雅黑&amp;quot;&amp;gt; 序言 &amp;lt;/font&amp;gt;&lt;/strong&gt;&lt;/h2&gt;
&lt;p&gt;&amp;lt;font size=3 face=&amp;quot;微软雅黑&amp;quot;&amp;gt;&lt;br /&gt;
 从现在开始，就要讲解事件部分了，CS3 的事件部分很简单，且非常有用，CS3 的事件部分使用 JavaScript 这个脚本语言进行编写，CS3 也对这个语言十分友好，并在游戏中提供了 API 的列表 (英文)，我之后也会根据 API 列表单独讲解这部分内容&lt;/p&gt;
&lt;p&gt;JavaScript，简称 JS，这个脚本语言本身与 Java 的开发公司没有关系，有小部分语言习惯继承自 Java 这里我会把它作为一个单独的语言进行讲解，如果你没有理解这部分内容，也没有关系，我会在后面介绍例子的时候详细介绍到，如果你对 JavaScript 嵌入至网页的功能感兴趣，你应该去网上直接学习 JAVAScript，而不是看这篇只为了 CS3 事件部分二撰写的 JS 教程&lt;/p&gt;
&lt;h2 id=&#34;font-size5-face微软雅黑数据类型font&#34;&gt;&lt;a class=&#34;anchor&#34; href=&#34;#font-size5-face微软雅黑数据类型font&#34;&gt;#&lt;/a&gt; &lt;strong&gt;&amp;lt;font size=5 face=&amp;quot;微软雅黑&amp;quot;&amp;gt; 数据类型 &amp;lt;/font&amp;gt;&lt;/strong&gt;&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;数据类型：数据类型就是一组数据的类型，每个数据都有类型，他们会被 JavaScrpit 以不同的方式储存起来&lt;/p&gt;
&lt;/blockquote&gt;
&lt;pre&gt;&lt;code&gt;1.整数型(int)
  整数型(int)也就是表示一个整数的类型，这是几个整数类型的例子:    
12345 
85924 
233 
66666 
 特点:是没有小数点的数字
      没有特殊符号
      整数型可以被计算

2.浮点型(float)
  浮点型(float)是一个类似于整数型的类型，他表示的是有少位数的小数点的类型，这是几个例子
3.1 
5.0 
9.6 
233.6 
  特点:有小数点
       只有一位小数
       最好不使用float或者double进行计算

2.5 双精度小数(double)
    这个类型不多做介绍，实际上在CS3中用不到

3.布尔值(bool)
  布尔值(bool)表示判断，只有true(是)false(否)两种形态，通常是在条件判断的时候使用，这是它仅有的两个例子：
true
false
  特点:布尔值没有特殊符号
       只有没有特殊符号true 和 false是布尔值
       表示判断，不能计算

3.字符串(string)
  字符串(string)，表示一段文本、数字、字母的内容，单纯的文字使用的类型，通常用在显示文字等基础操作上，十分重要，这是几个例子
“Hello！ World！”
&amp;quot;你指尖跃动的电光是我此生不变的信仰&amp;quot;
“私と付き合ってください”
&amp;quot;3.0&amp;quot;
&amp;quot;256&amp;quot;
&amp;quot;true&amp;quot;
&amp;quot;0&amp;quot;
这五组数据比较特殊，他们的前后都被引号(一定要使用英文引号&amp;quot;&amp;quot;)括了起来，而且不仅有英文、中文、日文，还有一些数字等，它的重要特征就是前后被引号括起来，
请与前几种类型区分起来，这种类型叫做string，也就是字符串，这个类型比较难以理解，我使用一个表格来综合介绍一下

字符串的特点:
1.前后被引号扩起
2.它表示的是一段文字，没有其他含义
3.字符串无法进行数字运算，但可以拼接
4.字符串无法表示数，他只是一段文字，所以说
&amp;quot;true&amp;quot;不等于true
&amp;quot;3.14&amp;quot;不等于3.14
5.字符串的使用非常广泛，经常用于一些CS3的提示文本等

在CS3的事件基础部分，我们只会用到这几个数据类型，CS3还自己增加了几种数据类型，会在使用的时候说道
&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&#34;font-size5-face微软雅黑数据类型font-2&#34;&gt;&lt;a class=&#34;anchor&#34; href=&#34;#font-size5-face微软雅黑数据类型font-2&#34;&gt;#&lt;/a&gt; &lt;strong&gt;&amp;lt;font size=5 face=&amp;quot;微软雅黑&amp;quot;&amp;gt; 数据类型 &amp;lt;/font&amp;gt;&lt;/strong&gt;&lt;/h2&gt;
&lt;p&gt;在一门语言中，变量也是必不可少的，下面我就来介绍一下关于变量的相关内容&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;变量，也就是可以变换的量，他就像一个箱子，你可以把你的数据放在这个特殊的箱子里，随时更改、删除、取出它，并进行其他操作&lt;/p&gt;
&lt;/blockquote&gt;
&lt;pre&gt;&lt;code&gt;我们说完了关于变量的概念，大家多少也都了解一些了，我现在详细介绍一下变量的内容，我们如果要使用一个变量
我们首先要对他进行初始化(声明)以便于我们在下面调用它，初始化一个变量很简单，你只需要输入一句代码：

初始化变量：
var 变量名
例子:
var 御坂美琴
这样你就初始化好了一个名为“御坂美琴”的变量，初始化好了以后我们就可以对这个变量进行操作了
我们就可以给这个“箱子”里面存入数据了(给变量赋值)，我们可以在变量里面放入各种类型的数据，比如string、int、float、boolean等

给变量赋值:
变量名 = 欲赋值的内容
例子:
御坂美琴 = 3.1415926
这样“御坂美琴”这个变量里面就储存了&amp;quot;3.1415926&amp;quot;这个float类型的数据
例子:
var 一方通行 注释：我们如果想使用“一方通行”这个变量，我们要先声明它
一方通行 = true
这样“一方通行”这个变量里面就储存了true这个boolean类型的数据

既然是变量，当然是可变的量，我们可以给他重复赋值
var a
a=5
a=32
现在a的内容是32，因为按照运行顺序，a先被赋值5，后来又被替换成了32，这就是变量的基本的特点

在看这段语句
var a
var b
a=3 a被赋值了3
b=a b被赋值了a的值，所以b被赋值了3
a=8 a被赋值了8，跟b没有关系
这部分一定要与C区分，js的理念相对来说较简单，现在我们可以看出，a现在是8，b是3

在编写的途中，初始化(var)变量的步骤可以省略,js会自动帮你进行初始化，不过我建议你进行初始化，这样能一目了然的看见变量的名称，以防混淆

但是我们如果想把一个数值转换为字符串，可以直接在这个数值后面加上点“.toString()”，比如6.2.toString()    
&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&#34;font-size5-face微软雅黑逻辑操作与计算font&#34;&gt;&lt;a class=&#34;anchor&#34; href=&#34;#font-size5-face微软雅黑逻辑操作与计算font&#34;&gt;#&lt;/a&gt; &lt;strong&gt;&amp;lt;font size=5 face=&amp;quot;微软雅黑&amp;quot;&amp;gt; 逻辑操作与计算 &amp;lt;/font&amp;gt;&lt;/strong&gt;&lt;/h2&gt;
&lt;p&gt;在写代码的途中，我们经常会用到一些计算，比如加减乘除等，这时候我们需要用到一些符号，比如 +-x/，下面我举几个例子&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;var a
a=1+1
这时a是2

var a
a=&amp;quot;1&amp;quot;+&amp;quot;1&amp;quot;
这时a是11

var a
var b 
var c
b = 5
c = 2
a = b * c
这时a是10

一些具体的运算会在接触时说道
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;在写代码的途中，一切逻辑的操作也是必不可少的，尤其是在 cs3 里面，我现在就简单介绍一下 if&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;if 如果
if语句中的else可以省略，如果省略，不成立将直接向下运行代码

if (条件)
&amp;#123;
如果条件成立，执行这里的代码
&amp;#125;
else 
&amp;#123;
如果条件不成立，执行这里的代码
&amp;#125;

例子:
var 方块名字
if(方块名字 == &amp;quot;minecraft:dirt&amp;quot;)
&amp;#123;
检测到泥土
&amp;#125;
else
&amp;#123;
没检测到泥土
&amp;#125;

要注意的是，在条件判断的时候，要打两个等号，这是条件判断可以使用的符号

== 相等
!= 不相等
&amp;gt; 大于
&amp;lt; 小于
&lt;/code&gt;&lt;/pre&gt;
&lt;hr /&gt;
&lt;p&gt;JS 的基础就讲到这里，自己认为讲的不明白，如果你没有听明白，是非常正常的事情，你可以在今后的学习实例中明白这些，同时有不明白的也可以在 MCBBS / 百度贴吧联系我，下一章我会开始融入实例进行讲解&lt;/p&gt;
</content>
        <category term="Minecraft" />
        <category term="模组开发" />
        <category term="CustomStuff3" />
        <updated>2017-11-23T02:46:56.000Z</updated>
    </entry>
    <entry>
        <id>https://turou.fun/minecraft/cs-project/world-gen/</id>
        <title>给CustomStuff3方块添加世界生成</title>
        <link rel="alternate" href="https://turou.fun/minecraft/cs-project/world-gen/"/>
        <content type="html">&lt;p&gt;&lt;img loading=&#34;lazy&#34; data-src=&#34;https://s2.loli.net/2023/01/10/k8xFHuVPMw9pe14.png&#34; alt=&#34;635589679383149678.png&#34; /&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&amp;lt;font size=3 face=&amp;quot;微软雅黑&amp;quot;&amp;gt;&lt;br /&gt;
 我们打开我们工程的主菜单，选择 World Generators 添加一个世界生成，然后点击 New 填写信息，我这里就以我们制作的魔法矿石为例，Name 可以随便填写，最好不使用中文字符&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Type 要生成的类型
ore 矿物类型
flower 花的类型
因为我们要创建矿物的世界生成，所以我们选择ore
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img loading=&#34;lazy&#34; data-src=&#34;https://miao.su/images/2017/08/02/TIM20170802130129e75f2.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;
&lt;p&gt;然后点击 Create 创建，创建完成后，我们点击我们刚刚创建的项目，点击 Edit 来编辑属性&lt;/p&gt;
&lt;p&gt;###allowedBiomes&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;矿物允许生成的生物群系，默认是 all，也就是全部，可以在这里添加多个生物群系，这样就只会在指定的生物群系生成，这里是生物群系对应的意思，可以在 MinecraftWiki 的 [这页] 找到，内容获取至 MinecraftWIKI, 可能有多出的部分，请自行查看&lt;/p&gt;
&lt;/blockquote&gt;
&lt;pre&gt;&lt;code&gt;all 全部生物群系
Ocean	海洋
Plains	草原
Desert	沙漠
Extreme Hills	峭壁
Forest	森林
Taiga	针叶林
Swampland	沼泽
River	河流
Hell	地狱（下界）
The End	末路之地（空岛）
FrozenOcean	冻洋
FrozenRiver	冻河
Ice Plains	冰原
Ice Mountains	雪山
MushroomIsland	蘑菇岛
MushroomIslandShore	蘑菇岛岸
Beach	沙滩
DesertHills	沙漠山丘
ForestHills	森林山丘
TaigaHills	针叶林山丘
Extreme Hills Edge	悬崖
Jungle	丛林
JungleHills	丛林山丘
JungleEdge	丛林边缘
Deep Ocean	深海
Stone Beach	石滩
Cold Beach	寒冷沙滩
Birch Forest	桦木森林
Birch Forest Hills	桦木森林山丘
Roofed Forest	黑森林
Cold Taiga	冷针叶林
Cold Taiga Hills	冷针叶林山丘
Mega Taiga	大型针叶林
Mega Taiga Hills	大型针叶林山丘
Extreme Hills+	峭壁+
Savanna	热带草原
Savanna Plateau	热带高原
Mesa	平顶山
Mesa Plateau F	平顶山高原 F
Mesa Plateau	平顶山高原
Sunflower Plains	向日葵草原
Desert M	沙漠 M
Extreme Hills M	峭壁 M
Flower Forest	繁花森林
Taiga M	针叶林 M
Swampland M	沼泽 M
Ice Plains Spikes	冰刺平原
Jungle M	丛林 M
JungleEdge M	丛林边缘 M
Birch Forest M	桦木森林 M
Birch Forest Hills M	桦木森林山丘 M
Roofed Forest M	黑森林 M
Cold Taiga M	冷针叶林 M
N/A	N/A
Mega Spruce Taiga	红木森林
Redwood Taiga Hills M	红木山丘
Extreme Hills+ M	峭壁+ M
Savanna M	热带草原 M
Savanna Plateau M	热带高原 M
Mesa (Bryce)	平顶山（岩柱）
Mesa Plateau F M	平顶山高原 F M
Mesa Plateau M	平顶山高原 M
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img loading=&#34;lazy&#34; data-src=&#34;https://miao.su/images/2017/08/02/TIM201708021319395db3b.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;
&lt;p&gt;###amount&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;每次生成的矿脉大小，只有编辑了 generatedBlock (一会说) 才能被编辑，下方的格子中将会按照你填写的数值随机生成一些矿脉，以便于你参考&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;img loading=&#34;lazy&#34; data-src=&#34;https://miao.su/images/2017/08/02/TIM20170802132138a5e0c.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;
&lt;p&gt;###dimensions&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;要生成这个方块的世界&lt;/p&gt;
&lt;/blockquote&gt;
&lt;pre&gt;&lt;code&gt;Overworld 主世界
Nether 地狱
End 末地
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img loading=&#34;lazy&#34; data-src=&#34;https://miao.su/images/2017/08/02/TIM20170802132318fad21.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;
&lt;p&gt;###generatedBlock&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;要生成的方块，点击中间的槽就可以修改，右键清除，只有选好了要生成的方块，才能更改矿脉的大小&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;###generationsPerChunk&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;一个区块允许出现几次生成的计算，也就是最多可以生成几撮矿石&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;###height&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;矿石的允许生成高度&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;img loading=&#34;lazy&#34; data-src=&#34;https://miao.su/images/2017/08/02/TIM20170802132635a10d1.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;
&lt;p&gt;###replacedBlocks&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;说这个之前我先说一下 Minecraft 的矿物生成机制：他会先正常生成地形，然后根据算法替换指定方块为特定矿石，一般不用修改。他会自动把主世界的石头，地狱的地狱岩，末地的末地石计算后替换为矿石，如果有特定需求，也可以修改&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;img loading=&#34;lazy&#34; data-src=&#34;https://miao.su/images/2017/08/02/TIM201708021329324c3fa.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;
&lt;p&gt;### 原版参数&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;这里稍微提一下与原版有关的参数，因为矿物生成本身有随机性，数据仅供参考&lt;/p&gt;
&lt;/blockquote&gt;
&lt;pre&gt;&lt;code&gt;煤矿石: amount:5~64
       dimensions:Overworld
       generatedBlock:&amp;lt;minecraft:coal_ore&amp;gt;
       generationsPerChunk:142.6
       height:1~255
铁矿石: amount:4~10
       dimensions:Overworld
       generatedBlock:&amp;lt;minecraft:iron_ore&amp;gt;
       generationsPerChunk:77
       height:1~63
金矿石: amount:1~16
       dimensions:Overworld
       generatedBlock:&amp;lt;minecraft:gold_ore&amp;gt;
       generationsPerChunk:8.2
       height:1~32
钻石矿石: amount:3~8
       dimensions:Overworld
       generatedBlock:&amp;lt;minecraft:diamond_ore&amp;gt;
       generationsPerChunk:1
       height:1~16
钻石矿石: amount:4~10
       dimensions:Nether
       generatedBlock:&amp;lt;minecraft:quartz_ore&amp;gt;
       generationsPerChunk:79
       height:7-117
青金石矿石: amount:4~8
       dimensions:Overworld
       generatedBlock:&amp;lt;minecraft:lapis_ore&amp;gt;
       generationsPerChunk:3.43
       height:1~31
红石矿石: amount:4~5
       dimensions:Overworld
       generatedBlock:&amp;lt;minecraft:redstone_ore&amp;gt;
       generationsPerChunk:24.8
       height:1~16
&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&#34;font-size5-face微软雅黑给方块添加自然生成font&#34;&gt;&lt;a class=&#34;anchor&#34; href=&#34;#font-size5-face微软雅黑给方块添加自然生成font&#34;&gt;#&lt;/a&gt; &lt;strong&gt;&amp;lt;font size=5 face=&amp;quot;微软雅黑&amp;quot;&amp;gt; 给方块添加自然生成 &amp;lt;/font&amp;gt;&lt;/strong&gt;&lt;/h2&gt;
&lt;p&gt;自然生成我这里就不细说了，实际上和矿物生成一样，如果有什么问题可以联系我，就是有一个 blockRate 不一样&lt;/p&gt;
&lt;p&gt;###blockRate&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;每次生成的区域方块的数量，也就是一撮方块的大小&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr /&gt;
&lt;p&gt;自然生成这部分也完成了，下一部分是其他的获取方式，今天也会更新出来，希望能顺利进行，第二章的内容就开始麻烦起来了，我的更新速度也不会像现在这么快了，因为我要自己去测试功能，好了就说这么多，下章见&lt;/p&gt;
</content>
        <category term="Minecraft" />
        <category term="模组开发" />
        <category term="CustomStuff3" />
        <updated>2017-11-10T02:46:56.000Z</updated>
    </entry>
    <entry>
        <id>https://turou.fun/minecraft/cs-project/recipes/</id>
        <title>给CustomStuff3物品添加配方</title>
        <link rel="alternate" href="https://turou.fun/minecraft/cs-project/recipes/"/>
        <content type="html">&lt;p&gt;&lt;img loading=&#34;lazy&#34; data-src=&#34;https://s2.loli.net/2023/01/10/k8xFHuVPMw9pe14.png&#34; alt=&#34;635589679383149678.png&#34; /&gt;&lt;/p&gt;
&lt;p&gt;在上个篇章，我们详细的介绍了物品 / 方块的创建与编辑，这章，我们将讲解如何给一个方块或物品添加合成或冶炼的配方，这次的内容比较简单，在制作之前，我先把我们需要用到的物品制作出来，方便我们一会加入合成冶炼配方，我制作了一把魔法镐和一个魔法矿石。&lt;/p&gt;
&lt;p&gt;&lt;img loading=&#34;lazy&#34; data-src=&#34;https://miao.su/images/2017/08/02/TIM2017080208525497276.png&#34; alt=&#34;&#34; /&gt;&lt;br /&gt;
&lt;img loading=&#34;lazy&#34; data-src=&#34;https://miao.su/images/2017/08/02/TIM2017080208531242343.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;
&lt;p&gt;然后我给他们设置属性和贴图，方便查看辨认，工具的贴图也放在 items 才能识别&lt;/p&gt;
&lt;p&gt;&lt;img loading=&#34;lazy&#34; data-src=&#34;https://miao.su/images/2017/08/02/TIM20170802090259c0ba1.png&#34; alt=&#34;&#34; /&gt;&lt;br /&gt;
&lt;img loading=&#34;lazy&#34; data-src=&#34;https://miao.su/images/2017/08/02/TIM20170802092837521c1.png&#34; alt=&#34;&#34; /&gt;&lt;br /&gt;
&lt;img loading=&#34;lazy&#34; data-src=&#34;https://miao.su/images/2017/08/02/TIM2017080209421693824.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;
&lt;h2 id=&#34;font-size5-face微软雅黑给这个物品添加合成配方font&#34;&gt;&lt;a class=&#34;anchor&#34; href=&#34;#font-size5-face微软雅黑给这个物品添加合成配方font&#34;&gt;#&lt;/a&gt; &lt;strong&gt;&amp;lt;font size=5 face=&amp;quot;微软雅黑&amp;quot;&amp;gt; 给这个物品添加合成配方 &amp;lt;/font&amp;gt;&lt;/strong&gt;&lt;/h2&gt;
&lt;p&gt;&amp;lt;font size=3 face=&amp;quot;微软雅黑&amp;quot;&amp;gt;&lt;br /&gt;
 一个正常的 MOD 是必须要有合成配方的，CS3 的 MOD 工程也是一样，所以现在我们来学习一下如何添加合成配方&lt;/p&gt;
&lt;p&gt;我们先回到 CS3 主界面，选择下方的 ShapedRecipes，也就是创建有序合成，然后点击 New 来新建一个合成&lt;/p&gt;
&lt;p&gt;&lt;img loading=&#34;lazy&#34; data-src=&#34;https://miao.su/images/2017/08/02/TIM20170802111031a2134.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;
&lt;p&gt;点击空白部分选择物品，右边的两个箭头增减产物的物品，下面的 Width 和 Height 是当前合成表的长宽，没什么用，点击空白部分后&lt;/p&gt;
&lt;p&gt;&lt;img loading=&#34;lazy&#34; data-src=&#34;https://miao.su/images/2017/08/02/TIM201708021113131de1b.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;font size=3 face=&amp;quot;微软雅黑&amp;quot;&amp;gt;&lt;br /&gt;
 可以在 Items 里面选择普通的物品，两个物品交换说明这两个物品在合成时是可以互相交换的，部分工具的耐久条会变化，这样的物品在合成时会忽略掉耐久值，在这里可以找到原版、CS3 工程和其他 MOD 的方块与物品，如果你无法找到对应的物品，你可以去 OreClasses 里面找找，选择 Ore Classes 会匹配物品的矿物词典，你自己创建的矿物词典也会显示在这里，，如果你无法编辑这个页面，请确定你使用 CS3 的版本是不是 0.7.9，如果你使用的 0.7.10，请回退版本，具体请查看索引页，如果想要更多关于合成的更改与设定，可以使用 MineTweaker 与这个 MOD 联动，你可以在 [这里] 找到 MineTweaker 的教程，注意这个页面会自动播放音乐&lt;br /&gt;
 [这里]:&lt;a href=&#34;http://www.mcbbs.net/thread-304800-1-3.html&#34;&gt;http://www.mcbbs.net/thread-304800-1-3.html&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;这是我创建的合成&lt;/p&gt;
&lt;p&gt;&lt;img loading=&#34;lazy&#34; data-src=&#34;https://miao.su/images/2017/08/02/TIM20170802112219e582b.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img loading=&#34;lazy&#34; data-src=&#34;https://miao.su/images/2017/08/02/TIM2017080211271006876.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;
&lt;p&gt;创建好的合成会在这里直观的显示出来&lt;/p&gt;
&lt;p&gt;&lt;img loading=&#34;lazy&#34; data-src=&#34;https://miao.su/images/2017/08/02/TIM20170802112536f3f2d.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;
&lt;p&gt;ShapelessRecipe (无序合成) 与有序合成的设置方法相似，这里就不再次叙述了，在这里设置的合成可以不用按照顺序来摆放&lt;/p&gt;
&lt;h2 id=&#34;font-size5-face微软雅黑给这个物品添加冶炼配方font&#34;&gt;&lt;a class=&#34;anchor&#34; href=&#34;#font-size5-face微软雅黑给这个物品添加冶炼配方font&#34;&gt;#&lt;/a&gt; &lt;strong&gt;&amp;lt;font size=5 face=&amp;quot;微软雅黑&amp;quot;&amp;gt; 给这个物品添加冶炼配方 &amp;lt;/font&amp;gt;&lt;/strong&gt;&lt;/h2&gt;
&lt;p&gt;&amp;lt;font size=3 face=&amp;quot;微软雅黑&amp;quot;&amp;gt;&lt;/p&gt;
&lt;p&gt;冶炼配方的添加也十分简单，我们先回到以你 MOD 命名的主菜单，选择 Smelting Recipes 来创建冶炼合成，点击空白来编辑物品，是与合成一样的，RecipeList 目前无法更改，不需要更改，保持 Vanilla 就好了&lt;/p&gt;
&lt;p&gt;&lt;img loading=&#34;lazy&#34; data-src=&#34;https://miao.su/images/2017/08/02/TIM201708021136205720d.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;font size=3 face=&amp;quot;微软雅黑&amp;quot;&amp;gt;&lt;br /&gt;
 既然我们说道冶炼，那么我们顺便也介绍下如何添加一个燃料吧，我先添加一个新的物品 “魔法燃料”&lt;/p&gt;
&lt;p&gt;&lt;img loading=&#34;lazy&#34; data-src=&#34;https://miao.su/images/2017/08/02/TIM20170802114913f0576.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;
&lt;p&gt;然后我们回到以你 MOD 命名的主菜单，选择 Fuels&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Duration 可以提供的烧制时间
单位为tick，一秒有20tick
木棍 100
木板 300
煤炭 1600
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;因为要做出是煤炭的 23 倍，所以 1600*23=36800&lt;/p&gt;
&lt;p&gt;&lt;img loading=&#34;lazy&#34; data-src=&#34;https://miao.su/images/2017/08/02/TIM2017080211552406ee6.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img loading=&#34;lazy&#34; data-src=&#34;https://miao.su/images/2017/08/02/TIM20170802115708c6974.png&#34; alt=&#34;&#34; /&gt;&lt;br /&gt;
&lt;img loading=&#34;lazy&#34; data-src=&#34;https://miao.su/images/2017/08/02/TIM20170802115729b17ff.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;
</content>
        <category term="Minecraft" />
        <category term="模组开发" />
        <category term="CustomStuff3" />
        <updated>2017-10-23T02:46:56.000Z</updated>
    </entry>
    <entry>
        <id>https://turou.fun/minecraft/cs-project/first-item/</id>
        <title>利用CustomStuff3创建第一个方块/物品</title>
        <link rel="alternate" href="https://turou.fun/minecraft/cs-project/first-item/"/>
        <content type="html">&lt;p&gt;&lt;img loading=&#34;lazy&#34; data-src=&#34;https://s2.loli.net/2023/01/10/k8xFHuVPMw9pe14.png&#34; alt=&#34;635589679383149678.png&#34; /&gt;&lt;/p&gt;
&lt;p&gt;我们创建好了 CS3 工程，便迫不及待的想要创建我们的第一个物品，我们点击我们刚刚创建的工程，会打开一个界面，在这里选择要创建的东西，比如我们现在想要创建一个物品，那么我们就可以点击 Items 进入创建物品的界面，因为我们要新建一个物品，所以我们点击 New 来进入创建物品的界面&lt;/p&gt;
&lt;p&gt;&lt;img loading=&#34;lazy&#34; data-src=&#34;https://miao.su/images/2017/07/31/TIM20170731171439cc521.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Name 在这里输入要创建物品的名字
不要输入中文、不要输入中文、不要输入中文
这里输入的名字可以为大小写字母和数字，不会显示出来
只是内部识别(/give时候用的)

Type 类型 这里指你要创建物品的类型，我们这里选择Normal
我也顺便介绍一下其他的类型，在你选择特定类型的时候，CS3会自动帮你调整有关数据

Normal 普通类型 
axe 斧头类型
boots 靴子类型
bucket 桶类型
food 食物类型
helmat 头盔类型
legs 护腿类型
pickaxe 镐类型
plate 胸甲类型
shovel 铲类型
sword 剑类型
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;lt;font size=3 face=&amp;quot;微软雅黑&amp;quot;&amp;gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;另外关于护甲值相关问题，将会在以后说道&lt;br /&gt;
在这其中有些类型有特定的内容，我会在遇到的时候说道&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;选择好后，我们就可以点击 Create 来创建我们的第一个物品了&lt;/p&gt;
&lt;p&gt;&lt;img loading=&#34;lazy&#34; data-src=&#34;https://miao.su/images/2017/07/31/TIM2017073119412368e1d.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;font size=3 face=&amp;quot;微软雅黑&amp;quot;&amp;gt;&lt;/p&gt;
&lt;p&gt;创建好以后，你会发现 Edit 按钮是黑色的，我们无法对这个物品进行编辑，因为你需要重启游戏，重启过后，我们就可以点击 “Edit” 进入物品的编辑界面了&lt;/p&gt;
&lt;p&gt;&lt;img loading=&#34;lazy&#34; data-src=&#34;https://miao.su/images/2017/08/01/TIM2017080107374243e37.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;
&lt;p&gt;然后你就可以对这个物品进行编辑了，我会在这里介绍到所有可供调整的内容，在介绍每一个内容时都会使用图片来举一个例子，以便大家理解&lt;/p&gt;
&lt;h3 id=&#34;anvilmaterial&#34;&gt;&lt;a class=&#34;anchor&#34; href=&#34;#anvilmaterial&#34;&gt;#&lt;/a&gt; anvilMaterial&lt;/h3&gt;
&lt;blockquote&gt;
&lt;p&gt;在铁砧被修复时需要用到的材料；如果你制作的东西是个工具或者有耐久值的东西，可以编辑此项，点击中间的槽就可以选择物品了，右键点击可以清除物品，选择完成后点击 Edit 来保存&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;img loading=&#34;lazy&#34; data-src=&#34;https://miao.su/images/2017/08/01/TIM20170801074124c390d.png&#34; alt=&#34;&#34; /&gt;&lt;br /&gt;
&lt;img loading=&#34;lazy&#34; data-src=&#34;https://miao.su/images/2017/08/01/TIM2017080107442705c2d.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;
&lt;h3 id=&#34;containeritem&#34;&gt;&lt;a class=&#34;anchor&#34; href=&#34;#containeritem&#34;&gt;#&lt;/a&gt; containerItem&lt;/h3&gt;
&lt;blockquote&gt;
&lt;p&gt;对不起，关于这项我无法理解是什么意思，经过一段时间的测试也没有发现设置 containerItem 有什么作用，我去 google 了一下也没有找到相关信息，如果你知道，可以联系我补全这段信息&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 id=&#34;creativetab&#34;&gt;&lt;a class=&#34;anchor&#34; href=&#34;#creativetab&#34;&gt;#&lt;/a&gt; creativeTab&lt;/h3&gt;
&lt;blockquote&gt;
&lt;p&gt;给你的物品选择一个创造模式物品栏，这样他就可以显示在创造模式中了，你也可以自己创建一个创造模式物品栏并把你的物品放进去，这会在以后说道，选择完成后点击 Edit 来保存，这是每项对应的内容&lt;/p&gt;
&lt;/blockquote&gt;
&lt;pre&gt;&lt;code&gt;buildingBlocks 建筑方块
decorations 装饰性方块
redstone 红石
transportation 交通运输
misc 杂项
search 搜索物品 (他会直接显示在里面，你可以搜索到，但是你放在其他的项目里面也可以搜索到)
food 食物
tools 工具
combat 战斗用品
brewing 酿造
materials 材料
inventory 生存模式物品栏 (请不要放置在这里，它会出现问题，无法正常使用)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img loading=&#34;lazy&#34; data-src=&#34;https://miao.su/images/2017/08/01/TIM20170801081436c39b5.png&#34; alt=&#34;&#34; /&gt;&lt;br /&gt;
&lt;img loading=&#34;lazy&#34; data-src=&#34;https://miao.su/images/2017/08/01/TIM201708010814574f798.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;
&lt;h3 id=&#34;damage&#34;&gt;&lt;a class=&#34;anchor&#34; href=&#34;#damage&#34;&gt;#&lt;/a&gt; damage&lt;/h3&gt;
&lt;blockquote&gt;
&lt;p&gt;这个比较简单，就是这个物品的攻击伤害，最大值为 2147483647，如果你输入的数字大于这个数字，会自动降低为这个数值&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;img loading=&#34;lazy&#34; data-src=&#34;https://miao.su/images/2017/08/01/TIM2017080108543661f27.png&#34; alt=&#34;&#34; /&gt;&lt;br /&gt;
&lt;img loading=&#34;lazy&#34; data-src=&#34;https://miao.su/images/2017/08/01/TIM201708010830573b881.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;
&lt;h3 id=&#34;displayname&#34;&gt;&lt;a class=&#34;anchor&#34; href=&#34;#displayname&#34;&gt;#&lt;/a&gt; displayName&lt;/h3&gt;
&lt;blockquote&gt;
&lt;p&gt;物品的显示名称，默认为 Unnamed，为空的话也会变成 Unnamed，可以填写 Windows 支持的任何文字 (中文日文英文大小写阿拉伯数字罗马数字)，不过如果你要输入中文请安装 [中文修复补丁 (Inputfix)] (这个是 1.7.10 的版本，如果你需要其他版本，请自行下载) 如果你不安装，在外面输入好在复制进来也是可以的&lt;br /&gt;
 [中文修复补丁 (Inputfix)]:&lt;a href=&#34;https://github.com/zlainsama/InputFix/releases/download/1.7.10-v5/InputFix-1.7.10-v5.jar&#34;&gt;https://github.com/zlainsama/InputFix/releases/download/1.7.10-v5/InputFix-1.7.10-v5.jar&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;img loading=&#34;lazy&#34; data-src=&#34;https://miao.su/images/2017/08/01/TIM20170801085836960bc.png&#34; alt=&#34;&#34; /&gt;&lt;br /&gt;
&lt;img loading=&#34;lazy&#34; data-src=&#34;https://miao.su/images/2017/08/01/TIM201708010837342a3c0.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;
&lt;h3 id=&#34;efficiency&#34;&gt;&lt;a class=&#34;anchor&#34; href=&#34;#efficiency&#34;&gt;#&lt;/a&gt; efficiency&lt;/h3&gt;
&lt;blockquote&gt;
&lt;p&gt;挖掘速度，只在类型 (上文有介绍) 为工具类 (比如 axe pickaxe 等等) 的时候有效果，要注意的是：就算你把速度调成 100，如果采掘等级 (下面会说) 没有达到钻石级别，挖黑曜石也是不会掉落的，这里使用 gif 来演示&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;efficiency 设置 1.0 的速度&lt;/p&gt;
&lt;p&gt;&lt;img loading=&#34;lazy&#34; data-src=&#34;https://miao.su/images/2017/08/01/bandicam2017-08-0109-03-19-765_14a503.gif&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;
&lt;p&gt;efficiency 设置 10.0 的速度&lt;/p&gt;
&lt;p&gt;&lt;img loading=&#34;lazy&#34; data-src=&#34;https://miao.su/images/2017/08/01/bandicam2017-08-0109-03-45-679308d2.gif&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;
&lt;h3 id=&#34;enchantability&#34;&gt;&lt;a class=&#34;anchor&#34; href=&#34;#enchantability&#34;&gt;#&lt;/a&gt; enchantability&lt;/h3&gt;
&lt;blockquote&gt;
&lt;p&gt;附魔能力：这个就比较复杂了，这个跟原版的附魔机制有关，首先我先上公式&lt;/p&gt;
&lt;/blockquote&gt;
&lt;pre&gt;&lt;code&gt;修改后的附魔等级 = 附魔等级 + 随机值(0, 附魔能力 / 4 * 2) + 1
（每步计算完毕后四舍五入）
&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote&gt;
&lt;p&gt;如果你需要的话，可以在 Minecraft 中文 Wiki 的&lt;a href=&#34;https://minecraft-zh.gamepedia.com/%E9%99%84%E9%AD%94%E6%9C%BA%E5%88%B6#.E7.AC.AC.E4.B8.80.E6.AD.A5_-_.E5.AF.B9.E9.99.84.E9.AD.94.E7.AD.89.E7.BA.A7.E5.8A.A0.E5.85.A5.E8.B0.83.E8.8A.82.E5.80.BC&#34;&gt;这个页面&lt;/a&gt;找到&lt;br /&gt;
这里有一个表格可供参考&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;img loading=&#34;lazy&#34; data-src=&#34;https://miao.su/images/2017/08/01/TIM2017080109262790e35.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;
&lt;h3 id=&#34;full3d&#34;&gt;&lt;a class=&#34;anchor&#34; href=&#34;#full3d&#34;&gt;#&lt;/a&gt; full3D&lt;/h3&gt;
&lt;blockquote&gt;
&lt;p&gt;让你的物品可以在手中 3D 显示出来，具体效果可以在游戏中查看，工具会自动开启 full3D 效果&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;Draw Full 3D: 是否使用 Full3D 的效果&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;没有开启 full3D 的显示效果&lt;/p&gt;
&lt;p&gt;&lt;img loading=&#34;lazy&#34; data-src=&#34;https://miao.su/images/2017/08/01/TIM20170801093106d8c40.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;
&lt;p&gt;开启了 full3D 的显示效果&lt;/p&gt;
&lt;p&gt;&lt;img loading=&#34;lazy&#34; data-src=&#34;https://miao.su/images/2017/08/01/TIM20170801093117cc95b.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;
&lt;h3 id=&#34;haseffect&#34;&gt;&lt;a class=&#34;anchor&#34; href=&#34;#haseffect&#34;&gt;#&lt;/a&gt; hasEffect&lt;/h3&gt;
&lt;blockquote&gt;
&lt;p&gt;是否开启光效，光效就像附魔的物品那样，会一直闪烁，下面上图，可能看的不是很清楚，可以自己测试一下&lt;/p&gt;
&lt;p&gt;Has Effect: 是否开启光效&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;关闭&lt;/p&gt;
&lt;p&gt;&lt;img loading=&#34;lazy&#34; data-src=&#34;https://miao.su/images/2017/08/01/TIM20170801093428577e7.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;
&lt;p&gt;开启&lt;/p&gt;
&lt;p&gt;&lt;img loading=&#34;lazy&#34; data-src=&#34;https://miao.su/images/2017/08/01/TIM20170801093441ca794.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;
&lt;h3 id=&#34;icon&#34;&gt;&lt;a class=&#34;anchor&#34; href=&#34;#icon&#34;&gt;#&lt;/a&gt; icon&lt;/h3&gt;
&lt;blockquote&gt;
&lt;p&gt;虽然比较麻烦，但是很重要，给你的物品添加图标&lt;/p&gt;
&lt;p&gt;在开始之前，首先你要准备一张正方形透明 png 图片，大小可以是 16x16、32x32、64x64、128x128 等等，但不建议太大，以免影响显示效果，而且必须背景透明，个人推荐使用 paint.net 来处理图片，也可以使用 Photoshop 等软件，但是 Windows 自带画图不可以，因为它不支持 alpha (透明) 通道，如果你发现你的材质在游戏中显示背景是白色的，那么就说明你的图片不是透明的，简单的图片你可以在&lt;a href=&#34;http://www.aigei.com/bgremover/&#34;&gt;这个网站&lt;/a&gt;进行转换&lt;/p&gt;
&lt;p&gt;我不会画画，所以就随便画了一个贴图&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;img loading=&#34;lazy&#34; data-src=&#34;https://miao.su/images/2017/08/01/b01db672c8a3dbf25edd7.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;然后我们给他命名，可以为大小写字母和数字，中文也可以，不过这里用什么名字是不会影响到在游戏中的查看的，我这里就命名为了 “魔法锭.png”&lt;/p&gt;
&lt;p&gt;然后我们把这个文件放入.minecraft/mods/ 你的工程 ID/assets/ 你的工程 ID/textures/items（因为我们要给物品加贴图，所以放到 items 里面）&lt;/p&gt;
&lt;p&gt;&lt;img loading=&#34;lazy&#34; data-src=&#34;https://miao.su/images/2017/08/01/TIM2017080109532713839.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;
&lt;p&gt;然后我们进入 icon，点击这里的 “...”&lt;br /&gt;
&lt;img loading=&#34;lazy&#34; data-src=&#34;https://miao.su/images/2017/08/01/TIM20170801101220e7e77.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;
&lt;p&gt;然后我们就能在 “Pack” 里面找到自己的材质，并设置了&lt;br /&gt;
&lt;img loading=&#34;lazy&#34; data-src=&#34;https://miao.su/images/2017/08/01/TIM20170801101335a90ba.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;
&lt;p&gt;左键点击要设置的材质，然后点击 Select 就可以了&lt;/p&gt;
&lt;p&gt;最后效果 (我开了 “hasEffect”)&lt;/p&gt;
&lt;p&gt;&lt;img loading=&#34;lazy&#34; data-src=&#34;https://miao.su/images/2017/08/01/TIM2017080110143864d04.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 id=&#34;information&#34;&gt;&lt;a class=&#34;anchor&#34; href=&#34;#information&#34;&gt;#&lt;/a&gt; information&lt;/h3&gt;
&lt;blockquote&gt;
&lt;p&gt;给你的物品添加一个描述 (可以使用任何文字，空格，还有颜色符号 §，如果你不会打这个符号，可以直接复制到编辑框里面) 鼠标指向左下角的你的物品图标可以预览现在的设定&lt;/p&gt;
&lt;/blockquote&gt;
&lt;pre&gt;&lt;code&gt;§4 红色              
§c 粉红色
§6 土黄色
§e 黄色
§2 绿色
§a 亮绿色
§b 靛蓝色
§3 青色
§1 蓝色
§9 亮蓝色
§d 粉色
§5 紫色
§f 白色
§7 灰色
§8 深灰色
§0 黑色
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img loading=&#34;lazy&#34; data-src=&#34;https://miao.su/images/2017/08/01/TIM20170801102717d7059.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;
&lt;h3 id=&#34;maxdamage&#34;&gt;&lt;a class=&#34;anchor&#34; href=&#34;#maxdamage&#34;&gt;#&lt;/a&gt; maxDamage&lt;/h3&gt;
&lt;blockquote&gt;
&lt;p&gt;最大耐久值，这个我就不多说了，自己测试下就可以了，默认值 100&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 id=&#34;maxstack&#34;&gt;&lt;a class=&#34;anchor&#34; href=&#34;#maxstack&#34;&gt;#&lt;/a&gt; maxStack&lt;/h3&gt;
&lt;blockquote&gt;
&lt;p&gt;最大堆叠数，默认是 64，如果你创建的类型是工具类的，默认是 1&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;img loading=&#34;lazy&#34; data-src=&#34;https://miao.su/images/2017/08/01/TIM2017080110301175ffd.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img loading=&#34;lazy&#34; data-src=&#34;https://miao.su/images/2017/08/01/TIM20170801103020378f1.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;
&lt;h3 id=&#34;maxusingduration&#34;&gt;&lt;a class=&#34;anchor&#34; href=&#34;#maxusingduration&#34;&gt;#&lt;/a&gt; maxUsingDuration&lt;/h3&gt;
&lt;blockquote&gt;
&lt;p&gt;这项也没有找到相关信息，大致意思是 “最大持续时间” 如果你知道它确切的作用，可以与我联系，补全这个部分&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 id=&#34;onblockdestroyed&#34;&gt;&lt;a class=&#34;anchor&#34; href=&#34;#onblockdestroyed&#34;&gt;#&lt;/a&gt; onBlockDestroyed&lt;/h3&gt;
&lt;blockquote&gt;
&lt;p&gt;这部分比较复杂，主要涉及到关于 CustomStuff3 的事件，会在以后进行讲解，如果你点击这些会闪退，请检查你是否把 js.jar 放进.minecraft/mods 文件夹内。我会暂时跳过这部分，以便统一讲解&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 id=&#34;renderscale&#34;&gt;&lt;a class=&#34;anchor&#34; href=&#34;#renderscale&#34;&gt;#&lt;/a&gt; renderScale&lt;/h3&gt;
&lt;blockquote&gt;
&lt;p&gt;这个物品拿在手中的显示大小，默认为 1.0，如果调整太大可能会不显示 (我试过了) 下面上图&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;5.0 的效果&lt;/p&gt;
&lt;p&gt;&lt;img loading=&#34;lazy&#34; data-src=&#34;https://miao.su/images/2017/08/01/TIM2017080110443803e52.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;
&lt;h3 id=&#34;toolclasses&#34;&gt;&lt;a class=&#34;anchor&#34; href=&#34;#toolclasses&#34;&gt;#&lt;/a&gt; toolClasses&lt;/h3&gt;
&lt;blockquote&gt;
&lt;p&gt;主要用于工具类型的物品，可以在这里指定挖掘等级与可挖掘的方块&lt;/p&gt;
&lt;p&gt;如果你类型选择的镐，这里默认会出现 pickaxe - 0，以此类推&lt;/p&gt;
&lt;p&gt;你可以选择删除，暂时删掉这个，并点击 New 建立一个新的设定&lt;/p&gt;
&lt;/blockquote&gt;
&lt;pre&gt;&lt;code&gt;Tool class(工具的类型)可以填写:
pickaxe 挖掘石头，矿物等
axe 挖掘木头
shovel 挖掘泥土，沙子等
all 挖掘全部方块(同时具有pickaxe, axe, shovel的属性)
noHarvest 什么都挖不了(根本挖不动，像挖基岩一样)
如果你想同时具有镐和铲的特性，可以用英文逗号来隔开，比如:
pickaxe,shovel

Harvest level(工具的挖掘等级)可以填写:
0 木头等级
1 石头等级
2 铁等级
3 钻石等级
详细的可以挖哪些方块可以进入游戏测试，填写完毕就可以点击Create了
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img loading=&#34;lazy&#34; data-src=&#34;https://miao.su/images/2017/08/01/TIM20170801105343ebec8.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;
&lt;h3 id=&#34;usingaction&#34;&gt;&lt;a class=&#34;anchor&#34; href=&#34;#usingaction&#34;&gt;#&lt;/a&gt; usingAction&lt;/h3&gt;
&lt;blockquote&gt;
&lt;p&gt;使用的特殊效果，可以自行测试，会有特殊的声音和特效，我没测试过&lt;/p&gt;
&lt;/blockquote&gt;
&lt;pre&gt;&lt;code&gt;none 无
eat 吃东西
drink 喝水
block 放置方块
bow 拉弓
&lt;/code&gt;&lt;/pre&gt;
&lt;hr /&gt;
&lt;p&gt;到这里物品部分就全部说完了，一共 6000 字，有点太啰嗦了，下面方块可能有与物品相似相同的条目，我就不会讲的这么详细了，如果有不懂的部分，可以翻上来看看，或者问我，如果我知道也会直接告诉你的，然后我说下下面方块的问题，方块种类一共 23 种，因为我非常敬业，所以我打算全部介绍，不知道会有多长，也没办法啦 233&lt;/p&gt;
&lt;hr /&gt;
&lt;h2 id=&#34;font-size5-face微软雅黑创建一个方块font&#34;&gt;&lt;a class=&#34;anchor&#34; href=&#34;#font-size5-face微软雅黑创建一个方块font&#34;&gt;#&lt;/a&gt; &lt;strong&gt;&amp;lt;font size=5 face=&amp;quot;微软雅黑&amp;quot;&amp;gt; 创建一个方块 &amp;lt;/font&amp;gt;&lt;/strong&gt;&lt;/h2&gt;
&lt;p&gt;&amp;lt;font size=3 face=&amp;quot;微软雅黑&amp;quot;&amp;gt;&lt;br /&gt;
    请做好心理准备，方块条目的方块类型特别多 (一共 22 种) 项也特别多，可能不会讲的特别详细，简单的就一笔带过了，不会放太多图片，而且内容可能会特别枯燥，我会全部介绍到，你们可以挑着看，以免丧失信心&lt;br /&gt;
    在我们 CS3 工程的界面里，选择 Blocks，然后点击 New 新建一个方块，方块类型有很多种，我先介绍一下&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Normal 普通类型
flat 平面类型(就是平铺在地上的一个方块)
fluid 流体类型
gravity 有重力类型(像沙子一样掉落)
ladder 梯子类型
pane 玻璃板
post 木桩类型(比较粗的不会互相连接的栅栏)
pressurePlate 压力板类型
slab 台阶类型
stairs 楼梯类型
button 按钮类型
torch 火把类型
trapDoor 活板门类型
wall 墙类型(像圆石墙那样的)
carpet 地毯
crossTexture 可以连接的材质
crossTexturePost 可以连接的木桩
door 门类型
facing 贴面(?)
fence 栅栏
fenceGate 栅栏门
CS3教程作者的吐槽：CS3作者你知道有个东西叫木匠方块吗
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;    那么我们就先创造一个 normal 方块，然后我来介绍方块的项&lt;/p&gt;
&lt;h3 id=&#34;blockspiston&#34;&gt;&lt;a class=&#34;anchor&#34; href=&#34;#blockspiston&#34;&gt;#&lt;/a&gt; blocksPiston&lt;/h3&gt;
&lt;blockquote&gt;
&lt;p&gt;是否可以被活塞推动&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 id=&#34;containeritem-2&#34;&gt;&lt;a class=&#34;anchor&#34; href=&#34;#containeritem-2&#34;&gt;#&lt;/a&gt; containerItem&lt;/h3&gt;
&lt;blockquote&gt;
&lt;p&gt;如果有了解的请联系我&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 id=&#34;creativetab-2&#34;&gt;&lt;a class=&#34;anchor&#34; href=&#34;#creativetab-2&#34;&gt;#&lt;/a&gt; creativeTab&lt;/h3&gt;
&lt;blockquote&gt;
&lt;p&gt;创造栏，可以使用自己创建的或者原版的，与原版对应的文字可以去上面介绍物品的那里查看&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 id=&#34;displayname-2&#34;&gt;&lt;a class=&#34;anchor&#34; href=&#34;#displayname-2&#34;&gt;#&lt;/a&gt; displayName&lt;/h3&gt;
&lt;blockquote&gt;
&lt;p&gt;在物品栏显示名称，可以是任何文字&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 id=&#34;drop&#34;&gt;&lt;a class=&#34;anchor&#34; href=&#34;#drop&#34;&gt;#&lt;/a&gt; drop&lt;/h3&gt;
&lt;blockquote&gt;
&lt;p&gt;挖掘这个方块可以得到的物品&lt;/p&gt;
&lt;p&gt;Amont 的意思是数量，也就是挖掉后会掉落一个这个方块本身&lt;/p&gt;
&lt;p&gt;我们可以点击 New 新建一个掉落，并且可以点击 Edit 进行更改，Min 是掉落的最小值，Max 是掉落的最大值，CS3 会取这个区间的一个值进行掉落，如果你填写相同的数值，将会 100% 掉落&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 id=&#34;expdrop&#34;&gt;&lt;a class=&#34;anchor&#34; href=&#34;#expdrop&#34;&gt;#&lt;/a&gt; expDrop&lt;/h3&gt;
&lt;blockquote&gt;
&lt;p&gt;挖掘这个方块能得到的经验&lt;/p&gt;
&lt;p&gt;Min 是最小值，指的是能获得经验的最小值，Max 是最大值，指的是能获得经验的最大值，CS3 会取这个区间的一个值进行掉落，如果你填写相同的数值，将会 100% 掉落，注意这个数字指的不是等级，实际掉落的多少你可以自行测试，因为 MC 每个等级所需要的经验是不同的&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 id=&#34;firespreadspeed&#34;&gt;&lt;a class=&#34;anchor&#34; href=&#34;#firespreadspeed&#34;&gt;#&lt;/a&gt; fireSpreadSpeed&lt;/h3&gt;
&lt;blockquote&gt;
&lt;p&gt;火的传播速度，默认为 10，这个可以自行测试，不过感觉没什么用呢&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 id=&#34;flammability&#34;&gt;&lt;a class=&#34;anchor&#34; href=&#34;#flammability&#34;&gt;#&lt;/a&gt; flammability&lt;/h3&gt;
&lt;blockquote&gt;
&lt;p&gt;可燃性，就是可以燃烧的时间长短吧&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 id=&#34;fortunemodifier&#34;&gt;&lt;a class=&#34;anchor&#34; href=&#34;#fortunemodifier&#34;&gt;#&lt;/a&gt; fortuneModifier&lt;/h3&gt;
&lt;blockquote&gt;
&lt;p&gt;幸运值，也就是时运附魔对方块挖掘的影响，要想使用需要打开界面底部的 useFortune 才可以，几率你们可以自己测试一下&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 id=&#34;hardness&#34;&gt;&lt;a class=&#34;anchor&#34; href=&#34;#hardness&#34;&gt;#&lt;/a&gt; hardness&lt;/h3&gt;
&lt;blockquote&gt;
&lt;p&gt;方块的硬度，数值越大，挖掘的越慢，感觉黑曜石应该是 8~10 左右，正常采掘的话还是 1 左右比较合适&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 id=&#34;harvesting&#34;&gt;&lt;a class=&#34;anchor&#34; href=&#34;#harvesting&#34;&gt;#&lt;/a&gt; harvesting&lt;/h3&gt;
&lt;blockquote&gt;
&lt;p&gt;在这里设置挖掘应该用的工具和挖掘等级，如果挖掘等级设置 0 (木头) 所有工具都可以采集，只是采集的快慢问题，关于工具和挖掘等级的设置可以在上面讲解物品时的 toolClasses 找到&lt;/p&gt;
&lt;p&gt;allow sick harvest 指的就是是否允许精准采集这个方块&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 id=&#34;hascollision&#34;&gt;&lt;a class=&#34;anchor&#34; href=&#34;#hascollision&#34;&gt;#&lt;/a&gt; hasCollision&lt;/h3&gt;
&lt;blockquote&gt;
&lt;p&gt;是否开启方块的碰撞箱，如果关闭碰撞箱，就可以穿过这个方块&lt;/p&gt;
&lt;p&gt;重要：发现碰撞箱 Bug：如果让头部穿过没有碰撞箱的方块，那么游戏会 Crash，且无法进入存档；解决方法：通过编辑器等软件调整玩家的位置从方块里出来就可以了&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 id=&#34;information-2&#34;&gt;&lt;a class=&#34;anchor&#34; href=&#34;#information-2&#34;&gt;#&lt;/a&gt; information&lt;/h3&gt;
&lt;blockquote&gt;
&lt;p&gt;给方块添加描述，可以使用颜色字符和所有文字空格等&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 id=&#34;isbeaconbase&#34;&gt;&lt;a class=&#34;anchor&#34; href=&#34;#isbeaconbase&#34;&gt;#&lt;/a&gt; isBeaconBase&lt;/h3&gt;
&lt;blockquote&gt;
&lt;p&gt;是否作为信标的基座&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 id=&#34;isburning&#34;&gt;&lt;a class=&#34;anchor&#34; href=&#34;#isburning&#34;&gt;#&lt;/a&gt; isBurning&lt;/h3&gt;
&lt;blockquote&gt;
&lt;p&gt;接触到是否会受到火焰伤害，但是我自己测试没有效果&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 id=&#34;iswood&#34;&gt;&lt;a class=&#34;anchor&#34; href=&#34;#iswood&#34;&gt;#&lt;/a&gt; isWood&lt;/h3&gt;
&lt;blockquote&gt;
&lt;p&gt;是否是木头，如果打开了这个方块会阻止相邻树叶的消失&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 id=&#34;light&#34;&gt;&lt;a class=&#34;anchor&#34; href=&#34;#light&#34;&gt;#&lt;/a&gt; light&lt;/h3&gt;
&lt;blockquote&gt;
&lt;p&gt;方块发光的强度&lt;/p&gt;
&lt;/blockquote&gt;
&lt;pre&gt;&lt;code&gt;萤石为15
火把为12
南瓜灯为15
红石火把为7
&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&#34;material&#34;&gt;&lt;a class=&#34;anchor&#34; href=&#34;#material&#34;&gt;#&lt;/a&gt; material&lt;/h3&gt;
&lt;blockquote&gt;
&lt;p&gt;材料，会影响到挖掘后的粒子效果和一些特殊的效果，更改后必须重启才能应用&lt;/p&gt;
&lt;/blockquote&gt;
&lt;pre&gt;&lt;code&gt;ground 地面(放置在地面上的正常方块)
ice 冰
vine 藤蔓
craftedSnow 雪块
wood 木头
glass 玻璃
redstoneLight 红石灯
plants 植物
cactus 仙人掌
iron 铁块
fire 火
grass 草方块
leaves 树叶
sponge 海绵
clay 粘土块
pumpkin 南瓜
water 水
tnt TNT
sand 沙子
cloth 羊毛布料
lava 岩浆
circuits 红石电路
rock 岩石
snow 雪
&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&#34;maxstack-2&#34;&gt;&lt;a class=&#34;anchor&#34; href=&#34;#maxstack-2&#34;&gt;#&lt;/a&gt; maxStack&lt;/h3&gt;
&lt;blockquote&gt;
&lt;p&gt;最大堆叠，最大是 64&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 id=&#34;on系列&#34;&gt;&lt;a class=&#34;anchor&#34; href=&#34;#on系列&#34;&gt;#&lt;/a&gt; on 系列&lt;/h3&gt;
&lt;blockquote&gt;
&lt;p&gt;在介绍事件时介绍&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 id=&#34;opacity&#34;&gt;&lt;a class=&#34;anchor&#34; href=&#34;#opacity&#34;&gt;#&lt;/a&gt; opacity&lt;/h3&gt;
&lt;blockquote&gt;
&lt;p&gt;方块能放置的最大高度，最大为 255&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 id=&#34;pick&#34;&gt;&lt;a class=&#34;anchor&#34; href=&#34;#pick&#34;&gt;#&lt;/a&gt; pick&lt;/h3&gt;
&lt;blockquote&gt;
&lt;p&gt;捡起的意思，不过我测试没什么用，不知道是什么问题&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 id=&#34;placement&#34;&gt;&lt;a class=&#34;anchor&#34; href=&#34;#placement&#34;&gt;#&lt;/a&gt; placement&lt;/h3&gt;
&lt;blockquote&gt;
&lt;p&gt;放置位置&lt;/p&gt;
&lt;/blockquote&gt;
&lt;pre&gt;&lt;code&gt;Can place on floor 是否可以在地上放
Can place on ceiling 是否可以再天花板上放
Can place on wall 是否可以在墙上放
&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&#34;resistance&#34;&gt;&lt;a class=&#34;anchor&#34; href=&#34;#resistance&#34;&gt;#&lt;/a&gt; resistance&lt;/h3&gt;
&lt;blockquote&gt;
&lt;p&gt;抗爆炸能力，自行测试数值，越高越抗爆炸&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 id=&#34;slipperiness&#34;&gt;&lt;a class=&#34;anchor&#34; href=&#34;#slipperiness&#34;&gt;#&lt;/a&gt; slipperiness&lt;/h3&gt;
&lt;blockquote&gt;
&lt;p&gt;湿滑值，默认为 0.6，冰的值可能会高一些&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 id=&#34;stepsound&#34;&gt;&lt;a class=&#34;anchor&#34; href=&#34;#stepsound&#34;&gt;#&lt;/a&gt; stepSound&lt;/h3&gt;
&lt;blockquote&gt;
&lt;p&gt;发出的声音&lt;/p&gt;
&lt;/blockquote&gt;
&lt;pre&gt;&lt;code&gt;Play Place 播放放置这种方块的声音
Play Break 播放破坏这种方块的声音
Play Step 播放在上面行走的声音

stone 石头
metal 材料
ladder 梯子
wood 木头
glass 玻璃
sand 沙子
cloth 羊毛布料
anvil 铁砧
grass 草方块
gravel 砂砾
&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&#34;textures&#34;&gt;&lt;a class=&#34;anchor&#34; href=&#34;#textures&#34;&gt;#&lt;/a&gt; textures&lt;/h3&gt;
&lt;blockquote&gt;
&lt;p&gt;因为上面也已经介绍过了物品的材质，这里会简单的介绍一下&lt;/p&gt;
&lt;p&gt;我们可以看到右边有四个紫黑方块，这里是你材质设置的及时预览 (你设置在底部的材质在这里看不到，你可以设置好后在游戏中查看)&lt;/p&gt;
&lt;/blockquote&gt;
&lt;pre&gt;&lt;code&gt;bottom 底部材质
top 顶部材质
north 北边材质
south 南边材质
east 东边材质
west 西边材质

Transparent 透明材质(建议开启) 实际效果可以在预览看到，这个会让你在底部设置的透明材质得以显示
Semi Transparent 半透明 透光度比全透明低一点，根据你的需求来打开火关闭这项
Tile Transparent 边框显示 如果这项打开，两个透明方块的连接边的材质会显现出来，为了效果，建议关闭，如果有特殊需求可以打开
&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote&gt;
&lt;p&gt;必须使用 png 的图片格式，将你的 png 文件放置在.minecraft/mods/ 你的工程 ID/assets/ 你的工程 ID/textures/blocks，便会在游戏中显示出来&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 id=&#34;tileentity&#34;&gt;&lt;a class=&#34;anchor&#34; href=&#34;#tileentity&#34;&gt;#&lt;/a&gt; tileEntity&lt;/h3&gt;
&lt;blockquote&gt;
&lt;p&gt;也就是带数据的方块，这个内容比较复杂且不经常用到 (挺经常的)，这个会在扩展篇 (如果我研究明白的话) 进行讲解&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 id=&#34;usefortune&#34;&gt;&lt;a class=&#34;anchor&#34; href=&#34;#usefortune&#34;&gt;#&lt;/a&gt; useFortune&lt;/h3&gt;
&lt;blockquote&gt;
&lt;p&gt;是否允许时运，这个可以与上文的 fortuneModifier 配合使用，具体数据请自行测试&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr /&gt;
&lt;p&gt;方块部分更新完了，不过还有很多其他的类型没有讲解，你们可以自行测试，听听名字大概就知道是什么东西了，主要是材质的区别，关于材质问题，因为我对材质特别没有信心，所以我就不演示 (实际上是懒) 了，如果对某种类型有疑问，可以联系我，如果我知道的话，会进行讲解&lt;/p&gt;
&lt;hr /&gt;
&lt;p&gt;这小节终于讲完了，我从 7:00 开始码字，一直码到晚上 19 点，这期间大部分时间一直在测试功能，有很多功能我介绍的比较牵强，其实我自己也没搞明白，这部分就交给脑补能力很大的观众们来自行补全了啦，就说到这里，然后我可能在这章完成后将本教程上传至 MCBBS，希望大家多多支持，我也是第一次做教程！&lt;/p&gt;
</content>
        <category term="Minecraft" />
        <category term="模组开发" />
        <category term="CustomStuff3" />
        <updated>2017-09-12T02:46:56.000Z</updated>
    </entry>
    <entry>
        <id>https://turou.fun/minecraft/cs-project/environment/</id>
        <title>创建CustomStuff3开发环境</title>
        <link rel="alternate" href="https://turou.fun/minecraft/cs-project/environment/"/>
        <content type="html">&lt;p&gt;&lt;img loading=&#34;lazy&#34; data-src=&#34;https://s2.loli.net/2023/01/10/k8xFHuVPMw9pe14.png&#34; alt=&#34;635589679383149678.png&#34; /&gt;&lt;/p&gt;
&lt;p&gt;如果想创建一个 CS3 的 MOD 工程，我们首先要下载 CS3 本体和他的前置，如果去 Curse 下载 CustomStuff3 的话，建议下载 0.7.9 版本而不是 0.7.10，本教程也是建立在 0.7.9 上的。要注意的是本 MOD 需要一个前置 &amp;quot;js.jar&amp;quot;，如果不安装这个前置，MOD 可以启动，但事件及触发动作部分无法使用，所以请安装 &amp;quot;js.jar&amp;quot;&lt;/p&gt;
&lt;p&gt;如果你使用的不是 Minecraft1.7.10 版本，你可以去 MCBBS 或 Curse 找到相对应的版本，不过 js.jar 是所有版本通用的&lt;/p&gt;
&lt;p&gt;我在这里提供了下载地址&lt;/p&gt;
&lt;p&gt;百度云 (国内): &lt;a href=&#34;http://pan.baidu.com/s/1eR5biBC&#34;&gt;http://pan.baidu.com/s/1eR5biBC&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;当你下载完成，将 2 个文件放入装有 Forge 的客户端 (推荐使用 Forge1614) 的.minecraft/mods 目录下就可以了，当你启动游戏可以在 Mods 选项里面看见 CustomStuff3 的身影 (js.jar 不会显示)。这样 CS3 的准备工作就完成了，你可以新建一个世界以便于接下来的工作。&lt;/p&gt;
&lt;p&gt;进入了游戏，点击键盘上的 &amp;quot;C&amp;quot; 键 (可以在选项 / 控制中更改) 来打开 CS3 的 GUI，我习惯把这里称作 &amp;quot;CS3 主界面&amp;quot;&lt;br /&gt;
&lt;img loading=&#34;lazy&#34; data-src=&#34;https://miao.su/images/2017/07/31/TIM20170731100506112b7.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;
&lt;p&gt;CS3 提供了一个可以交互的 GUI，这也是与 CS2 最大的不同之处，除了事件的编写，其他操作都可以在 GUI 下完成，十分方便，虽然 CS3 没有提供本地化的方式，但是我会讲解 GUI 中比较难懂的部分，以便于英文初学者也可以学习本教程&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;GUI: 则操作界面，用户可以在这里操作一些按钮达到一些效果&lt;br /&gt;
这里指我们可以在一个界面点击一些按钮输入一些简单的信息来轻松制作 CS3 工程&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;然后我就来说一下这个 GUI 中界面的功能:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;ContentPacks MOD包 可以在这里新建/编辑/删除你的CS3创建的MOD工程    
JavaScript JAVA脚本 可以在这里查看一些可以调用的API，会在以后讲事件的时候说到 
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;我们这章的主题就是创建一个 CS3 工程，所以我们点击 “ContentPacks” 会出现下面这个界面&lt;br /&gt;
&lt;img loading=&#34;lazy&#34; data-src=&#34;https://miao.su/images/2017/07/31/TIM20170731162652efcf7.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;
&lt;p&gt;在这里会显示当前所有 CS3 工程，你可以创建多个 CS3 工程，但无法在这里删除它们，你需要到.minecraft/mods 文件夹下找到 你的工程 ID 文件夹并删除它才可以删除一个 CS3 工程，工程 ID 会在下面说道，我们现在点击 New 来新建一个工程&lt;/p&gt;
&lt;p&gt;另外你们可能会注意到，New 的 N 下面有一个下划线，你可以通过 Alt 键 + 标注下划线的字母来模仿点击这个按键，也就是这个按钮的快捷键，比如现在点击 Alt+N 相当于点击”New“这个按钮&lt;/p&gt;
&lt;p&gt;点击以后，你就可以创建你自己的 CS3 工程了&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Name 在这里输入你要创建CS3工程的名字
他会显示在Mods页面里面，不会对MOD中物品造成影响
支持大小写字母和数字，不建议使用支持中文，不可以为空(重要)  

ID 在这里输入你的工程ID
会在.minecraft/mods里面生成同名文件夹，不可以删除，否则会失去当前工程的所有内容
工程ID会影响你创建的方块/物品/材质的内部名称(也就是/give后面要填写的名称)，一旦确定不可修改
支持大小写字母和数字，不能使用中文，不可以为空(重要)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img loading=&#34;lazy&#34; data-src=&#34;https://miao.su/images/2017/07/31/TIM2017073116453906890.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;你可以看到在 ID 的后方有一个蓝色的问号，这是 MOD 的提示按钮，ID 后方的提示按钮的大致意思是 “ID 会决定创建的方块 / 物品 / 材质的内部名称” 我们以后也可能看到这种问号&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;在你创建好工程后，不会立即显示，你需要重启 Minecraft 后，他就会显示在这个界面里，就像这样&lt;br /&gt;
&lt;img loading=&#34;lazy&#34; data-src=&#34;https://miao.su/images/2017/07/31/TIM201707311646593ccd9.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;
&lt;p&gt;这就是本章的全部内容！&lt;/p&gt;
</content>
        <category term="Minecraft" />
        <category term="模组开发" />
        <category term="CustomStuff3" />
        <updated>2017-08-17T02:46:56.000Z</updated>
    </entry>
    <entry>
        <id>https://turou.fun/minecraft/cs-project/introduction/</id>
        <title>CustomStuff3 介绍</title>
        <link rel="alternate" href="https://turou.fun/minecraft/cs-project/introduction/"/>
        <content type="html">&lt;h1 id=&#34;customstuff3-帮助文档&#34;&gt;&lt;a class=&#34;anchor&#34; href=&#34;#customstuff3-帮助文档&#34;&gt;#&lt;/a&gt; CustomStuff3 帮助文档&lt;/h1&gt;
&lt;p&gt;&lt;img loading=&#34;lazy&#34; data-src=&#34;https://s2.loli.net/2023/01/10/k8xFHuVPMw9pe14.png&#34; alt=&#34;635589679383149678.png&#34; /&gt;&lt;/p&gt;
&lt;h2 id=&#34;序言&#34;&gt;&lt;a class=&#34;anchor&#34; href=&#34;#序言&#34;&gt;#&lt;/a&gt; 序言&lt;/h2&gt;
&lt;p&gt;CustomStuff3 是一个可以制作 MOD 的 MOD，制作出来的 MOD 我们把它叫做 CS3 工程，依靠 CS3 的强大脚本以及作者的良心制作，以及能实现大部分功能 (你可以看看下面的索引) 虽然这个 MOD 制作起来比较方便，不过还是有些地方大家可能不理解或者没有理解完全，这也就引发了我做 CS3 帮助文档的欲望，而且我本身也是最近才接触 CS3，大部分功能都靠测试，可能有些部分讲的有问题或者有错误，如果你知道，欢迎给我反馈，如果本教程有完结的那一天，会在页尾感谢您的帮助。这个 MOD 本身很强大，但貌似也有些问题 (我之前测试 Entity 操作的时候) 如果我遇到了问题，可能会暂时搁置这个问题，如果我找到了解决方法，会重新补全空缺的，另外本教程是我一个人在制作，更新进度可能有点慢，还有 10 多天就开学了，我看看能不能尽量填完这个坑，也算是锻炼锻炼自己的毅力吧！&lt;/p&gt;
&lt;p&gt;CustomStuff3 (以下简称 CS3) 出来也有些时间了，不过也没有见到有人写过 CS3 的教程，官方也没有放出关于 CS3 的 Docs，不过 MOD 内置一个 API 的列表方便调用，也算是 CS3 制作组的良心，本教程面向于有脚本语言或编程语言基础的人，如果你没有基础，你也可以试着阅读一下，因为脚本语言本身就不难。在本教程中我会介绍 CS3 能使用的全部 API 及功能，有些我测试不成功或者有问题的部分，如果在看这个教程的你知道的话，可以反馈给我。&lt;/p&gt;
&lt;p&gt;另外本教程将围绕一个名为 &amp;quot;符文魔法&amp;quot; 的 MOD 进行教学，将在学习中渐渐完善 &amp;quot;符文魔法&amp;quot; 从中学习 CS3 的相关内容&lt;/p&gt;
&lt;p&gt;文档使用版本：Minecraft1.7.10 Forge10.13.4.1614 CustomStuff3 0.7.9&lt;/p&gt;
&lt;p&gt;在本教程的撰写之中，1.7.10 版 CS3 的版本号已经更新到了 0.7.10，但是我在测试中，发现 0.7.10 在合成制作方面有特别大的问题，所以没有使用 0.7.10 来讲解，如果你使用的是 0.7.10，你可以直接删掉原来的 CS3，装上 0.7.9 版本的 CS3，你所有的工程都会被保留，如果你不使用合成功能或使用其他 MOD 的合成添加，你可以选择使用 [0.7.10] 的版本。&lt;br /&gt;
[0.7.10]:&lt;a href=&#34;https://coding.net/u/TROU2004/p/CustomStuff3/git/raw/master/Download/CustomStuff0.7.10.rar&#34;&gt;https://coding.net/u/TROU2004/p/CustomStuff3/git/raw/master/Download/CustomStuff0.7.10.rar&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;TROU on 2017/8/1 11:09&lt;/p&gt;
&lt;p&gt;&lt;img loading=&#34;lazy&#34; data-src=&#34;https://miao.su/images/2017/07/31/7bdb64a1532b414c88630ae9ae86b87415776.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;
&lt;h2 id=&#34;目录&#34;&gt;&lt;a class=&#34;anchor&#34; href=&#34;#目录&#34;&gt;#&lt;/a&gt; 目录&lt;/h2&gt;
&lt;h3 id=&#34;第一章-开启魔法的大门&#34;&gt;&lt;a class=&#34;anchor&#34; href=&#34;#第一章-开启魔法的大门&#34;&gt;#&lt;/a&gt; 第一章 开启魔法的大门&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;创建 CS3 工程并命名&lt;/li&gt;
&lt;li&gt;创建第一个方块 / 物品&lt;/li&gt;
&lt;li&gt;给这个方块 / 物品添加合成 / 冶炼的配方&lt;/li&gt;
&lt;li&gt;给这个方块添加世界生成&lt;/li&gt;
&lt;li&gt;给方块 / 物品添加其他获取方式&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;第二章-施法前的准备&#34;&gt;&lt;a class=&#34;anchor&#34; href=&#34;#第二章-施法前的准备&#34;&gt;#&lt;/a&gt; 第二章 施法前的准备&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;创建一个自己的箱子&lt;/li&gt;
&lt;li&gt;创建一个 GUI&lt;/li&gt;
&lt;li&gt;创建一个盔甲材料&lt;/li&gt;
&lt;li&gt;创建一个矿物词典&lt;/li&gt;
&lt;li&gt;创建一个村民交易&lt;/li&gt;
&lt;li&gt;关于生物生成&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;第三章-给我们的符文加入魔力&#34;&gt;&lt;a class=&#34;anchor&#34; href=&#34;#第三章-给我们的符文加入魔力&#34;&gt;#&lt;/a&gt; 第三章 给我们的符文加入魔力&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;JavaScript 基础&lt;/li&gt;
&lt;li&gt;简单 JS&lt;/li&gt;
&lt;li&gt;CustomStuff3 API 帮助文档&lt;/li&gt;
&lt;/ul&gt;
</content>
        <category term="Minecraft" />
        <category term="模组开发" />
        <category term="CustomStuff3" />
        <updated>2017-08-10T02:46:56.000Z</updated>
    </entry>
</feed>
