Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
57 changes: 55 additions & 2 deletions build.gradle
Original file line number Diff line number Diff line change
@@ -1,9 +1,17 @@
import org.gradle.api.tasks.testing.logging.TestExceptionFormat
import org.gradle.api.tasks.testing.logging.TestLogEvent

plugins {
id 'java'
}

repositories {
mavenLocal()
// mavenLocal() seems to always break on my PC (looking for linux binaries even though I'm on windows):
// Execution failed for task ':test'.
// > Could not resolve all files for configuration ':testRuntimeClasspath'.
// > Could not find lwjgl-opengl-3.3.2-natives-linux-arm64.jar (org.lwjgl:lwjgl-opengl:3.3.2).
// Searched in the following locations:
// file:/C:/Users/Lexer/.m2/repository/org/lwjgl/lwjgl-opengl/3.3.2/lwjgl-opengl-3.3.2-natives-linux-arm64.jar
maven {
url = 'https://repo.runelite.net'
}
Expand All @@ -21,11 +29,13 @@ dependencies {
testImplementation 'junit:junit:4.12'
testImplementation group: 'net.runelite', name:'client', version: runeLiteVersion
testImplementation group: 'net.runelite', name:'jshell', version: runeLiteVersion
testImplementation 'org.mockito:mockito-core:3.1.0'
testImplementation 'com.google.inject.extensions:guice-testlib:4.1.0'
}


java {
targetCompatibility = "1.11"
targetCompatibility = "1.11"
}

group = 'com.attacktimer'
Expand All @@ -35,3 +45,46 @@ tasks.withType(JavaCompile) {
options.encoding = 'UTF-8'
options.release.set(11)
}

// Code based on https://gist.github.com/lwasyl/6ae0b8a66903729033e18ffc7a503597#file-fancy_logging_original-gradle
tasks.withType(Test) {
// if no `-Pupdate=all` is passed to gradle then we assume none
def update=project.properties['update'] ?: "none"
// allow specific system properties to be forwarded
systemProperty "update", update
outputs.upToDateWhen { false } // always force tests to be re-run
testLogging {
// set options for log level LIFECYCLE
events TestLogEvent.FAILED,
TestLogEvent.PASSED,
TestLogEvent.SKIPPED,
TestLogEvent.STANDARD_OUT
exceptionFormat TestExceptionFormat.FULL
showExceptions true
showCauses true
showStackTraces true

// set options for log level DEBUG and INFO
debug {
events TestLogEvent.STARTED,
TestLogEvent.FAILED,
TestLogEvent.PASSED,
TestLogEvent.SKIPPED,
TestLogEvent.STANDARD_ERROR,
TestLogEvent.STANDARD_OUT
exceptionFormat TestExceptionFormat.FULL
}
info.events = debug.events
info.exceptionFormat = debug.exceptionFormat

afterSuite { desc, result ->
if (!desc.parent)
{ // will match the outermost suite
def output = "Results: ${result.resultType} (${result.testCount} tests, ${result.successfulTestCount} successes, ${result.failedTestCount} failures, ${result.skippedTestCount} skipped)"
def startItem = '| ', endItem = ' |'
def repeatLength = startItem.length() + output.length() + endItem.length()
println('\n' + ('-' * repeatLength) + '\n' + startItem + output + endItem + '\n' + ('-' * repeatLength))
}
}
}
}
54 changes: 43 additions & 11 deletions src/main/java/com/attacktimer/AttackTimerMetronomePlugin.java
Original file line number Diff line number Diff line change
Expand Up @@ -31,14 +31,20 @@
import com.attacktimer.VariableSpeed.VariableSpeed;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import com.google.common.io.ByteArrayDataOutput;
import com.google.inject.Provides;
import java.awt.Color;
import java.awt.Dimension;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.channels.SeekableByteChannel;
import java.nio.charset.StandardCharsets;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.regex.Pattern;
import java.util.stream.Stream;

import javax.inject.Inject;
import net.runelite.api.Actor;
Expand Down Expand Up @@ -105,11 +111,7 @@ public enum AttackState {

public int tickPeriod = 0;

final int ATTACK_DELAY_NONE = 0;

private int uiUnshowDebounceTickCount = 0;
private int uiUnshowDebounceTicksMax = 1;

public int attackDelayHoldoffTicks = ATTACK_DELAY_NONE;

public AttackState attackState = AttackState.NOT_ATTACKING;
Expand All @@ -120,7 +122,17 @@ public enum AttackState {

public Color CurrentColor = Color.WHITE;

public int DEFAULT_SIZE_UNIT_PX = 25;
private Spellbook currentSpellBook = Spellbook.STANDARD;
private int lastEquippingMonotonicValue = -1;
private int soundEffectTick = -1;
private int soundEffectId = -1;

public int pendingEatDelayTicks = 0;


private static final int uiUnshowDebounceTicksMax = 1;
private static final int ATTACK_DELAY_NONE = 0;
public static final int DEFAULT_SIZE_UNIT_PX = 25;

public static final int SALAMANDER_SET_ANIM_ID = 952; // Used by all 4 types of salamander https://oldschool.runescape.wiki/w/Salamander

Expand Down Expand Up @@ -157,13 +169,8 @@ public enum AttackState {
private final int FAST_EAT_ATTACK_DELAY_TICKS = 2;

public static final int EQUIPPING_MONOTONIC = 384; // From empirical testing this clientint seems to always increase whenever the player equips an item
private Spellbook currentSpellBook = Spellbook.STANDARD;
private int lastEquippingMonotonicValue = -1;
private int soundEffectTick = -1;
private int soundEffectId = -1;
public Dimension DEFAULT_SIZE = new Dimension(DEFAULT_SIZE_UNIT_PX, DEFAULT_SIZE_UNIT_PX);
public static final Dimension DEFAULT_SIZE = new Dimension(DEFAULT_SIZE_UNIT_PX, DEFAULT_SIZE_UNIT_PX);

private int pendingEatDelayTicks = 0;

// region subscribers

Expand All @@ -181,6 +188,7 @@ public void onVarbitChanged(VarbitChanged varbitChanged)
@Subscribe
public void onVarClientIntChanged(VarClientIntChanged varClientIntChanged)
{
if (!config.enableMetronome()) return;
final int currentMagicVarBit = client.getVarcIntValue(EQUIPPING_MONOTONIC);
if (currentMagicVarBit <= lastEquippingMonotonicValue)
{
Expand All @@ -206,6 +214,7 @@ public void onVarClientIntChanged(VarClientIntChanged varClientIntChanged)
@Subscribe
public void onSoundEffectPlayed(SoundEffectPlayed event)
{
if (!config.enableMetronome()) return;
// event.getSource() will be null if the player cast a spell, it's only for area sounds.
soundEffectTick = client.getTickCount();
soundEffectId = event.getSoundId();
Expand Down Expand Up @@ -401,6 +410,7 @@ public boolean isAttackCooldownPending()
@Subscribe
public void onChatMessage(ChatMessage event)
{
if (!config.enableMetronome()) return;
final String message = event.getMessage();

if (EAT_MESSAGE.matcher(message).find())
Expand All @@ -419,6 +429,7 @@ public void onChatMessage(ChatMessage event)
@Subscribe
public void onInteractingChanged(InteractingChanged interactingChanged)
{
if (!config.enableMetronome()) return;
Actor source = interactingChanged.getSource();
Actor target = interactingChanged.getTarget();

Expand Down Expand Up @@ -453,6 +464,7 @@ private void applyAndClearEats() {
@Subscribe
public void onGameTick(GameTick tick)
{
if (!config.enableMetronome()) return;
VariableSpeed.onGameTick(client, tick);
boolean isAttacking = isPlayerAttacking();
switch (attackState) {
Expand Down Expand Up @@ -510,4 +522,24 @@ protected void shutDown() throws Exception
overlayManager.remove(barOverlay);
attackDelayHoldoffTicks = 0;
}

public void writeState(ByteArrayDataOutput outChannel)
{
StringBuilder sb = new StringBuilder();
// @formatter:off
sb.append("tickPeriod: "); sb.append(this.tickPeriod);sb.append(SEPARATOR);
sb.append("uiUnshowDebounceTickCount: "); sb.append(this.uiUnshowDebounceTickCount);sb.append(SEPARATOR);
sb.append("attackDelayHoldoffTicks: "); sb.append(this.attackDelayHoldoffTicks);sb.append(SEPARATOR);
sb.append("attackState: "); sb.append(this.attackState);sb.append(SEPARATOR);
sb.append("renderedState: "); sb.append(this.renderedState);sb.append(SEPARATOR);
sb.append("pendingEatDelayTicks: "); sb.append(this.pendingEatDelayTicks);sb.append(SEPARATOR);
sb.append("currentSpellBook: "); sb.append(this.currentSpellBook);sb.append(SEPARATOR);
sb.append("lastEquippingMonotonicValue: "); sb.append(this.lastEquippingMonotonicValue);sb.append(SEPARATOR);
sb.append("soundEffectTick: "); sb.append(this.soundEffectTick);sb.append(SEPARATOR);
sb.append("soundEffectId: "); sb.append(this.soundEffectId);sb.append("\n");
// @formatter:on
byte[] bytes = sb.toString().getBytes(StandardCharsets.UTF_8);
outChannel.write(bytes);
}
private static final String SEPARATOR = ", ";
}
27 changes: 18 additions & 9 deletions src/test/java/com/attacktimer/EnumTests.java
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
package com.attacktimer;

/*
* Copyright (c) 2024, Lexer747 <https://github.com/Lexer747>
* Copyright (c) 2024-2026, Lexer747 <https://github.com/Lexer747>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
Expand Down Expand Up @@ -37,21 +37,25 @@ public class EnumTests
public void runtimeCheck()
{
// Ensures every enum has no duplicate values
System.out.println("===== Animation Data =====");
for (AnimationData a : AnimationData.values())
{
System.out.println(a.toString());
System.out.print(a.toString() + ", ");
}
System.out.println("\n===== Powered Staves =====");
for (PoweredStaves a : PoweredStaves.values())
{
System.out.println(a.toString());
System.out.print(a.toString() + ", ");
}
System.out.println("\n===== Casting Sound Data =====");
for (CastingSoundData a : CastingSoundData.values())
{
System.out.println(a.toString());
System.out.print(a.toString() + ", ");
}
System.out.println("\n===== Weapon Type =====");
for (WeaponType a : WeaponType.values())
{
System.out.println(a.toString());
System.out.print(a.toString() + ", ");
}
assertFalse(PoweredStaves.LOCAL_DEBUGGING);
}
Expand All @@ -64,22 +68,27 @@ public void missingIdReport()
String fails = new String();
for (PoweredStaves staff : PoweredStaves.values())
{
for (int id : staff.getIds()) {
for (int id : staff.getIds())
{
boolean containsKey = PoweredStaves.poweredStaves.get(id).containsKey(PoweredStaves.UNKNOWN_SPELL);
if (containsKey)
{
failed = true;
fails += MessageFormatter.format("Staff | {} IDs:{} doesn't have a spell associated\n", staff, staff.getIds()).getMessage();
fails += MessageFormatter
.format("Staff | {} IDs:{} doesn't have a spell associated\n", staff, staff.getIds())
.getMessage();
}
break;
}
if (staff.getProjectiles() == null)
{
fails += MessageFormatter.format("Staff | {} doesn't have a projectile associated\n", staff).getMessage();
fails += MessageFormatter.format("Staff | {} doesn't have a projectile associated\n", staff)
.getMessage();
}
}

if (failed) {
if (failed)
{
fail(fails);
}
}
Expand Down
Loading
Loading