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 Object _donorLock = new Object();
private NautHashMap<Player, NautHashMap<String, Integer>> _gemQueue = new NautHashMap<Player, NautHashMap<String, Integer>>(); 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) public DonationManager(JavaPlugin plugin, String webAddress)
{ {
@ -247,4 +248,56 @@ public class DonationManager extends MiniPlugin
} }
}, caller, name, uuid.toString(), amount); }, 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.setCancelled(true);
event.getItem().remove(); 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); 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.setCancelled(true);
event.getItem().remove(); 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); 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.setCancelled(true);
event.getItem().remove(); 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); event.getPlayer().getWorld().playSound(event.getPlayer().getLocation(), Sound.ORB_PICKUP, 1f, 2f);
} }

View File

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

View File

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

View File

@ -16,6 +16,7 @@
using LOC.Website.Common.Contexts; using LOC.Website.Common.Contexts;
using System.Data.Entity.Infrastructure; using System.Data.Entity.Infrastructure;
using System.Transactions; using System.Transactions;
using System.Runtime.CompilerServices;
public class AccountAdministrator : IAccountAdministrator public class AccountAdministrator : IAccountAdministrator
{ {
@ -23,7 +24,7 @@
private readonly IGameServerMonitor _gameServerMonitor; private readonly IGameServerMonitor _gameServerMonitor;
private readonly ILogger _logger; 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) 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) 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); using (var repository = _repositoryFactory.CreateRepository())
account.LoadNavigationProperties(repository.Context);
account.LastLogin = DateTime.Now.Ticks;
// Expire punishments
if (account.Punishments != null)
{ {
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) 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(); using (var repository = _repositoryFactory.CreateRepository())
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"))
{ {
var gemTransaction = new GemTransaction var account = repository.Where<Account>(x => x.Name == token.Name).FirstOrDefault();
{
Source = token.Source,
Account = account,
Amount = token.Amount,
Date = (long)TimeUtil.GetCurrentMilliseconds()
};
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; return true;
@ -226,31 +248,35 @@
public bool CoinReward(GemRewardToken token) 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(); using (var repository = _repositoryFactory.CreateRepository())
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"))
{ {
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, var coinTransaction = new CoinTransaction
Account = account, {
Amount = token.Amount, Source = token.Source,
Date = (long)TimeUtil.GetCurrentMilliseconds() 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; return true;
@ -358,9 +384,9 @@
public string PurchaseGameSalesPackage(PurchaseToken token) public string PurchaseGameSalesPackage(PurchaseToken token)
{ {
lock (_transactionLock) try
{ {
try lock (getAccountLock(token.AccountName))
{ {
using (var repository = _repositoryFactory.CreateRepository()) using (var repository = _repositoryFactory.CreateRepository())
{ {
@ -390,7 +416,7 @@
repository.Edit(account); repository.Edit(account);
if (account.PvpTransactions == null) if (account.PvpTransactions == null)
account.PvpTransactions = new List<GameTransaction> {accountTransaction}; account.PvpTransactions = new List<GameTransaction> { accountTransaction };
else else
{ {
account.PvpTransactions.Add(accountTransaction); account.PvpTransactions.Add(accountTransaction);
@ -403,10 +429,10 @@
return TransactionResponse.Success.ToString(); return TransactionResponse.Success.ToString();
} }
} }
catch (Exception exception) }
{ catch (Exception exception)
return TransactionResponse.Failed.ToString() + ":" + exception.Message; {
} return TransactionResponse.Failed.ToString() + ":" + exception.Message;
} }
} }
@ -420,43 +446,46 @@
public void SaveCustomBuild(CustomBuildToken token) public void SaveCustomBuild(CustomBuildToken token)
{ {
using (var repository = _repositoryFactory.CreateRepository()) lock (getAccountLock(token.PlayerName))
{ {
var account = using (var repository = _repositoryFactory.CreateRepository())
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)
{ {
customBuild = repository.Add(token.GetCustomBuild()); var account =
account.CustomBuilds.Add(customBuild); repository.Where<Account>(x => x.Name == token.PlayerName).Include(x => x.CustomBuilds).First();
}
else
{
token.UpdateCustomBuild(customBuild);
repository.Edit(customBuild);
}
if (customBuild.Active) var customBuild =
{ account.CustomBuilds.FirstOrDefault(
foreach ( x => String.Equals(x.PvpClass, token.PvpClass) && x.CustomBuildNumber == token.CustomBuildNumber);
var otherClassBuild in
account.CustomBuilds.Where( if (customBuild == null)
x =>
String.Equals(x.PvpClass, token.PvpClass) && x.CustomBuildNumber != customBuild.CustomBuildNumber)
.ToList())
{ {
otherClassBuild.Active = false; customBuild = repository.Add(token.GetCustomBuild());
repository.Edit(otherClassBuild); 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) public string PurchaseUnknownSalesPackage(UnknownPurchaseToken token)
{ {
lock (_transactionLock) try
{ {
try lock (getAccountLock(token.AccountName))
{ {
using (var repository = _repositoryFactory.CreateRepository()) using (var repository = _repositoryFactory.CreateRepository())
{ {
@ -532,16 +561,17 @@
return TransactionResponse.Success.ToString(); return TransactionResponse.Success.ToString();
} }
} }
catch (Exception exception) }
{ catch (Exception exception)
return TransactionResponse.Failed.ToString() + ":" + exception.Message; {
} return TransactionResponse.Failed.ToString() + ":" + exception.Message;
} }
} }
public string UpdateRank(RankUpdateToken token) public string UpdateRank(RankUpdateToken token)
{ {
Rank rank = null; Rank rank = null;
var expire = DateTime.Now.AddMonths(1).AddMilliseconds(-DateTime.Now.Millisecond);
try try
{ {
@ -559,8 +589,6 @@
if (rank == null) if (rank == null)
return account.Rank.ToString(); return account.Rank.ToString();
var expire = DateTime.Now.AddMonths(1).AddMilliseconds(-DateTime.Now.Millisecond);
account.Rank = rank; account.Rank = rank;
account.RankExpire = expire; account.RankExpire = expire;
account.RankPerm = token.Perm; account.RankPerm = token.Perm;
@ -574,7 +602,14 @@
using (var repository = _repositoryFactory.CreateRepository()) using (var repository = _repositoryFactory.CreateRepository())
{ {
var account = repository.Where<Account>(x => String.Equals(x.Name, token.Name)).Include(x => x.Rank).FirstOrDefault(); 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) catch (Exception ex)

File diff suppressed because it is too large Load Diff

Binary file not shown.