private static void HandleRequestMatch(GameSession session, PacketReader packet)
    {
        long characterId = packet.ReadLong();

        Player otherPlayer = session.FieldManager.State.Players.Values
                             .FirstOrDefault(x => x.Value.CharacterId == characterId)?.Value;

        if (otherPlayer is null)
        {
            return;
        }

        RPS rpsEvent = DatabaseManager.Events.FindRockPaperScissorsEvent();

        if (rpsEvent is null)
        {
            return;
        }

        if (!session.Player.Inventory.HasItem(rpsEvent.VoucherId))
        {
            session.Send(NoticePacket.Notice(SystemNotice.MicrogameRpsOpenBannerFailedNotExistTicket, NoticeType.Chat | NoticeType.FastText));
            return;
        }

        session.Player.RPSOpponentId = otherPlayer.CharacterId;

        otherPlayer.Session.Send(RockPaperScissorsPacket.RequestMatch(session.Player.CharacterId));
    }
    private static void HandleCancelRequestMatch(GameSession session, PacketReader packet)
    {
        long   characterId = packet.ReadLong();
        Player otherPlayer = session.FieldManager.State.Players
                             .FirstOrDefault(x => x.Value.Value.CharacterId == characterId).Value?.Value;

        if (otherPlayer is null)
        {
            return;
        }

        otherPlayer.Session.Send(RockPaperScissorsPacket.CancelRequestMatch(characterId));
    }
    private static void HandleOpenMatch(GameSession session)
    {
        RPS rpsEvent = DatabaseManager.Events.FindRockPaperScissorsEvent();

        if (rpsEvent is null)
        {
            return;
        }

        if (!session.Player.Inventory.HasItem(rpsEvent.VoucherId))
        {
            session.Send(NoticePacket.Notice(SystemNotice.MicrogameRpsOpenBannerFailedNotExistTicket, NoticeType.Chat | NoticeType.FastText));
            return;
        }

        session.Send(RockPaperScissorsPacket.Open());
    }
    private static void HandleConfirmMatch(GameSession session, PacketReader packet)
    {
        long characterId = packet.ReadLong();

        Player otherPlayer = session.FieldManager.State.Players
                             .FirstOrDefault(x => x.Value.Value.CharacterId == characterId).Value?.Value;

        if (otherPlayer == null)
        {
            return;
        }

        session.Player.RPSOpponentId = otherPlayer.CharacterId;

        otherPlayer.Session.Send(RockPaperScissorsPacket.ConfirmMatch(session.Player.CharacterId));
        otherPlayer.Session.Send(RockPaperScissorsPacket.BeginMatch());
        session.Send(RockPaperScissorsPacket.BeginMatch());
    }
    private static void HandleSelectRpsChoice(GameSession session, PacketReader packet)
    {
        session.Player.RPSSelection = (RpsChoice)packet.ReadInt();

        // delay for 1 sec for opponent to update their selection
        Task.Run(async() =>
        {
            await Task.Delay(1000);
        });

        // confirm if opponent is still in the map
        Player opponent = session.FieldManager.State.Players
                          .FirstOrDefault(x => x.Value.Value.CharacterId == session.Player.RPSOpponentId).Value?.Value;

        if (opponent == null)
        {
            return;
        }

        // handle choices
        RpsResult[,] resultMatrix =
        {
            {
                RpsResult.Draw, RpsResult.Lose, RpsResult.Win
            },
            {
                RpsResult.Win, RpsResult.Draw, RpsResult.Lose
            },
            {
                RpsResult.Lose, RpsResult.Win, RpsResult.Draw
            }
        };

        RpsResult result = resultMatrix[(int)session.Player.RPSSelection, (int)opponent.RPSSelection];

        RPS rpsEvent = DatabaseManager.Events.FindRockPaperScissorsEvent();

        if (rpsEvent is null)
        {
            return;
        }

        Item voucher = session.Player.Inventory.GetById(rpsEvent.VoucherId);

        if (voucher is null)
        {
            return;
        }

        session.Player.Inventory.ConsumeItem(session, voucher.Uid, 1);

        GameEventUserValue dailyMatches = GameEventHelper.GetUserValue(session.Player, rpsEvent.Id,
                                                                       TimeInfo.Tomorrow(), GameEventUserValueType.RPSDailyMatches);

        int.TryParse(dailyMatches.EventValue, out int dailyMatchCount);

        dailyMatchCount++;

        dailyMatches.UpdateValue(session, dailyMatchCount);
        session.Send(RockPaperScissorsPacket.MatchResults(result, session.Player.RPSSelection, opponent.RPSSelection));
    }