Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature/Improvement: Live Contest Stats #839

Draft
wants to merge 35 commits into
base: beta
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
35 commits
Select commit Hold shift + click to select a range
9f3562c
wip live contest stats
Obsidianninja11 Dec 27, 2023
8e15045
It's actually working now :o
Obsidianninja11 Dec 28, 2023
95757aa
Forgot to delete debugging line
Obsidianninja11 Dec 28, 2023
55b109a
Better example data
Obsidianninja11 Dec 28, 2023
cd1aedc
Actually 1200 is better than 1199 i think
Obsidianninja11 Dec 28, 2023
a70b87b
Better default position
Obsidianninja11 Dec 28, 2023
ee3d26d
More accurate score prediction (especially when starting late)
Obsidianninja11 Dec 29, 2023
d47380f
subtract 999 ms bc weird utils adds 999 ms wtf?!
Obsidianninja11 Dec 29, 2023
726a5c7
Removed debug info (forgot to remove before oops)
Obsidianninja11 Dec 29, 2023
21c04d8
Use RepoPattern
Obsidianninja11 Dec 31, 2023
7071bb1
Fixes
Obsidianninja11 Jan 2, 2024
9867ac0
Revert "Fixes"
Obsidianninja11 Jan 2, 2024
3038c22
Fixed some stuff
Obsidianninja11 Jan 2, 2024
df479b1
Minor code cleanup + consistencies
Obsidianninja11 Jan 2, 2024
0fa4065
Merge branch 'beta' into jacob-contest-live-stats
Obsidianninja11 Mar 24, 2024
05c02bd
Fixes
Obsidianninja11 Apr 8, 2024
274a1cf
Merge branch 'beta' into jacob-contest-live-stats
Obsidianninja11 Apr 8, 2024
a9666b6
Fix
Obsidianninja11 Apr 8, 2024
647ee9e
Merge branch 'beta' into jacob-contest-live-stats
Obsidianninja11 Apr 10, 2024
cb41ea1
Fixes + simpletimemark
Obsidianninja11 Apr 11, 2024
c448046
Removed debug thing (oops)
Obsidianninja11 Apr 11, 2024
11fc600
Participated -> participating in live stats
Obsidianninja11 Apr 11, 2024
5f93e21
Merge branch 'beta' into jacob-contest-live-stats
Obsidianninja11 Apr 17, 2024
2abd959
Merge remote-tracking branch 'upstream/beta' into jacob-contest-live-…
Obsidianninja11 May 30, 2024
af10276
Some cleanup and fixes
Obsidianninja11 May 30, 2024
43a782f
Merge branch 'hannibal002:beta' into jacob-contest-live-stats
Obsidianninja11 May 30, 2024
4190bbb
Merge branch 'refs/heads/beta' into fork/Obsidianninja11/jacob-contes…
CalMWolfs Jun 6, 2024
fc91b2f
fix merge
CalMWolfs Jun 6, 2024
87601ff
Merge branch 'hannibal002:beta' into jacob-contest-live-stats
Obsidianninja11 Aug 10, 2024
9860066
Merge branch 'beta' into jacob-contest-live-stats
Obsidianninja11 Oct 9, 2024
059d884
Translator fix and sort of rewrite
Obsidianninja11 Oct 9, 2024
0752041
Fix merge
Obsidianninja11 Oct 9, 2024
f9d2df5
Merge remote-tracking branch 'origin/jacob-contest-live-stats' into j…
Obsidianninja11 Oct 9, 2024
f8f7195
Merge branch 'hannibal002:beta' into jacob-contest-live-stats
Obsidianninja11 Oct 13, 2024
1a0b45b
Merge branch 'hannibal002:beta' into jacob-contest-live-stats
Obsidianninja11 Oct 17, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,11 @@ public class GardenConfig {
@Accordion
public AtmosphericFilterDisplayConfig atmosphericFilterDisplay = new AtmosphericFilterDisplayConfig();

@Expose
@ConfigOption(name = "Jacob Contest Stats", desc = "")
@Accordion
public JacobContestStatsConfig jacobContestStats = new JacobContestStatsConfig();

@Expose
@ConfigOption(name = "Plot Price", desc = "Show the price of the plot in coins when inside the Configure Plots inventory.")
@ConfigEditorBoolean
Expand Down Expand Up @@ -180,6 +185,10 @@ public class GardenConfig {
@FeatureToggle
public boolean jacobContestTimes = true;

@Expose
@ConfigLink(owner = GardenConfig.class, field = "jacobContestTimes")
public Position jacobContestTimesPosition = new Position(-359, 149, false, true);

@Expose
@ConfigOption(
name = "Custom BPS",
Expand All @@ -198,19 +207,6 @@ public class GardenConfig {
)
public double jacobContestCustomBpsValue = 19.9;

@Expose
@ConfigLink(owner = GardenConfig.class, field = "jacobContestTimes")
public Position jacobContestTimesPosition = new Position(-359, 149, false, true);

@Expose
@ConfigOption(
name = "Contest Summary",
desc = "Show the average Blocks Per Second and blocks clicked at the end of a Jacob Farming Contest in chat."
)
@ConfigEditorBoolean
@FeatureToggle
public boolean jacobContestSummary = true;

@Expose
@ConfigOption(
name = "Personal Best Increase FF",
Expand All @@ -220,7 +216,6 @@ public class GardenConfig {
@FeatureToggle
public boolean contestPersonalBestIncreaseFF = true;

// Does not have a config element!
@Expose
public Position cropSpeedMeterPos = new Position(278, -236, false, true);

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
package at.hannibal2.skyhanni.config.features.garden;

import at.hannibal2.skyhanni.config.FeatureToggle;
import at.hannibal2.skyhanni.config.core.config.Position;
import com.google.gson.annotations.Expose;
import io.github.notenoughupdates.moulconfig.annotations.ConfigEditorBoolean;
import io.github.notenoughupdates.moulconfig.annotations.ConfigEditorDraggableList;
import io.github.notenoughupdates.moulconfig.annotations.ConfigLink;
import io.github.notenoughupdates.moulconfig.annotations.ConfigOption;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

public class JacobContestStatsConfig {

@Expose
@ConfigOption(
name = "Contest Stats",
desc = "Show contest stats during a contest and after a contest in chat."
)
@ConfigEditorBoolean
@FeatureToggle
public boolean jacobContestSummary = true;

@Expose
@ConfigOption(name = "Contest Stats Text", desc = "Drag to change the order of the overlay."
)
@ConfigEditorDraggableList
public List<ContestStatsTextEntry> text = new ArrayList<>(Arrays.asList(
ContestStatsTextEntry.TITLE,
ContestStatsTextEntry.START_TIME,
ContestStatsTextEntry.BLOCKS_BROKEN,
ContestStatsTextEntry.BPS,
ContestStatsTextEntry.POSITION,
ContestStatsTextEntry.PREDICTED_SCORE
));

public enum ContestStatsTextEntry {
TITLE("§e§lSugar Cane Contest Stats"),
START_TIME("§7Started §b3s §7into contest"),
PARTICIPATING_TIME("§7Participating for §b15m 25s"),
BLOCKS_BROKEN("§7Blocks Broken: §e14,781"),
BPS("§7Blocks per Second: §c19.94"),
POSITION("§7Position: Top §b0.1%"),
PREDICTED_SCORE("§7Predicted Score: §e915,430");

private final String str;

ContestStatsTextEntry(String str) {
this.str = str;
}

@Override
public String toString() {
return str;
}
}

@Expose
Obsidianninja11 marked this conversation as resolved.
Show resolved Hide resolved
@ConfigLink(owner = JacobContestStatsConfig.class, field = "jacobContestSummary")
public Position pos = new Position(0, 240, false, true);
}
Obsidianninja11 marked this conversation as resolved.
Show resolved Hide resolved
Original file line number Diff line number Diff line change
Expand Up @@ -3,63 +3,185 @@ package at.hannibal2.skyhanni.features.garden.contest
import at.hannibal2.skyhanni.data.ClickType
import at.hannibal2.skyhanni.events.CropClickEvent
import at.hannibal2.skyhanni.events.FarmingContestEvent
import at.hannibal2.skyhanni.events.GuiRenderEvent
import at.hannibal2.skyhanni.events.ScoreboardUpdateEvent
import at.hannibal2.skyhanni.events.TabListUpdateEvent
import at.hannibal2.skyhanni.features.garden.GardenAPI
import at.hannibal2.skyhanni.skyhannimodule.SkyHanniModule
import at.hannibal2.skyhanni.utils.ChatUtils
import at.hannibal2.skyhanni.utils.NumberUtil.addSeparators
import at.hannibal2.skyhanni.utils.NumberUtil.formatLong
import at.hannibal2.skyhanni.utils.NumberUtil.roundTo
import at.hannibal2.skyhanni.utils.NumberUtil.prettyRound
import at.hannibal2.skyhanni.utils.RegexUtils.matchMatcher
import at.hannibal2.skyhanni.utils.RenderUtils.renderStrings
import at.hannibal2.skyhanni.utils.SimpleTimeMark
import at.hannibal2.skyhanni.utils.TimeUtils
import at.hannibal2.skyhanni.utils.TimeUtils.format
import at.hannibal2.skyhanni.utils.repopatterns.RepoPattern
import net.minecraftforge.fml.common.eventhandler.SubscribeEvent
import kotlin.time.Duration.Companion.seconds
import kotlin.time.DurationUnit

@SkyHanniModule
object JacobContestStatsSummary {

private val config get() = GardenAPI.config
private val config get() = GardenAPI.config.jacobContestStats
private var blocksBroken = 0
private var startTime = SimpleTimeMark.farPast()
private var startTimeIrl = SimpleTimeMark.farPast()
private var startTimeRelative: Long? = null
private var startScore: Long? = null
private var predictedScore: Long? = null
private var percent: Double? = null
private var medalColor = ""
private var contestStats = mutableListOf<String>()

private val tabContestPattern by RepoPattern.pattern(
"garden.jacob.contest.tab.data",
" §r(§e○|§6☘) §r§f(?<crop>.+) §r§f◆ Top §r(?<color>§.)(?<percent>(\\d|[.])+)%"
)
private val scoreboardContestTimeLeftPattern by RepoPattern.pattern(
"garden.jacob.contest.scoreboard.time.left",
" (§e○|§6☘) §f(?<crop>.+) §a(?<time>\\d+m\\d+s)"
)
private val scoreboardContestScorePattern by RepoPattern.pattern(
"garden.jacob.contest.scoreboard.score",
" (Collected|(?<medal>§b§lDIAMOND|§3§lPLATINUM|§6§lGOLD|§f§lSILVER|§c§lBRONZE) §fwith) §e(?<amount>.+)"
)

@SubscribeEvent
fun onCropClick(event: CropClickEvent) {
if (!isEnabled()) return
if (!FarmingContestAPI.inContest) return
if (event.clickType != ClickType.LEFT_CLICK) return

if (FarmingContestAPI.inContest && event.crop == FarmingContestAPI.contestCrop) {
if (event.crop == FarmingContestAPI.contestCrop) {
blocksBroken++
}
}

@SubscribeEvent
fun onTabListUpdate(event: TabListUpdateEvent) {
if (!isEnabled()) return
if (!FarmingContestAPI.inContest) return

for (line in event.tabList) {
tabContestPattern.matchMatcher(line) {
if (group("crop") != FarmingContestAPI.contestCrop?.cropName) return
percent = group("percent").toDouble() / 100
medalColor = group("color")
update()
}
}
}

@SubscribeEvent
fun onScoreBoardUpdate(event: ScoreboardUpdateEvent) {
if (!isEnabled()) return
if (!FarmingContestAPI.inContest) return

var timeLeft = 0L
var amount = 0L

for (line in event.scoreboard) {
scoreboardContestTimeLeftPattern.matchMatcher(line) {
timeLeft = TimeUtils.getDuration(group("time")).inWholeSeconds
}
scoreboardContestScorePattern.matchMatcher(line) {
amount = group("amount").fixScoreAmount()
}
}

val fixedStartTime = startTimeRelative
val fixedStartScore = startScore
if (fixedStartTime == null || fixedStartScore == null) {
startTimeRelative = 1200 - timeLeft
startScore = amount
predictedScore = null
} else {
val amountGained = amount - fixedStartScore
val timeParticipated = 1200 - timeLeft - fixedStartTime
predictedScore = amount + amountGained / timeParticipated * (1200 - fixedStartTime)
}
update()
}

private fun String.fixScoreAmount(): Long =
this.formatLong() * when (this.split(',').last().length) {
2 -> 10
1 -> 100
else -> 1
}

fun update() {
val formattedStartTime = ((startTimeRelative ?: return) * 1000 - 999).seconds.format()
contestStats.clear()
val cropName = FarmingContestAPI.contestCrop?.cropName
val duration = startTimeIrl.passedSince()
val durationInSeconds = duration.toDouble(DurationUnit.SECONDS)
val timeParticipated = duration.format()
val blocksPerSecond = (blocksBroken.toDouble() / durationInSeconds).roundTo(2)
val formattedPercent = (percent?.times(100))?.prettyRound(1)
val position = if (percent == null) "§eNo data yet" else "Top $medalColor$formattedPercent%"

val unsortedList = mutableListOf<String>()
unsortedList.add("§e§l$cropName Contest Stats")
unsortedList.add("§7Started §b$formattedStartTime §7into contest")
unsortedList.add("§7Participating for §b$timeParticipated")
unsortedList.add("§7Blocks Broken: §e${blocksBroken.addSeparators()}")
unsortedList.add("§7Blocks per Second: §c$blocksPerSecond")
unsortedList.add("§7Position: $position")
unsortedList.add("§7Predicted Score: §e${predictedScore?.addSeparators() ?: "Calculating..."}")


for (index in config.text) {
contestStats.add(unsortedList[index.ordinal])
}
}

@SubscribeEvent
fun onRenderOverlay(event: GuiRenderEvent.GuiOverlayRenderEvent) {
if (!isEnabled()) return
if (!FarmingContestAPI.inContest) return

config.pos.renderStrings(contestStats, 2, "Jacob Contest Stats")
}

@SubscribeEvent
fun onFarmingContest(event: FarmingContestEvent) {
if (!isEnabled()) return

when (event.phase) {
FarmingContestPhase.START -> {
ChatUtils.chat("Started tracking your Jacob Contest Blocks Per Second!")
startTime = SimpleTimeMark.now()
startTimeIrl = SimpleTimeMark.now()
}

FarmingContestPhase.STOP -> {
val duration = startTime.passedSince()
val blocksPerSecond = (blocksBroken.toDouble() / duration.inWholeSeconds).roundTo(2)
val cropName = event.crop.cropName
ChatUtils.chat("Stats for $cropName Contest:")
val time = duration.format()
ChatUtils.chat("§7Blocks Broken in total: §e${blocksBroken.addSeparators()}")
val color = getBlocksPerSecondColor(blocksPerSecond)
ChatUtils.chat("§7Average Blocks Per Second: $color$blocksPerSecond")
val duration = startTimeIrl.passedSince()
val durationInSeconds = duration.toDouble(DurationUnit.SECONDS)
val blocksPerSecond = (blocksBroken.toDouble() / durationInSeconds).roundTo(2)
val time = startTimeIrl.passedSince().format()
val formattedPercent = (percent?.times(100))?.prettyRound(1)
val position = if (percent == null) "§eNo data yet" else "Top $medalColor$formattedPercent%"

ChatUtils.chat("§l$cropName Contest Stats")
ChatUtils.chat("§7Participated for §b$time")
ChatUtils.chat("§7Total Blocks Broken: §e${blocksBroken.addSeparators()}")
ChatUtils.chat("§7Average Blocks per Second: §c$blocksPerSecond")
ChatUtils.chat("§7Position: $position")
}

FarmingContestPhase.CHANGE -> {
ChatUtils.chat("You changed the crop during the contest, resetting the Blocks Per Second calculation..")
startTime = SimpleTimeMark.now()
startTimeIrl = SimpleTimeMark.now()
}
}
startTimeRelative = null
percent = null
blocksBroken = 0
}

private fun getBlocksPerSecondColor(blocksPerSecond: Double) = if (blocksPerSecond > 19) "§c" else "§a"

fun isEnabled() = GardenAPI.inGarden() && config.jacobContestSummary
}
8 changes: 8 additions & 0 deletions src/main/java/at/hannibal2/skyhanni/utils/NumberUtil.kt
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,14 @@ object NumberUtil {
}
}

/**
* Only shows decimal places if necessary.
*/
fun Double.prettyRound(decimals: Int): String {
val rounded = this.roundTo(decimals)
return (if (rounded % 1 == 0.0) rounded else rounded.toInt()).toString()
}

/**
* This code was unmodified and taken under CC BY-SA 3.0 license
* @link https://stackoverflow.com/a/22186845
Expand Down
Loading