78 lines
3.1 KiB
C#
78 lines
3.1 KiB
C#
using System.Collections.Generic;
|
||
using System.Text;
|
||
using XWorld.Framework;
|
||
using XWorld.Framework.Protocol;
|
||
using RPS.Core;
|
||
|
||
namespace RPS.Server
|
||
{
|
||
public sealed class RpsServerRoom : IGameServerRoom
|
||
{
|
||
public const ushort SnapshotOpcode = 1;
|
||
public const ushort ChoiceOpcode = 1; // 客户端出招消息 opcode
|
||
|
||
private readonly RpsLogic _logic = new RpsLogic();
|
||
private IRoomCtx _ctx;
|
||
private RpsState _state;
|
||
private readonly Dictionary<int, int> _seatOf = new Dictionary<int, int>(); // playerId->seat
|
||
private readonly int[] _pidOfSeat = new int[2]; // seat->playerId(结算用;AI 也有 pid)
|
||
private bool _ended;
|
||
|
||
public void OnRoomStart(IReadOnlyList<PlayerInfo> players, IRoomCtx ctx)
|
||
{
|
||
_ctx = ctx;
|
||
_state = _logic.CreateInitial(new RoomConfig { GameId = "rps", Version = 1, Seed = 1, PlayerCount = players.Count }, ctx.Random);
|
||
for (int i = 0; i < players.Count && i < 2; i++)
|
||
{
|
||
_seatOf[players[i].PlayerId] = i;
|
||
_pidOfSeat[i] = players[i].PlayerId;
|
||
_state.IsAi[i] = players[i].IsAI;
|
||
}
|
||
Broadcast();
|
||
}
|
||
|
||
public void OnMessage(int playerId, NetMessage message)
|
||
{
|
||
if (message.Opcode != ChoiceOpcode) return;
|
||
if (!_seatOf.TryGetValue(playerId, out int seat)) return;
|
||
var r = new PacketReader(message.Payload);
|
||
var choice = (Choice)r.ReadByte();
|
||
_state = _logic.Step(_state, RpsInput.Choose(seat, choice), _ctx.Random).State;
|
||
}
|
||
|
||
public void OnTick(float dt)
|
||
{
|
||
if (_ended) return;
|
||
// Choosing 阶段:AI 座位等概率出招(不读对手),仅在尚未出招时
|
||
if (_state.Phase == RpsPhase.Choosing)
|
||
for (int seat = 0; seat < 2; seat++)
|
||
if (_state.IsAi[seat] && _state.Choices[seat] == Choice.None)
|
||
{
|
||
var c = new[] { Choice.Rock, Choice.Paper, Choice.Scissors }[_ctx.Random.Next(3)];
|
||
_state = _logic.Step(_state, RpsInput.Choose(seat, c), _ctx.Random).State;
|
||
}
|
||
|
||
var res = _logic.Step(_state, RpsInput.Tick(dt), _ctx.Random);
|
||
_state = res.State;
|
||
Broadcast();
|
||
|
||
if (_state.Phase == RpsPhase.Finished)
|
||
{
|
||
_ended = true;
|
||
int s0 = _state.Scores[0], s1 = _state.Scores[1];
|
||
bool draw = _state.Winner == 2;
|
||
_ctx.Logger.Info($"rps finished, winner seat={_state.Winner}, scores {s0}:{s1}");
|
||
_ctx.EndRoom(new RoomEndResult
|
||
{
|
||
WinnerPlayerId = draw ? 0 : _pidOfSeat[_state.Winner],
|
||
ResultBlob = Encoding.UTF8.GetBytes(draw ? $"平局 {s0} : {s1}" : $"最终比分 {s0} : {s1}")
|
||
});
|
||
}
|
||
}
|
||
|
||
public void OnRoomEnd() => _ctx.Logger.Info("rps room end");
|
||
|
||
private void Broadcast() => _ctx.Broadcast(new NetMessage(SnapshotOpcode, _logic.Encode(_state)));
|
||
}
|
||
}
|