mirror of
https://github.com/mm201/pkmn-classic-framework.git
synced 2026-04-24 15:26:48 -05:00
Most stuff really works!
This commit is contained in:
parent
aa9e8942f9
commit
4716449659
|
|
@ -44,6 +44,8 @@
|
|||
</providers>
|
||||
</roleManager>
|
||||
|
||||
<customErrors mode="Off" />
|
||||
|
||||
</system.web>
|
||||
|
||||
<system.webServer>
|
||||
|
|
|
|||
|
|
@ -23,6 +23,7 @@
|
|||
<DefineConstants>DEBUG;TRACE</DefineConstants>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
<PlatformTarget>x86</PlatformTarget>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
|
||||
<DebugType>pdbonly</DebugType>
|
||||
|
|
|
|||
|
|
@ -4,6 +4,8 @@ using System.Linq;
|
|||
using System.Web;
|
||||
using System.Web.SessionState;
|
||||
using PokeFoundations.Data;
|
||||
using PokeFoundations.Structures;
|
||||
using PokeFoundations.Support;
|
||||
|
||||
namespace PokeFoundations.GTS
|
||||
{
|
||||
|
|
@ -90,19 +92,27 @@ namespace PokeFoundations.GTS
|
|||
switch (session.URL)
|
||||
{
|
||||
default:
|
||||
sessions.Remove(session.Hash);
|
||||
|
||||
// unrecognized page url
|
||||
// should be error 404 once we're done debugging
|
||||
context.Response.Write("Almost there. Your path is:\n");
|
||||
context.Response.Write(session.URL);
|
||||
return;
|
||||
|
||||
|
||||
// Called during startup. Unknown purpose.
|
||||
case "/worldexchange/info.asp":
|
||||
sessions.Remove(session.Hash);
|
||||
|
||||
// todo: find out the meaning of this request.
|
||||
// is it simply done to check whether the GTS is online?
|
||||
context.Response.OutputStream.Write(new byte[] { 0x01, 0x00 }, 0, 2);
|
||||
break;
|
||||
|
||||
|
||||
// Called during startup. Seems to contain trainer profile stats.
|
||||
case "/common/setProfile.asp":
|
||||
sessions.Remove(session.Hash);
|
||||
|
||||
// todo: Figure out what fun stuff is contained in this blob!
|
||||
|
||||
context.Response.OutputStream.Write(new byte[]
|
||||
|
|
@ -110,15 +120,264 @@ namespace PokeFoundations.GTS
|
|||
0, 8);
|
||||
break;
|
||||
|
||||
|
||||
// Called during startup and when you check your pokemon's status.
|
||||
case "/worldexchange/result.asp":
|
||||
{
|
||||
sessions.Remove(session.Hash);
|
||||
|
||||
/* After the above step(s) or performing any of
|
||||
* the tasks below other than searching, the game
|
||||
* makes a request to /pokemondpds/worldexchange/result.asp.
|
||||
* If the game has had a Pokémon sent to it via a trade,
|
||||
* the server responds with the entire encrypted Pokémon
|
||||
* save struct. Otherwise, if there is a Pokémon deposited
|
||||
* in the GTS, it responds with 0x0004; if not, it responds
|
||||
* with 0x0005. */
|
||||
|
||||
GtsDatagram4 datagram = DataAbstract.Instance.GtsDataForUser4(pid);
|
||||
|
||||
if (datagram == null)
|
||||
{
|
||||
|
||||
|
||||
// No pokemon in the system
|
||||
context.Response.OutputStream.Write(new byte[]
|
||||
{ 0x05, 0x00 }, 0, 2);
|
||||
}
|
||||
break;
|
||||
else if (datagram.IsExchanged > 0)
|
||||
{
|
||||
// traded pokemon arriving!!!
|
||||
context.Response.OutputStream.Write(datagram.Save(), 0, 292);
|
||||
}
|
||||
else
|
||||
{
|
||||
// my existing pokemon is in the system, untraded
|
||||
context.Response.OutputStream.Write(new byte[]
|
||||
{ 0x04, 0x00 }, 0, 2);
|
||||
}
|
||||
|
||||
} break;
|
||||
|
||||
// Called after result.asp returns 4 when you check your pokemon's status
|
||||
case "/worldexchange/get.asp":
|
||||
{
|
||||
sessions.Remove(session.Hash);
|
||||
|
||||
// this is only called if result.asp returned 4.
|
||||
// todo: what does this do if the contained pokemon is traded??
|
||||
|
||||
GtsDatagram4 datagram = DataAbstract.Instance.GtsDataForUser4(pid);
|
||||
|
||||
if (datagram == null)
|
||||
{
|
||||
// No pokemon in the system
|
||||
// what do here?
|
||||
}
|
||||
else
|
||||
{
|
||||
// just write the datagram whether traded or not...
|
||||
context.Response.OutputStream.Write(datagram.Save(), 0, 292);
|
||||
}
|
||||
} break;
|
||||
|
||||
// Called after result.asp returns an inbound pokemon datagram to delete it
|
||||
case "/worldexchange/delete.asp":
|
||||
{
|
||||
sessions.Remove(session.Hash);
|
||||
|
||||
GtsDatagram4 datagram = DataAbstract.Instance.GtsDataForUser4(pid);
|
||||
if (datagram == null)
|
||||
{
|
||||
context.Response.OutputStream.Write(new byte[]
|
||||
{ 0x00, 0x00 }, 0, 2);
|
||||
}
|
||||
else if (datagram.IsExchanged > 0)
|
||||
{
|
||||
// delete the arrived pokemon from the system
|
||||
// todo: add transactions
|
||||
// todo: log the successful trade?
|
||||
// (either here or when the trade is done)
|
||||
bool success = DataAbstract.Instance.GtsDeletePokemon4(pid);
|
||||
if (success)
|
||||
{
|
||||
context.Response.OutputStream.Write(new byte[] { 0x01, 0x00 }, 0, 2);
|
||||
}
|
||||
else
|
||||
{
|
||||
context.Response.OutputStream.Write(new byte[] { 0x00, 0x00 }, 0, 2);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// own pokemon is there, fail. Use return.asp instead.
|
||||
context.Response.OutputStream.Write(new byte[] { 0x00, 0x00 }, 0, 2);
|
||||
}
|
||||
} break;
|
||||
|
||||
// called to delete your own pokemon after taking it back
|
||||
case "/worldexchange/return.asp":
|
||||
{
|
||||
sessions.Remove(session.Hash);
|
||||
|
||||
GtsDatagram4 datagram = DataAbstract.Instance.GtsDataForUser4(pid);
|
||||
if (datagram == null)
|
||||
{
|
||||
context.Response.OutputStream.Write(new byte[]
|
||||
{ 0x00, 0x00 }, 0, 2);
|
||||
}
|
||||
else if (datagram.IsExchanged > 0)
|
||||
{
|
||||
// a traded pokemon is there, fail. Use delete.asp instead.
|
||||
context.Response.OutputStream.Write(new byte[] { 0x00, 0x00 }, 0, 2);
|
||||
}
|
||||
else
|
||||
{
|
||||
// delete own pokemon
|
||||
// todo: add transactions
|
||||
bool success = DataAbstract.Instance.GtsDeletePokemon4(pid);
|
||||
if (success)
|
||||
{
|
||||
context.Response.OutputStream.Write(new byte[] { 0x01, 0x00 }, 0, 2);
|
||||
}
|
||||
else
|
||||
{
|
||||
context.Response.OutputStream.Write(new byte[] { 0x00, 0x00 }, 0, 2);
|
||||
}
|
||||
}
|
||||
} break;
|
||||
|
||||
// Called when you deposit a pokemon into the system.
|
||||
case "/worldexchange/post.asp":
|
||||
{
|
||||
if (data.Length != 296)
|
||||
{
|
||||
Error400(context);
|
||||
return;
|
||||
}
|
||||
|
||||
// keep the datagram in memory while we wait for post_finish.asp request
|
||||
byte[] datagramBinary = new byte[292];
|
||||
Array.Copy(data, 4, datagramBinary, 0, 292);
|
||||
session.Tag = new GtsDatagram4(datagramBinary);
|
||||
// todo: delete any other post.asp sessions registered under this PID
|
||||
|
||||
context.Response.OutputStream.Write(new byte[] { 0x01, 0x00 }, 0, 2);
|
||||
|
||||
|
||||
} break;
|
||||
|
||||
case "/worldexchange/post_finish.asp":
|
||||
{
|
||||
sessions.Remove(session.Hash);
|
||||
|
||||
if (data.Length != 12)
|
||||
{
|
||||
Error400(context);
|
||||
return;
|
||||
}
|
||||
|
||||
// find a matching session which contains our datagram
|
||||
GtsSession4 prevSession = FindSession(sessions, pid, "/worldexchange/post.asp");
|
||||
|
||||
sessions.Remove(prevSession.Hash);
|
||||
AssertHelper.Assert(prevSession.Tag is GtsDatagram4);
|
||||
GtsDatagram4 datagram = (GtsDatagram4)prevSession.Tag;
|
||||
|
||||
if (DataAbstract.Instance.GtsDepositPokemon4(datagram))
|
||||
context.Response.OutputStream.Write(new byte[] { 0x01, 0x00 }, 0, 2);
|
||||
else
|
||||
context.Response.OutputStream.Write(new byte[] { 0x00, 0x00 }, 0, 2);
|
||||
|
||||
} break;
|
||||
|
||||
// the search request has a funny bit string request of search terms
|
||||
// and just returns a chunk of datagrams end to end.
|
||||
case "/worldexchange/search.asp":
|
||||
{
|
||||
sessions.Remove(session.Hash);
|
||||
|
||||
if (data.Length < 11 || data.Length > 12)
|
||||
{
|
||||
Error400(context);
|
||||
return;
|
||||
}
|
||||
|
||||
int resultsCount = (int)data[10];
|
||||
if (resultsCount < 1) break; // optimize away requests for no rows
|
||||
|
||||
ushort species = BitConverter.ToUInt16(data, 4);
|
||||
Genders gender = (Genders)data[6];
|
||||
byte minLevel = data[7];
|
||||
byte maxLevel = data[8];
|
||||
// byte 9 unknown
|
||||
byte country = 0;
|
||||
if (data.Length > 11) country = data[11];
|
||||
|
||||
if (resultsCount > 7) resultsCount = 7; // stop DDOS
|
||||
GtsDatagram4[] datagrams = DataAbstract.Instance.GtsSearch4(species, gender, minLevel, maxLevel, country, resultsCount);
|
||||
foreach (GtsDatagram4 datagram in datagrams)
|
||||
{
|
||||
context.Response.OutputStream.Write(datagram.Save(), 0, 292);
|
||||
}
|
||||
|
||||
} break;
|
||||
|
||||
// the exchange request uploads a datagram of the exchangee pokemon
|
||||
// plus the desired PID to trade for at the very end.
|
||||
case "/worldexchange/exchange.asp":
|
||||
{
|
||||
if (data.Length != 300)
|
||||
{
|
||||
Error400(context);
|
||||
return;
|
||||
}
|
||||
|
||||
byte[] uploadData = new byte[292];
|
||||
Array.Copy(data, 4, uploadData, 0, 292);
|
||||
GtsDatagram4 upload = new GtsDatagram4(uploadData);
|
||||
int targetPid = BitConverter.ToInt32(data, 296);
|
||||
GtsDatagram4 result = DataAbstract.Instance.GtsDataForUser4(targetPid);
|
||||
|
||||
// todo: maybe strong type this
|
||||
object[] tag = new object[2];
|
||||
tag[0] = upload;
|
||||
tag[1] = result;
|
||||
session.Tag = tag;
|
||||
|
||||
GtsDatagram4 tradedResult = result.Clone();
|
||||
tradedResult.FlagTraded(upload); // only real purpose is to generate a proper response
|
||||
|
||||
context.Response.OutputStream.Write(result.Save(), 0, 292);
|
||||
|
||||
} break;
|
||||
|
||||
case "/worldexchange/exchange_finish.asp":
|
||||
{
|
||||
sessions.Remove(session.Hash);
|
||||
|
||||
if (data.Length != 12)
|
||||
{
|
||||
Error400(context);
|
||||
return;
|
||||
}
|
||||
|
||||
// find a matching session which contains our datagram
|
||||
GtsSession4 prevSession = FindSession(sessions, pid, "/worldexchange/exchange.asp");
|
||||
|
||||
sessions.Remove(prevSession.Hash);
|
||||
AssertHelper.Assert(prevSession.Tag is object[]);
|
||||
object[] tag = (object[])prevSession.Tag;
|
||||
AssertHelper.Assert(tag.Length == 2);
|
||||
AssertHelper.Assert(tag[0] is GtsDatagram4);
|
||||
AssertHelper.Assert(tag[0] is GtsDatagram4);
|
||||
|
||||
GtsDatagram4 upload = (GtsDatagram4)tag[0];
|
||||
GtsDatagram4 result = (GtsDatagram4)tag[1];
|
||||
|
||||
if (DataAbstract.Instance.GtsTradePokemon4(upload, result))
|
||||
context.Response.OutputStream.Write(new byte[] { 0x01, 0x00 }, 0, 2);
|
||||
else
|
||||
context.Response.OutputStream.Write(new byte[] { 0x00, 0x00 }, 0, 2);
|
||||
|
||||
} break;
|
||||
}
|
||||
}
|
||||
else
|
||||
|
|
@ -139,5 +398,24 @@ namespace PokeFoundations.GTS
|
|||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private GtsSession4 FindSession(Dictionary<String, GtsSession4> sessions, int pid, String url)
|
||||
{
|
||||
GtsSession4 result = null;
|
||||
|
||||
foreach (GtsSession4 sess in sessions.Values)
|
||||
{
|
||||
if (sess.PID == pid && sess.URL == url)
|
||||
{
|
||||
if (result != null)
|
||||
{
|
||||
// todo: there's more than one matching session... delete them all.
|
||||
}
|
||||
return sess; // temp until I get it to cleanup old sessions
|
||||
result = sess;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -95,6 +95,12 @@ namespace PokeFoundations.GTS
|
|||
}
|
||||
}
|
||||
|
||||
public object Tag
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
public static String CreateToken()
|
||||
{
|
||||
if (m_rng == null) m_rng = new RNGCryptoServiceProvider();
|
||||
|
|
|
|||
|
|
@ -36,6 +36,9 @@ namespace PokeFoundations.Data
|
|||
public abstract bool GtsDeletePokemon4(int pid);
|
||||
|
||||
public abstract bool GtsTradePokemon4(int pidSrc, int pidDest);
|
||||
public abstract bool GtsTradePokemon4(GtsDatagram4 upload, GtsDatagram4 result);
|
||||
|
||||
public abstract GtsDatagram4[] GtsSearch4(ushort species, Genders gender, byte minLevel, byte maxLevel, byte country, int count);
|
||||
|
||||
#endregion
|
||||
}
|
||||
|
|
|
|||
|
|
@ -35,60 +35,73 @@ namespace PokeFoundations.Data
|
|||
#endregion
|
||||
|
||||
#region GTS
|
||||
public GtsDatagram4 GtsDataForUser4(MySqlTransaction tran, int pid)
|
||||
{
|
||||
MySqlDataReader reader = (MySqlDataReader)tran.ExecuteReader("SELECT Data, Species, Gender, Level, " +
|
||||
"RequestedSpecies, RequestedGender, RequestedMinLevel, RequestedMaxLevel, " +
|
||||
"Unknown1, TrainerGender, Unknown2, TimeDeposited, TimeWithdrawn, pid, " +
|
||||
"TrainerName, TrainerOT, TrainerCountry, TrainerRegion, TrainerClass, " +
|
||||
"IsExchanged, TrainerVersion, TrainerLanguage FROM GtsPokemon4 WHERE pid = @pid",
|
||||
new MySqlParameter("@pid", pid));
|
||||
|
||||
if (!reader.Read())
|
||||
{
|
||||
reader.Close();
|
||||
return null;
|
||||
}
|
||||
GtsDatagram4 result = Datagram4FromReader(reader);
|
||||
#if DEBUG
|
||||
AssertHelper.Equals(result.PID, pid);
|
||||
#endif
|
||||
reader.Close();
|
||||
return result;
|
||||
}
|
||||
|
||||
public override GtsDatagram4 GtsDataForUser4(int pid)
|
||||
{
|
||||
using (MySqlConnection db = CreateConnection())
|
||||
{
|
||||
db.Open();
|
||||
MySqlDataReader reader = (MySqlDataReader)db.ExecuteReader("SELECT Data, Species, Gender, Level, " +
|
||||
"RequestedSpecies, RequestedGender, RequestedMinLevel, RequestedMaxLevel, " +
|
||||
"Unknown1, TrainerGender, Unknown2, TimeDeposited, TimeWithdrawn, pid, " +
|
||||
"TrainerName, TrainerOT, TrainerCountry, TrainerRegion, TrainerClass, " +
|
||||
"IsExchanged, TrainerVersion, TrainerLanguage FROM GtsPokemon4 WHERE pid = @pid",
|
||||
new MySqlParameter("@pid", pid));
|
||||
|
||||
if (!reader.Read()) return null;
|
||||
GtsDatagram4 result = new GtsDatagram4();
|
||||
|
||||
byte[] data = new byte[236];
|
||||
reader.GetBytes(0, 0, data, 0, 236);
|
||||
result.Data = data;
|
||||
data = null;
|
||||
|
||||
result.Species = reader.GetUInt16(1);
|
||||
result.Gender = (Genders)reader.GetByte(2);
|
||||
result.Level = reader.GetByte(3);
|
||||
result.RequestedSpecies = reader.GetUInt16(4);
|
||||
result.RequestedGender = (Genders)reader.GetByte(5);
|
||||
result.RequestedMinLevel = reader.GetByte(6);
|
||||
result.RequestedMaxLevel = reader.GetByte(7);
|
||||
result.Unknown1 = reader.GetByte(8);
|
||||
result.TrainerGender = (GtsTrainerGenders)reader.GetByte(9);
|
||||
result.Unknown2 = reader.GetByte(10);
|
||||
result.TimeDeposited = reader.GetDateTime(11);
|
||||
result.TimeWithdrawn = reader.GetDateTime(12);
|
||||
result.PID = reader.GetInt32(13);
|
||||
|
||||
data = new byte[16];
|
||||
reader.GetBytes(14, 0, data, 0, 16);
|
||||
result.TrainerName = data;
|
||||
data = null;
|
||||
|
||||
result.TrainerOT = reader.GetUInt16(15);
|
||||
result.TrainerCountry = reader.GetByte(16);
|
||||
result.TrainerRegion = reader.GetByte(17);
|
||||
result.TrainerClass = reader.GetByte(18);
|
||||
result.IsExchanged = reader.GetByte(19);
|
||||
result.TrainerVersion = reader.GetByte(20);
|
||||
result.TrainerLanguage = reader.GetByte(21);
|
||||
#if DEBUG
|
||||
AssertHelper.Equals(result.PID, pid);
|
||||
#endif
|
||||
reader.Close();
|
||||
MySqlTransaction tran = db.BeginTransaction();
|
||||
GtsDatagram4 result = GtsDataForUser4(tran, pid);
|
||||
tran.Commit();
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
public bool GtsDepositPokemon4(MySqlTransaction tran, GtsDatagram4 datagram)
|
||||
{
|
||||
if (datagram.Data.Length != 236) throw new FormatException("pkm data must be 236 bytes.");
|
||||
if (datagram.TrainerName.Length != 16) throw new FormatException("Trainer name must be 16 bytes.");
|
||||
// note that IsTraded being true in the datagram is not an error condition
|
||||
// since it might have use later on. You should check for this in the upload handler.
|
||||
|
||||
long count = (long)tran.ExecuteScalar("SELECT Count(*) FROM GtsPokemon4 WHERE pid = @pid",
|
||||
new MySqlParameter("@pid", datagram.PID));
|
||||
|
||||
if (count > 0)
|
||||
{
|
||||
// This player already has a pokemon in the system.
|
||||
// we can possibly allow multiples under some future conditions
|
||||
return false;
|
||||
}
|
||||
|
||||
tran.ExecuteNonQuery("INSERT INTO GtsPokemon4 " +
|
||||
"(Data, Species, Gender, Level, RequestedSpecies, RequestedGender, " +
|
||||
"RequestedMinLevel, RequestedMaxLevel, Unknown1, TrainerGender, " +
|
||||
"Unknown2, TimeDeposited, TimeWithdrawn, pid, TrainerName, TrainerOT, " +
|
||||
"TrainerCountry, TrainerRegion, TrainerClass, IsExchanged, TrainerVersion, " +
|
||||
"TrainerLanguage) " +
|
||||
"VALUES (@Data, @Species, @Gender, @Level, @RequestedSpecies, " +
|
||||
"@RequestedGender, @RequestedMinLevel, @RequestedMaxLevel, @Unknown1, " +
|
||||
"@TrainerGender, @Unknown2, @TimeDeposited, @TimeWithdrawn, @pid, " +
|
||||
"@TrainerName, @TrainerOT, @TrainerCountry, @TrainerRegion, @TrainerClass, " +
|
||||
"@IsExchanged, @TrainerVersion, @TrainerLanguage)",
|
||||
ParamsFromDatagram4(datagram));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public override bool GtsDepositPokemon4(GtsDatagram4 datagram)
|
||||
{
|
||||
if (datagram.Data.Length != 236) throw new FormatException("pkm data must be 236 bytes.");
|
||||
|
|
@ -99,64 +112,14 @@ namespace PokeFoundations.Data
|
|||
using (MySqlConnection db = CreateConnection())
|
||||
{
|
||||
db.Open();
|
||||
|
||||
/*
|
||||
MySqlDataReader reader = (MySqlDataReader)db.ExecuteReader("SELECT Data, Species, Gender, Level, " +
|
||||
"RequestedSpecies, RequestedGender, RequestedMinLevel, RequestedMaxLevel, " +
|
||||
"Unknown1, TrainerGender, Unknown2, TimeDeposited, TimeWithdrawn, pid, " +
|
||||
"TrainerName, TrainerOT, TrainerCountry, TrainerRegion, TrainerClass, " +
|
||||
"IsExchanged, TrainerVersion, TrainerLanguage FROM GtsPokemon4 WHERE pid = @pid",
|
||||
new MySqlParameter("@pid", pid));
|
||||
*
|
||||
* */
|
||||
|
||||
MySqlTransaction tran = db.BeginTransaction();
|
||||
|
||||
long count = (long)tran.ExecuteScalar("SELECT Count(*) FROM GtsPokemon4 WHERE pid = @pid",
|
||||
new MySqlParameter("@pid", datagram.PID));
|
||||
|
||||
if (count > 0)
|
||||
if (!GtsDepositPokemon4(tran, datagram))
|
||||
{
|
||||
// This player already has a pokemon in the system.
|
||||
// we can possibly allow multiples under some future conditions
|
||||
tran.Rollback();
|
||||
return false;
|
||||
}
|
||||
|
||||
tran.ExecuteNonQuery("INSERT INTO GtsPokemon4 " +
|
||||
"(Data, Species, Gender, Level, RequestedSpecies, RequestedGender, " +
|
||||
"RequestedMinLevel, RequestedMaxLevel, Unknown1, TrainerGender, " +
|
||||
"Unknown2, TimeDeposited, TimeWithdrawn, pid, TrainerName, TrainerOT, " +
|
||||
"TrainerCountry, TrainerRegion, TrainerClass, IsExchanged, TrainerVersion, " +
|
||||
"TrainerLanguage) " +
|
||||
"VALUES (@Data, @Species, @Gender, @Level, @RequestedSpecies, " +
|
||||
"@RequestedGender, @RequestedMinLevel, @RequestedMaxLevel, @Unknown1, " +
|
||||
"@TrainerGender, @Unknown2, @TimeDeposited, @TimeWithdrawn, @pid, " +
|
||||
"@TrainerName, @TrainerOT, @TrainerCountry, @TrainerRegion, @TrainerClass, " +
|
||||
"@IsExchanged, @TrainerVersion, @TrainerLanguage)",
|
||||
new MySqlParameter("@Data", datagram.Data),
|
||||
new MySqlParameter("@Species", datagram.Species),
|
||||
new MySqlParameter("@Gender", (byte)datagram.Gender),
|
||||
new MySqlParameter("@Level", datagram.Level),
|
||||
new MySqlParameter("@RequestedSpecies", datagram.RequestedSpecies),
|
||||
new MySqlParameter("@RequestedGender", (byte)datagram.RequestedGender),
|
||||
new MySqlParameter("@RequestedMinLevel", datagram.RequestedMinLevel),
|
||||
new MySqlParameter("@RequestedMaxLevel", datagram.RequestedMaxLevel),
|
||||
new MySqlParameter("@Unknown1", datagram.Unknown1),
|
||||
new MySqlParameter("@TrainerGender", (byte)datagram.TrainerGender),
|
||||
new MySqlParameter("@Unknown2", datagram.Unknown2),
|
||||
new MySqlParameter("@TimeDeposited", datagram.TimeDeposited),
|
||||
new MySqlParameter("@TimeWithdrawn", datagram.TimeWithdrawn),
|
||||
new MySqlParameter("@pid", datagram.PID),
|
||||
new MySqlParameter("@TrainerName", datagram.TrainerName),
|
||||
new MySqlParameter("@TrainerOT", datagram.TrainerOT),
|
||||
new MySqlParameter("@TrainerCountry", datagram.TrainerCountry),
|
||||
new MySqlParameter("@TrainerRegion", datagram.TrainerRegion),
|
||||
new MySqlParameter("@TrainerClass", datagram.TrainerClass),
|
||||
new MySqlParameter("@IsExchanged", datagram.IsExchanged),
|
||||
new MySqlParameter("@TrainerVersion", datagram.TrainerVersion),
|
||||
new MySqlParameter("@TrainerLanguage", datagram.TrainerLanguage));
|
||||
|
||||
tran.Commit();
|
||||
return true;
|
||||
}
|
||||
|
|
@ -171,21 +134,29 @@ namespace PokeFoundations.Data
|
|||
return (int)o;
|
||||
}
|
||||
|
||||
public bool GtsDeletePokemon4(MySqlTransaction tran, int pid)
|
||||
{
|
||||
int pkmnId = GtsGetDepositId(pid, tran);
|
||||
if (pkmnId == 0) return false;
|
||||
|
||||
tran.ExecuteNonQuery("DELETE FROM GtsPokemon4 WHERE id = @id",
|
||||
new MySqlParameter("@id", pkmnId));
|
||||
return true;
|
||||
}
|
||||
|
||||
public override bool GtsDeletePokemon4(int pid)
|
||||
{
|
||||
using (MySqlConnection db = CreateConnection())
|
||||
{
|
||||
db.Open();
|
||||
MySqlTransaction tran = db.BeginTransaction();
|
||||
int pkmnId = GtsGetDepositId(pid, tran);
|
||||
if (pkmnId == 0)
|
||||
|
||||
if (!GtsDeletePokemon4(tran, pid))
|
||||
{
|
||||
tran.Rollback();
|
||||
return false;
|
||||
}
|
||||
|
||||
tran.ExecuteNonQuery("DELETE FROM GtsPokemon4 WHERE id = @id",
|
||||
new MySqlParameter("@id", pkmnId));
|
||||
|
||||
tran.Commit();
|
||||
return true;
|
||||
}
|
||||
|
|
@ -193,9 +164,174 @@ namespace PokeFoundations.Data
|
|||
|
||||
public override bool GtsTradePokemon4(int pidSrc, int pidDest)
|
||||
{
|
||||
// not needed yet.
|
||||
return false;
|
||||
}
|
||||
|
||||
public override bool GtsTradePokemon4(GtsDatagram4 upload, GtsDatagram4 result)
|
||||
{
|
||||
GtsDatagram4 traded = upload.Clone();
|
||||
traded.FlagTraded(result);
|
||||
|
||||
using (MySqlConnection db = CreateConnection())
|
||||
{
|
||||
db.Open();
|
||||
MySqlTransaction tran = db.BeginTransaction();
|
||||
|
||||
GtsDatagram4 resultOrig = GtsDataForUser4(tran, result.PID);
|
||||
if (resultOrig == null || resultOrig != result)
|
||||
{
|
||||
// looks like the pokemon was ninja'd between the Exchange and Exchange_finish
|
||||
tran.Rollback();
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!GtsDeletePokemon4(tran, result.PID))
|
||||
{
|
||||
tran.Rollback();
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!GtsDepositPokemon4(tran, traded))
|
||||
{
|
||||
tran.Rollback();
|
||||
return false;
|
||||
}
|
||||
|
||||
tran.Commit();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
public override GtsDatagram4[] GtsSearch4(ushort species, Genders gender, byte minLevel, byte maxLevel, byte country, int count)
|
||||
{
|
||||
using (MySqlConnection db = CreateConnection())
|
||||
{
|
||||
List<MySqlParameter> _params = new List<MySqlParameter>();
|
||||
String where = "WHERE Species = @species";
|
||||
_params.Add(new MySqlParameter("@species", species));
|
||||
|
||||
if (gender != Genders.Either)
|
||||
{
|
||||
where += " AND Gender = @gender";
|
||||
_params.Add(new MySqlParameter("@gender", (byte)gender));
|
||||
}
|
||||
|
||||
if (minLevel > 0 && maxLevel > 0)
|
||||
{
|
||||
where += " AND Level BETWEEN @min_level AND @max_level";
|
||||
_params.Add(new MySqlParameter("@min_level", minLevel));
|
||||
_params.Add(new MySqlParameter("@max_level", maxLevel));
|
||||
}
|
||||
else if (minLevel > 0)
|
||||
{
|
||||
where += " AND Level >= @min_level";
|
||||
_params.Add(new MySqlParameter("@min_level", minLevel));
|
||||
}
|
||||
else if (maxLevel > 0)
|
||||
{
|
||||
where += " AND Level <= @max_level";
|
||||
_params.Add(new MySqlParameter("@max_level", maxLevel));
|
||||
}
|
||||
|
||||
if (country > 0)
|
||||
{
|
||||
where += " AND TrainerCountry = @country";
|
||||
_params.Add(new MySqlParameter("@country", country));
|
||||
}
|
||||
|
||||
_params.Add(new MySqlParameter("@count", count));
|
||||
|
||||
db.Open();
|
||||
// todo: sort me in creative ways
|
||||
MySqlDataReader reader = (MySqlDataReader)db.ExecuteReader("SELECT Data, Species, Gender, Level, " +
|
||||
"RequestedSpecies, RequestedGender, RequestedMinLevel, RequestedMaxLevel, " +
|
||||
"Unknown1, TrainerGender, Unknown2, TimeDeposited, TimeWithdrawn, pid, " +
|
||||
"TrainerName, TrainerOT, TrainerCountry, TrainerRegion, TrainerClass, " +
|
||||
"IsExchanged, TrainerVersion, TrainerLanguage FROM GtsPokemon4 " + where +
|
||||
" ORDER BY TimeDeposited DESC LIMIT @count",
|
||||
_params.ToArray());
|
||||
|
||||
List<GtsDatagram4> datagrams = new List<GtsDatagram4>(count);
|
||||
|
||||
while (reader.Read())
|
||||
{
|
||||
datagrams.Add(Datagram4FromReader(reader));
|
||||
}
|
||||
|
||||
return datagrams.ToArray();
|
||||
}
|
||||
}
|
||||
|
||||
private static GtsDatagram4 Datagram4FromReader(MySqlDataReader reader)
|
||||
{
|
||||
GtsDatagram4 result = new GtsDatagram4();
|
||||
|
||||
byte[] data = new byte[236];
|
||||
reader.GetBytes(0, 0, data, 0, 236);
|
||||
result.Data = data;
|
||||
data = null;
|
||||
|
||||
result.Species = reader.GetUInt16(1);
|
||||
result.Gender = (Genders)reader.GetByte(2);
|
||||
result.Level = reader.GetByte(3);
|
||||
result.RequestedSpecies = reader.GetUInt16(4);
|
||||
result.RequestedGender = (Genders)reader.GetByte(5);
|
||||
result.RequestedMinLevel = reader.GetByte(6);
|
||||
result.RequestedMaxLevel = reader.GetByte(7);
|
||||
result.Unknown1 = reader.GetByte(8);
|
||||
result.TrainerGender = (GtsTrainerGenders)reader.GetByte(9);
|
||||
result.Unknown2 = reader.GetByte(10);
|
||||
result.TimeDeposited = reader.GetDateTime(11);
|
||||
result.TimeWithdrawn = reader.GetDateTime(12);
|
||||
result.PID = reader.GetInt32(13);
|
||||
|
||||
data = new byte[16];
|
||||
reader.GetBytes(14, 0, data, 0, 16);
|
||||
result.TrainerName = data;
|
||||
data = null;
|
||||
|
||||
result.TrainerOT = reader.GetUInt16(15);
|
||||
result.TrainerCountry = reader.GetByte(16);
|
||||
result.TrainerRegion = reader.GetByte(17);
|
||||
result.TrainerClass = reader.GetByte(18);
|
||||
result.IsExchanged = reader.GetByte(19);
|
||||
result.TrainerVersion = reader.GetByte(20);
|
||||
result.TrainerLanguage = reader.GetByte(21);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private static MySqlParameter[] ParamsFromDatagram4(GtsDatagram4 datagram)
|
||||
{
|
||||
MySqlParameter[] result = new MySqlParameter[22];
|
||||
|
||||
result[0] = new MySqlParameter("@Data", datagram.Data);
|
||||
result[1] = new MySqlParameter("@Species", datagram.Species);
|
||||
result[2] = new MySqlParameter("@Gender", (byte)datagram.Gender);
|
||||
result[3] = new MySqlParameter("@Level", datagram.Level);
|
||||
result[4] = new MySqlParameter("@RequestedSpecies", datagram.RequestedSpecies);
|
||||
result[5] = new MySqlParameter("@RequestedGender", (byte)datagram.RequestedGender);
|
||||
result[6] = new MySqlParameter("@RequestedMinLevel", datagram.RequestedMinLevel);
|
||||
result[7] = new MySqlParameter("@RequestedMaxLevel", datagram.RequestedMaxLevel);
|
||||
result[8] = new MySqlParameter("@Unknown1", datagram.Unknown1);
|
||||
result[9] = new MySqlParameter("@TrainerGender", (byte)datagram.TrainerGender);
|
||||
result[10] = new MySqlParameter("@Unknown2", datagram.Unknown2);
|
||||
result[11] = new MySqlParameter("@TimeDeposited", datagram.TimeDeposited);
|
||||
result[12] = new MySqlParameter("@TimeWithdrawn", datagram.TimeWithdrawn);
|
||||
result[13] = new MySqlParameter("@pid", datagram.PID);
|
||||
result[14] = new MySqlParameter("@TrainerName", datagram.TrainerName);
|
||||
result[15] = new MySqlParameter("@TrainerOT", datagram.TrainerOT);
|
||||
result[16] = new MySqlParameter("@TrainerCountry", datagram.TrainerCountry);
|
||||
result[17] = new MySqlParameter("@TrainerRegion", datagram.TrainerRegion);
|
||||
result[18] = new MySqlParameter("@TrainerClass", datagram.TrainerClass);
|
||||
result[19] = new MySqlParameter("@IsExchanged", datagram.IsExchanged);
|
||||
result[20] = new MySqlParameter("@TrainerVersion", datagram.TrainerVersion);
|
||||
result[21] = new MySqlParameter("@TrainerLanguage", datagram.TrainerLanguage);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -15,6 +15,15 @@ namespace PokeFoundations.Structures
|
|||
[Serializable()]
|
||||
public class GtsDatagram4
|
||||
{
|
||||
public GtsDatagram4()
|
||||
{
|
||||
}
|
||||
|
||||
public GtsDatagram4(byte[] data)
|
||||
{
|
||||
Load(data);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Obfuscated Pokémon (pkm) data. 236 bytes
|
||||
/// </summary>
|
||||
|
|
@ -48,8 +57,8 @@ namespace PokeFoundations.Structures
|
|||
public GtsTrainerGenders TrainerGender;
|
||||
public byte Unknown2;
|
||||
|
||||
public DateTime TimeDeposited;
|
||||
public DateTime TimeWithdrawn;
|
||||
public DateTime ? TimeDeposited;
|
||||
public DateTime ? TimeWithdrawn;
|
||||
|
||||
/// <summary>
|
||||
/// User ID of the player (not Personality Value)
|
||||
|
|
@ -90,8 +99,8 @@ namespace PokeFoundations.Structures
|
|||
s.WriteByte(Unknown1);
|
||||
s.WriteByte((byte)TrainerGender);
|
||||
s.WriteByte(Unknown2);
|
||||
s.Write(BitConverter.GetBytes(TimeDeposited.ToBinary()), 0, 8);
|
||||
s.Write(BitConverter.GetBytes(TimeWithdrawn.ToBinary()), 0, 8);
|
||||
s.Write(BitConverter.GetBytes(DateToTimestamp(TimeDeposited)), 0, 8);
|
||||
s.Write(BitConverter.GetBytes(DateToTimestamp(TimeWithdrawn)), 0, 8);
|
||||
s.Write(BitConverter.GetBytes(PID), 0, 4);
|
||||
s.Write(TrainerName, 0, 0x10);
|
||||
s.Write(BitConverter.GetBytes(TrainerOT), 0, 2);
|
||||
|
|
@ -121,10 +130,8 @@ namespace PokeFoundations.Structures
|
|||
Unknown1 = data[0xF5];
|
||||
TrainerGender = (GtsTrainerGenders)data[0xF6];
|
||||
Unknown2 = data[0xF7];
|
||||
// todo: Figure out what the correct binary storage format is for
|
||||
// official GTS timestamps
|
||||
TimeDeposited = DateTime.FromBinary(BitConverter.ToInt64(data, 0xF8));
|
||||
TimeWithdrawn = DateTime.FromBinary(BitConverter.ToInt64(data, 0x100));
|
||||
TimeDeposited = TimestampToDate(BitConverter.ToUInt64(data, 0xF8));
|
||||
TimeWithdrawn = TimestampToDate(BitConverter.ToUInt64(data, 0x100));
|
||||
PID = BitConverter.ToInt32(data, 0x108);
|
||||
TrainerName = new byte[0x10];
|
||||
Array.Copy(data, 0x10C, TrainerName, 0, 0x10);
|
||||
|
|
@ -136,5 +143,68 @@ namespace PokeFoundations.Structures
|
|||
TrainerVersion = data[0x122];
|
||||
TrainerLanguage = data[0x123];
|
||||
}
|
||||
|
||||
public GtsDatagram4 Clone()
|
||||
{
|
||||
// todo: I am not very efficient
|
||||
return new GtsDatagram4(Save());
|
||||
}
|
||||
|
||||
public void FlagTraded(GtsDatagram4 other)
|
||||
{
|
||||
Species = other.Species;
|
||||
Gender = other.Gender;
|
||||
Level = other.Level;
|
||||
RequestedSpecies = other.RequestedSpecies;
|
||||
RequestedGender = other.RequestedGender;
|
||||
RequestedMinLevel = other.RequestedMinLevel;
|
||||
RequestedMaxLevel = other.RequestedMaxLevel;
|
||||
TimeDeposited = other.TimeDeposited;
|
||||
TimeWithdrawn = DateTime.Now; // figure out where this really comes from. It seems to psychically know the player's timezone
|
||||
PID = other.PID;
|
||||
IsExchanged = 0x01;
|
||||
}
|
||||
|
||||
public static DateTime ? TimestampToDate(ulong timestamp)
|
||||
{
|
||||
if (timestamp == 0) return null;
|
||||
|
||||
ushort year = (ushort)((timestamp >> 0x30) & 0xffff);
|
||||
byte month = (byte)((timestamp >> 0x28) & 0xff);
|
||||
byte day = (byte)((timestamp >> 0x20) & 0xff);
|
||||
byte hour = (byte)((timestamp >> 0x18) & 0xff);
|
||||
byte minute = (byte)((timestamp >> 0x10) & 0xff);
|
||||
byte second = (byte)((timestamp >> 0x08) & 0xff);
|
||||
//byte fractional = (byte)(timestamp & 0xff);
|
||||
|
||||
// allow ArgumentOutOfRangeExceptions to escape
|
||||
return new DateTime(year, month, day, hour, minute, second);
|
||||
}
|
||||
|
||||
public static ulong DateToTimestamp(DateTime ? date)
|
||||
{
|
||||
if (date == null) return 0;
|
||||
DateTime date2 = (DateTime)date;
|
||||
|
||||
return (ulong)(date2.Year & 0xffff) << 0x30
|
||||
| (ulong)(date2.Month & 0xff) << 0x28
|
||||
| (ulong)(date2.Day & 0xff) << 0x20
|
||||
| (ulong)(date2.Hour & 0xff) << 0x18
|
||||
| (ulong)(date2.Minute & 0xff) << 0x10
|
||||
| (ulong)(date2.Second & 0xff) << 0x08;
|
||||
}
|
||||
|
||||
public static bool operator ==(GtsDatagram4 a, GtsDatagram4 b)
|
||||
{
|
||||
if ((object)a == null && (object)b == null) return true;
|
||||
if ((object)a == null || (object)b == null) return false;
|
||||
// todo: optimize me
|
||||
return a.Save().SequenceEqual(b.Save());
|
||||
}
|
||||
|
||||
public static bool operator !=(GtsDatagram4 a, GtsDatagram4 b)
|
||||
{
|
||||
return !(a == b);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user