Lock a GTS offer when initiating a trade.

This commit is contained in:
Greg Edwards
2022-07-27 02:06:44 -04:00
parent f9a0a78172
commit 233e7feafa
5 changed files with 65 additions and 1 deletions

View File

@@ -443,6 +443,14 @@ namespace PkmnFoundations.GTS
return;
}
if (!Database.Instance.GtsLockPokemon4(targetPid, pid))
{
// failed to acquire lock, implying someone else beat us here. Say already traded.
SessionManager.Remove(session);
response.Write(new byte[] { 0x02, 0x00 }, 0, 2);
break;
}
// uncomment these two lines if you're replaying gamestats requests and need to skip the random token
//session = new GamestatsSession(this.GameId, this.Salt, pid, "/pokemondpds/worldexchange/exchange.asp");
//SessionManager.Add(session);

View File

@@ -406,6 +406,14 @@ namespace PkmnFoundations.GTS
return;
}
if (!Database.Instance.GtsLockPokemon5(targetPid, pid))
{
// failed to acquire lock, implying someone else beat us here. Say already traded.
SessionManager.Remove(session);
response.Write(new byte[] { 0x02, 0x00 }, 0, 2);
break;
}
object[] tag = new GtsRecord5[2];
tag[0] = upload;
tag[1] = result;

View File

@@ -349,6 +349,23 @@ namespace PkmnFoundations.Data
return true;
}
public override bool GtsLockPokemon4(int offer_pid, int partner_pid)
{
return WithTransaction(tran => GtsLockPokemon4(offer_pid, partner_pid));
}
public bool GtsLockPokemon4(MySqlTransaction tran, int offer_pid, int partner_pid)
{
DateTime now = DateTime.UtcNow;
int rows = tran.ExecuteNonQuery("UPDATE GtsPokemon4 SET LockedUntil = @locked_until, LockedBy = @locked_by " +
"WHERE LockedUntil < @now OR LockedUntil IS NULL",
new MySqlParameter("@locked_until", now.AddSeconds(GTS_LOCK_DURATION)),
new MySqlParameter("@locked_by", partner_pid),
new MySqlParameter("@now", now));
return rows > 0;
}
public override bool GtsTradePokemon4(GtsRecord4 upload, GtsRecord4 result, int partner_pid)
{
return WithTransactionSuccessful(tran => GtsTradePokemon4(tran, upload, result, partner_pid));
@@ -1520,6 +1537,23 @@ namespace PkmnFoundations.Data
return true;
}
public override bool GtsLockPokemon5(int offer_pid, int partner_pid)
{
return WithTransaction(tran => GtsLockPokemon5(offer_pid, partner_pid));
}
public bool GtsLockPokemon5(MySqlTransaction tran, int offer_pid, int partner_pid)
{
DateTime now = DateTime.UtcNow;
int rows = tran.ExecuteNonQuery("UPDATE GtsPokemon5 SET LockedUntil = @locked_until, LockedBy = @locked_by " +
"WHERE LockedUntil < @now OR LockedUntil IS NULL",
new MySqlParameter("@locked_until", now.AddSeconds(GTS_LOCK_DURATION)),
new MySqlParameter("@locked_by", partner_pid),
new MySqlParameter("@now", now));
return rows > 0;
}
public override bool GtsTradePokemon5(GtsRecord5 upload, GtsRecord5 result, int partner_pid)
{
return WithTransactionSuccessful(tran => GtsTradePokemon5(tran, upload, result, partner_pid));
@@ -2491,6 +2525,10 @@ namespace PkmnFoundations.Data
private void BattleVideoUpdateHypeTimes(MySqlTransaction tran, string tableName, DateTime hypeTime)
{
// todo: run this less often by caching the HypeTimestamp somewhere
// run it only once a week
// use cached timestamp for insertions/updates too
// common case: HypeTimestamp is in the past and needs to be updated and decay applied.
tran.ExecuteNonQuery("UPDATE " + tableName + " " +
"SET Hype = Hype / (1 << FLOOR(DATEDIFF(@hypetime, HypeTimestamp) / @decay)), HypeTimestamp = @hypetime " +

View File

@@ -96,6 +96,8 @@ namespace PkmnFoundations.Data
return dateNow.AddDays(-(int)dateNow.DayOfWeek);
}
internal const double GTS_LOCK_DURATION = 60.0d;
#endregion
#region GTS 4
@@ -108,6 +110,8 @@ namespace PkmnFoundations.Data
public abstract bool GtsDeletePokemon4(int pid);
public abstract bool GtsLockPokemon4(int offer_pid, int partner_pid);
public abstract bool GtsTradePokemon4(int pidSrc, int pidDest);
public abstract bool GtsTradePokemon4(GtsRecord4 upload, GtsRecord4 result, int partner_pid);
@@ -158,6 +162,8 @@ namespace PkmnFoundations.Data
public abstract bool GtsDeletePokemon5(int pid);
public abstract bool GtsLockPokemon5(int offer_pid, int partner_pid);
public abstract bool GtsTradePokemon5(int pidSrc, int pidDest);
public abstract bool GtsTradePokemon5(GtsRecord5 upload, GtsRecord5 result, int partner_pid);

View File

@@ -329,7 +329,9 @@ CREATE TABLE IF NOT EXISTS `GtsPokemon4` (
`IsExchanged` tinyint(3) unsigned NOT NULL,
`TrainerVersion` tinyint(3) unsigned NOT NULL,
`TrainerLanguage` tinyint(3) unsigned NOT NULL,
`ParseVersion` int(11) unsigned DEFAULT NULL,
`ParseVersion` int(10) unsigned DEFAULT NULL,
`LockedBy` int(11) DEFAULT NULL,
`LockedUntil` datetime DEFAULT NULL,
PRIMARY KEY (`id`),
KEY `pid` (`pid`),
KEY `Species` (`Species`),
@@ -368,6 +370,8 @@ CREATE TABLE IF NOT EXISTS `GtsPokemon5` (
`TrainerBadges` tinyint(3) unsigned NOT NULL,
`TrainerUnityTower` tinyint(3) unsigned NOT NULL,
`ParseVersion` int(10) unsigned DEFAULT NULL,
`LockedBy` int(11) DEFAULT NULL,
`LockedUntil` datetime DEFAULT NULL,
PRIMARY KEY (`id`),
KEY `pid` (`pid`),
KEY `Species` (`Species`),