15 releases (8 stable)

2.2.5 Jul 2, 2024
2.2.4 Jul 1, 2024
2.1.0 Jun 15, 2024
0.2.5 Apr 29, 2024
0.1.1 Apr 17, 2024

#64 in Database implementations

GPL-3.0 license

58KB
1.5K SLoC

Entropy | 熵

Rust Game. 一个Rust小游戏。




入门

欢迎游玩Entropy! 目前游戏以HTTP API的格式提供服务,Python SDK以及更多客户端正在开发中。

首先,你需要连接到游戏服务器,并注册一个账号。

注册

API端点

POST /player

请求内容

请求不需要鉴权;请求需附带json格式的参数表,详情如下:

参数名 类型 描述
name String 用户名
password String 密码

返回内容

不出意外的话,服务器返回状态码200,附带内容为一个玩家对象的所有信息,格式为json:

返回值 类型 描述
id int 身份号,用以登陆的唯一编号
name String 用户名,对外展示的名字
password String 密码

例子

使用helloworld作为用户名,123456作为密码(仅仅演示用,非常不推荐简单的密码)注册一个账号,则:对/player端点POST方法发起请求,并附带信息Body:

{
    "name": "helloworld",
    "password": "helloworld"
}

服务器返回状态码200,并返回内容:

{
    "id": 449,
    "name": "helloworld",
    "password": "helloworld"

}

服务器返回的ID和密码请一定记录好,这就是登入的唯一凭证。

获取玩家信息🔒

API端点

GET /player
是的没错,和注册是同一个端口,但是请求方法变为了GET
这是一个需要鉴权的接口,在请求的同时需要提供用户ID和密码,否则服务器将返回400错误

鉴权相关

服务器采用HTTP协议的Basic基础鉴权方案,在每一次请求中附带用户ID和密码,如果访问需要鉴权的接口但未提供相应的信息时,服务器会返回400错误。
关于更多鉴权请参阅MDN的文档

请求内容

GET请求无需附带请求体,鉴权内容中已附带玩家账号信息

返回内容

如果鉴权成功,则返回玩家的所有信息

返回值 类型 描述
id int 身份号,用以登陆的唯一编号
name String 用户名,对外展示的名字
password String 密码

返回内容与同URI的POST方法接口完全一致,这都是数据库玩家表包含的字段

创建第一个Guest🔒

人生旅程,皆为过客。一个玩家可以控制多个实体角色,在Entropy游戏中,这些实体被称作Guest,这个特殊称谓取代了Character,一定程度上减小了代码量,同时也是参考了虚拟机系统中的Server-Client-Guest的三级名称。

一个玩家注册时是没有控制Guest的,常识地讲,玩家需要一个Guest来进行游玩,为此需要访问/player/guest/spawn端口,这个端口也是需要使用ID和密码鉴权的。

API端点

GET /player/guest/spawn

我们从/player深入,来到了/player/guest,这代表着玩家的Guest操作,创建操作被命名为spawn,似乎很常规

鉴权相关

该端点需要鉴权,方式同上

请求内容

GET请求无需附带请求体,鉴权内容中已附带玩家账号信息

返回内容

如果鉴权成功,则立即创建一个Guest,并将这个Guest的主人设置为请求的玩家,完成这一切后返回新创建的Guest的信息。

每个玩家通过这个端点创生的第一个Guest是免费的,没有任何开销。但这样的spawn也只有一次机会。 如果玩家在已有guest的情况下再次调用这个端点,则会报错。
稍后会介绍其他创建更多Guest的方法。

返回值 类型 描述
id int Guest的身份号,用以查询、控制等的唯一编号
energy int 能量,一种收集物
pos (int,int) Guest的位置,一个二维坐标
temperature int Guest的温度,一种状态
master_id int Guest的拥有者的ID,对应了玩家ID

查看玩家控制的Guest🔒

API端点

GET /player/guest

鉴权相关

该接口需要鉴权,方式同上

请求内容

玩家信息已包含在鉴权信息内,无需提交额外的请求内容

返回内容

将会返回一个json的列表对象,其中每一个元素就是一个Guest的所有属性,详细字段如下:

元素属性 类型 描述
id int Guest的身份号,用以查询、控制等的唯一编号
energy int 能量,一种收集物
pos (int,int) Guest的位置,一个二维坐标
temperature int Guest的温度,一种状态
master_id int Guest的拥有者的ID,对应了玩家ID

例如:

[
  {
    "id": 1,
    "energy": 19,
    "pos": [
      0,
      0
    ],
    "temperature": 19,
    "master_id": 1
  }
]



游戏的坐标系统,GRID

GRID这个名字灵感来源于《创:战纪》中的电子世界网络,类似的用法还有国家电网 State Grid。 而实际上这个名字的意思就是一个坐标系统——平面直角坐标系。

Entropy的世界是二维的,拥有xy两个维度,但是在数据库中的存储并不是两个字段(主要原因是Sea-ORM对pgsql的二维索引并不友好),所以干脆直接 将两个维度压缩为一个扁平维度进行存储,在对网络服务的时候由定制的序列化策略,在对坐标序列化时将低维度的索引转换为二维的坐标(这样做的缺点是不支持数据库空间索引,但Sea-ORM本来就不支持二维索引)。

虽然上面转换的过程看起来很复杂,但是不要紧,坐标系统本身其实很简单。

