Skip to content

Commit 0ae0a2f

Browse files
committed
Implemented all missing features
1 parent bf761c3 commit 0ae0a2f

37 files changed

+613
-178
lines changed

README.md

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,26 @@
1+
<div align="center">
2+
13
# PexNPC
2-
A lightweight NPC plugin for Minecraft with fast updates
4+
### A lightweight NPC plugin with fast updates
5+
<br/>
6+
</div>
7+
8+
## Information
9+
PexNPC offers a few advantages over some other NPC plugins:
10+
- Fully packet based, which results in a better performance
11+
- Lightweight and small in file size
12+
- Fast updates for new Minecraft versions. Most of the time, the updates arrive at the same day as the Paper API does
13+
14+
Please note, that this plugin only supports the latest version of Minecraft. Older versions may or may not work.
15+
## Building
16+
```
17+
./gradlew build
18+
```
19+
The resulting JAR file will be located at `/build/libs/PexNPC-VERSION.jar`
20+
21+
## Statistics
22+
[![bStats Graph](https://bstats.org/signatures/bukkit/PexNPC.svg)](https://bstats.org/plugin/bukkit/PexNPC)
23+
## Support
24+
You can find support on our [Discord server](https://discord.gg/TA8E5s7HWn)
25+
## Download
26+
You can download the latest release [here](https://pascalpex.de/files/pexnpc/PexNPC.jar)

src/main/java/de/pascalpex/pexnpc/PexNPC.java

Lines changed: 25 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,8 @@
88
import de.pascalpex.pexnpc.npc.NPCSender;
99
import de.pascalpex.pexnpc.npc.PlaceableNPC;
1010
import de.pascalpex.pexnpc.util.MessageHandler;
11-
import de.pascalpex.pexnpc.util.external.Metrics;
1211
import de.pascalpex.pexnpc.util.VersionChecker;
12+
import de.pascalpex.pexnpc.util.external.Metrics;
1313
import de.pascalpex.pexnpc.util.external.PlaceholderAPIAdapter;
1414
import org.bukkit.Bukkit;
1515
import org.bukkit.entity.Player;
@@ -47,9 +47,6 @@ public void onEnable() {
4747

4848
versionChecker = new VersionChecker(pluginVersion);
4949
versionChecker.clearUpdateNotified();
50-
if (Config.getUpdateChecker()) {
51-
versionChecker.fetchNewestVersion();
52-
}
5350

5451
this.getServer().getMessenger().registerOutgoingPluginChannel(this, "BungeeCord");
5552

@@ -81,6 +78,8 @@ public void onDisable() {
8178
NPCSender.removeEverything();
8279
placedNPCs.clear();
8380

81+
NPCClickListener.inspectors.clear();
82+
8483
this.getServer().getMessenger().unregisterOutgoingPluginChannel(this);
8584
Bukkit.getConsoleSender().sendMessage(MessageHandler.prefixedMini("<red>PexNPC " + pluginVersion + " von Pascalpex wurde deaktiviert."));
8685
}
@@ -101,22 +100,18 @@ public static VersionChecker getVersionChecker() {
101100
return versionChecker;
102101
}
103102

104-
public static PacketReader getPacketReader() {
105-
return packetReader;
106-
}
107-
108103
public static PlaceableNPC findNPCbyMinecraftID(int id) {
109-
for(PlaceableNPC placeableNPC : PexNPC.getPlacedNpcs()) {
110-
if(placeableNPC.getServerPlayer().getId() == id) {
104+
for (PlaceableNPC placeableNPC : PexNPC.getPlacedNpcs()) {
105+
if (placeableNPC.getServerPlayer().getId() == id) {
111106
return placeableNPC;
112107
}
113108
}
114109
return null;
115110
}
116111

117112
public static PlaceableNPC findNPCbyID(long id) {
118-
for(PlaceableNPC placeableNPC : PexNPC.getPlacedNpcs()) {
119-
if(placeableNPC.getNpc().getId() == id) {
113+
for (PlaceableNPC placeableNPC : PexNPC.getPlacedNpcs()) {
114+
if (placeableNPC.getNpc().getId() == id) {
120115
return placeableNPC;
121116
}
122117
}
@@ -127,19 +122,35 @@ private static void loadAllNPCs() {
127122
placedNPCs.clear();
128123

129124
List<NPC> npcs = NPCData.getAllNpcs();
130-
for(NPC npc : npcs) {
125+
for (NPC npc : npcs) {
131126
PlaceableNPC placeableNPC = new PlaceableNPC(npc);
132127
NPCSender.sendNpcToPlayers(placeableNPC);
133128
placedNPCs.add(placeableNPC);
134129
}
135130
Bukkit.getConsoleSender().sendMessage(MessageHandler.prefixedMini("Loaded <gold>" + npcs.size() + " <aqua>NPCs"));
136131
}
137132

138-
public static void reload() {
133+
public void reload() {
139134
NPCSender.removeEverything();
140135

136+
this.getServer().getMessenger().unregisterOutgoingPluginChannel(this);
137+
this.getServer().getMessenger().registerOutgoingPluginChannel(this, "BungeeCord");
138+
139+
for (Player player : Bukkit.getOnlinePlayers()) {
140+
try {
141+
packetReader.uninject(player);
142+
packetReader.inject(player);
143+
} catch (NoSuchFieldException | IllegalAccessException e) {
144+
logger.log(Level.SEVERE, "Could not inject the PacketReader!");
145+
}
146+
}
147+
148+
NPCClickListener.inspectors.clear();
149+
141150
Config.load();
142151
NPCData.load();
152+
MessageHandler.prefix = MessageHandler.parse(Config.getPrefix());
153+
143154
loadAllNPCs();
144155
}
145156

src/main/java/de/pascalpex/pexnpc/PexNPCBootstrap.java

Lines changed: 30 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
package de.pascalpex.pexnpc;
22

3-
import com.mojang.brigadier.arguments.LongArgumentType;
43
import com.mojang.brigadier.arguments.StringArgumentType;
54
import com.mojang.brigadier.tree.LiteralCommandNode;
65
import de.pascalpex.pexnpc.commands.IDArgument;
@@ -15,6 +14,7 @@
1514
import org.bukkit.Bukkit;
1615
import org.bukkit.entity.Player;
1716
import org.bukkit.plugin.java.JavaPlugin;
17+
import org.jetbrains.annotations.NotNull;
1818

1919
public class PexNPCBootstrap implements PluginBootstrap {
2020
@Override
@@ -35,35 +35,53 @@ public void bootstrap(BootstrapContext context) {
3535
.then(Commands.literal("list")
3636
.executes(new ListSubcommand()))
3737
.then(Commands.literal("delete")
38-
.then(Commands.argument("npc", idArgument)))
38+
.then(Commands.argument("npc", idArgument)
39+
.executes(new DeleteSubcommand())))
3940
.then(Commands.literal("name")
4041
.then(Commands.argument("npc", idArgument)
41-
.then(Commands.argument("name", StringArgumentType.greedyString()))))
42+
.then(Commands.argument("name", StringArgumentType.greedyString())
43+
.executes(new NameSubcommand()))))
4244
.then(Commands.literal("movehere")
43-
.then(Commands.argument("npc", idArgument)))
45+
.then(Commands.argument("npc", idArgument)
46+
.executes(new MovehereSubcommand())))
4447
.then(Commands.literal("tp")
4548
.then(Commands.argument("npc", idArgument)
4649
.executes(new TpSubcommand())))
4750
.then(Commands.literal("skin")
4851
.then(Commands.argument("npc", idArgument)
49-
.then(Commands.argument("skin", StringArgumentType.greedyString())
52+
.then(Commands.argument("skin", StringArgumentType.word())
5053
.suggests((cmdContext, builder) -> {
54+
String currentInput = "";
55+
try {
56+
currentInput = StringArgumentType.getString(cmdContext, "skin").toLowerCase();
57+
} catch (IllegalArgumentException ignored) {
58+
} // Command does not contain a skin argument yet
5159
for (Player player : Bukkit.getOnlinePlayers()) {
52-
builder.suggest(player.getName());
60+
String playerName = player.getName();
61+
if (playerName.toLowerCase().startsWith(currentInput)) {
62+
builder.suggest(playerName);
63+
}
5364
}
5465
return builder.buildFuture();
55-
}))))
66+
})
67+
.executes(new SkinSubcommand()))))
5668
.then(Commands.literal("cmd")
5769
.then(Commands.argument("npc", idArgument)
58-
.then(Commands.argument("cmd", StringArgumentType.greedyString()))))
70+
.then(Commands.argument("cmd", StringArgumentType.greedyString())
71+
.executes(new CmdSubcommand()))))
5972
.then(Commands.literal("msg")
6073
.then(Commands.argument("npc", idArgument)
61-
.then(Commands.argument("msg", StringArgumentType.greedyString()))))
74+
.then(Commands.argument("msg", StringArgumentType.greedyString())
75+
.executes(new MsgSubcommand()))))
6276
.then(Commands.literal("item")
6377
.then(Commands.argument("npc", idArgument)
64-
.then(Commands.argument("slot", new NPCSlotArgument()))))
78+
.then(Commands.argument("slot", new NPCSlotArgument())
79+
.executes(new ItemSubcommand()))))
6580
.then(Commands.literal("clear")
66-
.then(Commands.argument("npc", idArgument)))
81+
.then(Commands.argument("npc", idArgument)
82+
.executes(new ClearSubcommand())))
83+
.then(Commands.literal("inspect")
84+
.executes(new InspectSubcommand()))
6785
.executes(helpSubcommand)
6886
.build();
6987

