目录

我的学习分享

记录精彩的程序人生

X

Using Marauroa-Role Playing Design

转自https://stendhalgame.org/wiki/RolePlayingDesign

这可能是构成Marauroa的所有中间件中最复杂的部分。
角色扮演设计是决定为Marauroa创建新游戏的简单因素。 我们不得不选择轻松创建基于时间限制的游戏。 Marauroa将在这种游戏(也称为实时游戏)中更好地工作。

角色扮演设计试图通用和游戏无关(独立于正在制作的游戏)。 RPManager背后的基本思想是:

  forever
  {
      Execute Actions
      Send Perceptions
      Wait for next turn
  }

为此,我们使用了几个类:

  • RPManager is coded in Marauroa and doesn't need to be modified.
  • IRPRuleProcessor is the interface that you need to modify in order to personalize the actions that your game will execute.
  • RPWorld is the class that you need to extend in order to implement the onInit and onFinish methods which personalize what happens when you initialise the server and what happens when you close the server.
  • IRPZone is an interface that you could implement if you wanted to achive the highest personalization possible of the engine, however, I would use MarauroaRPZone instead as that uses our great Delta2 feature.

RPManager

RP管理器的目标是处理游戏的RP。 这意味着:

  • run RPActions from clients
  • manage RPWorld
  • control triggers for events
  • control AI

这是一项非常复杂的巨大任务。 因此,我们将此行为拆分为较小的子类。

RPManager公开了一个简单的GameManager接口:

  • addRPAction
    This function queues an action for a particular player to be executed on the next turn.
  • getRPObject
    This is an interface to manage RPWorld to ease the aquisition of the RPObject when exiting the game.
  • onInit Player
  • onExit Player
    These are callback functions that are used by GameManager to notify that a player has entered or exited the game.
  • transferContent
    is a callback function that is called by RPRuleProcessor to stream content to players.

RPManager的主要流程是:

forever
{
    Procced through every action in this turn
    {
        rpRuleProcessor executes action
    }

    Build Perception
    Remove timed out players

    Wait for Turn completion.
    Go to Next Turn
}

RPScheduler是处理为每个玩家排队的动作的类。 应该在这里处理Action管理的所有复杂性。

RPRuleProcessor是Action代码,初始化和退出代码的包装类。
所有Action代码必须在这里。
通过实施RPRuleProcessor,您可以将Marauroa个性化为您想要制作的游戏。 但是,请记住,您仅限于实时风格的游戏。

Objects and Actions

整个Marauroa系统由两个主要实体RPAction和RPObject管理。 还有几个辅助类,如Attributes,RPSlot和RPClass

Attributes

Attributes是name-value形式的值对的集合。 我们几乎可以在Attribute对象中存储任何基本类型:

  • strings
  • integer
  • floats
  • boolean

我们不能在属性中存储结构,但您可以将数据组转换为字符串并将其存储为字符串。 Marauroa为此提供了辅助方法。

Objects

Marauroa服务器中的所有信息都包含在RPObjects中。 一个对象由几个属性组成(一个属性类似于一个变量,因为它有一个名称并包含一个值)和Slots。 Slot是属于对象的容器或容器数组,用于托管(存储)其中的其他对象。

强制对象属性:id,type和zoneid

id是Object的唯一标识,zoneid是对象所在区域的标识,type是对象类的类,因此您可以共享该类的所有实例的属性。

id在包含该对象的区域内唯一。

注意:引擎提供了两种特殊类型的属性:

  • 以!开头的属性
    除了对象的所有者之外,对所有其他用户完全隐藏。
  • 以#开头的属性
    对所有用户都是完全隐藏的。

Classes of Objects Explained

对象类是构造Marauroa数据结构的基本方法。 给定对象的属性类型必须等于您要使用的类型类的RPClass名称。

该类定义属性的类型,其可见性,并为其分配内部代码,用于加速搜索和节省带宽。 您可以将一个类建立在另一个类上,这个特性称为继承(一个新类是从已经存在的类创建的,并包含所有原始类的方法和数据并扩展它)。

可用的数据类型是:

  • Strings
  • Short strings ( up to 255 bytes )
  • Integers ( 4 bytes )
  • Shorts ( 2 bytes )
  • Byte ( 1 byte )
  • Flag ( it is a binary attribute )

属性可以是可见的,这意味着客户端在更改时会看到它们,或者如果客户端看不到它们则会看不到它们。

Slots

对象可以驻留在其他对象内部,就像你的口袋里有钥匙一样。 Slots的目标是提供更丰富的游戏体验,同时减少区域中的对象数量。

为了让对象进入,我们需要我们的hoster对象有插槽来放置它们。一个槽只能处理一个对象。

例如,头像可以具有:

  • left hand
  • right hand
  • backpack
  • left pocket
  • right pocket

我们可以在每个插槽中存储对象。

一旦对象存储在对象槽内,访问存储对象的唯一方法是通过包含我们存储对象的对象。

作为属性,插槽有两种特殊类型:

  • Slots names that start with ! are only sent to owner player. (Hence only seen by the owner)
  • Slots names that start with # are not sent to players. (Invisible to all)

Actions

为了表达客户端做某事的意愿,它必须向服务器发送MessageC2SAction消息。

动作由几个属性组成。 (属性类似于变量,因为它具有名称并包含值)。

有可选和必需的属性。 如果未找到强制属性,则RPServerManager将跳过该消息。

