Skip to content

Commit 8343cb7

Browse files
committed
Add options to InteractiveScriptListener to configure how interactive script notifications are handled
1 parent bdbecd2 commit 8343cb7

File tree

5 files changed

+162
-24
lines changed

5 files changed

+162
-24
lines changed

core/src/main/java/org/mini2Dx/miniscript/core/InteractiveScriptListener.java

Lines changed: 74 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,16 @@
1313
import java.util.concurrent.atomic.AtomicReference;
1414

1515
public class InteractiveScriptListener implements ScriptInvocationListener {
16+
/**
17+
* When true sends notifications then allows next interaction script.
18+
* Defaults to false which allows next interaction script then notifies.
19+
*/
20+
public static boolean NOTIFY_THEN_ALLOW_INTERACTION = false;
21+
/**
22+
* When true waits for game thread notifications to be processed before proceeding
23+
*/
24+
public static boolean WAIT_FOR_GAME_THREAD_NOTIFICATIONS = false;
25+
1626
private final AtomicInteger scriptId = new AtomicInteger();
1727
private final AtomicReference<ScriptInvocationListener> invocationListener = new AtomicReference<>();
1828

@@ -32,69 +42,112 @@ public void track(int scriptId, ScriptInvocationListener invocationListener) {
3242
@Override
3343
public void onScriptBegin(int scriptId) {
3444
final ScriptInvocationListener invocationListener = this.invocationListener.get();
35-
if(scriptId == this.scriptId.get()) {
36-
if(invocationListener != null) {
37-
if (invocationListener.callOnGameThread()) {
38-
scriptingEngine.scriptNotifications.offer(
39-
new ScriptBeginNotification(invocationListener, scriptId));
40-
} else {
41-
invocationListener.onScriptBegin(scriptId);
42-
}
43-
}
45+
if(scriptId != this.scriptId.get()) {
46+
return;
47+
}
48+
if(invocationListener == null) {
49+
return;
50+
}
51+
if (invocationListener.callOnGameThread()) {
52+
scriptingEngine.scriptNotifications.offer(
53+
new ScriptBeginNotification(invocationListener, scriptId));
54+
} else {
55+
invocationListener.onScriptBegin(scriptId);
4456
}
4557
}
4658

4759
@Override
4860
public void onScriptSuccess(int scriptId, ScriptExecutionResult executionResult) {
4961
final ScriptInvocationListener invocationListener = this.invocationListener.get();
50-
if(scriptId == this.scriptId.get()) {
51-
invocationQueue.clearInteractiveScriptStatus();
62+
if(scriptId != this.scriptId.get()) {
63+
return;
64+
}
65+
clearInteractionStatusBeforeNotification();
5266

67+
try {
5368
if (invocationListener != null) {
5469
if (invocationListener.callOnGameThread()) {
55-
scriptingEngine.scriptNotifications.offer(
56-
new ScriptSuccessNotification(invocationListener, scriptId, executionResult));
70+
final ScriptSuccessNotification notification = new ScriptSuccessNotification(
71+
invocationListener, scriptId, executionResult);
72+
scriptingEngine.scriptNotifications.offer(notification);
73+
if(WAIT_FOR_GAME_THREAD_NOTIFICATIONS) {
74+
notification.waitForNotification();
75+
}
5776
} else {
5877
invocationListener.onScriptSuccess(scriptId, executionResult);
5978
}
6079
}
80+
} finally {
81+
clearInteractionStatusAfterNotification();
6182
}
6283
}
6384

6485
@Override
6586
public void onScriptSkipped(int scriptId) {
6687
final ScriptInvocationListener invocationListener = this.invocationListener.get();
67-
if(scriptId == this.scriptId.get()) {
68-
invocationQueue.clearInteractiveScriptStatus();
88+
if(scriptId != this.scriptId.get()) {
89+
return;
90+
}
91+
clearInteractionStatusBeforeNotification();
6992

93+
try {
7094
if (invocationListener != null) {
7195
if (invocationListener.callOnGameThread()) {
72-
scriptingEngine.scriptNotifications
73-
.offer(new ScriptSkippedNotification(invocationListener, scriptId));
96+
final ScriptSkippedNotification notification =
97+
new ScriptSkippedNotification(invocationListener, scriptId);
98+
scriptingEngine.scriptNotifications.offer(notification);
99+
if(WAIT_FOR_GAME_THREAD_NOTIFICATIONS) {
100+
notification.waitForNotification();
101+
}
74102
} else {
75103
invocationListener.onScriptSkipped(scriptId);
76104
}
77105
}
106+
} finally {
107+
clearInteractionStatusAfterNotification();
78108
}
79109
}
80110

81111
@Override
82112
public void onScriptException(int scriptId, Exception e) {
83113
final ScriptInvocationListener invocationListener = this.invocationListener.get();
84-
if(scriptId == this.scriptId.get()) {
85-
invocationQueue.clearInteractiveScriptStatus();
114+
if(scriptId != this.scriptId.get()) {
115+
return;
116+
}
117+
clearInteractionStatusBeforeNotification();
86118

119+
try {
87120
if (invocationListener != null) {
88121
if (invocationListener.callOnGameThread()) {
89-
scriptingEngine.scriptNotifications
90-
.offer(new ScriptExceptionNotification(invocationListener, scriptId, e));
122+
ScriptExceptionNotification notification =
123+
new ScriptExceptionNotification(invocationListener, scriptId, e);
124+
scriptingEngine.scriptNotifications.offer(notification);
125+
if(WAIT_FOR_GAME_THREAD_NOTIFICATIONS) {
126+
notification.waitForNotification();
127+
}
91128
} else {
92129
invocationListener.onScriptException(scriptId, e);
93130
}
94131
} else {
95132
e.printStackTrace();
96133
}
134+
} finally {
135+
clearInteractionStatusAfterNotification();
136+
}
137+
}
138+
139+
private void clearInteractionStatusBeforeNotification() {
140+
if(NOTIFY_THEN_ALLOW_INTERACTION) {
141+
return;
142+
}
143+
invocationQueue.clearInteractiveScriptStatus();
144+
}
145+
146+
private void clearInteractionStatusAfterNotification() {
147+
if(!NOTIFY_THEN_ALLOW_INTERACTION) {
148+
return;
97149
}
150+
invocationQueue.clearInteractiveScriptStatus();
98151
}
99152

100153
public ScriptInvocationListener getInvocationListener() {

core/src/main/java/org/mini2Dx/miniscript/core/notification/ScriptExceptionNotification.java

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,13 +25,16 @@
2525

2626
import org.mini2Dx.miniscript.core.ScriptInvocationListener;
2727

28+
import java.util.concurrent.atomic.AtomicBoolean;
29+
2830
/**
2931
*
3032
*/
3133
public class ScriptExceptionNotification implements ScriptNotification {
3234
private final ScriptInvocationListener invocationListener;
3335
private final int scriptId;
3436
private final Exception exception;
37+
private final AtomicBoolean notified = new AtomicBoolean(false);
3538

3639
public ScriptExceptionNotification(ScriptInvocationListener invocationListener, int scriptId, Exception exception) {
3740
this.invocationListener = invocationListener;
@@ -41,7 +44,24 @@ public ScriptExceptionNotification(ScriptInvocationListener invocationListener,
4144

4245
@Override
4346
public void process() {
44-
invocationListener.onScriptException(scriptId, exception);
47+
try {
48+
invocationListener.onScriptException(scriptId, exception);
49+
} finally {
50+
notified.set(true);
51+
}
52+
53+
synchronized(this) {
54+
notifyAll();
55+
}
4556
}
4657

58+
public void waitForNotification() {
59+
while(!notified.get()) {
60+
synchronized(this) {
61+
try {
62+
wait();
63+
} catch (InterruptedException e) {}
64+
}
65+
}
66+
}
4767
}

core/src/main/java/org/mini2Dx/miniscript/core/notification/ScriptSkippedNotification.java

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,12 +25,15 @@
2525

2626
import org.mini2Dx.miniscript.core.ScriptInvocationListener;
2727

28+
import java.util.concurrent.atomic.AtomicBoolean;
29+
2830
/**
2931
*
3032
*/
3133
public class ScriptSkippedNotification implements ScriptNotification {
3234
private final ScriptInvocationListener invocationListener;
3335
private final int scriptId;
36+
private final AtomicBoolean notified = new AtomicBoolean(false);
3437

3538
public ScriptSkippedNotification(ScriptInvocationListener invocationListener, int scriptId) {
3639
this.invocationListener = invocationListener;
@@ -39,6 +42,24 @@ public ScriptSkippedNotification(ScriptInvocationListener invocationListener, in
3942

4043
@Override
4144
public void process() {
42-
invocationListener.onScriptSkipped(scriptId);
45+
try {
46+
invocationListener.onScriptSkipped(scriptId);
47+
} finally {
48+
notified.set(true);
49+
}
50+
51+
synchronized(this) {
52+
notifyAll();
53+
}
54+
}
55+
56+
public void waitForNotification() {
57+
while(!notified.get()) {
58+
synchronized(this) {
59+
try {
60+
wait();
61+
} catch (InterruptedException e) {}
62+
}
63+
}
4364
}
4465
}

core/src/main/java/org/mini2Dx/miniscript/core/notification/ScriptSuccessNotification.java

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,13 +26,16 @@
2626
import org.mini2Dx.miniscript.core.ScriptExecutionResult;
2727
import org.mini2Dx.miniscript.core.ScriptInvocationListener;
2828

29+
import java.util.concurrent.atomic.AtomicBoolean;
30+
2931
/**
3032
*
3133
*/
3234
public class ScriptSuccessNotification implements ScriptNotification {
3335
private final ScriptInvocationListener invocationListener;
3436
private final int scriptId;
3537
private final ScriptExecutionResult executionResult;
38+
private final AtomicBoolean notified = new AtomicBoolean(false);
3639

3740
public ScriptSuccessNotification(ScriptInvocationListener invocationListener, int scriptId,
3841
ScriptExecutionResult executionResult) {
@@ -43,7 +46,24 @@ public ScriptSuccessNotification(ScriptInvocationListener invocationListener, in
4346

4447
@Override
4548
public void process() {
46-
invocationListener.onScriptSuccess(scriptId, executionResult);
49+
try {
50+
invocationListener.onScriptSuccess(scriptId, executionResult);
51+
} finally {
52+
notified.set(true);
53+
}
54+
55+
synchronized(this) {
56+
notifyAll();
57+
}
4758
}
4859

60+
public void waitForNotification() {
61+
while(!notified.get()) {
62+
synchronized(this) {
63+
try {
64+
wait();
65+
} catch (InterruptedException e) {}
66+
}
67+
}
68+
}
4969
}

core/src/test/java/org/mini2Dx/miniscript/core/ScriptInvocationTest.java

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,30 @@ public void testSamePriority() {
7575
Assert.assertEquals(invocation1, queue.poll());
7676
}
7777

78+
@Test
79+
public void testSamePriorityWithTimestamp() {
80+
final ScriptInvocation invocation1 = new ScriptInvocation(null);
81+
final ScriptInvocation invocation2 = new ScriptInvocation(null);
82+
83+
invocation1.setPriority(0);
84+
invocation2.setPriority(0);
85+
86+
invocation1.setInvokeTimestamp(System.nanoTime());
87+
invocation2.setInvokeTimestamp(invocation1.getInvokeTimestamp() - 1);
88+
89+
queue.offer(invocation1);
90+
queue.offer(invocation2);
91+
92+
Assert.assertEquals(invocation2, queue.poll());
93+
Assert.assertEquals(invocation1, queue.poll());
94+
95+
queue.offer(invocation2);
96+
queue.offer(invocation1);
97+
98+
Assert.assertEquals(invocation2, queue.poll());
99+
Assert.assertEquals(invocation1, queue.poll());
100+
}
101+
78102
@Test
79103
public void testSamePriorityMultiple() {
80104
final List<ScriptInvocation> expectedResults = new ArrayList<>();

0 commit comments

Comments
 (0)