@@ -72,7 +90,7 @@ public void bootstrap(BootstrapContext context) {
7290
}
7391

7492
@Override
75-
public JavaPlugin createPlugin(PluginProviderContext context) {
93+
public @NotNull JavaPlugin createPlugin(@NotNull PluginProviderContext context) {
7694
return new PexNPC();
7795
}
7896
}

src/main/java/de/pascalpex/pexnpc/commands/IDArgument.java

Lines changed: 10 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -19,9 +19,9 @@
1919

2020
public class IDArgument implements CustomArgumentType.Converted<PlaceableNPC, Long> {
2121
@Override
22-
public PlaceableNPC convert(@NotNull Long nativeType) throws CommandSyntaxException {
22+
public @NotNull PlaceableNPC convert(@NotNull Long nativeType) throws CommandSyntaxException {
2323
PlaceableNPC placeableNPC = PexNPC.findNPCbyID(nativeType);
24-
if(placeableNPC != null) {
24+
if (placeableNPC != null) {
2525
return placeableNPC;
2626
}
2727

@@ -30,18 +30,19 @@ public PlaceableNPC convert(@NotNull Long nativeType) throws CommandSyntaxExcept
3030
}
3131

3232
@Override
33-
public @NotNull ArgumentType getNativeType() {
33+
public @NotNull ArgumentType<Long> getNativeType() {
3434
return LongArgumentType.longArg(1);
3535
}
3636

3737
@Override
3838
public <S> @NotNull CompletableFuture<Suggestions> listSuggestions(@NotNull CommandContext<S> context, @NotNull SuggestionsBuilder builder) {
39-
for(PlaceableNPC placeableNPC : PexNPC.getPlacedNpcs()) {
40-
String currentInput = "";
41-
try {
42-
currentInput = context.getArgument("npc", Integer.class).toString();
43-
} catch (IllegalArgumentException ignored) {}
44-
if(String.valueOf(placeableNPC.getNpc().getId()).startsWith(currentInput)) {
39+
String currentInput = "";
40+
try {
41+
currentInput = context.getInput().split(" ")[2];
42+
} catch (ArrayIndexOutOfBoundsException ignored) {
43+
} // Command does not contain the argument yet
44+
for (PlaceableNPC placeableNPC : PexNPC.getPlacedNpcs()) {
45+
if (String.valueOf(placeableNPC.getNpc().getId()).startsWith(currentInput)) {
4546
builder.suggest(String.valueOf(placeableNPC.getNpc().getId()), MessageComponentSerializer.message().serialize(MessageHandler.parseSection(placeableNPC.getNpc().getName())));
4647
}
4748
}

src/main/java/de/pascalpex/pexnpc/commands/NPCSlotArgument.java

Lines changed: 6 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,6 @@
11
package de.pascalpex.pexnpc.commands;
22

3-
import com.mojang.brigadier.LiteralMessage;
43
import com.mojang.brigadier.Message;
5-
import com.mojang.brigadier.StringReader;
64
import com.mojang.brigadier.arguments.ArgumentType;
75
import com.mojang.brigadier.arguments.StringArgumentType;
86
import com.mojang.brigadier.context.CommandContext;
@@ -20,24 +18,24 @@
2018

2119
public class NPCSlotArgument implements CustomArgumentType.Converted<NPCItemSlot, String> {
2220
@Override
23-
public NPCItemSlot convert(@NotNull String nativeType) throws CommandSyntaxException {
24-
for(NPCItemSlot slot : NPCItemSlot.values()) {
25-
if(slot.getName().equalsIgnoreCase(nativeType)) {
21+
public @NotNull NPCItemSlot convert(@NotNull String nativeType) throws CommandSyntaxException {
22+
for (NPCItemSlot slot : NPCItemSlot.values()) {
23+
if (slot.getName().equalsIgnoreCase(nativeType)) {
2624
return slot;
2725
}
2826
}
29-
final Message exceptionMessage = MessageComponentSerializer.message().serialize(MessageHandler.errorMessage("The NPC slot you entered is invalid"));
27+
final Message exceptionMessage = MessageComponentSerializer.message().serialize(MessageHandler.errorMessage("The NPC slot you entered is invalid. Valid options are HAND, OFFHAND, HELMET, CHESTPLATE, LEGGINGS and BOOTS"));
3028
throw new CommandSyntaxException(new SimpleCommandExceptionType(exceptionMessage), exceptionMessage);
3129
}
3230

3331
@Override
34-
public @NotNull ArgumentType getNativeType() {
32+
public @NotNull ArgumentType<String> getNativeType() {
3533
return StringArgumentType.word();
3634
}
3735

3836
@Override
3937
public <S> @NotNull CompletableFuture<Suggestions> listSuggestions(@NotNull CommandContext<S> context, @NotNull SuggestionsBuilder builder) {
40-
for(NPCItemSlot slot : NPCItemSlot.values()) {
38+
for (NPCItemSlot slot : NPCItemSlot.values()) {
4139
builder.suggest(slot.getName());
4240
}
4341
return builder.buildFuture();
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
package de.pascalpex.pexnpc.commands.subcommands;
2+
3+
import com.mojang.brigadier.Command;
4+
import com.mojang.brigadier.context.CommandContext;
5+
import de.pascalpex.pexnpc.files.NPCData;
6+
import de.pascalpex.pexnpc.npc.NPC;
7+
import de.pascalpex.pexnpc.npc.NPCEquipment;
8+
import de.pascalpex.pexnpc.npc.NPCSender;
9+
import de.pascalpex.pexnpc.npc.PlaceableNPC;
10+
import de.pascalpex.pexnpc.util.MessageHandler;
11+
import io.papermc.paper.command.brigadier.CommandSourceStack;
12+
import org.bukkit.command.CommandSender;
13+
14+
public class ClearSubcommand implements Command<CommandSourceStack> {
15+
@Override
16+
public int run(CommandContext<CommandSourceStack> context) {
17+
CommandSender sender = context.getSource().getSender();
18+
PlaceableNPC placeableNPC = context.getArgument("npc", PlaceableNPC.class);
19+
20+
NPCSender.removeNPC(placeableNPC);
21+
22+
NPC npc = placeableNPC.getNpc();
23+
npc.setEquipment(new NPCEquipment());
24+
npc.setCommand("");
25+
npc.setMessage("");
26+
27+
NPCSender.sendNpcToPlayers(placeableNPC);
28+
NPCData.saveNpc(placeableNPC.getNpc());
29+
30+
sender.sendMessage(MessageHandler.prefixedMini("The NPC with the ID <gold>" + placeableNPC.getNpc().getId() + " <aqua>no longer has any commands, messages or items"));
31+
32+
return SINGLE_SUCCESS;
33+
}
34+
}
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
package de.pascalpex.pexnpc.commands.subcommands;
2+
3+
import com.mojang.brigadier.Command;
4+
import com.mojang.brigadier.arguments.StringArgumentType;
5+
import com.mojang.brigadier.context.CommandContext;
6+
import de.pascalpex.pexnpc.files.NPCData;
7+
import de.pascalpex.pexnpc.npc.PlaceableNPC;
8+
import de.pascalpex.pexnpc.util.MessageHandler;
9+
import io.papermc.paper.command.brigadier.CommandSourceStack;
10+
import org.bukkit.command.CommandSender;
11+
12+
public class CmdSubcommand implements Command<CommandSourceStack> {
13+
@Override
14+
public int run(CommandContext<CommandSourceStack> context) {
15+
CommandSender sender = context.getSource().getSender();
16+
PlaceableNPC placeableNPC = context.getArgument("npc", PlaceableNPC.class);
17+
String cmd = StringArgumentType.getString(context, "cmd");
18+
19+
placeableNPC.getNpc().setCommand(cmd);
20+
NPCData.saveNpc(placeableNPC.getNpc());
21+
22+
sender.sendMessage(MessageHandler.prefixedMini("The NPC with the ID <gold>" + placeableNPC.getNpc().getId() + " <aqua>now executes the command <gold>/" + cmd));
23+
24+
return SINGLE_SUCCESS;
25+
}
26+
}

src/main/java/de/pascalpex/pexnpc/commands/subcommands/CreateSubcommand.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,13 +22,13 @@ public int run(CommandContext<CommandSourceStack> context) throws CommandSyntaxE
2222
CommandSender sender = context.getSource().getSender();
2323
Entity executor = context.getSource().getExecutor();
2424

25-
if(!(executor instanceof Player target)) {
25+
if (!(executor instanceof Player target)) {
2626
sender.sendMessage(MessageHandler.errorMessage("The command executor must be a player"));
2727
return SINGLE_SUCCESS;
2828
}
2929

3030
String name = StringArgumentType.getString(context, "name").replace("&", "§");
31-
if(!Util.checkName(name)) {
31+
if (Util.isNameInvalid(name)) {
3232
sender.sendMessage(MessageHandler.errorMessage("The first 16 characters of this name are already in use"));
3333
return SINGLE_SUCCESS;
3434
}
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
package de.pascalpex.pexnpc.commands.subcommands;
2+
3+
import com.mojang.brigadier.Command;
4+
import com.mojang.brigadier.context.CommandContext;
5+
import de.pascalpex.pexnpc.PexNPC;
6+
import de.pascalpex.pexnpc.files.NPCData;
7+
import de.pascalpex.pexnpc.npc.NPCSender;
8+
import de.pascalpex.pexnpc.npc.PlaceableNPC;
9+
import de.pascalpex.pexnpc.util.MessageHandler;
10+
import io.papermc.paper.command.brigadier.CommandSourceStack;
11+
import org.bukkit.command.CommandSender;
12+
13+
public class DeleteSubcommand implements Command<CommandSourceStack> {
14+
@Override
15+
public int run(CommandContext<CommandSourceStack> context) {
16+
CommandSender sender = context.getSource().getSender();
17+
PlaceableNPC placeableNPC = context.getArgument("npc", PlaceableNPC.class);
18+
19+
PexNPC.getPlacedNpcs().remove(placeableNPC);
20+
NPCSender.removeNPC(placeableNPC);
21+
NPCData.deleteNpc(placeableNPC.getNpc());
22+
23+
sender.sendMessage(MessageHandler.prefixedMini("The NPC with the ID <gold>" + placeableNPC.getNpc().getId() + " <aqua>got deleted"));
24+
25+
return SINGLE_SUCCESS;
26+
}
27+
}

0 commit comments

Comments
 (0)