1
\$\begingroup\$

I have coded a prize system in where I add a prize in the database and it calls the CheckPrizes(player) every time a player logs in to the application. I won't go in to detail of how they log in but thats the basics of it.

PrizeManager.cs:

using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Data;
using System.Linq;
using Sahara.Base.Game.Players;

namespace Sahara.Base.Game.Prizes
{
    internal sealed class PrizeManager
    {
        private readonly ConcurrentDictionary<int, Prize> _prizes;
        private readonly ConcurrentDictionary<int, List<int>>  _prizeLogs;

        public PrizeManager()
        {
            _prizes = new ConcurrentDictionary<int, Prize>();
            _prizeLogs = new ConcurrentDictionary<int, List<int>>();

            LoadPrizes(false);
        }

        private void LoadPrizes(bool clearBefore)
        {
            if (clearBefore && _prizes.Count > 0)
            {
                _prizes.Clear();
            }

            using (var mysqlConnection = Sahara.GetServer().GetMySql().GetConnection())
            {
                mysqlConnection.OpenConnection();
                mysqlConnection.SetQuery("SELECT * FROM `server_rewards` WHERE `enabled` = @enabled");
                mysqlConnection.AddParameter("enabled", "1");

                var dataTable = mysqlConnection.GetTable();

                if (dataTable == null)
                {
                    return;
                }

                foreach (DataRow prizeRow in dataTable.Rows)
                {
                    _prizes.TryAdd(Convert.ToInt32(prizeRow["id"]), new Prize(Convert.ToInt32(prizeRow["id"]), Convert.ToInt32(prizeRow["reward_start"]), Convert.ToInt32(prizeRow["reward_end"]), Sahara.GetServer().GetUtility().GetPrizeTypeFromString(Convert.ToString(prizeRow["reward_type"])), Convert.ToString(prizeRow["reward_data"]), Convert.ToString(prizeRow["message"])));
                }

                mysqlConnection.CloseConnection();
            }
        }

        public void CheckPrizes(Player player)
        {
            if (player?.GetPlayerData() == null)
            {
                return;
            }

            foreach (var prizeEntry in _prizes.Where(prizeEntry => !ReceivedPrize(player.GetPlayerData().PlayerId, prizeEntry.Key)).Where(prizeEntry => prizeEntry.Value.Ready()))
            {
                prizeEntry.Value.OnReceive(player);
            }
        }

        public void LogReceivedPrize(int playerId, int prizeId)
        {
            if (!_prizeLogs.ContainsKey(playerId))
            {
                _prizeLogs.TryAdd(playerId, new List<int>());
            }

            if (!_prizeLogs[playerId].Contains(prizeId))
            {
                _prizeLogs[playerId].Add(prizeId);
            }

            using (var mysqlConnection = Sahara.GetServer().GetMySql().GetConnection())
            {
                mysqlConnection.OpenConnection();

                mysqlConnection.SetQuery("INSERT INTO `server_reward_logs` VALUES (@playerId, @prizeId)");
                mysqlConnection.AddParameter("playerId", playerId);
                mysqlConnection.AddParameter("prizeId", prizeId);
                mysqlConnection.RunQuery();

                mysqlConnection.CloseConnection();
            }
        }

        private bool ReceivedPrize(int playerId, int prizeId) => _prizeLogs.ContainsKey(playerId) && _prizeLogs[playerId].Contains(prizeId);
    }
}

Prize.cs:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Sahara.Base.Game.Players;
using Sahara.Core.Net.Messages.Outgoing.Packets.Inventory.Purse;

namespace Sahara.Base.Game.Prizes
{
    internal sealed class Prize : IPrize
    {
        private readonly int _prizeId;
        private readonly int _prizeStart;
        private readonly int _prizeEnd;
        private readonly PrizeType _prizeType;
        private readonly string _prizeData;
        private readonly string _prizeMessage;

        public Prize(int prizeId, int prizeStart, int prizeEnd, PrizeType prizeType, string prizeData, string prizeMessage)
        {
            _prizeId = prizeId;
            _prizeStart = prizeStart;
            _prizeEnd = prizeEnd;
            _prizeType = prizeType;
            _prizeData = prizeData;
            _prizeMessage = prizeMessage;
        }

