minecraft mod_使用Java和Fabric创建Minecraft Mod

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调用TitleScreeninit()方法,我们的方法将包括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.jsonFabric文档,并阅读我们项目文件中定义的每个字段。

如果您以后喜欢阅读文档,则只需查看entrypointsmixins属性即可。

我敢打赌,您已经可以在这里看到连接了。 在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的环境。 这些属性是serverclient ,但是在这种情况下,我们不使用它们。

该文件遵循在Mixin Docs的Mixin配置文件部分中定义的规范。 与以前一样,我建议您转到文档并进一步了解此文件😄

{ 
   "required" :   true , 
   "package" :   "starter.mixin" , 
   "compatibilityLevel" :   "JAVA_8" , 
   "mixins" :   [ "StarterMixin" ], 
   "injectors" :   { 
     "defaultRequire" :   1 
   } 
 } 

让我们构建我们的mod!

现在我们已经熟悉了该项目,让我们动手制作模型吧!

在这种情况下,mod只会改变游戏的一种机制: 受到伤害 。 我们将做到这一点,以便每当玩家受到伤害时,它将与服务器中的其他玩家切换位置和库存。

为此,我们将需要一个mixin,将其插入PlayerEntity类中的代码,更具体地说,恰好在PlayerEntity damage()方法结束之前。

检测玩家何时受到伤害

让我们在名为SwitchPlayerEntityMixin.javastarter.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框架所必需的。

sourceamount都是原始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类有两个我们将使用的方法, serializedeserialize 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.javaStarterMod.java 。 不要忘记删除fabric.mod.jsonstarters.mixins.json对这些文件的引用。

我还建议您将包从starter程序重命名为所需的名称,只记得记住更改项目中的每个实例。

您可以在入门仓库的分支final-code代码中找到该代码的最新版本。 单击此处查看该mod的最终版本

包装国防部

如果您熟悉Minecraft Mod,则可能已经知道这些Mod通常打包在.zip.jar文件中,然后将它们放入Minecraft服务器或客户端的mods文件夹中。

要创建一个捆绑包,您只需要运行以下命令:

$ ./gradlew build

如果一切都能正确编译,则可以在项目的./build/libs文件夹中找到.jar 。 在大多数情况下,您会希望选择不带源的生产版本,但是在某些情况下,将开发版本与源一起提供可能会更好。

./build/libs文件夹中的starter-1.0.0.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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值