CoinBombs and GemBombs now queue their amounts to prevent excessive database calls.

Removed excess account row information retrieval on web api when awarding gems/coins.
Added account specific transactional locks in webapi.
This commit is contained in:
Jonathan Williams 2014-11-05 14:42:11 -08:00
parent ecad60eee3
commit 4254225462
9 changed files with 454 additions and 360 deletions

View File

@ -28,6 +28,7 @@ public class DonationManager extends MiniPlugin
private Object _donorLock = new Object();
private NautHashMap<Player, NautHashMap<String, Integer>> _gemQueue = new NautHashMap<Player, NautHashMap<String, Integer>>();
private NautHashMap<Player, NautHashMap<String, Integer>> _coinQueue = new NautHashMap<Player, NautHashMap<String, Integer>>();
public DonationManager(JavaPlugin plugin, String webAddress)
{
@ -247,4 +248,56 @@ public class DonationManager extends MiniPlugin
}
}, caller, name, uuid.toString(), amount);
}
public void RewardCoinsLater(final String caller, final Player player, final int amount)
{
if (!_coinQueue.containsKey(player))
_coinQueue.put(player, new NautHashMap<String, Integer>());
int totalAmount = amount;
if (_coinQueue.get(player).containsKey(caller))
totalAmount += _coinQueue.get(player).get(caller);
_coinQueue.get(player).put(caller, totalAmount);
//Do Temp Change
Donor donor = Get(player.getName());
if (donor != null)
donor.addCoins(amount);
}
@EventHandler
public void UpdateCoinQueue(UpdateEvent event)
{
if (event.getType() != UpdateType.SLOWER)
return;
for (Player player : _coinQueue.keySet())
{
String caller = null;
int total = 0;
for (String curCaller : _coinQueue.get(player).keySet())
{
caller = curCaller;
total += _coinQueue.get(player).get(curCaller);
}
if (caller == null)
continue;
//Actually Add Gems
RewardCoins(null, caller, player.getName(), player.getUniqueId(), total, false);
System.out.println("Queue Added [" + player + "] with Coins [" + total + "] for [" + caller + "]");
//Clean
_coinQueue.get(player).clear();
}
//Clean
_coinQueue.clear();
}
}

View File

@ -110,7 +110,7 @@ public class ItemCoinBomb extends ItemGadget
event.setCancelled(true);
event.getItem().remove();
Manager.getDonationManager().RewardCoins(null, this.GetName() + " Pickup", event.getPlayer().getName(), event.getPlayer().getUniqueId(), 4);
Manager.getDonationManager().RewardCoinsLater(GetName() + " Pickup", event.getPlayer(), 4);
event.getPlayer().getWorld().playSound(event.getPlayer().getLocation(), Sound.ORB_PICKUP, 1f, 2f);

View File

@ -189,7 +189,7 @@ public class ItemGemBomb extends ItemGadget
event.setCancelled(true);
event.getItem().remove();
Manager.getDonationManager().RewardGems(null, this.GetName() + " Pickup", event.getPlayer().getName(), event.getPlayer().getUniqueId(), 4);
Manager.getDonationManager().RewardGemsLater(GetName() + " Pickup", event.getPlayer(), 4);
event.getPlayer().getWorld().playSound(event.getPlayer().getLocation(), Sound.ORB_PICKUP, 1f, 2f);
}

View File

@ -143,7 +143,7 @@ public class MorphVillager extends MorphGadget implements IThrown
event.setCancelled(true);
event.getItem().remove();
Manager.getDonationManager().RewardGems(null, this.GetName() + " Pickup", event.getPlayer().getName(), event.getPlayer().getUniqueId(), 16);
Manager.getDonationManager().RewardGemsLater(GetName() + " Pickup", event.getPlayer(), 16);
event.getPlayer().getWorld().playSound(event.getPlayer().getLocation(), Sound.ORB_PICKUP, 1f, 2f);
}

View File

@ -2,10 +2,14 @@
{
public class GemRewardToken
{
public int OriginalBalance;
public string Name { get; set; }
public string Source { get; set; }
public int Amount { get; set; }
public int Retries { get; set; }
}
}

