minecraft mod
几周前,YouTube向我推荐了一个来自Dream's Channel的Minecraft视频,他在其中尝试击败游戏,而他的朋友George试图阻止他。 该视频非常有趣,让我可以探索更多内容。
目前,有很多人在录制Minecraft视频并将其上传到YouTube,但是这两个人找到了一种使他们的内容与众不同的方法。 基本上, 他们构建自己的插件来更改游戏规则,然后记录自己尝试击败修改后的游戏的情况。 我只能说我喜欢他们的内容,非常高兴看到您可以用代码的力量完成什么。
几天后,我有了一个开发Minecraft mod的想法,然后想为什么不呢? 这会很有趣的!
选择工具
就像在《我的世界》中一样,我们需要一些工具,但是在这种情况下,它们将帮助我们创建第一个mod。
多种工具可帮助您构建Minecraft Mod,我之所以选择Fabric,是因为我通常玩的其中一个Mod是用它构建的。
Minecraft使用Java,Fabric也使用Java,这意味着我们还需要安装Java开发工具包或JDK。 更具体地说,我们需要JDK 8能够编译我们的mod。 您可以在此页面上下载 。
最后但并非最不重要的一点是,我们需要选择一个代码编辑器,在这种情况下,我选择了Visual Studio Code,因为它是我最喜欢的编辑器。 不过,您可以在本指南中使用任何喜欢的编辑器,因为大多数步骤将在CLI中执行。
设置项目
对于本指南,我们将使用入门工具快速进入实际构建我们的第一个mod的过程。 让我们完成以下步骤:
1.克隆/下载资源库
如果使用Git,只需克隆仓库即可:
$ git clone https://github.com/HorusGoul/fabric-mod-starter.git
否则, 请单击此链接进行下载 。
2.使用代码编辑器打开项目文件夹
使用Visual Studio代码:
$ code fabric-mod-starter
3.在项目文件夹中打开一个终端,然后运行客户端
$ cd fabric-mod-starter
$ ./gradlew runClient
注意:在Windows中,您需要改为运行.\gradlew.bat runClient
。
4.检查一切是否正常
Minecraft实例现在应该在您的计算机上运行,并且控制台应该已经在其他行上打印了这两行:
...
[main/INFO]: [STDOUT]: Hello Fabric world!
...
[main/INFO]: [STDOUT]: This line is printed by an example mod mixin!
...
如果不是这种情况,请重新检查所有内容,如果似乎没有任何效果,请发表评论或给我发送PM,我会尽力帮助您。
了解项目
目前,我们已经可以开始编写代码了,但是让我们熟悉一些文件。
gradle.properties
在此文件中,我们可以配置一些在构建mod时将使用的值。 例如,如果我们要使用Fabric或Minecraft的新功能,我们可以更改Minecraft版本,结构加载器版本,mod版本和其他属性,这些属性可能需要更改。
# Done to increase the memory available to gradle.
org.gradle.jvmargs = -Xmx1G
# Fabric Properties
# check these on https://fabricmc.net/use
minecraft_version = 1.15.1
yarn_mappings = 1.15.1+build.1
loader_version = 0.7.3+build.176
# Mod Properties
mod_version = 1.0.0
maven_group = starter
archives_base_name = starter
# Dependencies
# currently not on the main fabric site, check on the maven: https://maven.fabricmc.net/net/fabricmc/fabric-api/fabric-api
fabric_version = 0.4.25+build.282-1.15
src/main
在src
文件夹中,我们将能够找到另一个名为main
文件夹。 那就是我们mod的代码和资源所在的位置。
src/main/java
所有Java代码都位于此文件夹内。 在这里,我们可以找到一个名为starter
的软件包,其中包含两个项目: StarterMod.java
文件和mixin
软件包。
我们可以在mixin
包中找到文件StarterMixin.java
。
提示:如果您使用的是Visual Studio Code,建议您安装Java Extension Pack 。 它将为您的编辑器提供许多实用程序,从而简化Java开发。
StarterMod.java
如我们所见,这是我们mod的主要入口点,它属于starter
包,并实现onInitialize()
方法,该方法仅显示Hello Fabric world!
到控制台。
package starter ;
import net.fabricmc.api.ModInitializer ;
public class StarterMod implements ModInitializer {
@Override
public void onInitialize () {
// This code runs as soon as Minecraft is in a mod-load-ready state.
// However, some things (like resources) may still be uninitialized.
// Proceed with mild caution.
System . out . println ( "Hello Fabric world!" );
}
}
StarterMixin.java
此类属于starter.mixin
包。 我们的mod非常小,因此我们不必担心项目的文件结构。 让我们假设所有的mixins都位于starter.mixin
包中。
什么是mixin?
Mixins负责将代码注入游戏的现有类中。 例如,在StarterMixin.java
,我们正在init()
方法的开头(HEAD)插入一个方法,该方法在Minecraft的TitleScreen
类中实现。
现在,如果我们加载该混合,一旦Minecraft调用TitleScreen
的init()
方法,我们的方法将包括System.out.println("This line is printed by an example mod mixin!");
也将被称为!
这是mixins魔术的一部分,这只是冰山一角,就目前而言,这就是我们构建mod所需要的。 如果您想获得更深入的知识,则应查阅Mixin docs 。
package starter . mixin ;
import net.minecraft.client.gui.screen.TitleScreen ;
import org.spongepowered.asm.mixin.Mixin ;
import org.spongepowered.asm.mixin.injection.At ;
import org.spongepowered.asm.mixin.injection.Inject ;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo ;
@Mixin ( TitleScreen . class )
public class StarterMixin {
@Inject ( at = @At ( "HEAD" ), method = "init()V" )
private void init ( CallbackInfo info ) {
System . out . println ( "This line is printed by an example mod mixin!" );
}
}
src/main/resources
在resources
文件夹中,我们可以找到assets
文件夹,该文件夹现在仅包含我们mod的图标。 除了该文件夹,还有两个JSON文件:
fabric.mod.json
对于此文件,我建议您进入有关fabric.mod.json的Fabric文档,并阅读我们项目文件中定义的每个字段。
如果您以后喜欢阅读文档,则只需查看entrypoints
和mixins
属性即可。
我敢打赌,您已经可以在这里看到连接了。 在entrypoints
点,我们告诉Fabric哪个Java类应该充当mod的主要入口点。
然后是mixins
属性,我们只需告诉Fabric我们要包含在mod中的任何Mixin配置文件的位置即可。 在这种情况下,我们只有一个starter.mixins.json
。
{
"schemaVersion" : 1 ,
"id" : "starter" ,
"version" : "${version}" ,
"name" : "Starter Mod" ,
"description" : "Describe your mod!" ,
"authors" : [ "Your Name" ],
"contact" : {
"homepage" : "https://horuslugo.com" ,
"sources" : "https://github.com/HorusGoul/fabric-mod-starter"
},
"license" : "MIT" ,
"icon" : "assets/starter/icon.png" ,
"environment" : "*" ,
"entrypoints" : {
"main" : [ "starter.StarterMod" ]
},
"mixins" : [ "starter.mixins.json" ],
"depends" : {
"fabricloader" : ">=0.7.3" ,
"minecraft" : "1.15.x"
},
"suggests" : {
"flamingo" : "*"
}
}
starter.mixins.json
还记得我们的StarterMixin类吗? 这就是我们如何告诉工具链要包含在我们的mod中的mixins的方法。 package
属性是我们定义混合包所在的Java包的地方,而mixins
数组内部是可以将所有要包含在游戏中的混合包类放入其中的地方。
除了mixins
之外,还有两个其他属性可让我们指定要加载某些mixin的环境。 这些属性是server
和client
,但是在这种情况下,我们不使用它们。
该文件遵循在Mixin Docs的Mixin配置文件部分中定义的规范。 与以前一样,我建议您转到文档并进一步了解此文件😄
{
"required" : true ,
"package" : "starter.mixin" ,
"compatibilityLevel" : "JAVA_8" ,
"mixins" : [ "StarterMixin" ],
"injectors" : {
"defaultRequire" : 1
}
}
让我们构建我们的mod!
现在我们已经熟悉了该项目,让我们动手制作模型吧!
在这种情况下,mod只会改变游戏的一种机制: 受到伤害 。 我们将做到这一点,以便每当玩家受到伤害时,它将与服务器中的其他玩家切换位置和库存。
为此,我们将需要一个mixin,将其插入PlayerEntity
类中的代码,更具体地说,恰好在PlayerEntity
damage()
方法结束之前。
检测玩家何时受到伤害
让我们在名为SwitchPlayerEntityMixin.java
的starter.mixin
包中创建这个新的mixin:
package starter . mixin ;
import org.spongepowered.asm.mixin.Mixin ;
import org.spongepowered.asm.mixin.injection.At ;
import org.spongepowered.asm.mixin.injection.Inject ;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable ;
import net.minecraft.entity.damage.DamageSource ;
import net.minecraft.entity.player.PlayerEntity ;
@Mixin ( PlayerEntity . class )
public class SwitchPlayerEntityMixin {
@Inject ( at = @At ( "RETURN" ), method = "damage" )
private void onDamage ( DamageSource source , float amount , CallbackInfoReturnable info ) {
System . out . println ( "The player received damage!" );
}
}
不要忘记将其添加到starter.mixins.json
文件中:
{
"required" : true ,
"package" : "starter.mixin" ,
"compatibilityLevel" : "JAVA_8" ,
"mixins" : [ "StarterMixin" , "SwitchPlayerEntityMixin" ],
"injectors" : {
"defaultRequire" : 1
}
}
现在,在控制台中执行./gradlew runClient
命令,以创意模式启动Minecraft世界,抓住一些即时伤害药水并尝试受伤。
就像在GIF中一样,每次玩家受伤时,您都应该能够在控制台中看到一条新行,这意味着我们可以继续解释发生的情况。
看一下mixin代码,我们的目的是在方法damage
结束时执行onDamage
方法,这就是为什么我们使用字符串RETURN
而不是HEAD
。 另外,我们将需要损坏source
和造成的损坏amount
。 最后一个参数info
是Mixin框架所必需的。
source
和amount
都是原始damage
方法接收的参数,这就是我们可以在方法中使用它们的原因。
访问当前播放器
现在,mod每次玩家受伤时都只是打印一条线,我们的下一个目标是访问玩家实例。
我们必须首先记住onDamage
方法在PlayerEntity
实例内部。 我们可以利用它,并简单地使用this
来访问实例属性和方法。 问题出在编译器对我们大喊大叫时,因为它认为我们是SwitchPlayerEntityMixin
的实例。
我们没有办法告诉编译器该方法正在另一种类的内部执行,因此可以使用以下技巧:
PlayerEntity self = ( PlayerEntity ) ( Object ) this ;
这样,我们告诉编译器this
是一个Object
,然后将其转换为PlayerEntity
。 和瞧! 我们可以访问受到损坏的播放器,现在我们可以更新打印行以显示播放器的名称。
...
@Mixin ( PlayerEntity . class )
public class SwitchPlayerEntityMixin {
@Inject ( at = @At ( "RETURN" ), method = "damage" )
private void onDamage ( DamageSource source , float amount , CallbackInfoReturnable info ) {
PlayerEntity self = ( PlayerEntity ) ( Object ) this ;
System . out . println ( "The player " + self . getGameProfile (). getName () + " received damage" );
}
}
与其他玩家切换位置
现在我们可以访问玩家的属性和方法,可以使用其中之一来访问整个world
。
world
属性引用了当前正在玩的《我的世界》,而我们可以做的一件事情就是获取在线玩家的名单。
使用该列表,我们可以选择其中一个参与者,然后在下面的代码中看到他们交换位置:
package starter . mixin ;
import java.util.List ;
import org.spongepowered.asm.mixin.Mixin ;
import org.spongepowered.asm.mixin.injection.At ;
import org.spongepowered.asm.mixin.injection.Inject ;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable ;
import net.minecraft.entity.damage.DamageSource ;
import net.minecraft.entity.player.PlayerEntity ;
import net.minecraft.util.math.BlockPos ;
@Mixin ( PlayerEntity . class )
public class SwitchPlayerEntityMixin {
@Inject ( at = @At ( "RETURN" ), method = "damage" )
private void onDamage ( DamageSource source , float amount , CallbackInfoReturnable info ) {
PlayerEntity self = ( PlayerEntity ) ( Object ) this ;
// Get all the players in the current minecraft world
List < PlayerEntity > players = ( List < PlayerEntity >) self . world . getPlayers ();
// The player we'll switch positions with.
PlayerEntity otherPlayer ;
// Stop the execution if the player is playing alone.
if ( players . size () <= 1 ) {
return ;
}
// Get a random player from the players list.
// Repeat this process until we have a player that is
// not the player who got hurt.
do {
int index = ( int ) Math . floor ( Math . random () * players . size ());
otherPlayer = players . get ( index );
} while ( otherPlayer == self );
// Get the block position of both players
BlockPos selfPos = self . getBlockPos ();
BlockPos otherPlayerPos = otherPlayer . getBlockPos ();
// Teleport damaged player to the other player's coordinates
// We set the Y to 300 in order to avoid a collision with the other player.
//
// We add 0.5 to both X and Z because that's the center point of a block
// and the players could suffocate under certain circumstances if we didn't
self . teleport ( otherPlayerPos . getX () + 0.5 , 300 , otherPlayerPos . getZ () + 0.5 );
// Teleport the other player to the position of the damaged player.
otherPlayer . teleport ( selfPos . getX () + 0.5 , selfPos . getY (), selfPos . getZ () + 0.5 );
// Finally change the Y to the real value and complete the teleport of both
// players.
self . teleport ( otherPlayerPos . getX () + 0.5 , otherPlayerPos . getY (), otherPlayerPos . getZ () + 0.5 );
}
}
实施此操作后,您将需要两个Minecraft客户端才能对其进行测试。 您可以通过使用./gradlew runClient
打开一个客户端,然后使用./gradlew runClient
的官方Minecraft客户端来实现。 然后,在LAN中打开测试环境,并与其他客户端一起加入。
交换库存
现在,我们将添加mod的最后一个功能:交换玩家的库存。
要交换两个参与者的库存,我们必须克隆每个库存,然后,我们可以替换并交换它们。 可以使用inventory
属性访问播放器的inventory
。
PlayerInventory
类有两个我们将使用的方法, serialize
和deserialize
serialize
。 第一个允许我们通过将清单的内容放入ListTag
来克隆清单的内容,然后,我们可以使用第二个清单将ListTag
的内容替换为ListTag
内的内容。
这是代码:
// ... teleports ...
// Only swap inventories if the players are alive
if ( self . getHealth () > 0.0f && otherPlayer . getHealth () > 0.0f ) {
// Get the inventories of both players
ListTag selfInventory = self . inventory . serialize ( new ListTag ());
ListTag otherPlayerInventory = otherPlayer . inventory . serialize ( new ListTag ());
// Swap them
self . inventory . deserialize ( otherPlayerInventory );
otherPlayer . inventory . deserialize ( selfInventory );
}
您可能已经注意到,如果两个玩家都还活着,我们只会交换库存,因为如果不包括此支票,则每当一个玩家死亡时,其中一个库存就会丢失。
最终代码
如果您到此为止,恭喜! 您已经建立了第一个Minecraft mod,现在应该删除不需要的文件,例如StarterMixin.java
和StarterMod.java
。 不要忘记删除fabric.mod.json
和starters.mixins.json
对这些文件的引用。
我还建议您将包从starter
程序重命名为所需的名称,只记得记住更改项目中的每个实例。
您可以在入门仓库的分支final-code
代码中找到该代码的最新版本。 单击此处查看该mod的最终版本 。
包装国防部
如果您熟悉Minecraft Mod,则可能已经知道这些Mod通常打包在.zip
或.jar
文件中,然后将它们放入Minecraft服务器或客户端的mods
文件夹中。
要创建一个捆绑包,您只需要运行以下命令:
$ ./gradlew build
如果一切都能正确编译,则可以在项目的./build/libs
文件夹中找到.jar
。 在大多数情况下,您会希望选择不带源的生产版本,但是在某些情况下,将开发版本与源一起提供可能会更好。
就是这样,您现在可以将.jar
放到mods
文件夹中,只是不要忘记首先安装Fabric API,为此,如果您想学习如何做,可以阅读其Wiki中的Installation Fabric部分。 。
学习资源
如果您想了解有关Minecraft Modding的更多信息,以下一些资源可能会派上用场:
面料维基 。 这篇文章中已经提到了这一点,但是请认真地检查一下,因为我没有涉及很多内容!
Forge API 。 Forge API是最著名的Minecraft API,您可能想将其检出,因为其中已经构建了一些最佳的mod!
ScriptCraft 。 似乎有一种使用JavaScript构建mod的方法,因此,如果您具有Web开发背景,则可能需要尝试使用它。
MCreator 。 正如他们的网站所说,MCreator是一款软件,可使用直观易学的界面或集成的代码编辑器制作Minecraft模组和数据包。 如果您知道某个想开始编程的人,这可能是介绍他们的好方法!
结论
构建我的第一个Minecraft Mod很有趣,因为我对我最喜欢的游戏的工作方式有了更多的了解,我什至设法制作了一些非常有趣的游戏。
另外,我以此为契机撰写本文,因为我认为修改是入门编程的好方法,并且许多Minecraft播放器可能会引起兴趣并最终学习了很多有关软件开发的知识。
希望您在阅读本文时过得愉快。 如果您决定继续学习,我邀请您与我们分享您的进步,谁知道,也许Minecraft Modding社区可以诞生在dev.to inside内部
翻译自: https://dev.to/horusgoul/creating-a-minecraft-mod-using-java-and-fabric-3bmo
minecraft mod