mirror of
https://github.com/mm201/pkmn-classic-framework.git
synced 2026-04-25 16:20:08 -05:00
Implemented TrainerRankingsPerformRollover. (more to come)
This commit is contained in:
parent
8ca024e63c
commit
aa0bba7be5
|
|
@ -424,13 +424,7 @@ namespace PkmnFoundations.GlobalTerminalService
|
|||
// Including more than 3 RecordTypes in the response will give error 10609.
|
||||
|
||||
var submission = new TrainerRankingsSubmission(pid, data, 0x140);
|
||||
if (!Database.Instance.TrainerRankingsSubmit(submission))
|
||||
{
|
||||
logEntry.AppendLine("Submission error on database end.");
|
||||
type = EventLogEntryType.Error;
|
||||
response.Write(new byte[] { 0x02, 0x00 }, 0, 2);
|
||||
break;
|
||||
}
|
||||
Database.Instance.TrainerRankingsSubmit(submission);
|
||||
|
||||
response.Write(new byte[] { 0x00, 0x00 }, 0, 2);
|
||||
|
||||
|
|
|
|||
|
|
@ -2641,7 +2641,95 @@ namespace PkmnFoundations.Data
|
|||
|
||||
public override bool TrainerRankingsPerformRollover()
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
return WithTransaction(tran => TrainerRankingsPerformRollover(tran));
|
||||
}
|
||||
|
||||
public bool TrainerRankingsPerformRollover(MySqlTransaction tran)
|
||||
{
|
||||
// Use one single date for this transaction to avoid any possible inconsistencies.
|
||||
// An inconsistency here could lead to creating an extra leaderboard for the past week.
|
||||
DateTime now = DateTime.UtcNow;
|
||||
|
||||
// 1. Check if current leaderboard is still valid.
|
||||
// It's best to just check that a future end date is in existence.
|
||||
// Adding more checks, e.g. start < now < end, could allow things to get funny if the clock changes.
|
||||
// Just use the futuremost leaderboard as the current one, and start a new one if we think that leaderboard is in the past.
|
||||
DataTable tblLastReport = tran.ExecuteDataTable(
|
||||
"SELECT report_id, StartDate, EndDate FROM pkmncf_terminal_trainer_rankings_reports ORDER BY EndDate DESC LIMIT 1",
|
||||
new MySqlParameter("@now", now));
|
||||
|
||||
if (tblLastReport.Rows.Count > 0)
|
||||
{
|
||||
DataRow rowLastReport = tblLastReport.Rows[0];
|
||||
if (DatabaseExtender.Cast<DateTime>(rowLastReport["EndDate"]) > now) return false; // found leaderboard still good
|
||||
|
||||
int lastReportId = DatabaseExtender.Cast<int>(rowLastReport["report_id"]);
|
||||
|
||||
// update leaderboard aggregates for the most recently concluded leaderboard
|
||||
tran.ExecuteProcedure("pkmncf_terminal_proc_create_leaderboards_for_report", new MySqlParameter("@report_id", lastReportId));
|
||||
}
|
||||
|
||||
// 2. Pick new record-types, preferring ones which haven't been used in a while.
|
||||
DataTable tblRecordTypes = tran.ExecuteDataTable("SELECT RecordType, MAX(StartDate) AS LastUsed " +
|
||||
"FROM ( " +
|
||||
"SELECT RecordType1 AS RecordType, StartDate FROM pkmncf_terminal_trainer_rankings_reports " +
|
||||
"UNION " +
|
||||
"SELECT RecordType2 AS RecordType, StartDate FROM pkmncf_terminal_trainer_rankings_reports " +
|
||||
"UNION " +
|
||||
"SELECT RecordType3 AS RecordType, StartDate FROM pkmncf_terminal_trainer_rankings_reports " +
|
||||
") AS tbl4 " +
|
||||
"GROUP BY RecordType");
|
||||
|
||||
TrainerRankingsRecordTypes[] enumValues = (TrainerRankingsRecordTypes[])Enum.GetValues(typeof(TrainerRankingsRecordTypes));
|
||||
|
||||
// outer join enum values to include those recordtypes that have never been used
|
||||
var combined = enumValues
|
||||
.Join(tblRecordTypes.AsEnumerable(),
|
||||
i => i,
|
||||
row => (TrainerRankingsRecordTypes)DatabaseExtender.Cast<int>(row["RecordType"]),
|
||||
(i, row) => new { RecordType = i, LastUsed = row == null ? DateTime.MinValue : DatabaseExtender.Cast<DateTime>(row["LastUsed"]) })
|
||||
.OrderBy(r => r.LastUsed).ToList(); // oldest first
|
||||
|
||||
List<TrainerRankingsRecordTypes> chosen = new List<TrainerRankingsRecordTypes>(3);
|
||||
Random rand = new Random(); // todo: use better rng here. Maybe when the framework gets good RNGs
|
||||
|
||||
// pick a random sampling of 3 recordtypes near the start of the list (oldest) but not always just the first 3 for a little randomness but not too much:
|
||||
for (int i = 0; i < 3; i++)
|
||||
{
|
||||
// here we do a kind of "itunes shuffle" type algorithm where
|
||||
// we cycle through recordtypes but vary the order a little bit
|
||||
// each time.
|
||||
// We do this by choosing from the recordypes in the bottom 10%
|
||||
// according to last used date and iterating
|
||||
|
||||
DateTime cutoff = DateLerp(combined[0].LastUsed, combined[combined.Count - 1].LastUsed, 0.1);
|
||||
int acceptableCount = combined.Count(row => row.LastUsed <= cutoff); // xxx: can do faster since we know this is sorted
|
||||
int chosenIndex = rand.Next(acceptableCount);
|
||||
chosen.Add(combined[chosenIndex].RecordType);
|
||||
combined.RemoveAt(chosenIndex);
|
||||
}
|
||||
|
||||
// 3. Init a new report.
|
||||
// Note that if more than a whole week has passed since a visit, go ahead and skip the blank week(s). It's a little white lie but makes the data look more presentable than a bunch of 0s.
|
||||
DateTime startDate = now.Date.AddDays(-(int)now.Date.DayOfWeek); // DayOfWeek zero-based starting on sunday
|
||||
DateTime endDate = startDate.AddDays(7);
|
||||
|
||||
tran.ExecuteNonQuery("INSERT INTO pkmncf_terminal_trainer_rankings_reports " +
|
||||
"(StartDate, EndDate, RecordType1, RecordType2, RecordType3) VALUES " +
|
||||
"(@start_date, @end_date, @record_type1, @record_type2, @record_type3)",
|
||||
new MySqlParameter("@start_date", startDate),
|
||||
new MySqlParameter("@end_date", endDate),
|
||||
new MySqlParameter("@record_type1", chosen[0]),
|
||||
new MySqlParameter("@record_type2", chosen[1]),
|
||||
new MySqlParameter("@record_type3", chosen[2]));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private static DateTime DateLerp(DateTime first, DateTime second, double weight)
|
||||
{
|
||||
TimeSpan diff = second - first;
|
||||
return first + new TimeSpan((long)(diff.Ticks * weight));
|
||||
}
|
||||
|
||||
public override IList<TrainerRankingsRecordTypes> TrainerRankingsGetActiveRecordTypes()
|
||||
|
|
|
|||
|
|
@ -807,6 +807,68 @@ CREATE TABLE IF NOT EXISTS `pkmncf_pokedex_types` (
|
|||
|
||||
-- Data exporting was unselected.
|
||||
|
||||
-- Dumping structure for procedure gts.pkmncf_terminal_proc_create_leaderboards_for_record
|
||||
DELIMITER //
|
||||
CREATE PROCEDURE `pkmncf_terminal_proc_create_leaderboards_for_record`(
|
||||
IN `@report_id` INT,
|
||||
IN `@record_type` INT
|
||||
)
|
||||
SQL SECURITY INVOKER
|
||||
BEGIN
|
||||
|
||||
SELECT @start_date := StartDate
|
||||
FROM pkmncf_terminal_trainer_rankings_reports WHERE report_id = @report_id;
|
||||
|
||||
INSERT INTO pkmncf_terminal_trainer_rankings_leaderboards_class
|
||||
SELECT @report_id AS report_id, TrainerClass, @record_type AS RecordType, SUM(VALUE) AS Score
|
||||
FROM pkmncf_terminal_trainer_rankings_records
|
||||
INNER JOIN pkmncf_terminal_trainer_rankings_teams
|
||||
ON pkmncf_terminal_trainer_rankings_records.pid = pkmncf_terminal_trainer_rankings_teams.pid
|
||||
WHERE pkmncf_terminal_trainer_rankings_records.LastUpdated >= @start_date
|
||||
AND RecordType = @record_type
|
||||
GROUP BY TrainerClass;
|
||||
|
||||
INSERT INTO pkmncf_terminal_trainer_rankings_leaderboards_month
|
||||
SELECT @report_id AS report_id, TrainerClass, @record_type AS RecordType, SUM(VALUE) AS Score
|
||||
FROM pkmncf_terminal_trainer_rankings_records
|
||||
INNER JOIN pkmncf_terminal_trainer_rankings_teams
|
||||
ON pkmncf_terminal_trainer_rankings_records.pid = pkmncf_terminal_trainer_rankings_teams.pid
|
||||
WHERE pkmncf_terminal_trainer_rankings_records.LastUpdated >= @start_date
|
||||
AND RecordType = @record_type
|
||||
GROUP BY BirthMonth;
|
||||
|
||||
INSERT INTO pkmncf_terminal_trainer_rankings_leaderboards_pokemon
|
||||
SELECT @report_id AS report_id, TrainerClass, @record_type AS RecordType, SUM(VALUE) AS Score
|
||||
FROM pkmncf_terminal_trainer_rankings_records
|
||||
INNER JOIN pkmncf_terminal_trainer_rankings_teams
|
||||
ON pkmncf_terminal_trainer_rankings_records.pid = pkmncf_terminal_trainer_rankings_teams.pid
|
||||
WHERE pkmncf_terminal_trainer_rankings_records.LastUpdated >= @start_date
|
||||
AND RecordType = @record_type
|
||||
GROUP BY FavouritePokemon;
|
||||
|
||||
END//
|
||||
DELIMITER ;
|
||||
|
||||
-- Dumping structure for procedure gts.pkmncf_terminal_proc_create_leaderboards_for_report
|
||||
DELIMITER //
|
||||
CREATE PROCEDURE `pkmncf_terminal_proc_create_leaderboards_for_report`(
|
||||
IN `@report_id` INT
|
||||
)
|
||||
SQL SECURITY INVOKER
|
||||
BEGIN
|
||||
SELECT @record_type_1 := RecordType1, @record_type_2 := RecordType2, @record_type_3 := RecordType3
|
||||
FROM pkmncf_terminal_trainer_rankings_reports WHERE report_id = @report_id;
|
||||
|
||||
DELETE FROM pkmncf_terminal_trainer_rankings_leaderboards_class WHERE report_id = @report_id;
|
||||
DELETE FROM pkmncf_terminal_trainer_rankings_leaderboards_month WHERE report_id = @report_id;
|
||||
DELETE FROM pkmncf_terminal_trainer_rankings_leaderboards_pokemon WHERE report_id = @report_id;
|
||||
|
||||
CALL pkmncf_terminal_proc_create_leaderboards_for_record(@report_id, @record_type_1);
|
||||
CALL pkmncf_terminal_proc_create_leaderboards_for_record(@report_id, @record_type_2);
|
||||
CALL pkmncf_terminal_proc_create_leaderboards_for_record(@report_id, @record_type_3);
|
||||
END//
|
||||
DELIMITER ;
|
||||
|
||||
-- Dumping structure for table gts.pkmncf_terminal_trainer_rankings_leaderboards_class
|
||||
CREATE TABLE IF NOT EXISTS `pkmncf_terminal_trainer_rankings_leaderboards_class` (
|
||||
`report_id` int(11) NOT NULL,
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user