From 4254225462da2bec923bd68f375edffd39f8f9b2 Mon Sep 17 00:00:00 2001 From: Jonathan Williams Date: Wed, 5 Nov 2014 14:42:11 -0800 Subject: [PATCH] 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. --- .../core/donation/DonationManager.java | 53 ++ .../core/gadget/gadgets/ItemCoinBomb.java | 2 +- .../core/gadget/gadgets/ItemGemBomb.java | 2 +- .../core/gadget/gadgets/MorphVillager.java | 2 +- .../LOC.Core/Tokens/Client/GemRewardToken.cs | 4 + .../LOC.Core/Tokens/Client/RankUpdateToken.cs | 2 + .../Models/AccountAdministrator.cs | 281 ++++++----- .../LOC.Website.Web.Publish.xml | 468 +++++++++--------- Website/LOCWebsite.suo | Bin 474624 -> 474624 bytes 9 files changed, 454 insertions(+), 360 deletions(-) diff --git a/Plugins/Mineplex.Core/src/mineplex/core/donation/DonationManager.java b/Plugins/Mineplex.Core/src/mineplex/core/donation/DonationManager.java index 2d9f69b77..95ae0a980 100644 --- a/Plugins/Mineplex.Core/src/mineplex/core/donation/DonationManager.java +++ b/Plugins/Mineplex.Core/src/mineplex/core/donation/DonationManager.java @@ -28,6 +28,7 @@ public class DonationManager extends MiniPlugin private Object _donorLock = new Object(); private NautHashMap> _gemQueue = new NautHashMap>(); + private NautHashMap> _coinQueue = new NautHashMap>(); 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()); + + 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(); + } } diff --git a/Plugins/Mineplex.Core/src/mineplex/core/gadget/gadgets/ItemCoinBomb.java b/Plugins/Mineplex.Core/src/mineplex/core/gadget/gadgets/ItemCoinBomb.java index 5d3dc54dd..ce4f2e781 100644 --- a/Plugins/Mineplex.Core/src/mineplex/core/gadget/gadgets/ItemCoinBomb.java +++ b/Plugins/Mineplex.Core/src/mineplex/core/gadget/gadgets/ItemCoinBomb.java @@ -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); diff --git a/Plugins/Mineplex.Core/src/mineplex/core/gadget/gadgets/ItemGemBomb.java b/Plugins/Mineplex.Core/src/mineplex/core/gadget/gadgets/ItemGemBomb.java index 6c0320d14..0bc53e215 100644 --- a/Plugins/Mineplex.Core/src/mineplex/core/gadget/gadgets/ItemGemBomb.java +++ b/Plugins/Mineplex.Core/src/mineplex/core/gadget/gadgets/ItemGemBomb.java @@ -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); } diff --git a/Plugins/Mineplex.Core/src/mineplex/core/gadget/gadgets/MorphVillager.java b/Plugins/Mineplex.Core/src/mineplex/core/gadget/gadgets/MorphVillager.java index 6cf022a13..3b26c7338 100644 --- a/Plugins/Mineplex.Core/src/mineplex/core/gadget/gadgets/MorphVillager.java +++ b/Plugins/Mineplex.Core/src/mineplex/core/gadget/gadgets/MorphVillager.java @@ -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); } diff --git a/Website/LOC.Core/Tokens/Client/GemRewardToken.cs b/Website/LOC.Core/Tokens/Client/GemRewardToken.cs index a34878748..ff796a453 100644 --- a/Website/LOC.Core/Tokens/Client/GemRewardToken.cs +++ b/Website/LOC.Core/Tokens/Client/GemRewardToken.cs @@ -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; } } } \ No newline at end of file diff --git a/Website/LOC.Core/Tokens/Client/RankUpdateToken.cs b/Website/LOC.Core/Tokens/Client/RankUpdateToken.cs index 088542d34..f52a90207 100644 --- a/Website/LOC.Core/Tokens/Client/RankUpdateToken.cs +++ b/Website/LOC.Core/Tokens/Client/RankUpdateToken.cs @@ -7,5 +7,7 @@ public string Rank { get; set; } public bool Perm { get; set; } + + public int Retries { get; set; } } } diff --git a/Website/LOC.Website.Common/Models/AccountAdministrator.cs b/Website/LOC.Website.Common/Models/AccountAdministrator.cs index 4f94fd15d..a95d73dad 100644 --- a/Website/LOC.Website.Common/Models/AccountAdministrator.cs +++ b/Website/LOC.Website.Common/Models/AccountAdministrator.cs @@ -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 _accountLocks = new ConditionalWeakTable(); 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(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(x => x.Uuid == loginToken.Uuid).FirstOrDefault() ?? (repository.Where(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(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(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(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(x => x.Name == token.Name).FirstOrDefault(); - repository.Add(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); + } + + 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(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(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); + } + + repository.Edit(account); + repository.CommitChanges(); - repository.Add(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 {accountTransaction}; + account.PvpTransactions = new List { 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(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(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(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) diff --git a/Website/LOC.Website.Web/LOC.Website.Web.Publish.xml b/Website/LOC.Website.Web/LOC.Website.Web.Publish.xml index 317457180..23627865d 100644 --- a/Website/LOC.Website.Web/LOC.Website.Web.Publish.xml +++ b/Website/LOC.Website.Web/LOC.Website.Web.Publish.xml @@ -1,12 +1,12 @@  - + @@ -19,17 +19,14 @@ - - + - - @@ -43,26 +40,26 @@ - + + - - + - - + + @@ -73,33 +70,31 @@ - - + - - + + - + + - - - + @@ -107,18 +102,19 @@ - + - + + - + @@ -127,21 +123,20 @@ - + - - + - + + - - + @@ -152,48 +147,54 @@ - + - + + + + + + - + + - + - - + - + + + - - + @@ -202,7 +203,7 @@ - + @@ -210,64 +211,61 @@ - - + + - + - + - + + - + - + - - + - - + + - + - + - + - - + + - - + + - - + - - @@ -281,49 +279,51 @@ - + + + + + - + - + + - - + - - - + @@ -334,30 +334,28 @@ + - - + + - - + - - @@ -365,41 +363,43 @@ - - - + + - + - - + + - + + + - + + @@ -414,123 +414,121 @@ - - - - + - - + - + + - + - + - + + - + - - + + - + - + - - + + - - + + - + - - + + + - + - + + - - + + - + - - + + - + - - - - - + + + + + - + - - @@ -546,20 +544,22 @@ - + + + + - @@ -567,26 +567,26 @@ - - - + + - + + - + @@ -597,20 +597,22 @@ + - + + - + @@ -618,59 +620,54 @@ - + - - + + - + - - - - - - + - - - + - + - - + + + - + + @@ -679,29 +676,28 @@ - - - - + - - + - - - + + + + + + @@ -709,91 +705,93 @@ - + + - + + - + - - + + - + - + - + - - + + + - + - + - + + - - + + - + - - + + - + - - - - - + + + + + - + - - @@ -809,20 +807,22 @@ - + + + + - @@ -830,26 +830,26 @@ - - - + + + - + @@ -860,20 +860,22 @@ + - + + - + @@ -881,7 +883,7 @@ - + @@ -889,51 +891,46 @@ - + - - - - - - + - - - + - + - - + + + - + + @@ -942,29 +939,28 @@ - - - - + - - + - + + + + @@ -972,84 +968,88 @@ - + + - + + - + - - + + - + - + - + - + + - + - + + - - + + - + - - + + - + - - - - - + + + + + - + \ No newline at end of file diff --git a/Website/LOCWebsite.suo b/Website/LOCWebsite.suo index 242dfc94b7f30d951ffa8e2a9153c23cf47b2382..0f4dc2361f5354abbd804ac90862ca71ec3e9584 100644 GIT binary patch delta 462 zcmZqpBh&ClWWS zK+Dr%4p?5|&Bnso1$6zs>6MdMHK!~6WLDxx1WISZlx+Xv4>V`GKsl2X|B~zrFaPU> zzU|O5h)c0rIohnEzy|7u8 zPaRVkyO}0#cL-u@X5nN6hTH!vm=&x~fezRNb-)j1MNnw5UI&Uzog65lJUKx`WO{-S Mv*`At;cV8=0D1_S(EtDd delta 374 zcmZqpBh&ClWL>`nGuMYfS7svm(46D865wiV7q|~s|F*_b#?}Z8$kRIC_FtymeporNy0Wc z))O(4FRsv;{-ui5pOsM+Xg-)*Qq3B`VFl#b0WsLN?O&={pYTkN(qU8C{-=X=LM7w> z>Ag&B;;bO^{%tH&X54;;nQaTxbcX^~`RNOGGAc~hwqjG6u4up}yNsR9ar>c-Z1Whm z&+}wcWdu2R`WG)Ye@=#Ep!w-AJC>Jtv$3#V13Gfw^q-ShHK!}7uqtsR0;MxyO16LT zXX{~PoH5<;JZtInb5|Kvrc0e+Qek3X+wKs=*31Gjn)NBrgiVtJMU*Eeh=@#25Mmac ael(oT8pMNR0~U_S2e$HTzYxx5@C*Pju7bk=