View File

@ -7,5 +7,7 @@
public string Rank { get; set; }
public bool Perm { get; set; }
public int Retries { get; set; }
}
}

View File

@ -16,6 +16,7 @@
using LOC.Website.Common.Contexts;
using System.Data.Entity.Infrastructure;
using System.Transactions;
using System.Runtime.CompilerServices;
public class AccountAdministrator : IAccountAdministrator
{
@ -23,7 +24,7 @@
private readonly IGameServerMonitor _gameServerMonitor;
private readonly ILogger _logger;
private readonly object _transactionLock = new object();
private static ConditionalWeakTable<string, object> _accountLocks = new ConditionalWeakTable<string, object>();
public AccountAdministrator(INautilusRepositoryFactory nautilusRepositoryFactory, ILogger logger)
{
@ -64,47 +65,66 @@
}
}
private object getAccountLock(string name)
{
object lockObject = null;
if (!_accountLocks.TryGetValue(name, out lockObject))
{
lockObject = new object();
_accountLocks.Add(name, lockObject);
}
return lockObject;
}
public Account Login(LoginRequestToken loginToken)
{
using (var repository = _repositoryFactory.CreateRepository())
lock (getAccountLock(loginToken.Name))
{
var account = repository.Where<Account>(x => x.Uuid == loginToken.Uuid).FirstOrDefault() ?? CreateAccount(loginToken, repository);
account.LoadNavigationProperties(repository.Context);
account.LastLogin = DateTime.Now.Ticks;
// Expire punishments
if (account.Punishments != null)
using (var repository = _repositoryFactory.CreateRepository())
{
foreach (var expiredPunishment in account.Punishments.Where(x => x.Active && (x.Duration - 0d) > 0 && TimeUtil.GetCurrentMilliseconds() > (x.Time + (x.Duration * 3600000))))
var account = repository.Where<Account>(x => x.Uuid == loginToken.Uuid).FirstOrDefault() ?? (repository.Where<Account>(x => x.Name == loginToken.Name).FirstOrDefault() ?? CreateAccount(loginToken, repository));
account.LoadNavigationProperties(repository.Context);
account.LastLogin = DateTime.Now.Ticks;
if (String.IsNullOrEmpty(account.Uuid))
account.Uuid = loginToken.Uuid;
// Expire punishments
if (account.Punishments != null)
{
expiredPunishment.Active = false;
foreach (var expiredPunishment in account.Punishments.Where(x => x.Active && (x.Duration - 0d) > 0 && TimeUtil.GetCurrentMilliseconds() > (x.Time + (x.Duration * 3600000))))
{
expiredPunishment.Active = false;
}
}
// Insert UUID if not there
if (String.IsNullOrEmpty(account.Uuid) && !String.IsNullOrEmpty(loginToken.Uuid))
{
account.Uuid = loginToken.Uuid;
}
// Update account name if changed
if (!String.Equals(account.Name, loginToken.Name))
{
account.Name = loginToken.Name;
}
/*
// Expire ranks
if ((account.Rank.Name == "ULTRA" || account.Rank.Name == "HERO") && !account.RankPerm && DateTime.Now.CompareTo(account.RankExpire) >= 0)
{
account.Rank = repository.Where<Rank>(x => x.Name == "ALL").First();
repository.Attach(account.Rank);
}
* */
repository.CommitChanges();
return account;
}
// Insert UUID if not there
if (String.IsNullOrEmpty(account.Uuid) && !String.IsNullOrEmpty(loginToken.Uuid))
{
account.Uuid = loginToken.Uuid;
}
// Update account name if changed
if (!String.Equals(account.Name, loginToken.Name))
{
account.Name = loginToken.Name;
}
/*
// Expire ranks
if ((account.Rank.Name == "ULTRA" || account.Rank.Name == "HERO") && !account.RankPerm && DateTime.Now.CompareTo(account.RankExpire) >= 0)
{
account.Rank = repository.Where<Rank>(x => x.Name == "ALL").First();
repository.Attach(account.Rank);
}
* */
repository.CommitChanges();
return account;
}
}
@ -194,31 +214,33 @@
public bool GemReward(GemRewardToken token)
{
using (var repository = _repositoryFactory.CreateRepository())
lock (getAccountLock(token.Name))
{
var account = repository.Where<Account>(x => x.Name == token.Name).FirstOrDefault();
account.LoadNavigationProperties(repository.Context);
if (account == null)
return false;
account.Gems += token.Amount;
if (!token.Source.Contains("Earned") && !token.Source.Contains("Tutorial") && !token.Source.Contains("Parkour"))
using (var repository = _repositoryFactory.CreateRepository())
{
var gemTransaction = new GemTransaction
{
Source = token.Source,
Account = account,
Amount = token.Amount,
Date = (long)TimeUtil.GetCurrentMilliseconds()
};
var account = repository.Where<Account>(x => x.Name == token.Name).FirstOrDefault();
repository.Add<GemTransaction>(gemTransaction);
if (account == null)
return false;
account.Gems += token.Amount;
if (!token.Source.Contains("Earned") && !token.Source.Contains("Tutorial") && !token.Source.Contains("Parkour"))
{
var gemTransaction = new GemTransaction
{
Source = token.Source,
Account = account,
Amount = token.Amount,
Date = (long)TimeUtil.GetCurrentMilliseconds()
};
repository.Add<GemTransaction>(gemTransaction);
}
repository.Edit(account);
repository.CommitChanges();
}
repository.Edit(account);
repository.CommitChanges();
}
return true;
@ -226,31 +248,35 @@
public bool CoinReward(GemRewardToken token)
{
using (var repository = _repositoryFactory.CreateRepository())
lock (getAccountLock(token.Name))
{
var account = repository.Where<Account>(x => x.Name == token.Name).FirstOrDefault();
account.LoadNavigationProperties(repository.Context);
if (account == null)
return false;
account.Coins += token.Amount;
if (!token.Source.Contains("Earned") && !token.Source.Contains("Tutorial") && !token.Source.Contains("Parkour"))
using (var repository = _repositoryFactory.CreateRepository())
{
var coinTransaction = new CoinTransaction
var account = repository.Where<Account>(x => x.Name == token.Name).FirstOrDefault();
if (account == null)
return false;
token.OriginalBalance = account.Coins;
account.Coins += token.Amount;
if (!token.Source.Contains("Earned") && !token.Source.Contains("Tutorial") && !token.Source.Contains("Parkour"))
{
Source = token.Source,
Account = account,
Amount = token.Amount,
Date = (long)TimeUtil.GetCurrentMilliseconds()
};
var coinTransaction = new CoinTransaction
{
Source = token.Source,
Account = account,
Amount = token.Amount,
Date = (long)TimeUtil.GetCurrentMilliseconds()
};
repository.Add<CoinTransaction>(coinTransaction);
}
repository.Edit(account);
repository.CommitChanges();
repository.Add<CoinTransaction>(coinTransaction);
}
repository.Edit(account);
repository.CommitChanges();
}
return true;
@ -358,9 +384,9 @@
public string PurchaseGameSalesPackage(PurchaseToken token)
{
lock (_transactionLock)
try
{
try
lock (getAccountLock(token.AccountName))
{
using (var repository = _repositoryFactory.CreateRepository())
{
@ -390,7 +416,7 @@
repository.Edit(account);
if (account.PvpTransactions == null)
account.PvpTransactions = new List<GameTransaction> {accountTransaction};
account.PvpTransactions = new List<GameTransaction> { accountTransaction };
else
{
account.PvpTransactions.Add(accountTransaction);
@ -403,10 +429,10 @@
return TransactionResponse.Success.ToString();
}
}
catch (Exception exception)
{
return TransactionResponse.Failed.ToString() + ":" + exception.Message;
}
}
catch (Exception exception)
{
return TransactionResponse.Failed.ToString() + ":" + exception.Message;
}
}
@ -420,43 +446,46 @@
public void SaveCustomBuild(CustomBuildToken token)
{
using (var repository = _repositoryFactory.CreateRepository())
lock (getAccountLock(token.PlayerName))
{
var account =
repository.Where<Account>(x => x.Name == token.PlayerName).Include(x => x.CustomBuilds).First();
var customBuild =
account.CustomBuilds.FirstOrDefault(
x => String.Equals(x.PvpClass, token.PvpClass) && x.CustomBuildNumber == token.CustomBuildNumber);
if (customBuild == null)
using (var repository = _repositoryFactory.CreateRepository())
{
customBuild = repository.Add(token.GetCustomBuild());
account.CustomBuilds.Add(customBuild);
}
else
{
token.UpdateCustomBuild(customBuild);
repository.Edit(customBuild);
}
var account =
repository.Where<Account>(x => x.Name == token.PlayerName).Include(x => x.CustomBuilds).First();
if (customBuild.Active)
{
foreach (
var otherClassBuild in
account.CustomBuilds.Where(
x =>
String.Equals(x.PvpClass, token.PvpClass) && x.CustomBuildNumber != customBuild.CustomBuildNumber)
.ToList())
var customBuild =
account.CustomBuilds.FirstOrDefault(
x => String.Equals(x.PvpClass, token.PvpClass) && x.CustomBuildNumber == token.CustomBuildNumber);
if (customBuild == null)
{
otherClassBuild.Active = false;
repository.Edit(otherClassBuild);
customBuild = repository.Add(token.GetCustomBuild());
account.CustomBuilds.Add(customBuild);
}
else
{
token.UpdateCustomBuild(customBuild);
repository.Edit(customBuild);
}
}
repository.Edit(account);
repository.CommitChanges();
if (customBuild.Active)
{
foreach (
var otherClassBuild in
account.CustomBuilds.Where(
x =>
String.Equals(x.PvpClass, token.PvpClass) && x.CustomBuildNumber != customBuild.CustomBuildNumber)
.ToList())
{
otherClassBuild.Active = false;
repository.Edit(otherClassBuild);
}
}
repository.Edit(account);
repository.CommitChanges();
}
}
}
@ -486,9 +515,9 @@
public string PurchaseUnknownSalesPackage(UnknownPurchaseToken token)
{
lock (_transactionLock)
try
{
try
lock (getAccountLock(token.AccountName))
{
using (var repository = _repositoryFactory.CreateRepository())
{
@ -532,16 +561,17 @@
return TransactionResponse.Success.ToString();
}
}
catch (Exception exception)
{
return TransactionResponse.Failed.ToString() + ":" + exception.Message;
}
}
catch (Exception exception)
{
return TransactionResponse.Failed.ToString() + ":" + exception.Message;
}
}
public string UpdateRank(RankUpdateToken token)
{
Rank rank = null;
var expire = DateTime.Now.AddMonths(1).AddMilliseconds(-DateTime.Now.Millisecond);
try
{
@ -559,8 +589,6 @@
if (rank == null)
return account.Rank.ToString();
var expire = DateTime.Now.AddMonths(1).AddMilliseconds(-DateTime.Now.Millisecond);
account.Rank = rank;
account.RankExpire = expire;
account.RankPerm = token.Perm;
@ -574,7 +602,14 @@
using (var repository = _repositoryFactory.CreateRepository())
{
var account = repository.Where<Account>(x => String.Equals(x.Name, token.Name)).Include(x => x.Rank).FirstOrDefault();
_logger.Log("INFO", "ACCOUNT " + account.Name + "'s rank is " + account.Rank.Name + " " + (account.RankPerm ? "Permanently" : "Monthly") + "." + " Rank expire : " + account.RankExpire.ToString());
if (token.Retries >= 3)
_logger.Log("ERROR", "Applying UpdateRank, retried 3 times and something didn't stick.");
else if (!account.Rank.Name.Equals(token.Rank) || account.RankPerm != token.Perm || account.RankExpire != expire)
{
token.Retries++;
UpdateRank(token);
}
}
}
catch (Exception ex)

File diff suppressed because it is too large Load Diff

Binary file not shown.