        public bool Ready()
        {
            var now = Sahara.GetServer().GetUtility().GetUnixNow();
            return (now >= _prizeStart && now <= _prizeEnd);
        }

        public void OnReceive(Player player)
        {
            switch (_prizeType)
            {
                case PrizeType.None:
                    return;
                case PrizeType.Badge:
                    if (!player.GetPlayerData().GetBadgeManagement().HasBadge(_prizeData))
                    {
                        player.GetPlayerData().GetBadgeManagement().GiveBadge(_prizeData, true);
                    }
                    break;
                case PrizeType.Credits:
                    player.GetPlayerData().Credits += Convert.ToInt32(_prizeData);
                    player.SendMessage(new CreditBalanceMessageComposer(player.GetPlayerData().Credits));
                    break;
                case PrizeType.Duckets:
                    player.GetPlayerData().Duckets += Convert.ToInt32(_prizeData);
                    player.SendMessage(new HabboActivityPointNotificationMessageComposer(player.GetPlayerData().Duckets, Convert.ToInt32(_prizeData)));
                    break;
                case PrizeType.Diamonds:
                    player.GetPlayerData().Diamonds += Convert.ToInt32(_prizeData);
                    player.SendMessage(new HabboActivityPointNotificationMessageComposer(player.GetPlayerData().Diamonds, Convert.ToInt32(_prizeData)));
                    break;
                default:
                    throw new ArgumentOutOfRangeException();
            }
        }
    }
}

PrizeType.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Sahara.Base.Game.Prizes
{
    internal enum PrizeType
    {
        Credits,
        Badge,
        Duckets,
        Diamonds, 
        None,
    }   
}

GetUnixNow method:

public double GetUnixNow()
{
    var ts = (DateTime.Now - new DateTime(1970, 1, 1, 0, 0, 0));
    return ts.TotalSeconds;
}}
\$\endgroup\$
2
\$\begingroup\$

PrizeManager.cs

First, don't call it Manager.

You don't need a parameterized query here, everything is hardcoded, WHERE 'enabled' = '1' is enough:

mysqlConnection.SetQuery("SELECT * FROM `server_rewards` WHERE `enabled` = @enabled");
mysqlConnection.AddParameter("enabled", "1");

Then this:

_prizes.TryAdd(Convert.ToInt32(prizeRow["id"]), new Prize(Convert.ToInt32(prizeRow["id"]), Convert.ToInt32(prizeRow["reward_start"]), Convert.ToInt32(prizeRow["reward_end"]), Sahara.GetServer().GetUtility().GetPrizeTypeFromString(Convert.ToString(prizeRow["reward_type"])), Convert.ToString(prizeRow["reward_data"]), Convert.ToString(prizeRow["message"])));

This line is too long, create a Prize object first (with every parameter in its own line) and then add it to the collection:

var prize = new Prize(Convert.ToInt32(prizeRow["id"]),
                      Convert.ToInt32(prizeRow["reward_start"]), 
                      Convert.ToInt32(prizeRow["reward_end"]), 
                      Sahara.GetServer().GetUtility().GetPrizeTypeFromString(Convert.ToString(prizeRow["reward_type"])), 
                      Convert.ToString(prizeRow["reward_data"]), 
                      Convert.ToString(prizeRow["message"])));
_prizes.TryAdd(Convert.ToInt32(prizeRow["id"]), prize);

After that, remove the line mysqlConnection.CloseConnection();, the connection is already in the using block, it will be disposed.

This query is mind bending, refactor it or explain it in a comment:

foreach (var prizeEntry in _prizes.Where(prizeEntry => !ReceivedPrize(player.GetPlayerData().PlayerId, prizeEntry.Key)).Where(prizeEntry => prizeEntry.Value.Ready()))

Prize.cs

Use DateTime and DateTimeOffset structures instead of integers for handling time (_prizeStart, _prizeEnd, Ready()), and don't use the GetUnixNow method.

\$\endgroup\$
0
\$\begingroup\$

Don't

SELECT *

Explicitly get only the columns you need

Use a DataReader rather than a DataTable

If you use HashSet for _prizeLogs you don't have to check Contain. You can just Add and if it is there then nothing happens

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.