Skip to content

Commit

Permalink
feat(actions): TabComplete action
Browse files Browse the repository at this point in the history
  • Loading branch information
PeyaPeyaPeyang committed Sep 25, 2023
1 parent 25869be commit 982fc10
Show file tree
Hide file tree
Showing 13 changed files with 415 additions and 11 deletions.
1 change: 0 additions & 1 deletion .idea/fileTemplates/PlayerAction.java

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 @@ -20,6 +20,7 @@ public static List<? extends AbstractServerAction<?>> getActions()
actions.add(new PluginEnableAction());
actions.add(new BroadcastMessageAction());
actions.add(new CommandDispatchAction());
actions.add(new TabCompleteAction());
actions.add(new WhitelistToggleAction());

return actions;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@

import lombok.EqualsAndHashCode;
import lombok.Value;
import org.apache.commons.lang.StringUtils;
import org.bukkit.Bukkit;
import org.bukkit.command.CommandSender;
import org.bukkit.event.Event;
Expand All @@ -11,7 +10,7 @@
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.kunlab.scenamatica.action.actions.AbstractActionArgument;
import org.kunlab.scenamatica.action.utils.PlayerUtils;
import org.kunlab.scenamatica.action.utils.CommandSenders;
import org.kunlab.scenamatica.commons.utils.MapUtils;
import org.kunlab.scenamatica.enums.ScenarioType;
import org.kunlab.scenamatica.interfaces.action.types.Executable;
Expand Down Expand Up @@ -43,13 +42,9 @@ public void execute(@NotNull ScenarioEngine engine, @Nullable CommandDispatchAct
{
argument = this.requireArgsNonNull(argument);

CommandSender sender;
if (argument.sender == null)
sender = Bukkit.getConsoleSender();
else
sender = PlayerUtils.getPlayerOrThrow(argument.sender);
CommandSender sender = CommandSenders.resolveSenderOrConsoleOrThrow(argument.getSender());

String command = argument.command;
String command = argument.getCommand();
if (command.startsWith("/")) // シンタックスシュガーのために, / から始まるやつにも対応
command = command.substring(1);

Expand All @@ -73,13 +68,13 @@ else if (event instanceof PlayerCommandPreprocessEvent) // player

if (argument.getCommand() != null)
{
Pattern pattern = Pattern.compile(argument.command);
Pattern pattern = Pattern.compile(argument.getCommand());
Matcher matcher = pattern.matcher(command);
if (!matcher.matches())
return false;
}

return argument.sender == null || sender != null && StringUtils.equalsIgnoreCase(argument.sender, sender.getName());
return CommandSenders.isSpecifiedSender(sender, argument.getSender());
}

@Override
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,161 @@
package org.kunlab.scenamatica.action.actions.server;

import lombok.EqualsAndHashCode;
import lombok.Value;
import org.bukkit.command.CommandSender;
import org.bukkit.event.Event;
import org.bukkit.event.server.TabCompleteEvent;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.kunlab.scenamatica.action.actions.AbstractActionArgument;
import org.kunlab.scenamatica.action.utils.CommandSenders;
import org.kunlab.scenamatica.commons.utils.MapUtils;
import org.kunlab.scenamatica.enums.ScenarioType;
import org.kunlab.scenamatica.interfaces.action.types.Executable;
import org.kunlab.scenamatica.interfaces.action.types.Watchable;
import org.kunlab.scenamatica.interfaces.scenario.ScenarioEngine;
import org.kunlab.scenamatica.interfaces.scenariofile.BeanSerializer;
import org.kunlab.scenamatica.interfaces.scenariofile.trigger.TriggerArgument;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class TabCompleteAction extends AbstractServerAction<TabCompleteAction.Argument>
implements Executable<TabCompleteAction.Argument>, Watchable<TabCompleteAction.Argument>
{

public static final String KEY_ACTION_NAME = "tab_complete";

private static boolean checkCompletions(@Nullable List<String> expected, List<String> actual, boolean strict)
{
if (expected == null)
return true;

if (strict && expected.size() != actual.size())
return false;

for (String expectedCompletion : expected)
{
Pattern pattern = Pattern.compile(expectedCompletion);
if (actual.stream().noneMatch(pattern.asPredicate()))
return false;
}

return true;
}

@Override
public String getName()
{
return KEY_ACTION_NAME;
}

@Override
public void execute(@NotNull ScenarioEngine engine, @Nullable Argument argument)
{
argument = this.requireArgsNonNull(argument);

CommandSender sender = CommandSenders.resolveSenderOrConsoleOrThrow(argument.getSender());
String buffer = argument.getBuffer();
List<String> completions = argument.getCompletions();
if (completions == null)
completions = new ArrayList<>(); // Collections.emptyList() だと不変なのでプラグインのイベントハンドラがバグるかも。

TabCompleteEvent event = new TabCompleteEvent(sender, buffer, completions);
engine.getPlugin().getServer().getPluginManager().callEvent(event);
}

@Override
public boolean isFired(@NotNull Argument argument, @NotNull ScenarioEngine engine, @NotNull Event event)
{
assert event instanceof TabCompleteEvent;
TabCompleteEvent e = (TabCompleteEvent) event;

if (argument.getBuffer() != null)
{
String expectedBuffer = argument.getBuffer();
String actualBuffer = e.getBuffer();

Pattern pattern = Pattern.compile(expectedBuffer);
Matcher matcher = pattern.matcher(actualBuffer);

if (!matcher.matches())
return false;
}


return checkCompletions(argument.getCompletions(), e.getCompletions(), argument.isStrict())
&& CommandSenders.isSpecifiedSender(e.getSender(), argument.getSender());
}

@Override
public List<Class<? extends Event>> getAttachingEvents()
{
return Collections.singletonList(
TabCompleteEvent.class
);
}

@Override
public Argument deserializeArgument(@NotNull Map<String, Object> map, @NotNull BeanSerializer serializer)
{
return new Argument(
(String) map.get(Argument.KEY_SENDER),
(String) map.get(Argument.KEY_BUFFER),
MapUtils.getAsListOrNull(map, Argument.KEY_COMPLETIONS),
MapUtils.getOrDefault(map, Argument.KEY_STRICT, true)
);
}

@Value
@EqualsAndHashCode(callSuper = true)
public static class Argument extends AbstractActionArgument
{
public static final String KEY_SENDER = "sender";
public static final String KEY_BUFFER = "buffer";
public static final String KEY_COMPLETIONS = "completions";
public static final String KEY_STRICT = "strict";

String sender;
String buffer;
List<String> completions;
boolean strict;

@Override
public boolean isSame(TriggerArgument argument)
{
if (!(argument instanceof Argument))
return false;

Argument arg = (Argument) argument;

return Objects.equals(this.sender, arg.sender)
&& Objects.equals(this.buffer, arg.buffer)
&& MapUtils.equals(this.completions, arg.completions
);
}

@Override
public void validate(@NotNull ScenarioEngine engine, @NotNull ScenarioType type)
{
if (type == ScenarioType.ACTION_EXECUTE)
throwIfNotPresent(KEY_BUFFER, this.buffer);
}

@Override
public String getArgumentString()
{
return buildArgumentString(
KEY_SENDER, this.sender,
KEY_BUFFER, this.buffer,
KEY_COMPLETIONS, this.completions,
KEY_STRICT, this.strict
);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
package org.kunlab.scenamatica.action.utils;

import org.bukkit.Bukkit;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player;

public class CommandSenders
{
public static final String CONSOLE_SENDER = "<CONSOLE>";

public static boolean isSpecifiedSender(CommandSender actualSender, String expectedSender)
{
if (expectedSender == null)
return true;

if (!(actualSender instanceof Player) && expectedSender.equalsIgnoreCase(CONSOLE_SENDER))
return true;

return actualSender.getName().equalsIgnoreCase(expectedSender);
}

public static CommandSender getCommandSenderOrThrow(String specifier)
{
if (specifier == null)
throw new IllegalArgumentException("specifier is null");

CommandSender sender = getCommandSenderOrNull(specifier);
if (sender == null)
throw new IllegalArgumentException("specifier is invalid");
else
return sender;
}

public static CommandSender getCommandSenderOrNull(String specifier)
{
if (specifier == null)
return null;

if (specifier.equalsIgnoreCase(CONSOLE_SENDER))
return Bukkit.getConsoleSender();

return PlayerUtils.getPlayerOrNull(specifier);
}

public static CommandSender resolveSenderOrConsoleOrThrow(String specifier)
{
if (specifier == null)
return Bukkit.getConsoleSender();

return getCommandSenderOrThrow(specifier);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,9 @@ else if (entry.getValue() instanceof List)

public static boolean equals(List<?> expected, List<?> actual)
{
if (expected == null || actual == null)
return expected == actual;

if (expected.size() != actual.size())
return false;
for (int i = 0; i < expected.size(); i++)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
# noinspection YAMLSchemaValidation
scenamatica: ${project.version}

name: actions_server_tab_complete_2
description: Testing tab_complete action without sender works or not
on:
- type: on_load
- type: manual_dispatch

context:
actors:
- name: Actor001

scenario:
- type: execute
action: tab_complete
with:
sender: Actor001
buffer: /say Hello
completions:
- World!
- type: expect
action: tab_complete
with:
buffer: /say Hello
completions:
- World!

Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
# noinspection YAMLSchemaValidation
scenamatica: ${project.version}

name: actions_server_tab_complete_3
description: Testing tab_complete action without buffer works or not
on:
- type: on_load
- type: manual_dispatch

context:
actors:
- name: Actor001

scenario:
- type: execute
action: tab_complete
with:
sender: Actor001
buffer: /say Hello
completions:
- World!
- type: expect
action: tab_complete
with:
sender: Actor001
completions:
- World!

Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
# noinspection YAMLSchemaValidation
scenamatica: ${project.version}

name: actions_server_tab_complete_4
description: Testing tab_complete action without completions works or not
on:
- type: on_load
- type: manual_dispatch

context:
actors:
- name: Actor001

scenario:
- type: execute
action: tab_complete
with:
sender: Actor001
buffer: /say Hello
completions:
- World!
- type: expect
action: tab_complete
with:
sender: Actor001
buffer: /say Hello

Loading

0 comments on commit 982fc10

Please sign in to comment.