强制操作属性是action_id和type。

action_id用于在感知中生成结果响应时识别Action

Perceptions

向客户端发送世界更新的基本结构称为感知。

感知有两种类型:

  • Sync perceptions: these are used to synchronize clients with the server world representation. This is the only valid way of knowing world's status.
  • Delta perception: this is used to send only the changes to the world since the last perception.

我们的实际Perception系统称为Delta2。 它非常依赖Marauroa核心,所以我建议你使用它:)

How Perceptions and Actions work

Action从客户端发送到服务器,以使角色执行操作。 为了让客户端知道服务器需要向客户端发送回复的操作结果。 怎么做?

在第一次尝试中,我们向客户回送他们行动的结果。 然而,这使代码变得非常困难,因为我们必须更新两个不同的东西,感知和行动。 相反,解决方案显得直观:为什么不加入行动回复和感知。

因此,动作回复存储在每个对象(执行动作)中,其中包含一组确定动作返回状态和属性的属性。 这种做出回复的方式使得它在RPManager上更加困难,但它简化了新客户端的创建。

请参阅Objects文档中的Actions回复,以确切了解返回的内容。 但是,请记住,返回结果取决于每个特定游戏。

Delta2 perception Algorithm

DPA背后的想法是避免每次都将所有对象发送到客户端,而只是那些已经修改过的对象。

想象一下,我们有1000个对象,只有O1和O505是每回合修改的活动对象。

传统方法:

- Get objects that our player should see ( 1000 objects )
- Send them to player ( 1000 objects )
- Next turn
- Get objects that our player should see ( 1000 objects )
- Send them to player
- Next turn
...

我希望你看到问题...我们发送的每个回合没有改变的对象。

增量感知算法:

- Get objects that our player should see ( 1000 objects )
- Reduce the list to the modified ones ( 1000 objects )
- Store also the objects that are not longer visible ( 0 objects )
- Send them to player ( 1000 objects )
- Next turn
- Get objects that our player should see ( 1000 objects )
- Reduce the list to the modified ones ( 2 objects )
- Store also the objects that are not longer visible ( 0 objects )
- Send them to player ( 2 objects )
- Next turn
...

增量感知算法的下一步非常明确:delta2这个想法是仅发送更改对象的更改。这样我们可以节省更多带宽,使感知大约为原始delta感知大小的20%。

delta2算法基于四个容器:

  • List of added objects
  • List of modified added attributes of objects
  • List of modified deleted attributes of objects
  • List of deleted objects

与DPA非常相关的区域是RPZone(参见本文档中的下方)

正如您所知,MPEG视频每X帧都会添加一个完整的帧,因此可以在文件损坏时用作同步。我们的想法是,如果您无法继续解压缩数据,则可以在下一个完整帧之前省略所有内容,然后再进行同步。这里的想法是类似的,如果我们无法与服务器同步,我们发送一个不同步消息,以便服务器将发送同步感知,以便客户端可以同步。请记住,UDP不是安全传输。

为了使感知起作用,在RPZone中调用modify方法很重要,因此修改后的对象存储在修改后的列表中。

Zones and Worlds

Marauroa的世界可以如此之大,如此巨大,我们需要将它们分成几块。 这些块中的每一个都是我们所说的RPZone。

所以我们的世界是由几个相互独立的RPZone组成的。 要从一个RPZone移动到另一个RPZone,您必须在RPRuleProcessor中编写正确的行为。 只需看看我们的任何编码示例。

RPWorld

正如我们已经说过的,RPWorld存储了几个彼此独立的RPZones。
RPWorld提供onInit和onFinish方法,这些方法在服务器初始化和服务器最终化时调用,以定义如何处理这些事件的世界。 没有默认行为,您需要扩展此类以重新定义行为。

它还提供了添加和获取新RPZones的方法:

  • addRPZone
  • getRPZone, which can be used with either RPZone.ID or RPObject.ID

最后,它还包含管理RPObjects的方法:

  • addRPObject, that need that our RPObject contains a valid RPZone.ID inside its RPObject.ID
  • getRPObject
  • modifyRPObject
  • changeZone that moves one object from the old zone to the new zone and adds a proper valid id.

最后,RPWorld还包含一个名为nextTurn的方法,由RPManager调用,从一个Turn到下一个Turn。 它重置delta2数据。

RPZone

对象必须存储在某个地方,我们现在使用Zones来存储它们。区域只是具有名称的对象的容器。

每个RPZone必须具有唯一的名称。

为了改善Marauroa平台的可修改性,我们将RPZone设置为一个界面,以便您可以根据需要实现它。

但在大多数情况下,如果您认为Delta2系统很好并且符合您的游戏风格,您可以使用MarauroaRPZone作为Delta2算法的参考实现。

实际的Marauroa RPZone包含几个数据结构:

  • a HashMap of RPObject.ID to RPObject
  • a List of RPObject
  • a Perception

这个想法是已经在区域中计算了感知因此节省了生成它通常所需的大量时间。所有数据结构都包含相同的对象,但hashmap用于使用RPObject.ID快速搜索对象。这是查找具有已知ID的对象的最常用方法。列表用于改善构建总体感知所需的时间。感知用于预先计算增量感知(即,找到区域的当前状态与上一次发送到客户端的先前状态之间的变化)

对于区域内的所有玩家来说,感知是相同的。

为了使感知起作用,您必须手动调用modify方法,以便通知区域有关角色或对象的更改。