游戏逻辑
Adrift 如何利用链上签到和随机性打造一款最后一名玩家生存的游戏。
Adrift 是一款链上生存游戏,玩家必须定期签到以避免被淘汰。所有游戏逻辑和随机性均由智能合约执行,管理员可以配置参数如间隔时间、宽限期和结果概率。这确保了游戏的公平性、透明性和趣味性。最后剩下的玩家将获胜。
玩家的游戏体验如下:
- 在游戏开始前注册。
- 定期签到以继续留在游戏中。
- 每次签到(由 Adrift 和 CheckInOutcomes 合约处理)会有一个随机结果:增益、减益或淘汰。
- 未签到或被淘汰即出局。
- 最后剩下的玩家获胜。
注册
// Key lines from register()
nextCheckIn[player] = CHECKIN_INITIALIZED;
playerCount++;
if (address(this).balance >= REGISTRATION_GAS) {
Address.sendValue(payable(player), REGISTRATION_GAS);
}- 在游戏开始前(在
gameStartTime之前)。 - 玩家通过调用 Adrift 合约中的
register函数进行注册。 - 初始化玩家的下一次签到时间。
- 玩家数量增加。
- 如果合约有足够的余额,玩家会收到注册的 gas 奖励(默认 1 个代币),以应用链的自定义 gas 代币支付。自定义 gas 代币使补贴变得简单且低成本。
- 触发一个包含注册详情的事件。
游戏开始
// Key lines from setGameStartTime()
gameStartTime = startTime;
emit GameStartTimeSet(startTime);- 游戏在预定义的
gameStartTime时间开始(由管理员设置)。 - 在此时间之前不允许签到。
签到
// Key lines from checkIn()
int256 outcome = checkInOutcomes.getOutcome(msg.sender);
if (outcome == checkInOutcomes.DISQUALIFIED_OUTCOME()) {
return disqualifyFromCheckIn(msg.sender, outcome);
}
nextCheckIn[msg.sender] = nextCheckInTime;
emit PlayerCheckedIn(msg.sender, block.timestamp, buffOrDebuff, nextCheckInTime, false);- 在游戏期间,玩家必须在其
nextCheckInTime到期之前调用checkIn。 - 每次签到的结果由 CheckInOutcomes 合约决定,可能是增益、减益或淘汰。
- 如果被淘汰,玩家将被移出游戏。
- 根据结果更新下一次签到时间。
- 触发一个包含签到结果的事件。
取消资格
// Key lines from disqualifyInactivePlayer/disqualifyFromCheckIn
_disqualify(player, nextCheckInTime);
nextCheckIn[player] = CHECKIN_DISQUALIFIED;
isPlayerDQed[player] = true;- 如果玩家错过了签到时间窗口,任何人都可以在 Adrift 合约中调用
disqualifyInactivePlayer将其移除。 - 签到期间也可能随机发生取消资格的情况。
- 玩家状态会更新为取消资格,并被排除在游戏之外。
- 系统会触发一个取消资格的事件。
我们运行一个简单的定时任务,定期调用心跳端点,自动取消不活跃玩家的资格,并在仅剩一名活跃玩家时结束游戏。
结果与随机性
// Key lines from CheckInOutcomes.sol
uint256 rand = uint256(keccak256(abi.encodePacked(random.random(), playerNonces[player]++, player)));
if (rand % PRECISION < DISQUALIFICATION_CHANCE) {
return DISQUALIFIED_OUTCOME;
}
uint256 outcome = (rand % OUTCOME_RANGE) + 1;
bool isNegative = (rand >> 128) % 2 == 0;
return isNegative ? -int256(outcome) : int256(outcome);// Key lines from Random.sol
uint256 public random;
function setRandom(uint256 _random) external onlyRandomnessAdmin {
random = _random;
}签到结果由 CheckInOutcomes 合约决定,该合约通过 Random 合约在每个区块中使用新的由排序器注入的随机性。当玩家签到时:
- 合约会结合最新的随机值、玩家地址和一个随机数生成器(nonce)生成一个唯一的随机数。
- 玩家有一定的概率(默认:2%)会立即被取消资格。
- 否则,会在设定范围内(默认:1–24)生成一个随机结果(增益或减益),其符号(正/负)也会随机选择。
Random合约的值由排序器设置,确保不可预测性和可审计性。
该系统确保每次签到都是不可预测的、公平的,并且可以在链上验证。
胜利与游戏结束
// Key lines from endGame()
if (playerCount == 1) {
winner = player;
}
gameEndTime = block.timestamp;
emit GameEnded(gameEndTime, winner);- 最后一名活跃玩家将被宣布为胜利者。
- 如果所有玩家都被取消资格,则没有胜利者。
- 游戏结束时间和胜利者将被记录并触发一个事件。