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
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的世界是二维的,拥有x和y两个维度,但是在数据库中的存储并不是两个字段(主要原因是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。在请求中需要提供
- 玩家鉴权信息,即Authentication Header
- 玩家控制的Guest的编号,如果该编号的Guest不存在或非玩家控制都会返回400错误
- 在请求体中写明移动的方向,必须为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度。
热力差发电的步骤为:
- 获取高低热源温度差$\Delta H$
- 通过卡诺公式计算效率$\eta = 1 - \frac{T_C}{T_H}$^2
- 计算实际可利用的温度差$\hat{\Delta T} = {\Delta H} * \eta$
- 对可利用温度差向下取整,得到实际温度变化$\Delta T = \lfloor\hat{\Delta T}\rfloor$
- 对高、低热源分别减去、加上温度变化,使温度变得“平均” $$ T'_C = T_C + \Delta T\T'_H = T_H - \Delta T $$
WIP
增殖更多Guest
WIP
尾声
恭喜你看到这里,你已经学会了全部的API了!
Dependencies
~32–45MB
~716K SLoC