mirror of
https://github.com/edgegamers/poor-sharptimer.git
synced 2025-12-06 04:42:48 -08:00
@@ -49,23 +49,52 @@ jobs:
|
||||
mkdir -p publish/addons/counterstrikesharp/plugins/SharpTimer/
|
||||
find ./publish -mindepth 1 -maxdepth 1 ! -name 'cfg' ! -name 'gamedata' -exec mv {} publish/addons/counterstrikesharp/plugins/SharpTimer/ \;
|
||||
mv gamedata/ ./publish/addons/counterstrikesharp/
|
||||
|
||||
|
||||
- name: Upload artifact
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: SharpTimer
|
||||
path: publish/*
|
||||
if-no-files-found: error
|
||||
|
||||
- name: Set version tag and zip file name
|
||||
id: version
|
||||
run: |
|
||||
VERSION="v1.0.${{ github.run_number }}"
|
||||
if [[ $GITHUB_REF == 'refs/heads/dev' ]]; then
|
||||
TAG="Prerelease $VERSION"
|
||||
else
|
||||
TAG="$VERSION"
|
||||
fi
|
||||
ZIP_NAME="SharpTimer_${VERSION}.zip"
|
||||
echo "TAG=$TAG" >> $GITHUB_ENV
|
||||
echo "ZIP_NAME=$ZIP_NAME" >> $GITHUB_ENV
|
||||
|
||||
- name: Zip published files
|
||||
run: |
|
||||
cd publish
|
||||
zip -r ${{ env.ZIP_NAME }} .
|
||||
|
||||
- name: Create GitHub Release
|
||||
uses: ncipollo/release-action@v1.16.0
|
||||
with:
|
||||
name: ${{ env.TAG }}
|
||||
artifacts: publish/${{ env.ZIP_NAME }}
|
||||
prerelease: ${{ contains(github.ref, 'dev') }}
|
||||
tag: v1.0.${{ github.run_number }}
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
post_webhook:
|
||||
needs: build
|
||||
runs-on: ubuntu-latest
|
||||
if: github.ref == 'refs/heads/dev'
|
||||
|
||||
steps:
|
||||
- name: POST Webhook
|
||||
run: |
|
||||
curl -X POST \
|
||||
--fail \
|
||||
-F token=${{ secrets.GITLAB_SECRET_TOKEN }} \
|
||||
-F ref=dev \
|
||||
https://gitlab.edgegamers.io/api/v4/projects/2683/trigger/pipeline
|
||||
- name: POST Webhook
|
||||
run: |
|
||||
curl -X POST \
|
||||
--fail \
|
||||
-F token=${{ secrets.GITLAB_SECRET_TOKEN }} \
|
||||
-F ref=dev \
|
||||
https://gitlab.edgegamers.io/api/v4/projects/2683/trigger/pipeline
|
||||
@@ -16,7 +16,7 @@
|
||||
<PackageReference Include="System.Data.SQLite" Version="1.0.118"/>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Content Include=".github\workflows\build.yml"/>
|
||||
<Content Include=".github\workflows\nightly.yml" />
|
||||
<Content Include="lang\**\*">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
<Link>lang\%(RecursiveDir)%(Filename)%(Extension)</Link>
|
||||
|
||||
@@ -24,6 +24,7 @@
|
||||
"map_finish": "{lime}{0} {white}finished the map!",
|
||||
"map_finish_bonus": "{lime}{0} {white}finished the Bonus {1}!",
|
||||
"map_finish_rank": "Rank: [{lime}{0}{default}] Times Finished: [{lime}{1}{default}]",
|
||||
"map_finish_group": "Group: {orchid}{0}",
|
||||
"top10_records": "Top 10 {0} Records for {1}:",
|
||||
"top10_records_bonus": "Top 10 {0} Records for Bonus {1} on {2}:",
|
||||
|
||||
@@ -39,7 +40,7 @@
|
||||
//rank
|
||||
"current_rank": "You are currently {0}{1}{lime}",
|
||||
"current_rank_points": "{default}({lime}{0}{default}) [{lime}{1}{default}]",
|
||||
"current_pb": "Your current PB on {lime}{0}{default}: {lime}{1}{default} [{lime}{2}{default}]",
|
||||
"current_pb": "Your current PB on {lime}{0}{default}: {lime}{1}{default} [{lime}{2}{default}] Group: {lime}{3}",
|
||||
"current_bonus_pb": "Your current PB on {lime}Bonus {0}{default}: {lime}{1}{default} [{lime}{2}{default}]",
|
||||
"current_sr": "Current Server Record on {lime}{0}{white}:",
|
||||
"current_sr_player": "{lime}{0} {white}- {lime}{1}",
|
||||
|
||||
@@ -838,6 +838,11 @@ namespace SharpTimer
|
||||
ranking = useGlobalRanks ? await GetPlayerServerPlacement(steamId, playerName) : await GetPlayerMapPlacementWithTotal(player, steamId, playerName, false, false, 0, style);
|
||||
rankIcon = useGlobalRanks ? await GetPlayerServerPlacement(steamId, playerName, true) : await GetPlayerMapPlacementWithTotal(player, steamId, playerName, true, false, 0, style);
|
||||
mapPlacement = await GetPlayerMapPlacementWithTotal(player, steamId, playerName, false, true, 0, style);
|
||||
var percentile =
|
||||
await GetPlayerMapPercentile(steamId, playerName);
|
||||
|
||||
SharpTimerDebug("Ranking: " + mapPlacement);
|
||||
int.TryParse(mapPlacement[..Math.Max(mapPlacement.IndexOf('/'), 0)], out var position);
|
||||
|
||||
foreach (var bonusRespawnPose in bonusRespawnPoses)
|
||||
{
|
||||
@@ -900,8 +905,9 @@ namespace SharpTimer
|
||||
|
||||
PrintToChat(player, rankMessage);
|
||||
|
||||
if (pbTicks != 0)
|
||||
PrintToChat(player, Localizer["current_pb", currentMapName!, FormatTime(pbTicks), mapPlacement]);
|
||||
if (pbTicks != 0) {
|
||||
PrintToChat(player, Localizer["current_pb", currentMapName!, FormatTime(pbTicks), mapPlacement, FormatGroup(position, percentile)]);
|
||||
}
|
||||
|
||||
if (playerTimers[playerSlot].CachedBonusInfo.Any())
|
||||
{
|
||||
@@ -915,7 +921,7 @@ namespace SharpTimer
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
SharpTimerError($"Error in RankCommandHandler: {ex}");
|
||||
SharpTimerError($"Error in RankCommandHandler: {ex} {ex.StackTrace}");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1643,7 +1643,7 @@ namespace SharpTimer
|
||||
|
||||
public void GainPointsMessage(string playerName, double newPoints, double playerPoints)
|
||||
{
|
||||
PrintToChatAll(Localizer["gained_points", playerName, Convert.ToInt32(newPoints - playerPoints), newPoints]);
|
||||
// PrintToChatAll(Localizer["gained_points", playerName, Convert.ToInt32(newPoints - playerPoints), newPoints]);
|
||||
}
|
||||
|
||||
public (string, int) FixMapAndBonus(string mapName)
|
||||
@@ -2048,7 +2048,8 @@ namespace SharpTimer
|
||||
switch (dbType)
|
||||
{
|
||||
case DatabaseType.MySQL:
|
||||
query = $@"SELECT PlayerName, GlobalPoints FROM {PlayerStatsTable} ORDER BY GlobalPoints DESC LIMIT 10";
|
||||
// query = $@"SELECT PlayerName, GlobalPoints FROM {PlayerStatsTable} ORDER BY GlobalPoints DESC LIMIT 10";
|
||||
query = $@"SELECT PlayerName, GlobalPoints FROM PlayerLeaderboard ORDER BY GlobalPoints DESC LIMIT 10";
|
||||
command = new MySqlCommand(query, (MySqlConnection)connection);
|
||||
break;
|
||||
case DatabaseType.PostgreSQL:
|
||||
@@ -2625,7 +2626,7 @@ namespace SharpTimer
|
||||
switch (dbType)
|
||||
{
|
||||
case DatabaseType.MySQL:
|
||||
selectQuery = $"SELECT GlobalPoints FROM {PlayerStatsTable} WHERE SteamID = @SteamID";
|
||||
selectQuery = $"SELECT GlobalPoints FROM PlayerLeaderboard WHERE SteamID = @SteamID";
|
||||
selectCommand = new MySqlCommand(selectQuery, (MySqlConnection)connection);
|
||||
break;
|
||||
case DatabaseType.PostgreSQL:
|
||||
@@ -2979,7 +2980,7 @@ namespace SharpTimer
|
||||
switch (dbType)
|
||||
{
|
||||
case DatabaseType.MySQL:
|
||||
selectQuery = $@"SELECT SteamID, PlayerName, GlobalPoints FROM {PlayerStatsTable}";
|
||||
selectQuery = $@"SELECT SteamID, PlayerName, GlobalPoints FROM PlayerLeaderboard ORDER BY GlobalPoints DESC";
|
||||
selectCommand = new MySqlCommand(selectQuery, (MySqlConnection)connection);
|
||||
break;
|
||||
case DatabaseType.PostgreSQL:
|
||||
@@ -3005,7 +3006,7 @@ namespace SharpTimer
|
||||
{
|
||||
string steamId = reader.GetString(0);
|
||||
string playerName = reader.IsDBNull(1) ? "Unknown" : reader.GetString(1);
|
||||
int globalPoints = reader.GetInt32(2);
|
||||
int globalPoints = Convert.ToInt32(reader["GlobalPoints"]);
|
||||
|
||||
if (globalPoints >= minGlobalPointsForRank) // Only add if GlobalPoints is above or equal to minGlobalPointsForRank
|
||||
{
|
||||
@@ -3017,11 +3018,8 @@ namespace SharpTimer
|
||||
}
|
||||
}
|
||||
|
||||
sortedPoints = sortedPoints.OrderByDescending(record => record.Value.GlobalPoints)
|
||||
.ToDictionary(record => record.Key, record => record.Value);
|
||||
|
||||
|
||||
|
||||
// sortedPoints = sortedPoints.OrderByDescending(record => record.Value.GlobalPoints)
|
||||
// .ToDictionary(record => record.Key, record => record.Value);
|
||||
return sortedPoints;
|
||||
}
|
||||
}
|
||||
|
||||
11
src/DB/Migrations/MySQL/005_AddPlayerLeaderboardView.sql
Normal file
11
src/DB/Migrations/MySQL/005_AddPlayerLeaderboardView.sql
Normal file
@@ -0,0 +1,11 @@
|
||||
CREATE TABLE IF NOT EXISTS TierPoints
|
||||
(
|
||||
Tier INT NOT NULL
|
||||
PRIMARY KEY,
|
||||
Points INT NULL,
|
||||
CompletionPoints FLOAT NULL,
|
||||
BasePoints FLOAT NULL,
|
||||
Divider FLOAT NULL,
|
||||
MaxPoints INT NULL
|
||||
);
|
||||
|
||||
112
src/DB/Migrations/MySQL/006_AddRankOverhaulViews.sql
Normal file
112
src/DB/Migrations/MySQL/006_AddRankOverhaulViews.sql
Normal file
@@ -0,0 +1,112 @@
|
||||
CREATE VIEW IF NOT EXISTS cs2_surf.MapCompletions AS
|
||||
SELECT `PlayerRecords`.`MapName` AS `MapName`,
|
||||
SUM(`PlayerRecords`.`TimesFinished`) AS `Completions`
|
||||
FROM `PlayerRecords`
|
||||
GROUP BY `PlayerRecords`.`MapName`;
|
||||
|
||||
CREATE TABLE IF NOT EXISTS MapTiers
|
||||
(
|
||||
MapName VARCHAR(255) NOT NULL
|
||||
PRIMARY KEY,
|
||||
Tier INT DEFAULT 1 NOT NULL
|
||||
);
|
||||
|
||||
CREATE VIEW IF NOT EXISTS cs2_surf.MapWRs AS
|
||||
SELECT `Map`.`MapName` AS `MapName`,
|
||||
GREATEST(`TP`.`MaxPoints`,
|
||||
COALESCE(`TP`.`BasePoints` + `TP`.`CompletionPoints` * `Map`.`Completions` / `TP`.`Divider`,
|
||||
`TP`.`MaxPoints`)) AS `WR`,
|
||||
GREATEST(`TP`.`MaxPoints`,
|
||||
COALESCE(`TP`.`BasePoints` + `TP`.`CompletionPoints` * `Map`.`Completions` / `TP`.`Divider`,
|
||||
`TP`.`MaxPoints`)) * 0.8 AS `Rank2`,
|
||||
GREATEST(`TP`.`MaxPoints`,
|
||||
COALESCE(`TP`.`BasePoints` + `TP`.`CompletionPoints` * `Map`.`Completions` / `TP`.`Divider`,
|
||||
`TP`.`MaxPoints`)) * 0.75 AS `Rank3`,
|
||||
GREATEST(`TP`.`MaxPoints`,
|
||||
COALESCE(`TP`.`BasePoints` + `TP`.`CompletionPoints` * `Map`.`Completions` / `TP`.`Divider`,
|
||||
`TP`.`MaxPoints`)) * 0.7 AS `Rank4`,
|
||||
GREATEST(`TP`.`MaxPoints`,
|
||||
COALESCE(`TP`.`BasePoints` + `TP`.`CompletionPoints` * `Map`.`Completions` / `TP`.`Divider`,
|
||||
`TP`.`MaxPoints`)) * 0.65 AS `Rank5`,
|
||||
GREATEST(`TP`.`MaxPoints`,
|
||||
COALESCE(`TP`.`BasePoints` + `TP`.`CompletionPoints` * `Map`.`Completions` / `TP`.`Divider`,
|
||||
`TP`.`MaxPoints`)) * 0.6 AS `Rank6`,
|
||||
GREATEST(`TP`.`MaxPoints`,
|
||||
COALESCE(`TP`.`BasePoints` + `TP`.`CompletionPoints` * `Map`.`Completions` / `TP`.`Divider`,
|
||||
`TP`.`MaxPoints`)) * 0.55 AS `Rank7`,
|
||||
GREATEST(`TP`.`MaxPoints`,
|
||||
COALESCE(`TP`.`BasePoints` + `TP`.`CompletionPoints` * `Map`.`Completions` / `TP`.`Divider`,
|
||||
`TP`.`MaxPoints`)) * 0.5 AS `Rank8`,
|
||||
GREATEST(`TP`.`MaxPoints`,
|
||||
COALESCE(`TP`.`BasePoints` + `TP`.`CompletionPoints` * `Map`.`Completions` / `TP`.`Divider`,
|
||||
`TP`.`MaxPoints`)) * 0.45 AS `Rank9`,
|
||||
GREATEST(`TP`.`MaxPoints`,
|
||||
COALESCE(`TP`.`BasePoints` + `TP`.`CompletionPoints` * `Map`.`Completions` / `TP`.`Divider`,
|
||||
`TP`.`MaxPoints`)) * 0.4 AS `Rank10`,
|
||||
GREATEST(`TP`.`MaxPoints`,
|
||||
COALESCE(`TP`.`BasePoints` + `TP`.`CompletionPoints` * `Map`.`Completions` / `TP`.`Divider`,
|
||||
`TP`.`MaxPoints`)) * 0.25 AS `Group1`,
|
||||
GREATEST(`TP`.`MaxPoints`,
|
||||
COALESCE(`TP`.`BasePoints` + `TP`.`CompletionPoints` * `Map`.`Completions` / `TP`.`Divider`,
|
||||
`TP`.`MaxPoints`)) * 0.25 / 1.5 AS `Group2`,
|
||||
GREATEST(`TP`.`MaxPoints`,
|
||||
COALESCE(`TP`.`BasePoints` + `TP`.`CompletionPoints` * `Map`.`Completions` / `TP`.`Divider`,
|
||||
`TP`.`MaxPoints`)) * 0.25 / 2.25 AS `Group3`,
|
||||
GREATEST(`TP`.`MaxPoints`,
|
||||
COALESCE(`TP`.`BasePoints` + `TP`.`CompletionPoints` * `Map`.`Completions` / `TP`.`Divider`,
|
||||
`TP`.`MaxPoints`)) * 0.25 / 3.375 AS `Group4`,
|
||||
GREATEST(`TP`.`MaxPoints`,
|
||||
COALESCE(`TP`.`BasePoints` + `TP`.`CompletionPoints` * `Map`.`Completions` / `TP`.`Divider`,
|
||||
`TP`.`MaxPoints`)) * 0.25 / 5.0625 AS `Group5`
|
||||
FROM ((`MapCompletions` `Map` JOIN `MapTiers` `MT`
|
||||
ON (`Map`.`MapName` = `MT`.`MapName`)) JOIN `TierPoints` `TP` ON (`MT`.`Tier` = `TP`.`Tier`));
|
||||
|
||||
CREATE VIEW IF NOT EXISTS cs2_surf.PlayerRanks AS
|
||||
SELECT `PlayerRecords`.`MapName` AS `MapName`,
|
||||
`PlayerRecords`.`SteamID` AS `SteamID`,
|
||||
RANK() OVER ( PARTITION BY `PlayerRecords`.`MapName` ORDER BY `PlayerRecords`.`TimerTicks`) AS `Rank`,
|
||||
PERCENT_RANK() OVER ( PARTITION BY `PlayerRecords`.`MapName` ORDER BY `PlayerRecords`.`TimerTicks`) AS `Percentile`
|
||||
FROM `PlayerRecords`
|
||||
WHERE `PlayerRecords`.`Style` = 0;
|
||||
|
||||
|
||||
|
||||
CREATE VIEW IF NOT EXISTS cs2_surf.PlayerPoints AS
|
||||
SELECT `Ranks`.`SteamID` AS `SteamID`,
|
||||
`MT`.`MapName` AS `MapName`,
|
||||
`Ranks`.`Percentile` AS `Percentile`,
|
||||
CASE
|
||||
WHEN `Ranks`.`Percentile` < 0.5 THEN CASE `Ranks`.`Rank`
|
||||
WHEN 1 THEN `MWR`.`WR`
|
||||
WHEN 2 THEN `MWR`.`Rank2`
|
||||
WHEN 3 THEN `MWR`.`Rank3`
|
||||
WHEN 4 THEN `MWR`.`Rank4`
|
||||
WHEN 5 THEN `MWR`.`Rank5`
|
||||
WHEN 6 THEN `MWR`.`Rank6`
|
||||
WHEN 7 THEN `MWR`.`Rank7`
|
||||
WHEN 8 THEN `MWR`.`Rank8`
|
||||
WHEN 9 THEN `MWR`.`Rank9`
|
||||
WHEN 10 THEN `MWR`.`Rank10` END
|
||||
ELSE CASE
|
||||
WHEN `Ranks`.`Percentile` < 0.03125 THEN `MWR`.`Group1`
|
||||
WHEN `Ranks`.`Percentile` < 0.06250 THEN `MWR`.`Group2`
|
||||
WHEN `Ranks`.`Percentile` < 0.12500 THEN `MWR`.`Group3`
|
||||
WHEN `Ranks`.`Percentile` < 0.25000 THEN `MWR`.`Group4`
|
||||
WHEN `Ranks`.`Percentile` < 0.50000 THEN `MWR`.`Group5`
|
||||
ELSE 0 END END + CASE `MT`.`Tier`
|
||||
WHEN 1 THEN 25
|
||||
WHEN 2 THEN 50
|
||||
WHEN 3 THEN 100
|
||||
WHEN 4 THEN 200
|
||||
WHEN 5 THEN 400
|
||||
WHEN 6 THEN 600
|
||||
WHEN 7 THEN 800
|
||||
WHEN 8 THEN 1000 END AS `Points`
|
||||
FROM ((`PlayerRanks` `Ranks` JOIN `MapWRs` `MWR`
|
||||
ON (`Ranks`.`MapName` = `MWR`.`MapName`)) JOIN `MapTiers` `MT` ON (`MWR`.`MapName` = `MT`.`MapName`));
|
||||
|
||||
CREATE VIEW IF NOT EXISTS cs2_surf.PlayerLeaderboard AS
|
||||
SELECT `PP`.`SteamID` AS `SteamID`, `PR`.`PlayerName` AS `PlayerName`, SUM(`PP`.`Points`) AS `GlobalPoints`
|
||||
FROM (`PlayerPoints` `PP` JOIN `PlayerRecords` `PR` ON (`PP`.`SteamID` = `PR`.`SteamID`))
|
||||
GROUP BY `PP`.`SteamID`;
|
||||
|
||||
@@ -126,5 +126,62 @@ namespace SharpTimer
|
||||
_ => 0,
|
||||
};
|
||||
}
|
||||
|
||||
public string FormatGroup(int placement, double percentile) {
|
||||
percentile *= 100;
|
||||
if (placement <= 10)
|
||||
return placement switch {
|
||||
1 => "First",
|
||||
2 => "Second",
|
||||
3 => "Third",
|
||||
> 0 => placement + "th",
|
||||
_ => "N/A"
|
||||
};
|
||||
|
||||
if (percentile > 0.5) return "N/A";
|
||||
|
||||
return percentile switch {
|
||||
double p when p <= 3.125 => "Group 1",
|
||||
double p when p <= 6.25 => "Group 2",
|
||||
double p when p <= 12.5 => "Group 3",
|
||||
double p when p <= 25 => "Group 4",
|
||||
double p when p <= 50 => "Group 5",
|
||||
_ => "N/A"
|
||||
};
|
||||
}
|
||||
|
||||
public int GetGroupIndex(double percentile, bool forGlobal = false)
|
||||
{
|
||||
// double baseMultiplier = points * 0.25;
|
||||
double divisor = 1.5;
|
||||
|
||||
double threshold1, threshold2, threshold3, threshold4, threshold5;
|
||||
if (forGlobal)
|
||||
{
|
||||
threshold1 = 3.125;
|
||||
threshold2 = 6.25;
|
||||
threshold3 = 12.5;
|
||||
threshold4 = 25;
|
||||
threshold5 = 50;
|
||||
}
|
||||
else
|
||||
{
|
||||
threshold1 = group1;
|
||||
threshold2 = group2;
|
||||
threshold3 = group3;
|
||||
threshold4 = group4;
|
||||
threshold5 = group5;
|
||||
}
|
||||
|
||||
return percentile switch
|
||||
{
|
||||
double p when p <= threshold1 => 1,
|
||||
double p when p <= threshold2 => 2,
|
||||
double p when p <= threshold3 => 3,
|
||||
double p when p <= threshold4 => 4,
|
||||
double p when p <= threshold5 => 5,
|
||||
_ => 0,
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -526,6 +526,9 @@ namespace SharpTimer
|
||||
}
|
||||
|
||||
string ranking = await GetPlayerMapPlacementWithTotal(player, steamID, playerName, false, true, bonusX, style);
|
||||
SharpTimerDebug($"Player {playerName} finished map with time {newticks} ticks, placing {ranking}");
|
||||
var percentile = await GetPlayerMapPercentile(steamID, playerName, currentMapName!, bonusX, style, false, newticks);
|
||||
int.TryParse(ranking[..ranking.IndexOf('/')], out int position);
|
||||
|
||||
bool newSR = GetNumberBeforeSlash(ranking) == 1 && (oldticks > newticks || oldticks == 0);
|
||||
bool beatPB = oldticks > newticks;
|
||||
@@ -570,11 +573,12 @@ namespace SharpTimer
|
||||
PlaySound(player, timerSound);
|
||||
}
|
||||
|
||||
if (enableDb || bonusX != 0)
|
||||
if (enableDb || bonusX != 0) {
|
||||
PrintToChatAll(Localizer["map_finish_rank", ranking, timesFinished]);
|
||||
PrintToChatAll(Localizer["map_finish_group", FormatGroup(position, percentile)]);
|
||||
}
|
||||
|
||||
PrintToChatAll(Localizer["timer_time", newTime, timeDifference]);
|
||||
// if (enableStyles) PrintToChatAll(Localizer["timer_style", GetNamedStyle(style)]);
|
||||
if (enableReplays == true && enableSRreplayBot == true && newSR && (oldticks > newticks || oldticks == 0))
|
||||
{
|
||||
_ = Task.Run(async () => await SpawnReplayBot());
|
||||
|
||||
Reference in New Issue
Block a user