Skip to content

Commit

Permalink
Add waitTick built-in to pressKey and pressMouse (#4362)
Browse files Browse the repository at this point in the history
  • Loading branch information
Earthcomputer authored Jan 14, 2025
1 parent 7d48d43 commit 5f21acd
Show file tree
Hide file tree
Showing 5 changed files with 72 additions and 14 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,8 @@ public interface TestInput {
* Starts holding down a key binding. The key binding will be held until it is released. The key binding must be
* bound. Does nothing if the key binding is already being held.
*
* <p><strong>Most key bindings will only start reacting to this when a tick is waited.</strong>
*
* @param keyBinding The key binding to hold
* @see #releaseKey(KeyBinding)
* @see #pressKey(KeyBinding)
Expand All @@ -45,6 +47,8 @@ public interface TestInput {
* Starts holding down a key binding. The key binding will be held until it is released. The key binding must be
* bound. Does nothing if the key binding is already being held.
*
* <p><strong>Most key bindings will only start reacting to this when a tick is waited.</strong>
*
* @param keyBindingGetter The function to get the key binding from the game options
* @see #releaseKey(Function)
* @see #pressKey(Function)
Expand All @@ -56,6 +60,8 @@ public interface TestInput {
* Starts holding down a key or mouse button. The key will be held until it is released. Does nothing if the key or
* mouse button is already being held.
*
* <p><strong>Most key bindings will only start reacting to this when a tick is waited.</strong>
*
* @param key The key or mouse button to hold
* @see #releaseKey(InputUtil.Key)
* @see #pressKey(InputUtil.Key)
Expand All @@ -66,6 +72,8 @@ public interface TestInput {
* Starts holding down a key. The key will be held until it is released. Does nothing if the key is already being
* held.
*
* <p><strong>Most key bindings will only start reacting to this when a tick is waited.</strong>
*
* @param keyCode The key code of the key to hold
* @see #releaseKey(int)
* @see #pressKey(int)
Expand All @@ -76,6 +84,8 @@ public interface TestInput {
* Starts holding down a mouse button. The mouse button will be held until it is released. Does nothing if the mouse
* button is already being held.
*
* <p><strong>Most key bindings will only start reacting to this when a tick is waited.</strong>
*
* @param button The mouse button to hold
* @see #releaseMouse(int)
* @see #pressMouse(int)
Expand Down Expand Up @@ -110,6 +120,8 @@ public interface TestInput {
/**
* Releases a key binding. The key binding must be bound. Does nothing if the key binding is not being held.
*
* <p><strong>Most key bindings will only react to this when a tick is waited.</strong>
*
* @param keyBinding The key binding to release
* @see #holdKey(KeyBinding)
* @see #releaseKey(Function)
Expand All @@ -119,6 +131,8 @@ public interface TestInput {
/**
* Releases a key binding. The key binding must be bound. Does nothing if the key binding is not being held.
*
* <p><strong>Most key bindings will only react to this when a tick is waited.</strong>
*
* @param keyBindingGetter The function to get the key binding from the game options
* @see #holdKey(Function)
* @see #releaseKey(KeyBinding)
Expand All @@ -128,6 +142,8 @@ public interface TestInput {
/**
* Releases a key or mouse button. Does nothing if the key or mouse button is not being held.
*
* <p><strong>Most key bindings will only react to this when a tick is waited.</strong>
*
* @param key The key or mouse button to release
* @see #holdKey(InputUtil.Key)
*/
Expand All @@ -136,6 +152,8 @@ public interface TestInput {
/**
* Releases a key. Does nothing if the key is not being held.
*
* <p><strong>Most key bindings will only react to this when a tick is waited.</strong>
*
* @param keyCode The GLFW key code of the key to release
* @see #holdKey(int)
*/
Expand All @@ -144,6 +162,8 @@ public interface TestInput {
/**
* Releases a mouse button. Does nothing if the mouse button is not being held.
*
* <p><strong>Most key bindings will only react to this when a tick is waited.</strong>
*
* @param button The GLFW mouse button to release
* @see #holdMouse(int)
*/
Expand Down Expand Up @@ -174,7 +194,10 @@ public interface TestInput {
void releaseAlt();

/**
* Presses and releases a key binding. The key binding must be bound.
* Presses and releases a key binding, then waits a tick. The key binding must be bound.
*
* <p>A tick is waited because most key bindings need a tick to happen to react to the press. If you don't want the
* delay, use {@link #holdKeyFor(KeyBinding, int) holdKeyFor} with a tick count of {@code 0}.
*
* @param keyBinding The key binding to press
* @see #holdKey(KeyBinding)
Expand All @@ -183,7 +206,10 @@ public interface TestInput {
void pressKey(KeyBinding keyBinding);

/**
* Presses and releases a key binding. The key binding must be bound.
* Presses and releases a key binding, then waits a tick. The key binding must be bound.
*
* <p>A tick is waited because most key bindings need a tick to happen to react to the press. If you don't want the
* delay, use {@link #holdKeyFor(Function, int) holdKeyFor} with a tick count of {@code 0}.
*
* @param keyBindingGetter The function to get the key binding from the game options
* @see #holdKey(Function)
Expand All @@ -192,15 +218,21 @@ public interface TestInput {
void pressKey(Function<GameOptions, KeyBinding> keyBindingGetter);

/**
* Presses and releases a key or mouse button.
* Presses and releases a key or mouse button, then waits a tick.
*
* <p>A tick is waited because most key bindings need a tick to happen to react to the press. If you don't want the
* delay, use {@link #holdKeyFor(InputUtil.Key, int) holdKeyFor} with a tick count of {@code 0}.
*
* @param key The key or mouse button to press.
* @see #holdKey(InputUtil.Key)
*/
void pressKey(InputUtil.Key key);

/**
* Presses and releases a key.
* Presses and releases a key, then waits a tick.
*
* <p>A tick is waited because most key bindings need a tick to happen to react to the press. If you don't want the
* delay, use {@link #holdKeyFor(int, int) holdKeyFor} with a tick count of {@code 0}.
*
* <p>For sending Unicode text input (e.g. into text boxes), use {@link #typeChar(int)} or
* {@link #typeChars(String)} instead.
Expand All @@ -211,7 +243,10 @@ public interface TestInput {
void pressKey(int keyCode);

/**
* Presses and releases a mouse button.
* Presses and releases a mouse button, then waits a tick.
*
* <p>A tick is waited because most key bindings need a tick to happen to react to the press. If you don't want the
* delay, use {@link #holdMouseFor(int, int) holdMouseFor} with a tick count of {@code 0}.
*
* @param button The GLFW mouse button to press
* @see #holdMouse(int)
Expand All @@ -222,6 +257,9 @@ public interface TestInput {
* Holds a key binding for the specified number of ticks and then releases it. Waits until this process is finished.
* The key binding must be bound.
*
* <p>Although the key will be released when this method returns, <strong>most key bindings will only react to this
* when a tick is waited.</strong>
*
* @param keyBinding The key binding to hold
* @param ticks The number of ticks to hold the key binding for
* @see #holdKey(KeyBinding)
Expand All @@ -233,6 +271,9 @@ public interface TestInput {
* Holds a key binding for the specified number of ticks and then releases it. Waits until this process is finished.
* The key binding must be bound.
*
* <p>Although the key will be released when this method returns, <strong>most key bindings will only react to this
* when a tick is waited.</strong>
*
* @param keyBindingGetter The key binding to hold
* @param ticks The number of ticks to hold the key binding for
* @see #holdKey(Function)
Expand All @@ -244,6 +285,9 @@ public interface TestInput {
* Holds a key or mouse button for the specified number of ticks and then releases it. Waits until this process is
* finished.
*
* <p>Although the key or mouse button will be released when this method returns, <strong>most key bindings will
* only react to this when a tick is waited.</strong>
*
* @param key The key or mouse button to hold
* @param ticks The number of ticks to hold the key or mouse button for
* @see #holdKey(InputUtil.Key)
Expand All @@ -253,6 +297,9 @@ public interface TestInput {
/**
* Holds a key for the specified number of ticks and then releases it. Waits until this process is finished.
*
* <p>Although the key will be released when this method returns, <strong>most key bindings will only react to this
* when a tick is waited.</strong>
*
* @param keyCode The GLFW key code of the key to hold
* @param ticks The number of ticks to hold the key for
* @see #holdKey(int)
Expand All @@ -263,6 +310,9 @@ public interface TestInput {
* Holds a mouse button for the specified number of ticks and then releases it. Waits until this process is
* finished.
*
* <p>Although the mouse button will be released when this method returns, <strong>most key bindings will only react
* to this when a tick is waited.</strong>
*
* @param button The GLFW mouse button to hold
* @param ticks The number of ticks to hold the mouse button for
* @see #holdMouse(int)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,14 @@
*
* <p>The game remains paused unless you explicitly unpause it using various waiting functions such as
* {@link net.fabricmc.fabric.api.client.gametest.v1.ClientGameTestContext#waitTick() ClientGameTestContext.waitTick()}.
* A side effect of this is that <strong>the results of your code may not be immediate if the game needs a tick to
* process them</strong>. A big example of this is key bindings, although some key binding methods have built-in tick
* waits to mitigate the issue. See the {@link net.fabricmc.fabric.api.client.gametest.v1.TestInput TestInput}
* documentation for details. Another pseudo-example is effects on the server need a tick to propagate to the client and
* vice versa, although this is related to packets more than the fact the game is suspended (see the network
* synchronization section below). A good strategy for debugging these issues is by
* {@linkplain net.fabricmc.fabric.api.client.gametest.v1.ClientGameTestContext#takeScreenshot(String) taking screenshots},
* which capture the immediate state of the game.
*
* <p>A few changes have been made to how the vanilla game threads run, to make tests more reproducible. Notably, there
* is exactly one server tick per client tick while a server is running (singleplayer or multiplayer). There is also a
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@
*
* <ul>
* <li>A packet can be either "in-flight", which is between the time it is sent and the time it is handled on the
* Netty thread on the receiving side, or it can be queued for handling on the receiving main thread, which it
* Netty thread on the receiving side, or it can be queued for handling on the receiving main thread, which is
* between when it is handled on the Netty thread and it is removed from the main thread task queue.
* <ul>
* <li>Some packets are handled directly on the Netty thread and never enter the second stage. The
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -209,6 +209,7 @@ public void pressKey(InputUtil.Key key) {

holdKey(key);
releaseKey(key);
context.waitTick();
}

@Override
Expand All @@ -229,7 +230,7 @@ public void pressMouse(int button) {
public void holdKeyFor(KeyBinding keyBinding, int ticks) {
ThreadingImpl.checkOnGametestThread("holdKeyFor");
Preconditions.checkNotNull(keyBinding, "keyBinding");
Preconditions.checkArgument(ticks > 0, "ticks must be positive");
Preconditions.checkArgument(ticks >= 0, "ticks cannot be negative");

holdKeyFor(getBoundKey(keyBinding, "hold"), ticks);
}
Expand All @@ -238,7 +239,7 @@ public void holdKeyFor(KeyBinding keyBinding, int ticks) {
public void holdKeyFor(Function<GameOptions, KeyBinding> keyBindingGetter, int ticks) {
ThreadingImpl.checkOnGametestThread("holdKeyFor");
Preconditions.checkNotNull(keyBindingGetter, "keyBindingGetter");
Preconditions.checkArgument(ticks > 0, "ticks must be positive");
Preconditions.checkArgument(ticks >= 0, "ticks cannot be negative");

KeyBinding keyBinding = context.computeOnClient(client -> keyBindingGetter.apply(client.options));
holdKeyFor(keyBinding, ticks);
Expand All @@ -248,7 +249,7 @@ public void holdKeyFor(Function<GameOptions, KeyBinding> keyBindingGetter, int t
public void holdKeyFor(InputUtil.Key key, int ticks) {
ThreadingImpl.checkOnGametestThread("holdKeyFor");
Preconditions.checkNotNull(key, "key");
Preconditions.checkArgument(ticks > 0, "ticks must be positive");
Preconditions.checkArgument(ticks >= 0, "ticks cannot be negative");

holdKey(key);
context.waitTicks(ticks);
Expand All @@ -258,15 +259,15 @@ public void holdKeyFor(InputUtil.Key key, int ticks) {
@Override
public void holdKeyFor(int keyCode, int ticks) {
ThreadingImpl.checkOnGametestThread("holdKeyFor");
Preconditions.checkArgument(ticks > 0, "ticks must be positive");
Preconditions.checkArgument(ticks >= 0, "ticks cannot be negative");

holdKeyFor(InputUtil.Type.KEYSYM.createFromCode(keyCode), ticks);
}

@Override
public void holdMouseFor(int button, int ticks) {
ThreadingImpl.checkOnGametestThread("holdMouseFor");
Preconditions.checkArgument(ticks > 0, "ticks must be positive");
Preconditions.checkArgument(ticks >= 0, "ticks cannot be negative");

holdKeyFor(InputUtil.Type.MOUSE.createFromCode(button), ticks);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -68,9 +68,8 @@ public void runTest(ClientGameTestContext context) {

{
context.getInput().pressKey(options -> options.chatKey);
context.waitTick();
context.getInput().typeChars("Hello, World!");
context.getInput().pressKey(InputUtil.GLFW_KEY_ENTER);
context.getInput().holdKeyFor(InputUtil.GLFW_KEY_ENTER, 0); // press without delay, enter not a keybind
context.waitTick(); // wait for the server to receive the chat message
context.takeScreenshot("chat_message_sent");
}
Expand All @@ -86,7 +85,7 @@ public void runTest(ClientGameTestContext context) {

{
context.getInput().pressKey(options -> options.inventoryKey);
context.waitTicks(2); // allow the client to process the key press, and then the server to receive the request
context.waitTick(); // wait for the server to receive the request
context.takeScreenshot("in_game_inventory");
context.setScreen(() -> null);
}
Expand Down

0 comments on commit 5f21acd

Please sign in to comment.