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

Combined beta changes #449

Merged
merged 29 commits into from
Oct 8, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
e672f1e
Refactor of how classes are instantiated
xpdota Apr 27, 2023
5f506be
Merge remote-tracking branch 'origin/master' into refactor-loading
xpdota Jul 12, 2023
92cd0fe
Merge branch 'master' into refactor-loading
xpdota Sep 27, 2023
3fea0a5
Run on startup request
xpdota Sep 27, 2023
479ebd1
Actions for waiting until a buff/cast duration falls below a certain …
xpdota Sep 27, 2023
d3741ac
Merge pull request #438 from xpdota/run-on-startup-request
xpdota Sep 27, 2023
8c1afd3
Sequential and easy trigger concurrency modes
xpdota Sep 27, 2023
fe40e1c
Changelog
xpdota Sep 27, 2023
efdc901
Merge pull request #440 from xpdota/sequential-trigger-concurrency
xpdota Sep 27, 2023
2c5395f
Fix hyperlinks in changelog area
xpdota Sep 27, 2023
8280c34
Fix changelog area
xpdota Sep 27, 2023
5e3b6af
Merge pull request #441 from xpdota/changelog-hyperlink-fix
xpdota Sep 27, 2023
002efa3
Possibly working package loading refactor
xpdota Sep 28, 2023
39cb102
Fix for tests
xpdota Sep 28, 2023
99f58e4
Merge remote-tracking branch 'origin/beta' into refactor-loading
xpdota Sep 28, 2023
32b56e1
Merge pull request #439 from xpdota/easy-trigger-duration-waits
xpdota Sep 28, 2023
3661248
Merge pull request #424 from xpdota/refactor-loading
xpdota Sep 28, 2023
d466c09
Clean up module loading
xpdota Sep 28, 2023
32fa8c9
Merge remote-tracking branch 'origin/beta' into refactor-loading
xpdota Sep 28, 2023
222c030
Fix changelog
xpdota Sep 28, 2023
cdc09be
Removed debug logging
xpdota Sep 28, 2023
afc6ecf
Groovy callout enhancements
xpdota Sep 29, 2023
65cab71
Fix typo
xpdota Sep 29, 2023
27de539
Merge pull request #443 from xpdota/groovy-callout-enhancements
xpdota Oct 1, 2023
87c5494
Merge remote-tracking branch 'origin/beta' into refactor-loading
xpdota Oct 6, 2023
5f28dd1
Fix merge conflict
xpdota Oct 6, 2023
2156d20
Merge pull request #442 from xpdota/refactor-loading
xpdota Oct 7, 2023
c43f7bd
Fix merge conflicts
xpdota Oct 7, 2023
78bc652
Merge remote-tracking branch 'origin/master' into beta
xpdota Oct 8, 2023
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
3 changes: 3 additions & 0 deletions .idea/codeStyles/Project.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,8 @@
import gg.xp.xivsupport.events.triggers.easytriggers.actions.GroovyAction;
import gg.xp.xivsupport.events.triggers.easytriggers.actions.SoundAction;
import gg.xp.xivsupport.events.triggers.easytriggers.actions.WaitAction;
import gg.xp.xivsupport.events.triggers.easytriggers.actions.WaitBuffDurationAction;
import gg.xp.xivsupport.events.triggers.easytriggers.actions.WaitCastDurationAction;
import gg.xp.xivsupport.events.triggers.easytriggers.actions.gui.ConditionalActionEditor;
import gg.xp.xivsupport.events.triggers.easytriggers.actions.gui.GroovyActionEditor;
import gg.xp.xivsupport.events.triggers.easytriggers.actions.gui.SoundActionEditor;
Expand Down Expand Up @@ -427,12 +429,15 @@ private Component generic(Object object, Object trigger) {
new ConditionDescription<>(ZoneIdFilter.class, Object.class, "Restrict the Zone ID in which this trigger may run", () -> new ZoneIdFilter(inject(XivState.class)), this::generic)
));

