Skip to content

Commit d30d544

Browse files
ryanbarlow97claude
andauthored
feat: log MythicLib skill casts and skill teleports (#19)
Log each MythicLib skill a player activates (MMOCore class skills, MMOItems abilities) as a command row at the caster's position, plus a row at the destination when the skill teleports them within 5 seconds. Silent triggers are skipped. "/co lookup a:skill" lists them, f:<name> narrows them, and a:skill only combines with a:command. New player-skills option; MythicLib is read through reflection and added as a softdepend. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
1 parent 202fe6b commit d30d544

10 files changed

Lines changed: 318 additions & 5 deletions

File tree

‎README.md‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ This repository is TF-Minecraft's fork of CoreProtect, developed by [PlayPro](ht
1212
- **Detailed activity searches** — narrow results by player, time, action, location, and affected materials.
1313
- **Targeted recovery** — roll back selected changes and restore previously rolled-back activity.
1414
- **Recovery previews** — review the effect of a rollback or restore before applying it.
15-
- **Broad world coverage** — logging includes player block changes, container contents, explosions, fire, liquids, many entity interactions, and where each online player is once a minute.
15+
- **Broad world coverage** — logging includes player block changes, container contents, explosions, fire, liquids, many entity interactions, skills players use and where they teleport them, and where each online player is once a minute.
1616
- **Staff investigation tools** — paginated lookups, inventory history, and WorldEdit selection support help investigate incidents of different sizes.
1717

1818
CoreProtect supports both investigation and recovery; the recorded history available to staff depends on the server's logging settings.

‎src/main/java/net/tfminecraft/coreprotect/command/LookupCommand.java‎

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929
import net.tfminecraft.coreprotect.language.Selector;
3030
import net.tfminecraft.coreprotect.model.action.EntityActionFilter;
3131
import net.tfminecraft.coreprotect.model.action.LookupActions;
32+
import net.tfminecraft.coreprotect.model.action.SkillLog;
3233
import net.tfminecraft.coreprotect.model.lookup.LookupOutputMode;
3334
import net.tfminecraft.coreprotect.model.lookup.LookupRollbackState;
3435
import net.tfminecraft.coreprotect.utility.Chat;
@@ -52,6 +53,9 @@ public static void runCommand(CommandSender player, Command command, boolean per
5253
ActionParser.ParseResult actionResult = CommandParser.parseActions(args, true);
5354
List<Integer> argAction = actionResult.getActions();
5455
EntityActionFilter argEntityActionFilter = actionResult.getEntityActionFilter();
56+
if (actionResult.isSkillLookup()) {
57+
argFilters = SkillLog.lookupFilters(argFilters);
58+
}
5559
List<Object> argBlocks = CommandParser.parseRestricted(player, args, argAction);
5660
Map<Object, Boolean> argExclude = CommandParser.parseExcluded(player, args, argAction);
5761
List<String> argExcludeUsers = CommandParser.parseExcludedUsers(player, args);
@@ -194,6 +198,11 @@ else if (resultc > 1) {
194198
Chat.sendMessage(player, Color.DARK_AQUA + "CoreProtect " + Color.WHITE + "- " + Phrase.build(Phrase.INVALID_ACTION));
195199
return;
196200
}
201+
// The skill prefix filter would also apply to the other action's rows, such as chat.
202+
if (actionResult.isSkillLookup() && argAction.stream().anyMatch(action -> action != LookupActions.COMMAND)) {
203+
Chat.sendMessage(player, Color.DARK_AQUA + "CoreProtect " + Color.WHITE + "- " + Phrase.build(Phrase.INCOMPATIBLE_ACTION, "a:skill"));
204+
return;
205+
}
197206
if (rollbackState != LookupRollbackState.ANY && (argAction.contains(LookupActions.CHAT) || argAction.contains(LookupActions.COMMAND) || argAction.contains(LookupActions.SESSION) || argAction.contains(LookupActions.USERNAME) || argAction.contains(LookupActions.SIGN))) {
198207
Chat.sendMessage(player, Color.DARK_AQUA + "CoreProtect " + Color.WHITE + "- " + Phrase.build(Phrase.INCOMPATIBLE_ACTION, rollbackState == LookupRollbackState.ROLLED_BACK ? "#rolledback" : "#restored"));
199208
return;

‎src/main/java/net/tfminecraft/coreprotect/command/TabHandler.java‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ public class TabHandler implements TabCompleter {
2525
// private static String[] COMMANDS = new String[] { "help", "inspect", "rollback", "restore", "lookup", "purge", "reload", "status", "near", "undo" }; // max 10!
2626
private static final String[] HELP = new String[] { "inspect", "rollback", "restore", "lookup", "purge", "teleport", "status", "params", "users", "time", "radius", "action", "include", "exclude", "filter" };
2727
private static final String[] PARAMS = new String[] { "user:", "time:", "radius:", "action:", "include:", "exclude:", "filter:", "#container" };
28-
private static final String[] ACTIONS = new String[] { "block", "+block", "-block", "click", "kill", "spawn", "+container", "-container", "container", "chat", "command", "+inventory", "-inventory", "inventory", "item", "+item", "-item", "sign", "session", "+session", "-session", "ping", "username" };
28+
private static final String[] ACTIONS = new String[] { "block", "+block", "-block", "click", "kill", "spawn", "+container", "-container", "container", "chat", "command", "skill", "+inventory", "-inventory", "inventory", "item", "+item", "-item", "sign", "session", "+session", "-session", "ping", "username" };
2929
private static final String[] NUMBERS = new String[] { "0", "1", "2", "3", "4", "5", "6", "7", "8", "9" };
3030
private static final String[] TIMES = new String[] { "w", "d", "h", "m", "s" };
3131
private static ArrayList<String> materials = null;

‎src/main/java/net/tfminecraft/coreprotect/command/parser/ActionParser.java‎

Lines changed: 24 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,8 @@ public static ParseResult parseActions(String[] inputArguments, boolean allowMul
4848
String[] argumentArray = inputArguments.clone();
4949
List<Integer> result = new ArrayList<>();
5050
EntityActionFilter entityActionFilter = EntityActionFilter.DEFAULT;
51+
boolean skillLookup = false;
52+
boolean commandLookup = false;
5153
int count = 0;
5254
int next = 0;
5355
for (String argument : argumentArray) {
@@ -65,6 +67,8 @@ public static ParseResult parseActions(String[] inputArguments, boolean allowMul
6567
else if (next == 1 || argument.startsWith("a:") || argument.startsWith("action:")) {
6668
result.clear();
6769
entityActionFilter = EntityActionFilter.NONE;
70+
skillLookup = false;
71+
commandLookup = false;
6872
argument = argument.replaceAll("action:", "");
6973
argument = argument.replaceAll("a:", "");
7074
if (argument.startsWith("#")) {
@@ -76,6 +80,8 @@ else if (next == 1 || argument.startsWith("a:") || argument.startsWith("action:"
7680
ParseResult parsedAction = parseActions(new String[] { "lookup", "a:" + action }, false);
7781
result.addAll(parsedAction.getActions());
7882
entityActionFilter = entityActionFilter.merge(parsedAction.getEntityActionFilter());
83+
skillLookup |= parsedAction.isSkillLookup();
84+
commandLookup |= parsedAction.getActions().contains(LookupActions.COMMAND) && !parsedAction.isSkillLookup();
7985
}
8086
}
8187
next = 0;
@@ -120,6 +126,12 @@ else if (argument.equals("chat") || argument.equals("chats") || argument.equals(
120126
}
121127
else if (argument.equals("command") || argument.equals("commands")) {
122128
result.add(LookupActions.COMMAND);
129+
commandLookup = true;
130+
}
131+
else if (argument.equals("skill") || argument.equals("skills") || argument.equals("ability") || argument.equals("abilities")) {
132+
// Skill casts are command rows; see SkillLog.
133+
result.add(LookupActions.COMMAND);
134+
skillLookup = true;
123135
}
124136
else if (argument.equals("logins") || argument.equals("login") || argument.equals("+session") || argument.equals("+sessions") || argument.equals("session+") || argument.equals("+connection") || argument.equals("connection+")) {
125137
result.add(LookupActions.SESSION);
@@ -180,17 +192,27 @@ else if (argument.equals("+item") || argument.equals("item+") || argument.equals
180192
}
181193
count++;
182194
}
183-
return new ParseResult(result, entityActionFilter);
195+
return new ParseResult(result, entityActionFilter, skillLookup && !commandLookup);
184196
}
185197

186198
public static final class ParseResult {
187199

188200
private final List<Integer> actions;
189201
private final EntityActionFilter entityActionFilter;
202+
private final boolean skillLookup;
190203

191-
private ParseResult(List<Integer> actions, EntityActionFilter entityActionFilter) {
204+
private ParseResult(List<Integer> actions, EntityActionFilter entityActionFilter, boolean skillLookup) {
192205
this.actions = actions;
193206
this.entityActionFilter = entityActionFilter;
207+
this.skillLookup = skillLookup;
208+
}
209+
210+
/**
211+
* Whether command rows should be limited to skill casts. Asking for commands as well
212+
* keeps every command row.
213+
*/
214+
public boolean isSkillLookup() {
215+
return skillLookup;
194216
}
195217

196218
public List<Integer> getActions() {

‎src/main/java/net/tfminecraft/coreprotect/config/Config.java‎

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,7 @@ public class Config extends Language {
100100
public boolean PLAYER_INTERACTIONS;
101101
public boolean PLAYER_MESSAGES;
102102
public boolean PLAYER_COMMANDS;
103+
public boolean PLAYER_SKILLS;
103104
public boolean PLAYER_SESSIONS;
104105
public boolean UNKNOWN_LOGGING;
105106
public boolean USERNAME_CHANGES;
@@ -172,6 +173,7 @@ public class Config extends Language {
172173
DEFAULT_VALUES.put("player-interactions", "true");
173174
DEFAULT_VALUES.put("player-messages", "true");
174175
DEFAULT_VALUES.put("player-commands", "true");
176+
DEFAULT_VALUES.put("player-skills", "true");
175177
DEFAULT_VALUES.put("player-sessions", "true");
176178
DEFAULT_VALUES.put("player-pings", "60");
177179
DEFAULT_VALUES.put("username-changes", "true");
@@ -223,6 +225,7 @@ public class Config extends Language {
223225
HEADERS.put("player-interactions", new String[] { "# Track player interactions, such as when a player opens a door, presses", "# a button, or opens a chest. Player interactions can't be rolled back." });
224226
HEADERS.put("player-messages", new String[] { "# Logs messages that players send in the chat." });
225227
HEADERS.put("player-commands", new String[] { "# Logs all commands used by players." });
228+
HEADERS.put("player-skills", new String[] { "# Logs MythicLib skills that players activate, and where skills teleport them.", "# Search them with \"/co lookup a:skill\"." });
226229
HEADERS.put("player-sessions", new String[] { "# Logs the logins and logouts of players." });
227230
HEADERS.put("player-pings", new String[] { "# Logs the position of each online player at this interval, in seconds.", "# Search them with \"/co lookup a:ping\". Set to \"0\" to disable." });
228231
HEADERS.put("username-changes", new String[] { "# Logs when a player changes their Minecraft username." });
@@ -303,6 +306,7 @@ private void readValues() {
303306
this.PLAYER_INTERACTIONS = this.getBoolean("player-interactions");
304307
this.PLAYER_MESSAGES = this.getBoolean("player-messages");
305308
this.PLAYER_COMMANDS = this.getBoolean("player-commands");
309+
this.PLAYER_SKILLS = this.getBoolean("player-skills");
306310
this.PLAYER_SESSIONS = this.getBoolean("player-sessions");
307311
this.PLAYER_PINGS = this.getInt("player-pings");
308312
this.USERNAME_CHANGES = this.getBoolean("username-changes");

‎src/main/java/net/tfminecraft/coreprotect/consumer/Queue.java‎

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -505,6 +505,10 @@ protected static void queuePlayerCommand(Player player, String message, long tim
505505
queueStandardData(new Object[] { null, Process.PLAYER_COMMAND, null, 0, null, 0, 0, null }, new String[] { player.getName(), null }, new Object[] { timestamp, player.getLocation().clone() }, false, Consumer.consumerStrings, message, Consumer.reserveConsumer());
506506
}
507507

508+
protected static void queuePlayerSkill(Player player, String message, long timestamp, Location location) {
509+
queueStandardData(new Object[] { null, Process.PLAYER_COMMAND, null, 0, null, 0, 0, null }, new String[] { player.getName(), null }, new Object[] { timestamp, location.clone() }, false, Consumer.consumerStrings, message, Consumer.reserveConsumer());
510+
}
511+
508512
protected static void queuePlayerInteraction(String user, BlockState block, Material type) {
509513
queueStandardData(new Object[] { null, Process.PLAYER_INTERACTION, type, 0, null, 0, 0, null }, new String[] { user, null }, block, false, Consumer.reserveConsumer());
510514
}

‎src/main/java/net/tfminecraft/coreprotect/listener/ListenerHandler.java‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,7 @@
6262
import net.tfminecraft.coreprotect.listener.player.PlayerTakeLecternBookListener;
6363
import net.tfminecraft.coreprotect.listener.player.ProjectileLaunchListener;
6464
import net.tfminecraft.coreprotect.listener.player.SignChangeListener;
65+
import net.tfminecraft.coreprotect.listener.player.SkillCastListener;
6566
import net.tfminecraft.coreprotect.listener.player.SpawnEggUseListener;
6667
import net.tfminecraft.coreprotect.listener.world.ChunkPopulateListener;
6768
import net.tfminecraft.coreprotect.listener.world.LeavesDecayListener;
@@ -203,6 +204,7 @@ public ListenerHandler(CoreProtect plugin) {
203204
pluginManager.registerEvents(new SpawnEggUseListener(), plugin);
204205
pluginManager.registerEvents(new PlayerTakeLecternBookListener(), plugin);
205206
pluginManager.registerEvents(new ProjectileLaunchListener(), plugin);
207+
SkillCastListener.register(plugin);
206208

207209
// World Listeners
208210
pluginManager.registerEvents(new ChunkPopulateListener(), plugin);

0 commit comments

Comments
 (0)