Skip to content

Commit 2109cca

Browse files
committed
refactor(skyblock): improve robustness and code quality
- Refactor AntiAFK to use a queue-based thread system to prevent thread leaks and improve reliability. - Configurable trigger slot for AntiAFK. - Modularize StrafingScript in main.ts with generic config setters and broken-down tick logic. - Improve type safety and readability across both files. - Fix potential race conditions in event listeners.
1 parent 08703ee commit 2109cca

File tree

2 files changed

+381
-444
lines changed

2 files changed

+381
-444
lines changed

src/skyblock/AntiAFK.ts

Lines changed: 113 additions & 62 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,21 @@
1-
21
import Config from '../libs/Config';
32
import { Key } from '../libs/KeyManager';
43
import { CommandManager } from '../libs/CommandBuilderWrapper';
54

65
interface AntiAFKConfig {
76
enabled: boolean;
7+
triggerSlot: number;
88
}
99

1010
export class AntiAFK {
1111
private readonly configPath = './config/jayc331-antiafk.json';
1212
private readonly scriptId = 'antiafk';
1313
private readonly defaultConfig: AntiAFKConfig = {
1414
enabled: true,
15+
triggerSlot: 15,
1516
};
1617

1718
private config: AntiAFKConfig;
18-
private readonly triggerSlot = 15;
1919
private isActive = false;
2020
private lastTitleTime = 0;
2121

@@ -24,9 +24,14 @@ export class AntiAFK {
2424
sneak: new Key('key.keyboard.left.shift'),
2525
attack: new Key('key.mouse.left'),
2626
};
27-
27+
2828
private onActivityChange?: (active: boolean) => void;
2929

30+
// Queue system
31+
private actionQueue: string[] = [];
32+
private isProcessingQueue = false;
33+
private processingThread: any = null; // Java Thread
34+
3035
constructor(onActivityChange?: (active: boolean) => void) {
3136
this.onActivityChange = onActivityChange;
3237
this.config = Config.readConfig(this.configPath, this.defaultConfig, this.scriptId);
@@ -35,20 +40,69 @@ export class AntiAFK {
3540
}
3641

3742
private registerListeners() {
38-
JsMacros.on('OpenScreen', JavaWrapper.methodToJava((event) => this.onOpenScreen(event)));
39-
JsMacros.on('Tick', JavaWrapper.methodToJava((event) => this.onTick(event)));
40-
this.listenForTitle();
43+
JsMacros.on(
44+
'OpenScreen',
45+
JavaWrapper.methodToJava((event) => this.onOpenScreen(event as Events.OpenScreen))
46+
);
47+
JsMacros.on(
48+
'Tick',
49+
JavaWrapper.methodToJava((event) => this.onTick(event as Events.Tick))
50+
);
51+
JsMacros.on(
52+
'Title',
53+
JavaWrapper.methodToJava((event) => this.onTitleEvent(event))
54+
);
55+
}
56+
57+
private onTitleEvent(event: any) {
58+
if (!this.config.enabled) return;
59+
// event.message is an IChatComponent, check docs if getString() is correct.
60+
// Usually getString() or getUnformattedText() work.
61+
const text = event.message ? event.message.getString() : '';
62+
if (!text) return;
63+
64+
const lowerText = text.toLowerCase();
65+
// If we are already active, we process everything. If not, we only process triggers.
66+
if (!this.isActive && !this.isRelevantTitle(lowerText)) return;
67+
68+
this.actionQueue.push(text);
69+
this.processQueue();
70+
}
71+
72+
private isRelevantTitle(lowerText: string): boolean {
73+
return (
74+
lowerText.includes('look') ||
75+
lowerText.includes('jump') ||
76+
lowerText.includes('sneak') ||
77+
lowerText.includes('punch') ||
78+
lowerText.includes('attack')
79+
);
4180
}
4281

43-
private listenForTitle() {
44-
JsMacros.once('Title', JavaWrapper.methodToJava((event) => {
45-
new (Packages.java.lang.Thread as any)(() => {
46-
this.onTitle(event);
47-
this.listenForTitle();
48-
}).start();
49-
}));
82+
private processQueue() {
83+
if (this.isProcessingQueue) return;
84+
if (this.actionQueue.length === 0) return;
85+
86+
this.isProcessingQueue = true;
87+
88+
this.processingThread = new (Packages.java.lang.Thread as any)(() => {
89+
try {
90+
while (this.actionQueue.length > 0) {
91+
const text = this.actionQueue.shift();
92+
if (text) this.onTitle(text);
93+
// Small delay between actions to prevent overlapping inputs
94+
Client.waitTick(2);
95+
}
96+
} catch (e) {
97+
Chat.log('§cAntiAFK Thread Error: ' + e);
98+
} finally {
99+
this.isProcessingQueue = false;
100+
this.processingThread = null;
101+
}
102+
});
103+
this.processingThread.start();
50104
}
51-
105+
52106
private setActive(active: boolean) {
53107
if (this.isActive === active) return;
54108
this.isActive = active;
@@ -65,6 +119,7 @@ export class AntiAFK {
65119
// 10 seconds timeout
66120
if (Date.now() - this.lastTitleTime > 10000) {
67121
this.setActive(false);
122+
this.actionQueue = []; // Clear queue on timeout
68123
}
69124
}
70125

@@ -75,59 +130,46 @@ export class AntiAFK {
75130
private onOpenScreen(event: Events.OpenScreen) {
76131
if (!this.config.enabled) return;
77132

78-
// We could add a check for specific GUI title if known, but for now rely on slot click
79-
// Use a small delay to ensure the GUI is fully ready and item is present
80-
const screen = event.screen;
81-
// Note: strict typings might be tricky with screen, check actual API if needed.
82-
// But usually we can access inventory from player context once screen is open.
133+
// Run logic in a thread because we use waitTick/sleep
134+
new (Packages.java.lang.Thread as any)(() => {
135+
try {
136+
this.handleOpenScreenLogic(event);
137+
} catch (e) {
138+
Chat.log('§cAntiAFK Screen Error: ' + e);
139+
}
140+
}).start();
141+
}
83142

84-
// We wait a bit then click
143+
private handleOpenScreenLogic(event: Events.OpenScreen) {
144+
// Wait a bit for GUI to populate
85145
Client.waitTick(5);
86-
87-
// Check if screen is still open (user might have closed it quickly)
88-
if (!Hud.getOpenScreen()) return;
89146

90-
// Check player to avoid NPE
147+
if (!Hud.getOpenScreen()) return;
91148
if (!Player.getPlayer()) return;
92-
149+
93150
const inv = Player.openInventory();
94151
if (!inv) return;
95-
96-
// Filter for "Activity Check" GUI
97-
if (!inv.getRawContainer().getTitleText().includes('Activity Check')) return;
98-
99-
// We assume if the GUI opens and we are enabled, we try to start the check.
100-
// In a real scenario, we should probably check the container name or item name.
101-
// But based on prompt: "A gui is opened containing an item... click to begin"
102-
103-
// Double check if slot is valid for this inventory?
104-
// inv.getSlot(this.config.triggerSlot) might throw or return null?
105-
152+
153+
const title = inv.getRawContainer().getTitleText();
154+
if (!title || !title.toString().includes('Activity Check')) return;
155+
106156
try {
107-
inv.click(this.triggerSlot);
108-
this.lastTitleTime = Date.now(); // Set time before activating to avoid instant timeout
157+
inv.click(this.config.triggerSlot);
158+
this.lastTitleTime = Date.now();
109159
this.setActive(true);
110160
Chat.log('§aAntiAFK: §7Clicking start slot...');
111161
} catch (e) {
112162
Chat.log('§cAntiAFK Error clicking slot: ' + e);
113163
}
114164
}
115165

116-
private onTitle(event: Events.Title) {
117-
if (!this.config.enabled) return;
118-
119-
if (!event.message) return;
120-
const text = event.message.getString().trim();
121-
const lowerText = text.toLowerCase();
122-
123-
// If we receive a title, and we are (or should be) active, update time
124-
if (this.isActive || lowerText.includes('look') || lowerText.includes('jump') || lowerText.includes('sneak') || lowerText.includes('punch')) {
125-
this.lastTitleTime = Date.now();
126-
if (!this.isActive) this.setActive(true);
127-
}
166+
private onTitle(text: string) {
167+
const textStr = text.trim();
168+
const lowerText = textStr.toLowerCase();
128169

129-
// Logging for debug
130-
// Chat.log('§7Title: ' + text);
170+
// Update activity timestamp
171+
this.lastTitleTime = Date.now();
172+
if (!this.isActive) this.setActive(true);
131173

132174
const player = Player.getPlayer();
133175
if (!player) return;
@@ -140,30 +182,30 @@ export class AntiAFK {
140182
player.lookAt(yaw - 90, player.getPitch());
141183
matched = true;
142184
} else if (lowerText.includes('look right')) {
143-
Chat.log('§aAntiAFK: §7Looking RIGHT');
185+
Chat.log('§aAntiAFK: §7Looking RIGHT');
144186
const yaw = player.getYaw();
145187
player.lookAt(yaw + 90, player.getPitch());
146188
matched = true;
147189
} else if (lowerText.includes('look down')) {
148-
Chat.log('§aAntiAFK: §7Looking DOWN');
190+
Chat.log('§aAntiAFK: §7Looking DOWN');
149191
player.lookAt(player.getYaw(), 90);
150192
matched = true;
151193
} else if (lowerText.includes('look up')) {
152-
Chat.log('§aAntiAFK: §7Looking UP');
194+
Chat.log('§aAntiAFK: §7Looking UP');
153195
player.lookAt(player.getYaw(), -90);
154196
matched = true;
155197
} else if (lowerText.includes('jump')) {
156-
Chat.log('§aAntiAFK: §7Jumping');
198+
Chat.log('§aAntiAFK: §7Jumping');
157199
this.keys.jump.click();
158200
matched = true;
159201
} else if (lowerText.includes('sneak')) {
160-
Chat.log('§aAntiAFK: §7Sneaking');
202+
Chat.log('§aAntiAFK: §7Sneaking');
161203
this.keys.sneak.set(true);
162204
Client.waitTick(10);
163205
this.keys.sneak.set(false);
164206
matched = true;
165207
} else if (lowerText.includes('punch') || lowerText.includes('attack')) {
166-
Chat.log('§aAntiAFK: §7Punching');
208+
Chat.log('§aAntiAFK: §7Punching');
167209
this.keys.attack.click();
168210
matched = true;
169211
}
@@ -176,11 +218,20 @@ export class AntiAFK {
176218
private registerCommands() {
177219
const cmd = CommandManager.create('antiafk');
178220

179-
cmd.literal('toggle')
180-
.executes(() => {
181-
this.config.enabled = !this.config.enabled;
221+
cmd.literal('toggle').executes(() => {
222+
this.config.enabled = !this.config.enabled;
223+
this.saveConfig();
224+
Chat.log(`§7AntiAFK is now ${this.config.enabled ? '§aENABLED' : '§cDISABLED'}`);
225+
});
226+
227+
cmd.literal('set')
228+
.literal('slot')
229+
.argument('slot', 'int')
230+
.executes((ctx) => {
231+
const slot = ctx.getArg('slot');
232+
this.config.triggerSlot = slot;
182233
this.saveConfig();
183-
Chat.log(`§7AntiAFK is now ${this.config.enabled ? '§aENABLED' : '§cDISABLED'}`);
234+
Chat.log(`§7AntiAFK trigger slot set to §a${slot}`);
184235
});
185236

186237
cmd.register();

0 commit comments

Comments
 (0)