3.1 端

Minecraft中的端(Sides)

当模组设计需要两端配合时,有一个需要理解的非常重要的概念:客户端和服务器。关于端有许多常见的误解和错误,这可能不会导致游戏崩溃的错误,但可能会产生意图之外的且通常无法预测的影响。

 

不同种类的端

当讨论“客户端”或“服务器”时,我们通常会非常直观地理解自己正在谈论的游戏的哪个部分。毕竟,客户端是用户与之交互的内容,服务器是用户连接多人游戏的地方。很容易,对吗?

事实证明,即使有两个这样的术语,也可能会出现一些含糊不清之处。这里我们区分了“客户端”和“服务器”的四种可能的含义:

  • 物理客户端 - 物理客户端是从启动器启动Minecraft时运行的整个程序。所有线程、进程、在游戏的图形化时的服务,和可交互的生命周期都是物理客户端的一部分。
  • 物理服务器 - 通常称为专用服务器,物理服务器是在您启动任何minecraft_server.jar时运行的整个程序,它不会显示可游玩的GUI。
  • 逻辑服务器 - 逻辑服务器运行游戏逻辑:生物(译者注:“生物【Mobs】是指游戏世界中除玩家外的有生命的,可自主移动的一类实体。”——摘自Minecraft中文Wiki)产生,天气,更新物品,健康,AI和其他所有游戏机制。逻辑服务器存在于物理服务器中,但也可以作为单人世界与逻辑客户端一起在物理客户端内运行。逻辑服务器始终在名为Server Thread的线程中运行。
  • 逻辑客户端 - 逻辑客户端接受来自玩家的输入并将其中继(relay)到逻辑服务器。此外,它还从逻辑服务器接收信息,并以图形化方式提供给玩家。逻辑客户端在Client Thread中运行,但它通常会生成其他几个线程来处理音频和区块渲染批处理等事务。

 

执行端特定操作

world.isRemote

这个布尔检查是检查当前所在端最常用的方法。在World对象上查询此字段可确定世界所属的逻辑端。也就是说,如果此字段为true,则该世界当前正在逻辑客户端上运行。如果该字段是false,则世界正在逻辑服务器上运行。因此,物理服务器的此字段将始终为false,但我们不能假设这一项为false就意味着物理服务器,因为该字段也可以false用于物理客户端内的逻辑服务器(换句话说,单人世界)。

要想知道是否应该运行游戏逻辑和其他机制,使用这个值检查即可。例如,如果你想要玩家每次点击你的方块时都受到伤害,或者让你的机器将泥土处理为钻石,你应该只在能确保world.isRemotefalse后再这样做。将游戏逻辑应用于逻辑客户端的话,轻则可能导致(数据)不同步(幽灵实体,不同步的统计数据等),重则崩溃。

这项检查应当作为你(需要时的)首选的默认做法。除了代理之外,程序很少需要通过其他方法来确定端并调整行为。

@SidedProxy

考虑到客户端和服务器模组单个“通用”jar包的使用,以及物理端两个jar包的分离,一个重要的问题出现:我们如何使用仅存在于某一个物理端的代码?所有在net.minecraft.client中的代码仅存在于物理客户端上,并且所有net.minecraft.server.dedicated中的代码仅存在于物理服务器上。如果你编写的任何类以任何方式引用这些名称,则当在不存在这些名称的环境中加载相应的类时,它们将使游戏崩溃。初学者中一个非常常见的错误是在方块或tile实体类中调用Minecraft.getMinecraft().<doStuff>(),而这个类一旦加载,物理服务器就会崩溃。

我们该如何解决这个问题?幸运的是,FML为我们提供了一个@SidedProxy注释。我们为它提供两个类的名称(一个用于serverSide,一个用于clientSide),并使用此注释标注一个字段。当模组运行时,FML将基于物理端的情况对两个类中的一个进行实例化。

注意: 重要的是要理解FML是基于物理端来选择代理进行实例化。单人世界(物理客户端中的逻辑服务器+逻辑客户端)仍将拥有你在clientSide中指定了其类型的代理!

一个常见的情况是注册渲染和模型,这东西必须从preInitinitpostInit主初始化方法被调用。但是,许多渲染相关的类和注册表在物理服务器上并不存在,并且可能会使物理服务器崩溃。因此,我们将这些操作放入客户端代理中,确保它们始终为物理客户端执行。

请记住,两个指定的代理必须具有可赋值给注释@SidedProxy中字段的类型。一种常见但不唯一的策略是将接口IProxy作为字段类型,然后用ClientProxyServerProxy两个类分别实现两个相应的物理端。

getEffectiveSide

为了在无权访问World对象而无法检查isRemote的情况下获取逻辑端,可以调用FMLCommonHandler.getEffectiveSide()。它通过查看当前运行的线程的名称来猜测程序所处的逻辑端。因为这是猜测,所以只有在其他选项不可用时才应使用此方法。几乎在所有情况下,你都应当选择检查world.isRemote而不是调用此方法。

getSide 和 @SideOnly

FMLCommonHandler.getSide()可以调用以检索运行代码的物理端。由于它是在启动时确定的,因此它不依赖于猜测来返回其结果。然而,此方法的用处有限。

使用@SideOnly记号标记方法或字段可以告知加载器:相应的成员应该从定义中被完全剥离而不是在指定的物理端剥离。通常,只有在浏览反编译的Minecraft代码时才能看到这些,也就是Mojang混淆器剥离出去的方法。一般几乎没有直接使用此记号的理由。只有在覆盖已使用@SideOnly定义的原版方法时才使用它。在其他大多数需要根据物理端来调度行为的情况下,请使用@SidedProxy或使用getSide()检查。

 

常见错误

关于逻辑端的连接

每当您想要将信息从一个逻辑端发送到另一个逻辑端时,你必须始终使用网络数据包。在单人场景中,将数据从逻辑服务器直接传输到逻辑客户端是非常诱人的。

实际上,这常常是通过静态字段无意中完成的。由于逻辑客户端和逻辑服务器在单人场景中共享相同的JVM,因此写入和读取静态字段的两个线程都将导致各种竞争条件和与线程相关的经典问题。

这个错误也可以通过访问物理客户端独占类来显式触发,如Minecraft(译者注:这里指代码中名为Minecraft的类)这种运行或可以在逻辑服务器上运行的公共代码。对于在物理客户端中调试的初学者来说,这个错误很容易被忽略。代码将在物理客户端(译者注:此处指单人游戏中)工作,但会立即在物理服务器上崩溃。

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值