// XXX - DO NOT CHANGE NAMES OF THESE CLASSES OR PACKAGE PATH - FQCN IS PART OF DESERIALIZATION!!!
private final List<ActionDescription<?, ?>> actions = new ArrayList<>(List.of(
new ActionDescription<>(CalloutAction.class, Event.class, "Basic TTS/Text Callout", CalloutAction::new, (callout, trigger) -> new CalloutActionPanel(callout)),
new ActionDescription<>(DurationBasedCalloutAction.class, HasDuration.class, "Duration-Based TTS/Text Callout", DurationBasedCalloutAction::new, (callout, trigger) -> new CalloutActionPanel(callout)),
new ActionDescription<>(AutoMarkTargetAction.class, HasTargetEntity.class, "Mark The Target", () -> new AutoMarkTargetAction(inject(GlobalUiRegistry.class)), this::generic),
new ActionDescription<>(ClearAllMarksAction.class, Event.class, "Clear All Marks", () -> new ClearAllMarksAction(inject(GlobalUiRegistry.class)), this::generic),
new ActionDescription<>(WaitAction.class, BaseEvent.class, "Wait a fixed time", WaitAction::new, this::generic),
new ActionDescription<>(WaitBuffDurationAction.class, BuffApplied.class, "Wait until buff duration falls below a specified amount", () -> new WaitBuffDurationAction(inject(StatusEffectRepository.class)), this::generic),
new ActionDescription<>(WaitCastDurationAction.class, AbilityCastStart.class, "Wait until cast duration falls below a specified amount", WaitCastDurationAction::new, this::generic),
new ActionDescription<>(GroovyAction.class, Event.class, "Custom script action", () -> new GroovyAction(inject(GroovyManager.class)), (action, trigger) -> new GroovyActionEditor<>(action, trigger)),
// (ActionDescription<ConditionalAction<BaseEvent>, BaseEvent>) new ActionDescription<>(ConditionalAction.class, BaseEvent.class, "If/Else Conditional Action", ConditionalAction::new, (action, trigger) -> new ConditionalActionEditor(this, action)),
new ActionDescription<>(SoundAction.class, Event.class, "Play Sound", SoundAction::new, (action, trigger) -> new SoundActionEditor(inject(SoundFilesManager.class), inject(SoundFileTab.class), action)),
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
package gg.xp.xivsupport.events.triggers.easytriggers.actions;

import com.fasterxml.jackson.annotation.JacksonInject;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.OptBoolean;
import gg.xp.xivsupport.events.actlines.events.AbilityCastStart;
import gg.xp.xivsupport.events.actlines.events.BuffApplied;
import gg.xp.xivsupport.events.actlines.events.HasStatusEffect;
import gg.xp.xivsupport.events.state.combatstate.StatusEffectRepository;
import gg.xp.xivsupport.events.triggers.easytriggers.conditions.Description;
import gg.xp.xivsupport.events.triggers.easytriggers.model.EasyTriggerContext;
import gg.xp.xivsupport.events.triggers.easytriggers.model.SqAction;
import gg.xp.xivsupport.events.triggers.marks.gui.AutoMarkGui;
import gg.xp.xivsupport.events.triggers.seq.SequentialTriggerController;
import gg.xp.xivsupport.gui.nav.GlobalUiRegistry;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class WaitBuffDurationAction implements SqAction<BuffApplied> {

private static final Logger log = LoggerFactory.getLogger(WaitBuffDurationAction.class);

@JsonIgnore
private final StatusEffectRepository buffs;

public WaitBuffDurationAction(@JacksonInject(useInput = OptBoolean.FALSE) StatusEffectRepository buffs) {
this.buffs = buffs;
}

@Description("Remaining Duration")
public long remainingDurationMs = 1000;
@Description("Stop Trigger if Buff Removed")
public boolean stopIfGone;

@Override
public String fixedLabel() {
return "Wait Until Buff Duration Below";
}

@Override
public String dynamicLabel() {
return "Wait until remaining cast duration <= %sms".formatted(remainingDurationMs);
}

@Override
public void accept(SequentialTriggerController<BuffApplied> stc, EasyTriggerContext context, BuffApplied event) {
while (true) {
BuffApplied latest = buffs.getLatest(event);
if (latest == null) {
if (stopIfGone) {
context.setStopProcessing(true);
}
return;
}
else {
// TODO: this does not handle the case of a buff being replaced with one of a shorter duration
long msToWait = latest.getEstimatedRemainingDuration().minusMillis(remainingDurationMs).toMillis();
if (msToWait > 0) {
stc.waitMs(msToWait);
}
else {
return;
}
}
}
}

@Override
public void accept(EasyTriggerContext context, BuffApplied event) {
// Handled above
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
package gg.xp.xivsupport.events.triggers.easytriggers.actions;

import gg.xp.reevent.events.BaseEvent;
import gg.xp.xivsupport.events.actlines.events.AbilityCastStart;
import gg.xp.xivsupport.events.triggers.easytriggers.conditions.Description;
import gg.xp.xivsupport.events.triggers.easytriggers.model.EasyTriggerContext;
import gg.xp.xivsupport.events.triggers.easytriggers.model.SqAction;
import gg.xp.xivsupport.events.triggers.seq.SequentialTriggerController;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class WaitCastDurationAction implements SqAction<AbilityCastStart> {

private static final Logger log = LoggerFactory.getLogger(WaitCastDurationAction.class);

@Description("Remaining Duration")
public long remainingDurationMs = 1000;

@Override
public String fixedLabel() {
return "Wait Until Cast Duration Below";
}

@Override
public String dynamicLabel() {
return "Wait until remaining cast duration <= %sms".formatted(remainingDurationMs);
}

@Override
public void accept(SequentialTriggerController<AbilityCastStart> stc, EasyTriggerContext context, AbilityCastStart event) {
long msToWait = event.getEstimatedRemainingDuration().minusMillis(remainingDurationMs).toMillis();
if (msToWait > 0) {
stc.waitMs(msToWait);
}
}

@Override
public void accept(EasyTriggerContext context, AbilityCastStart event) {
// Handled above
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,12 @@
@Retention(RetentionPolicy.RUNTIME)
public @interface IdType {
/**
* @return The class of item that the ID corresponds to.
* @return The class of instance that the ID corresponds to.
*/
Class<?> value();

/**
* @return True if a mapping from the given ID to a concrete item is required. False if you
* @return True if a mapping from the given ID to a concrete instance is required. False if you
* want to accept non-matched items.
*/
boolean matchRequired() default true;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,12 @@
import gg.xp.xivsupport.events.triggers.easytriggers.model.Condition;
import gg.xp.xivsupport.events.triggers.easytriggers.model.EasyTrigger;
import gg.xp.xivsupport.events.triggers.easytriggers.model.EventDescription;
import gg.xp.xivsupport.events.triggers.seq.SequentialTriggerConcurrencyMode;
import gg.xp.xivsupport.gui.GuiMain;
import gg.xp.xivsupport.gui.TitleBorderFullsizePanel;
import gg.xp.xivsupport.gui.extra.PluginTab;
import gg.xp.xivsupport.gui.library.ChooserDialog;
import gg.xp.xivsupport.gui.lists.FriendlyNameListCellRenderer;
import gg.xp.xivsupport.gui.nav.GlobalUiRegistry;
import gg.xp.xivsupport.gui.overlay.RefreshLoop;
import gg.xp.xivsupport.gui.tables.CustomColumn;
Expand Down Expand Up @@ -398,7 +400,17 @@ private class TriggerConfigPanel extends JPanel {
add(GuiUtil.labelFor("Event", eventTypeField), c);
c.gridx++;
add(eventTypeField, c);

c.gridy++;
c.gridx = 0;
JComboBox<SequentialTriggerConcurrencyMode> concModeSelector = new JComboBox<>(SequentialTriggerConcurrencyMode.values());
concModeSelector.setRenderer(new FriendlyNameListCellRenderer());
concModeSelector.setSelectedItem(trigger.getConcurrency());
concModeSelector.addItemListener(l -> {
trigger.setConcurrency((SequentialTriggerConcurrencyMode) concModeSelector.getSelectedItem());
});
add(GuiUtil.labelFor("Concurrency", concModeSelector), c);
c.gridx++;
add(concModeSelector, c);

c.gridx = 0;
c.gridy++;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
import gg.xp.reevent.events.Event;
import gg.xp.reevent.events.EventContext;
import gg.xp.xivsupport.events.triggers.seq.SequentialTrigger;
import gg.xp.xivsupport.events.triggers.seq.SequentialTriggerConcurrencyMode;
import gg.xp.xivsupport.events.triggers.seq.SqtTemplates;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
Expand All @@ -30,57 +31,62 @@ public class EasyTrigger<X> implements HasMutableConditions<X>, HasMutableAction
@JsonProperty(defaultValue = "true")
private boolean enabled = true;

@JsonProperty
private SequentialTriggerConcurrencyMode concurrency = SequentialTriggerConcurrencyMode.BLOCK_NEW;

private Class<X> eventType = (Class<X>) Event.class;
private List<Condition<? super X>> conditions = Collections.emptyList();
private List<Action<? super X>> actions = Collections.emptyList();
private String name = "Give me a name";
private int timeoutMs = 600_000;

// To account for the fact that the SQ might be recalculated while running,
// sqCurrent holds whatever is running, while sqBase holds the template
// TODO: unit test for this
private SequentialTrigger<BaseEvent> sqBase = SqtTemplates.nothing();
private SequentialTrigger<BaseEvent> sqCurrent = sqBase;
private EasyTriggerContext ctx;

public EasyTrigger() {
recalc();
// TODO: unit test for updating trigger while running
recalcFully();
}

public void handleEvent(EventContext context, Event event) {
if (!(event instanceof BaseEvent)) {
return;
}
if (sqCurrent.isActive()) {
sqCurrent.feed(context, (BaseEvent) event);
}
if (!enabled || eventType == null || !eventType.isInstance(event)) {
if (!(event instanceof BaseEvent) || !enabled || eventType == null) {
return;
}
X typedEvent = eventType.cast(event);
ctx = new EasyTriggerContext(context, this);
if (conditions.stream().allMatch(cond -> cond.test(ctx, typedEvent))) {
hits++;
sqCurrent = sqBase;
sqCurrent.feed(context, (BaseEvent) event);
}
else {
misses++;
}
sqBase.feed(context, (BaseEvent) event);
}

public void recalc() {
makeWritable();
conditions.sort(Comparator.comparing(Condition::sortOrder));
conditions.forEach(Condition::recalc);
actions.forEach(Action::recalc);
private boolean matchesStartCondition(X event) {
return conditions.stream().allMatch(cond -> cond.test(ctx, event));
}

private void recalcFully() {
sqBase = SqtTemplates.sq(timeoutMs,
eventType,
// The start condition is handled externally
se -> true,
event -> {
X typedEvent = eventType.cast(event);
if (matchesStartCondition(typedEvent)) {
hits++;
return true;
}
else {
misses++;
return false;
}
},
(e1, s) -> {
ctx.runActions((List) actions, s, (BaseEvent) e1);
});
recalc();
}

public void recalc() {
makeWritable();
conditions.sort(Comparator.comparing(Condition::sortOrder));
conditions.forEach(Condition::recalc);
actions.forEach(Action::recalc);
sqBase.setConcurrency(concurrency);
Stream.concat(conditions.stream(), actions.stream()).forEach(item -> {
if (item instanceof HasMutableEventType het) {
het.setEventType(getEventType());
Expand All @@ -95,7 +101,7 @@ public Class<X> getEventType() {

public void setEventType(Class<X> eventType) {
this.eventType = eventType;
recalc();
recalcFully();
}

@Override
Expand Down Expand Up @@ -142,6 +148,15 @@ public void removeCondition(Condition<? super X> condition) {
recalc();
}

public SequentialTriggerConcurrencyMode getConcurrency() {
return concurrency;
}

public void setConcurrency(SequentialTriggerConcurrencyMode concurrency) {
this.concurrency = concurrency;
recalc();
}

@Override
public List<Action<? super X>> getActions() {
return Collections.unmodifiableList(actions);
Expand Down Expand Up @@ -211,4 +226,70 @@ public void setEnabled(boolean enabled) {
// newTrigger.setConditions(new ArrayList<>(conditions));
// return newTrigger;
// }
// public void handleEventOld(EventContext context, Event event) {
// if (!(event instanceof BaseEvent)) {
// return;
// }
// switch (concurrency) {
// // Mirrors standard sequential trigger logic
// case BLOCK_NEW -> {
// // Block new - if currently active, feed.
// if (sqCurrent.isActive()) {
// sqCurrent.feed(context, (BaseEvent) event);
// // TODO: shouldn't there be a 'return' right here?
// return;
// }
// if (!enabled || eventType == null || !eventType.isInstance(event)) {
// return;
// }
// X typedEvent = eventType.cast(event);
// ctx = new EasyTriggerContext(context, this);
// if (matchesStartCondition(typedEvent)) {
// hits++;
// sqCurrent = sqBase;
// sqCurrent.feed(context, (BaseEvent) event);
// }
// else {
// misses++;
// }
// }
// case REPLACE_OLD -> {
// if (!enabled || eventType == null || !eventType.isInstance(event)) {
// return;
// }
// X typedEvent = eventType.cast(event);
// ctx = new EasyTriggerContext(context, this);
// if (matchesStartCondition(typedEvent)) {
// hits++;
// if (sqCurrent != null && sqCurrent.isActive()) {
// sqCurrent.stopSilently();
// }
// sqCurrent = sqBase;
// sqCurrent.feed(context, (BaseEvent) event);
// }
// else {
// if (sqCurrent.isActive()) {
// sqCurrent.feed(context, (BaseEvent) event);
// }
// misses++;
// }
// }
// case CONCURRENT -> {
// X typedEvent = eventType.cast(event);
// ctx = new EasyTriggerContext(context, this);
// var iter = sqMultiCurrent.iterator();
// while (iter.hasNext()) {
// SequentialTrigger<BaseEvent> next = iter.next();
// next.feed(context, (BaseEvent) event);
// if (!next.isActive()) {
// iter.remove();
// }
// }
// if (matchesStartCondition(typedEvent)) {
// hits++;
// sqMultiCurrent.add()
// }
// }
// }
// }
}
Loading
Loading