1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
|
package de.hysky.skyblocker.skyblock.garden;
import de.hysky.skyblocker.config.SkyblockerConfigManager;
import de.hysky.skyblocker.skyblock.tabhud.widget.hud.HudFarmingWidget;
import de.hysky.skyblocker.utils.ItemUtils;
import de.hysky.skyblocker.utils.Location;
import de.hysky.skyblocker.utils.Utils;
import it.unimi.dsi.fastutil.floats.FloatLongPair;
import it.unimi.dsi.fastutil.ints.IntLongPair;
import it.unimi.dsi.fastutil.longs.LongArrayFIFOQueue;
import it.unimi.dsi.fastutil.longs.LongPriorityQueue;
import net.fabricmc.fabric.api.client.message.v1.ClientReceiveMessageEvents;
import net.fabricmc.fabric.api.client.rendering.v1.HudRenderCallback;
import net.fabricmc.fabric.api.event.client.player.ClientPlayerBlockBreakEvents;
import net.minecraft.client.MinecraftClient;
import net.minecraft.item.ItemStack;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.text.NumberFormat;
import java.text.ParseException;
import java.util.ArrayDeque;
import java.util.Deque;
import java.util.Locale;
import java.util.Queue;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class FarmingHud {
private static final Logger LOGGER = LoggerFactory.getLogger(FarmingHud.class);
public static final NumberFormat NUMBER_FORMAT = NumberFormat.getInstance(Locale.US);
private static final Pattern COUNTER = Pattern.compile("Counter: (?<count>[\\d,]+) \\w+");
private static final Pattern FARMING_XP = Pattern.compile("§3\\+(?<xp>\\d+.?\\d*) Farming \\((?<percent>\\d+.?\\d*)%\\)");
private static final Deque<IntLongPair> counter = new ArrayDeque<>();
private static final LongPriorityQueue blockBreaks = new LongArrayFIFOQueue();
private static final Queue<FloatLongPair> farmingXp = new ArrayDeque<>();
private static float farmingXpPercentProgress;
public static void init() {
HudRenderCallback.EVENT.register((context, tickDelta) -> {
if (shouldRender()) {
if (!counter.isEmpty() && counter.peek().rightLong() + 10_000 < System.currentTimeMillis()) {
counter.poll();
}
if (!blockBreaks.isEmpty() && blockBreaks.firstLong() + 1000 < System.currentTimeMillis()) {
blockBreaks.dequeueLong();
}
if (!farmingXp.isEmpty() && farmingXp.peek().rightLong() + 1000 < System.currentTimeMillis()) {
farmingXp.poll();
}
ItemStack stack = MinecraftClient.getInstance().player.getMainHandStack();
Matcher matcher = ItemUtils.getNbtTooltip(stack, FarmingHud.COUNTER);
if (matcher != null) {
try {
int count = NUMBER_FORMAT.parse(matcher.group("count")).intValue();
if (counter.isEmpty() || counter.peekLast().leftInt() != count) {
counter.offer(IntLongPair.of(count, System.currentTimeMillis()));
}
} catch (ParseException e) {
LOGGER.error("[Skyblocker Farming HUD] Failed to parse counter", e);
}
}
HudFarmingWidget.INSTANCE.update();
HudFarmingWidget.INSTANCE.render(context, SkyblockerConfigManager.get().general.tabHud.enableHudBackground);
}
});
ClientPlayerBlockBreakEvents.AFTER.register((world, player, pos, state) -> {
if (shouldRender()) {
blockBreaks.enqueue(System.currentTimeMillis());
}
});
ClientReceiveMessageEvents.GAME.register((message, overlay) -> {
if (shouldRender() && overlay) {
Matcher matcher = FARMING_XP.matcher(message.getString());
if (matcher.matches()) {
try {
farmingXp.offer(FloatLongPair.of(NUMBER_FORMAT.parse(matcher.group("xp")).floatValue(), System.currentTimeMillis()));
farmingXpPercentProgress = NUMBER_FORMAT.parse(matcher.group("percent")).floatValue();
} catch (ParseException e) {
LOGGER.error("[Skyblocker Farming HUD] Failed to parse farming xp", e);
}
}
}
});
}
private static boolean shouldRender() {
return SkyblockerConfigManager.get().locations.garden.farmingHud.enableHud && Utils.getLocation() == Location.GARDEN;
}
public static int counter() {
return counter.isEmpty() ? 0 : counter.peekLast().leftInt();
}
public static float cropsPerMinute() {
if (counter.isEmpty()) {
return 0;
}
IntLongPair first = counter.peek();
IntLongPair last = counter.peekLast();
return (float) (last.leftInt() - first.leftInt()) / (last.rightLong() - first.rightLong()) * 60_000f;
}
public static int blockBreaks() {
return blockBreaks.size();
}
public static float farmingXpPercentProgress() {
return farmingXpPercentProgress;
}
public static double farmingXpPerHour() {
return farmingXp.stream().mapToDouble(FloatLongPair::leftFloat).sum() * 3600;
}
}
|