世界地图与节点

首先,整个世界是一个二维的梯度平面,世界由很多节点Node构成。每一个Node有两个属性。一个是坐标索引,是二维数组(i16,i16);另一个是节点值,是一个不定长的数组Vec,最长不超过1024,长度全随机。代码就像这样:

struct NodeID(i16, i16); // 二维空间坐标

struct Node{ // 这是一个Node
    id: NodeID,
    data: Vec<i8>, // 节点包含许多Cell
}

struct FlatID(i32); // 压缩后的扁平索引,用以数据库存储

为了交流方便,就将节点中数据数组的一个特定数值称之为单元Cell。
Cell既是一个字节,也是一个1位宽的无符号整数,Entropy游戏中一个Cell的值就被认为是这个Cell的温度。由此引出主题:

玩家通过Guest和Cell的温度差进行发电,收集能量;消耗能量,不断增殖

  • 玩家控制Guest只能访问与其同节点上的Cell进行发电
  • 发电的效率为卡诺热机的理论效率^1,关于卡诺循环详见知乎专栏
  • 经过效率折算后,收集能量与温度转换的比例是1:1,也就是1点温度差等价于1点温度
  • 小数部分在对玩家收获能量时向下取整,另一部分向上取整,总和不变

访问一个节点也很简单,甚至不需要鉴权

API端点

GET /node/:x/:y
GET /node/bytes/:x/:y

其中x是节点的横坐标,y是纵坐标;第一个端点将返回json格式数据,而第二个端点将直接返回Node的data属性对应的字节串(由于每个Cell宽度仅为1,所以这里不存在大小端序问题)

请求内容

不需要任何参数,坐标信息直接在API端点的URI中提供

返回内容

返回一个Node的信息,详情如下:

返回值 类型 描述
id (int, int) Node的坐标
data [int] Node的data部分,即由Cell构成的数字列表
值得注意的是,json返回结果中的Cell部分的温度为1位宽有符号整数,范围从-128到127;
而bytes返回的则是字节串,需要对每一位强制转换为int8才是Cell的温度。

例子

例如GET请求/node/1/1,返回:

{
  "id": [
    1,
    1
  ],
  "data": [
    -49,
    -28,
    -26,
    102,
    44
  ]
}

这里只是个简单的例子。事实上,Node的data部分的长度从0到1024不等,这么短的data出现概率很小,只是为了演示用的。

坐标与移动系统

世界是非连续的,无论Guest还是Node都有一个二维的整数坐标。

玩家只能与当前节点上的单元交互,但是玩家可以主动移动,每次只能向周围8个方向移动一个格子的距离。移动没有除了现实世界时间以外的任何消耗。

玩家移动的速度(单位时间内途径的节点数)取决于客户端请求频率和服务器算力。

开始移动

默认给予的Guest总是在(0, 0)位置上,我们定义第一个维度正方向为右,第二个维度正方向为上(和平面直角坐标系一致)。
玩家可以控制Guest向上下左右4个位置移动,每次移动1个格子、消耗1点能量,具体如下表:

方向 参数
[0,1]
[-1,0]
[1,0]
[0,-1]

现在我们开始正式移动,移动在Entropy中称作Walk,移动是Guest的一个方法。首先,玩家需要确定移动的是哪个Guest以及移动的方向。

API端点

POST /guest/walk/:id
其中id为玩家控制的Guest的编号,如果不清楚ID可以调用/player/guest接口查询全部Guest

请求内容

例如,我要将我控制的编号为449的Guest向右边移动一个单位,那么位移参数就是[1,0],控制ID就是449。在请求中需要提供

  1. 玩家鉴权信息,即Authentication Header
  2. 玩家控制的Guest的编号,如果该编号的Guest不存在或非玩家控制都会返回400错误
  3. 在请求体中写明移动的方向,必须为9个方向之一,超出限制也会返回400错误

请求体的参数如下:

参数名 类型 描述
to (int,int) 将要位移的方向,只能在上文4个方向中选一个

响应内容

如果移动成功,则会返回Guest的最新状态,即全部属性,具体如下(再次和前文重复)

返回值 类型 描述
id int Guest的身份号,用以查询、控制等的唯一编号
energy int 能量,一种收集物
pos (int,int) Guest的位置,一个二维坐标
temperature int Guest的温度,一种状态
master_id int Guest的拥有者的ID,对应了玩家ID

收获能量

Entropy的设计中,能量是从通过Guest的温差发电机产生的。

温度系统

Entropy的温度由一个字节表示,即在开尔文温标下,最低温0K度(绝对零度),最高温255K度。
热力差发电的步骤为:

  1. 获取高低热源温度差$\Delta H$
  2. 通过卡诺公式计算效率$\eta = 1 - \frac{T_C}{T_H}$^2
  3. 计算实际可利用的温度差$\hat{\Delta T} = {\Delta H} * \eta$
  4. 对可利用温度差向下取整,得到实际温度变化$\Delta T = \lfloor\hat{\Delta T}\rfloor$
  5. 对高、低热源分别减去、加上温度变化,使温度变得“平均” $$ T'_C = T_C + \Delta T\T'_H = T_H - \Delta T $$

WIP

增殖更多Guest

WIP

尾声

恭喜你看到这里,你已经学会了全部的API了!

Dependencies

~32–45MB
~716K SLoC