using System.Collections.Generic; using System.Text; using Fighter3D.Core; using XWorld.Framework; using XWorld.Framework.Protocol; namespace Fighter3D.Server { public sealed class FighterServerRoom : IGameServerRoom { public const ushort InputOpcode = 1; public const ushort SnapshotOpcode = 100; private readonly FighterLogic _logic = new FighterLogic(); private readonly Dictionary _seatOfPlayer = new Dictionary(); private readonly int[] _playerOfSeat = new int[2]; private IRoomCtx _ctx; private FighterState _state; private bool _ended; public void OnRoomStart(IReadOnlyList players, IRoomCtx ctx) { _ctx = ctx; _state = _logic.CreateInitial( new RoomConfig { GameId = "fighter3d", Version = 1, Seed = 1, PlayerCount = players.Count }, ctx.Random); for (int i = 0; i < players.Count && i < 2; i++) { _seatOfPlayer[players[i].PlayerId] = i; _playerOfSeat[i] = players[i].PlayerId; } Broadcast(); } public void OnMessage(int playerId, NetMessage message) { if (_ended || message.Opcode != InputOpcode) return; if (!_seatOfPlayer.TryGetValue(playerId, out int seat)) return; try { var reader = new PacketReader(message.Payload); int axis = reader.ReadVarInt(); var buttons = (FighterButtons)reader.ReadByte(); _state = _logic.Step(_state, FighterInput.Controls(seat, axis, buttons), _ctx.Random).State; } catch (System.Exception ex) { _ctx.Logger.Error($"fighter3d input decode failed: {ex.Message}"); } } public void OnTick(float deltaTime) { if (_ended) return; var result = _logic.Step(_state, FighterInput.Tick(deltaTime), _ctx.Random); _state = result.State; Broadcast(); if (_state.Phase == FighterPhase.Finished) EndRoomOnce(); } public void OnRoomEnd() { _ctx?.Logger.Info("fighter3d room end"); } private void Broadcast() { _ctx.Broadcast(new NetMessage(SnapshotOpcode, _logic.Encode(_state))); } private void EndRoomOnce() { if (_ended) return; _ended = true; int winnerPid = _state.WinnerSeat >= 0 && _state.WinnerSeat < 2 ? _playerOfSeat[_state.WinnerSeat] : 0; string text = _state.WinnerSeat == 2 ? "Fighter3D draw" : $"Fighter3D winner seat {_state.WinnerSeat}"; _ctx.EndRoom(new RoomEndResult { WinnerPlayerId = winnerPid, ResultBlob = Encoding.UTF8.GetBytes(text) }); } } }