extends NpcFlaggedObject {
@NotNull NpcSettings settings();
- @NotNull Platform platform();
+ @NotNull Platform platform();
- @NotNull NpcTracker npcTracker();
+ @NotNull NpcTracker npcTracker();
boolean shouldIncludePlayer(@NotNull P player);
@UnmodifiableView
@NotNull Collection includedPlayers();
- @NotNull Npc addIncludedPlayer(@NotNull P player);
+ boolean includesPlayer(@NotNull P player);
- @NotNull Npc removeIncludedPlayer(@NotNull P player);
+ @NotNull Npc addIncludedPlayer(@NotNull P player);
+
+ @NotNull Npc removeIncludedPlayer(@NotNull P player);
@UnmodifiableView
@NotNull Collection trackedPlayers();
- @NotNull Npc trackPlayer(@NotNull P player);
+ boolean tracksPlayer(@NotNull P player);
+
+ @NotNull Npc trackPlayer(@NotNull P player);
+
+ @NotNull Npc forceTrackPlayer(@NotNull P player);
+
+ @NotNull Npc stopTrackingPlayer(@NotNull P player);
- @NotNull Npc forceTrackPlayer(@NotNull P player);
+ @NotNull NpcSpecificOutboundPacket lookAt(@NotNull Position position);
- @NotNull Npc stopTrackingPlayer(@NotNull P player);
+ @NotNull NpcSpecificOutboundPacket changeItem(@NotNull ItemSlot slot, @NotNull I item);
- interface Builder extends NpcFlaggedBuilder> {
+ interface Builder extends NpcFlaggedBuilder> {
- @NotNull Builder entityId(int id);
+ @NotNull Builder entityId(int id);
- @NotNull Builder position(@NotNull Position position);
+ @NotNull Builder position(@NotNull Position position);
- @NotNull Builder profile(@NotNull Profile.Resolved profile);
+ @NotNull Builder profile(@NotNull Profile.Resolved profile);
- default @NotNull CompletableFuture> profile(@NotNull Profile profile) {
+ default @NotNull CompletableFuture> profile(@NotNull Profile profile) {
return this.profile(null, profile);
}
- @NotNull CompletableFuture> profile(@Nullable ProfileResolver resolver, @NotNull Profile profile);
+ @NotNull CompletableFuture> profile(@Nullable ProfileResolver resolver,
+ @NotNull Profile profile);
- @NotNull Builder npcSettings(@NotNull Consumer> decorator);
+ @NotNull Builder npcSettings(@NotNull Consumer> decorator);
- @NotNull Npc build();
+ @NotNull Npc build();
- @NotNull Npc buildAndTrack();
+ @NotNull Npc buildAndTrack();
}
}
diff --git a/api/src/main/java/com/github/juliarn/npclib/api/NpcTracker.java b/api/src/main/java/com/github/juliarn/npclib/api/NpcTracker.java
index 85aa88c..7e9b906 100644
--- a/api/src/main/java/com/github/juliarn/npclib/api/NpcTracker.java
+++ b/api/src/main/java/com/github/juliarn/npclib/api/NpcTracker.java
@@ -30,16 +30,16 @@
import org.jetbrains.annotations.Nullable;
import org.jetbrains.annotations.UnmodifiableView;
-public interface NpcTracker {
+public interface NpcTracker {
- @Nullable Npc npcById(int entityId);
+ @Nullable Npc npcById(int entityId);
- @Nullable Npc npcByUniqueId(@NotNull UUID uniqueId);
+ @Nullable Npc npcByUniqueId(@NotNull UUID uniqueId);
- void trackNpc(@NotNull Npc npc);
+ void trackNpc(@NotNull Npc npc);
- void stopTrackingNpc(@NotNull Npc npc);
+ void stopTrackingNpc(@NotNull Npc npc);
@UnmodifiableView
- @NotNull Collection> trackedNpcs();
+ @NotNull Collection> trackedNpcs();
}
diff --git a/api/src/main/java/com/github/juliarn/npclib/api/Platform.java b/api/src/main/java/com/github/juliarn/npclib/api/Platform.java
index e32839b..e7e8124 100644
--- a/api/src/main/java/com/github/juliarn/npclib/api/Platform.java
+++ b/api/src/main/java/com/github/juliarn/npclib/api/Platform.java
@@ -32,44 +32,52 @@
import net.kyori.event.EventBus;
import org.jetbrains.annotations.NotNull;
-public interface Platform {
+public interface Platform {
boolean debug();
+ @NotNull E extension();
+
@NotNull EventBus eventBus();
- @NotNull NpcTracker npcTracker();
+ @NotNull NpcTracker npcTracker();
@NotNull ProfileResolver profileResolver();
@NotNull PlatformTaskManager taskManager();
- @NotNull Npc.Builder newNpcBuilder();
+ @NotNull Npc.Builder newNpcBuilder();
+
+ @NotNull PlatformVersionAccessor versionAccessor();
@NotNull PlatformWorldAccessor worldAccessor();
- @NotNull PlatformPacketAdapter packetFactory();
+ @NotNull PlatformPacketAdapter packetFactory();
@NotNull Optional actionController();
- interface Builder {
+ interface Builder {
+
+ @NotNull Builder debug(boolean debug);
+
+ @NotNull Builder extension(@NotNull E extension);
- @NotNull Builder debug(boolean debug);
+ @NotNull Builder eventBus(@NotNull EventBus eventBus);
- @NotNull Builder eventBus(@NotNull EventBus eventBus);
+ @NotNull Builder npcTracker(@NotNull NpcTracker npcTracker);
- @NotNull Builder npcTracker(@NotNull NpcTracker npcTracker);
+ @NotNull Builder taskManager(@NotNull PlatformTaskManager taskManager);
- @NotNull Builder taskManager(@NotNull PlatformTaskManager taskManager);
+ @NotNull Builder profileResolver(@NotNull ProfileResolver profileResolver);
- @NotNull Builder profileResolver(@NotNull ProfileResolver profileResolver);
+ @NotNull Builder worldAccessor(@NotNull PlatformWorldAccessor worldAccessor);
- @NotNull Builder worldAccessor(@NotNull PlatformWorldAccessor worldAccessor);
+ @NotNull Builder versionAccessor(@NotNull PlatformVersionAccessor versionAccessor);
- @NotNull Builder packetFactory(@NotNull PlatformPacketAdapter packetFactory);
+ @NotNull Builder packetFactory(@NotNull PlatformPacketAdapter packetFactory);
- @NotNull Builder actionController(@NotNull Consumer decorator);
+ @NotNull Builder actionController(@NotNull Consumer decorator);
- @NotNull Platform build();
+ @NotNull Platform build();
}
}
diff --git a/api/src/main/java/com/github/juliarn/npclib/api/PlatformVersionAccessor.java b/api/src/main/java/com/github/juliarn/npclib/api/PlatformVersionAccessor.java
new file mode 100644
index 0000000..88a8678
--- /dev/null
+++ b/api/src/main/java/com/github/juliarn/npclib/api/PlatformVersionAccessor.java
@@ -0,0 +1,36 @@
+/*
+ * This file is part of npc-lib, licensed under the MIT License (MIT).
+ *
+ * Copyright (c) 2022 Julian M., Pasqual K. and contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+package com.github.juliarn.npclib.api;
+
+public interface PlatformVersionAccessor {
+
+ int major();
+
+ int minor();
+
+ int patch();
+
+ boolean atLeast(int major, int minor, int patch);
+}
diff --git a/api/src/main/java/com/github/juliarn/npclib/api/event/NpcEvent.java b/api/src/main/java/com/github/juliarn/npclib/api/event/NpcEvent.java
index 09bbf3f..901cb31 100644
--- a/api/src/main/java/com/github/juliarn/npclib/api/event/NpcEvent.java
+++ b/api/src/main/java/com/github/juliarn/npclib/api/event/NpcEvent.java
@@ -29,5 +29,5 @@
public interface NpcEvent {
- @NotNull Npc npc();
+ @NotNull Npc npc();
}
diff --git a/api/src/main/java/com/github/juliarn/npclib/api/profile/DefaultResolvedProfile.java b/api/src/main/java/com/github/juliarn/npclib/api/profile/DefaultResolvedProfile.java
index dbe57fc..ec5416f 100644
--- a/api/src/main/java/com/github/juliarn/npclib/api/profile/DefaultResolvedProfile.java
+++ b/api/src/main/java/com/github/juliarn/npclib/api/profile/DefaultResolvedProfile.java
@@ -38,7 +38,7 @@ public final class DefaultResolvedProfile implements Profile.Resolved {
private final UUID uniqueId;
private final Set properties;
- public DefaultResolvedProfile(
+ DefaultResolvedProfile(
@NotNull String name,
@NotNull UUID uniqueId,
@NotNull Set properties
diff --git a/api/src/main/java/com/github/juliarn/npclib/api/profile/MojangProfileResolver.java b/api/src/main/java/com/github/juliarn/npclib/api/profile/MojangProfileResolver.java
index e545848..70fc603 100644
--- a/api/src/main/java/com/github/juliarn/npclib/api/profile/MojangProfileResolver.java
+++ b/api/src/main/java/com/github/juliarn/npclib/api/profile/MojangProfileResolver.java
@@ -94,7 +94,7 @@ final class MojangProfileResolver implements ProfileResolver {
Set properties = GSON.fromJson(responseData.get("properties"), PROFILE_PROPERTIES_TYPE);
// create the profile from the received data
- return new DefaultResolvedProfile(name, uniqueId, properties);
+ return Profile.resolved(name, uniqueId, properties);
}));
}
diff --git a/api/src/main/java/com/github/juliarn/npclib/api/profile/Profile.java b/api/src/main/java/com/github/juliarn/npclib/api/profile/Profile.java
index 7aae369..d042b4e 100644
--- a/api/src/main/java/com/github/juliarn/npclib/api/profile/Profile.java
+++ b/api/src/main/java/com/github/juliarn/npclib/api/profile/Profile.java
@@ -44,10 +44,19 @@ public interface Profile {
}
static @NotNull Resolved resolved(@NotNull String name, @NotNull UUID uniqueId) {
+ return resolved(name, uniqueId, Collections.emptySet());
+ }
+
+ static @NotNull Resolved resolved(
+ @NotNull String name,
+ @NotNull UUID uniqueId,
+ @NotNull Set properties
+ ) {
Objects.requireNonNull(name, "name");
Objects.requireNonNull(uniqueId, "unique id");
+ Objects.requireNonNull(properties, "properties");
- return new DefaultResolvedProfile(name, uniqueId, Collections.emptySet());
+ return new DefaultResolvedProfile(name, uniqueId, properties);
}
boolean resolved();
diff --git a/api/src/main/java/com/github/juliarn/npclib/api/protocol/DefaultNpcSpecificOutboundPacket.java b/api/src/main/java/com/github/juliarn/npclib/api/protocol/DefaultNpcSpecificOutboundPacket.java
new file mode 100644
index 0000000..7a7bb18
--- /dev/null
+++ b/api/src/main/java/com/github/juliarn/npclib/api/protocol/DefaultNpcSpecificOutboundPacket.java
@@ -0,0 +1,69 @@
+/*
+ * This file is part of npc-lib, licensed under the MIT License (MIT).
+ *
+ * Copyright (c) 2022 Julian M., Pasqual K. and contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+package com.github.juliarn.npclib.api.protocol;
+
+import com.github.juliarn.npclib.api.Npc;
+import java.util.Collection;
+import java.util.function.Function;
+import org.jetbrains.annotations.NotNull;
+
+final class DefaultNpcSpecificOutboundPacket implements NpcSpecificOutboundPacket {
+
+ private final Npc target;
+ private final OutboundPacket outboundPacket;
+
+ public DefaultNpcSpecificOutboundPacket(
+ @NotNull Npc target,
+ @NotNull OutboundPacket outboundPacket
+ ) {
+ this.target = target;
+ this.outboundPacket = outboundPacket;
+ }
+
+ @Override
+ public @NotNull Npc npc() {
+ return this.target;
+ }
+
+ @Override
+ public void scheduleForTracked() {
+ this.outboundPacket.scheduleForTracked(this.target);
+ }
+
+ @Override
+ public void schedule(@NotNull P player) {
+ this.outboundPacket.schedule(player, this.target);
+ }
+
+ @Override
+ public void schedule(@NotNull Collection players) {
+ this.outboundPacket.schedule(players, this.target);
+ }
+
+ @Override
+ public void schedule(@NotNull Function, Collection> extractor) {
+ this.outboundPacket.schedule(extractor, this.target);
+ }
+}
diff --git a/api/src/main/java/com/github/juliarn/npclib/api/protocol/NpcSpecificOutboundPacket.java b/api/src/main/java/com/github/juliarn/npclib/api/protocol/NpcSpecificOutboundPacket.java
new file mode 100644
index 0000000..7ab1ae3
--- /dev/null
+++ b/api/src/main/java/com/github/juliarn/npclib/api/protocol/NpcSpecificOutboundPacket.java
@@ -0,0 +1,54 @@
+/*
+ * This file is part of npc-lib, licensed under the MIT License (MIT).
+ *
+ * Copyright (c) 2022 Julian M., Pasqual K. and contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+package com.github.juliarn.npclib.api.protocol;
+
+import com.github.juliarn.npclib.api.Npc;
+import java.util.Collection;
+import java.util.Objects;
+import java.util.function.Function;
+import org.jetbrains.annotations.NotNull;
+
+public interface NpcSpecificOutboundPacket {
+
+ static @NotNull NpcSpecificOutboundPacket fromOutboundPacket(
+ @NotNull Npc npc,
+ @NotNull OutboundPacket packet
+ ) {
+ Objects.requireNonNull(npc, "npc");
+ Objects.requireNonNull(packet, "packet");
+
+ return new DefaultNpcSpecificOutboundPacket<>(npc, packet);
+ }
+
+ @NotNull Npc npc();
+
+ void scheduleForTracked();
+
+ void schedule(@NotNull P player);
+
+ void schedule(@NotNull Collection players);
+
+ void schedule(@NotNull Function, Collection> extractor);
+}
diff --git a/api/src/main/java/com/github/juliarn/npclib/api/protocol/OutboundPacket.java b/api/src/main/java/com/github/juliarn/npclib/api/protocol/OutboundPacket.java
index 245d5b6..7a23127 100644
--- a/api/src/main/java/com/github/juliarn/npclib/api/protocol/OutboundPacket.java
+++ b/api/src/main/java/com/github/juliarn/npclib/api/protocol/OutboundPacket.java
@@ -26,15 +26,27 @@
import com.github.juliarn.npclib.api.Npc;
import java.util.Collection;
+import java.util.function.Function;
import org.jetbrains.annotations.NotNull;
@FunctionalInterface
-public interface OutboundPacket {
+public interface OutboundPacket {
- @NotNull OutboundPacket schedule(@NotNull P player, @NotNull Npc npc);
+ void schedule(@NotNull P player, @NotNull Npc npc);
- default @NotNull OutboundPacket schedule(@NotNull Collection players, @NotNull Npc npc) {
+ default void scheduleForTracked(@NotNull Npc npc) {
+ this.schedule(Npc::trackedPlayers, npc);
+ }
+
+ default void schedule(@NotNull Function, Collection> extractor, @NotNull Npc npc) {
+ this.schedule(extractor.apply(npc), npc);
+ }
+
+ default void schedule(@NotNull Collection players, @NotNull Npc npc) {
players.forEach(player -> this.schedule(player, npc));
- return this;
+ }
+
+ default @NotNull NpcSpecificOutboundPacket toSpecific(@NotNull Npc targetNpc) {
+ return NpcSpecificOutboundPacket.fromOutboundPacket(targetNpc, this);
}
}
diff --git a/api/src/main/java/com/github/juliarn/npclib/api/protocol/PlatformPacketAdapter.java b/api/src/main/java/com/github/juliarn/npclib/api/protocol/PlatformPacketAdapter.java
index 01f4f04..fca2831 100644
--- a/api/src/main/java/com/github/juliarn/npclib/api/protocol/PlatformPacketAdapter.java
+++ b/api/src/main/java/com/github/juliarn/npclib/api/protocol/PlatformPacketAdapter.java
@@ -24,17 +24,32 @@
package com.github.juliarn.npclib.api.protocol;
+import com.github.juliarn.npclib.api.Platform;
+import com.github.juliarn.npclib.api.protocol.enums.EntityAnimation;
+import com.github.juliarn.npclib.api.protocol.enums.ItemSlot;
+import com.github.juliarn.npclib.api.protocol.enums.PlayerInfoAction;
+import com.github.juliarn.npclib.api.protocol.meta.EntityMetadataFactory;
import org.jetbrains.annotations.NotNull;
-public interface PlatformPacketAdapter {
+public interface PlatformPacketAdapter {
- @NotNull OutboundPacket createEntitySpawnPacket();
+ @NotNull OutboundPacket createEntitySpawnPacket();
- @NotNull OutboundPacket createEntityRemovePacket();
+ @NotNull OutboundPacket createEntityRemovePacket();
- @NotNull OutboundPacket createPlayerInfoPacket(@NotNull PlayerInfoAction action);
+ @NotNull OutboundPacket createPlayerInfoPacket(@NotNull PlayerInfoAction action);
- @NotNull OutboundPacket createRotationPacket(float yaw, float pitch);
+ @NotNull OutboundPacket createRotationPacket(float yaw, float pitch);
- @NotNull OutboundPacket createAnimationPacket(@NotNull EntityAnimation animation);
+ @NotNull OutboundPacket createAnimationPacket(@NotNull EntityAnimation animation);
+
+ @NotNull OutboundPacket createEquipmentPacket(@NotNull ItemSlot slot, @NotNull I item);
+
+ @NotNull OutboundPacket createCustomPayloadPacket(@NotNull String channelId, byte[] payload);
+
+ @NotNull OutboundPacket createEntityMetaPacket(
+ @NotNull T value,
+ @NotNull EntityMetadataFactory metadata);
+
+ void initialize(@NotNull Platform platform);
}
diff --git a/api/src/main/java/com/github/juliarn/npclib/api/protocol/EntityAnimation.java b/api/src/main/java/com/github/juliarn/npclib/api/protocol/enums/EntityAnimation.java
similarity index 96%
rename from api/src/main/java/com/github/juliarn/npclib/api/protocol/EntityAnimation.java
rename to api/src/main/java/com/github/juliarn/npclib/api/protocol/enums/EntityAnimation.java
index 075df35..6fdd5f8 100644
--- a/api/src/main/java/com/github/juliarn/npclib/api/protocol/EntityAnimation.java
+++ b/api/src/main/java/com/github/juliarn/npclib/api/protocol/enums/EntityAnimation.java
@@ -22,7 +22,7 @@
* THE SOFTWARE.
*/
-package com.github.juliarn.npclib.api.protocol;
+package com.github.juliarn.npclib.api.protocol.enums;
public enum EntityAnimation {
diff --git a/api/src/main/java/com/github/juliarn/npclib/api/protocol/enums/EntityPose.java b/api/src/main/java/com/github/juliarn/npclib/api/protocol/enums/EntityPose.java
new file mode 100644
index 0000000..a62c143
--- /dev/null
+++ b/api/src/main/java/com/github/juliarn/npclib/api/protocol/enums/EntityPose.java
@@ -0,0 +1,37 @@
+/*
+ * This file is part of npc-lib, licensed under the MIT License (MIT).
+ *
+ * Copyright (c) 2022 Julian M., Pasqual K. and contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+package com.github.juliarn.npclib.api.protocol.enums;
+
+public enum EntityPose {
+
+ STANDING,
+ FALL_FLYING,
+ SLEEPING,
+ SWIMMING,
+ SPIN_ATTACK,
+ CROUCHING,
+ LONG_JUMPING,
+ DYING
+}
diff --git a/api/src/main/java/com/github/juliarn/npclib/api/protocol/enums/ItemSlot.java b/api/src/main/java/com/github/juliarn/npclib/api/protocol/enums/ItemSlot.java
new file mode 100644
index 0000000..baa6fb4
--- /dev/null
+++ b/api/src/main/java/com/github/juliarn/npclib/api/protocol/enums/ItemSlot.java
@@ -0,0 +1,35 @@
+/*
+ * This file is part of npc-lib, licensed under the MIT License (MIT).
+ *
+ * Copyright (c) 2022 Julian M., Pasqual K. and contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+package com.github.juliarn.npclib.api.protocol.enums;
+
+public enum ItemSlot {
+
+ MAIN_HAND,
+ OFF_HAND,
+ FEET,
+ LEGS,
+ CHEST,
+ HEAD
+}
diff --git a/api/src/main/java/com/github/juliarn/npclib/api/protocol/PlayerInfoAction.java b/api/src/main/java/com/github/juliarn/npclib/api/protocol/enums/PlayerInfoAction.java
similarity index 95%
rename from api/src/main/java/com/github/juliarn/npclib/api/protocol/PlayerInfoAction.java
rename to api/src/main/java/com/github/juliarn/npclib/api/protocol/enums/PlayerInfoAction.java
index b9c3af1..2fe8d01 100644
--- a/api/src/main/java/com/github/juliarn/npclib/api/protocol/PlayerInfoAction.java
+++ b/api/src/main/java/com/github/juliarn/npclib/api/protocol/enums/PlayerInfoAction.java
@@ -22,7 +22,7 @@
* THE SOFTWARE.
*/
-package com.github.juliarn.npclib.api.protocol;
+package com.github.juliarn.npclib.api.protocol.enums;
public enum PlayerInfoAction {
diff --git a/api/src/main/java/com/github/juliarn/npclib/api/protocol/meta/DefaultEntityMetadata.java b/api/src/main/java/com/github/juliarn/npclib/api/protocol/meta/DefaultEntityMetadata.java
new file mode 100644
index 0000000..9f92fbd
--- /dev/null
+++ b/api/src/main/java/com/github/juliarn/npclib/api/protocol/meta/DefaultEntityMetadata.java
@@ -0,0 +1,51 @@
+/*
+ * This file is part of npc-lib, licensed under the MIT License (MIT).
+ *
+ * Copyright (c) 2022 Julian M., Pasqual K. and contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+package com.github.juliarn.npclib.api.protocol.meta;
+
+import com.github.juliarn.npclib.api.protocol.enums.EntityPose;
+
+interface DefaultEntityMetadata {
+
+ // https://wiki.vg/Entity_metadata#Entity - see index 0 and 6
+ EntityMetadataFactory SNEAKING = EntityMetadataFactory.metaFactoryBuilder()
+ .baseIndex(0)
+ .type(Byte.class)
+ .inputConverter(value -> (byte) (value ? 0x02 : 0x00))
+ .addRelatedMetadata(EntityMetadataFactory.metaFactoryBuilder()
+ .baseIndex(6)
+ .type(EntityPose.class)
+ .inputConverter(value -> value ? EntityPose.CROUCHING : EntityPose.STANDING)
+ .availabilityChecker(versionAccessor -> versionAccessor.atLeast(1, 14, 0))
+ .build())
+ .build();
+
+ // https://wiki.vg/Entity_metadata#Player
+ EntityMetadataFactory SKIN_LAYERS = EntityMetadataFactory.metaFactoryBuilder()
+ .baseIndex(10)
+ .type(Byte.class)
+ .indexShiftVersions(9, 9, 10, 14, 14, 15, 17)
+ .inputConverter(value -> (byte) (value ? 0xff : 0x00))
+ .build();
+}
diff --git a/api/src/main/java/com/github/juliarn/npclib/api/protocol/meta/DefaultEntityMetadataFactory.java b/api/src/main/java/com/github/juliarn/npclib/api/protocol/meta/DefaultEntityMetadataFactory.java
new file mode 100644
index 0000000..d82c9e5
--- /dev/null
+++ b/api/src/main/java/com/github/juliarn/npclib/api/protocol/meta/DefaultEntityMetadataFactory.java
@@ -0,0 +1,154 @@
+/*
+ * This file is part of npc-lib, licensed under the MIT License (MIT).
+ *
+ * Copyright (c) 2022 Julian M., Pasqual K. and contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+package com.github.juliarn.npclib.api.protocol.meta;
+
+import com.github.juliarn.npclib.api.PlatformVersionAccessor;
+import java.lang.reflect.Type;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.function.Function;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Unmodifiable;
+
+final class DefaultEntityMetadataFactory implements EntityMetadataFactory {
+
+ private final int baseIndex;
+ private final int[] indexShitVersions;
+
+ private final Type type;
+ private final Function inputConverter;
+
+ private final Collection> relatedMetadata;
+ private final Function availabilityChecker;
+
+ public DefaultEntityMetadataFactory(
+ int baseIndex,
+ int[] indexShitVersions,
+ @NotNull Type type,
+ @NotNull Function inputConverter,
+ @NotNull Collection> relatedMetadata,
+ @NotNull Function availabilityChecker
+ ) {
+ this.baseIndex = baseIndex;
+ this.indexShitVersions = indexShitVersions;
+ this.type = type;
+ this.inputConverter = inputConverter;
+ this.relatedMetadata = Collections.unmodifiableCollection(relatedMetadata);
+ this.availabilityChecker = availabilityChecker;
+ }
+
+ @Override
+ @Unmodifiable
+ public @NotNull Collection> relatedMetadata() {
+ return this.relatedMetadata;
+ }
+
+ @Override
+ @SuppressWarnings("unchecked")
+ public @NotNull EntityMetadata create(@NotNull I input, @NotNull PlatformVersionAccessor versionAccessor) {
+ // check if the meta is available
+ if (this.availabilityChecker.apply(versionAccessor)) {
+ // try to convert the given input value
+ O value = this.inputConverter.apply(input);
+ if (value != null) {
+ // calculate the index & create the meta
+ int index = this.baseIndex + this.calcIndexShift(versionAccessor);
+ return new AvailableEntityMetadata<>(index, value, this.type);
+ }
+ }
+
+ // not available
+ return (EntityMetadata) UnavailableEntityMetadata.INSTANCE;
+ }
+
+ private int calcIndexShift(@NotNull PlatformVersionAccessor versionAccessor) {
+ int shift = 0;
+ for (int version : this.indexShitVersions) {
+ if (versionAccessor.minor() >= version) {
+ shift++;
+ }
+ }
+ return shift;
+ }
+
+ private static final class AvailableEntityMetadata implements EntityMetadata {
+
+ private final int index;
+
+ private final O value;
+ private final Type type;
+
+ private AvailableEntityMetadata(int index, @NotNull O value, @NotNull Type type) {
+ this.index = index;
+ this.value = value;
+ this.type = type;
+ }
+
+ @Override
+ public int index() {
+ return this.index;
+ }
+
+ @Override
+ public boolean available() {
+ return true;
+ }
+
+ @Override
+ public @NotNull O value() {
+ return this.value;
+ }
+
+ @Override
+ public @NotNull Type type() {
+ return this.type;
+ }
+ }
+
+ private static final class UnavailableEntityMetadata implements EntityMetadata {
+
+ private static final UnavailableEntityMetadata INSTANCE = new UnavailableEntityMetadata();
+
+ @Override
+ public int index() {
+ throw new UnsupportedOperationException("Unavailable entity metadata cannot be accessed");
+ }
+
+ @Override
+ public boolean available() {
+ return false;
+ }
+
+ @Override
+ public @NotNull Object value() {
+ throw new UnsupportedOperationException("Unavailable entity metadata cannot be accessed");
+ }
+
+ @Override
+ public @NotNull Class type() {
+ throw new UnsupportedOperationException("Unavailable entity metadata cannot be accessed");
+ }
+ }
+}
diff --git a/api/src/main/java/com/github/juliarn/npclib/api/protocol/meta/DefaultEntityMetadataFactoryBuilder.java b/api/src/main/java/com/github/juliarn/npclib/api/protocol/meta/DefaultEntityMetadataFactoryBuilder.java
new file mode 100644
index 0000000..b893b71
--- /dev/null
+++ b/api/src/main/java/com/github/juliarn/npclib/api/protocol/meta/DefaultEntityMetadataFactoryBuilder.java
@@ -0,0 +1,111 @@
+/*
+ * This file is part of npc-lib, licensed under the MIT License (MIT).
+ *
+ * Copyright (c) 2022 Julian M., Pasqual K. and contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+package com.github.juliarn.npclib.api.protocol.meta;
+
+import com.github.juliarn.npclib.api.PlatformVersionAccessor;
+import java.lang.reflect.Type;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Objects;
+import java.util.function.Function;
+import org.jetbrains.annotations.NotNull;
+
+final class DefaultEntityMetadataFactoryBuilder implements EntityMetadataFactory.Builder {
+
+ private int baseIndex = 0;
+ private int[] indexShitVersions = new int[0];
+
+ private Type type;
+ private Function inputConverter;
+
+ private Collection> relatedMetadata;
+ private Function availabilityChecker;
+
+ @Override
+ public @NotNull EntityMetadataFactory.Builder baseIndex(int index) {
+ this.baseIndex = index;
+ return this;
+ }
+
+ @Override
+ public @NotNull EntityMetadataFactory.Builder indexShiftVersions(int... versions) {
+ this.indexShitVersions = versions;
+ return this;
+ }
+
+ @Override
+ public @NotNull EntityMetadataFactory.Builder type(@NotNull Type type) {
+ this.type = Objects.requireNonNull(type, "type");
+ return this;
+ }
+
+ @Override
+ public @NotNull EntityMetadataFactory.Builder inputConverter(@NotNull Function mapper) {
+ this.inputConverter = Objects.requireNonNull(mapper, "mapper");
+ return this;
+ }
+
+ @Override
+ public @NotNull EntityMetadataFactory.Builder addRelatedMetadata(
+ @NotNull EntityMetadataFactory relatedMetadata
+ ) {
+ if (this.relatedMetadata == null) {
+ this.relatedMetadata = new HashSet<>();
+ }
+
+ this.relatedMetadata.add(relatedMetadata);
+ return this;
+ }
+
+ @Override
+ public @NotNull EntityMetadataFactory.Builder availabilityChecker(
+ @NotNull Function checker
+ ) {
+ this.availabilityChecker = Objects.requireNonNull(checker, "checker");
+ return this;
+ }
+
+ @Override
+ public @NotNull EntityMetadataFactory build() {
+ // fill in default empty values
+ if (this.relatedMetadata == null) {
+ this.relatedMetadata = Collections.emptySet();
+ }
+
+ if (this.availabilityChecker == null) {
+ this.availabilityChecker = accessor -> true;
+ }
+
+ return new DefaultEntityMetadataFactory<>(
+ this.baseIndex,
+ this.indexShitVersions,
+ Objects.requireNonNull(this.type, "type"),
+ Objects.requireNonNull(this.inputConverter, "inputConverter"),
+ this.relatedMetadata,
+ this.availabilityChecker
+ );
+ }
+}
diff --git a/api/src/main/java/com/github/juliarn/npclib/api/protocol/meta/EntityMetadata.java b/api/src/main/java/com/github/juliarn/npclib/api/protocol/meta/EntityMetadata.java
new file mode 100644
index 0000000..3fa7f27
--- /dev/null
+++ b/api/src/main/java/com/github/juliarn/npclib/api/protocol/meta/EntityMetadata.java
@@ -0,0 +1,39 @@
+/*
+ * This file is part of npc-lib, licensed under the MIT License (MIT).
+ *
+ * Copyright (c) 2022 Julian M., Pasqual K. and contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+package com.github.juliarn.npclib.api.protocol.meta;
+
+import java.lang.reflect.Type;
+import org.jetbrains.annotations.NotNull;
+
+public interface EntityMetadata {
+
+ int index();
+
+ boolean available();
+
+ @NotNull O value();
+
+ @NotNull Type type();
+}
diff --git a/api/src/main/java/com/github/juliarn/npclib/api/protocol/meta/EntityMetadataFactory.java b/api/src/main/java/com/github/juliarn/npclib/api/protocol/meta/EntityMetadataFactory.java
new file mode 100644
index 0000000..260b215
--- /dev/null
+++ b/api/src/main/java/com/github/juliarn/npclib/api/protocol/meta/EntityMetadataFactory.java
@@ -0,0 +1,69 @@
+/*
+ * This file is part of npc-lib, licensed under the MIT License (MIT).
+ *
+ * Copyright (c) 2022 Julian M., Pasqual K. and contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+package com.github.juliarn.npclib.api.protocol.meta;
+
+import com.github.juliarn.npclib.api.PlatformVersionAccessor;
+import java.lang.reflect.Type;
+import java.util.Collection;
+import java.util.function.Function;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Unmodifiable;
+
+public interface EntityMetadataFactory {
+
+ static @NotNull EntityMetadataFactory.Builder metaFactoryBuilder() {
+ return new DefaultEntityMetadataFactoryBuilder<>();
+ }
+
+ static @NotNull EntityMetadataFactory sneakingMetaFactory() {
+ return DefaultEntityMetadata.SNEAKING;
+ }
+
+ static @NotNull EntityMetadataFactory skinLayerMetaFactory() {
+ return DefaultEntityMetadata.SKIN_LAYERS;
+ }
+
+ @Unmodifiable
+ @NotNull Collection> relatedMetadata();
+
+ @NotNull EntityMetadata create(@NotNull I input, @NotNull PlatformVersionAccessor versionAccessor);
+
+ interface Builder {
+
+ @NotNull Builder baseIndex(int index);
+
+ @NotNull Builder indexShiftVersions(int... versions);
+
+ @NotNull Builder type(@NotNull Type type);
+
+ @NotNull Builder inputConverter(@NotNull Function mapper);
+
+ @NotNull Builder addRelatedMetadata(@NotNull EntityMetadataFactory relatedMetadata);
+
+ @NotNull Builder availabilityChecker(@NotNull Function checker);
+
+ @NotNull EntityMetadataFactory build();
+ }
+}
diff --git a/api/src/main/java/com/github/juliarn/npclib/api/settings/NpcProfileResolver.java b/api/src/main/java/com/github/juliarn/npclib/api/settings/NpcProfileResolver.java
index b71e9c5..6ac8302 100644
--- a/api/src/main/java/com/github/juliarn/npclib/api/settings/NpcProfileResolver.java
+++ b/api/src/main/java/com/github/juliarn/npclib/api/settings/NpcProfileResolver.java
@@ -36,5 +36,5 @@ public interface NpcProfileResolver {
return (player, npc) -> CompletableFuture.completedFuture(npc.profile());
}
- @NotNull CompletableFuture resolveNpcProfile(@NotNull P player, @NotNull Npc, P, ?> npc);
+ @NotNull CompletableFuture resolveNpcProfile(@NotNull P player, @NotNull Npc, P, ?, ?> npc);
}
diff --git a/api/src/main/java/com/github/juliarn/npclib/api/settings/NpcTrackingRule.java b/api/src/main/java/com/github/juliarn/npclib/api/settings/NpcTrackingRule.java
index 7e3a569..f65b6ed 100644
--- a/api/src/main/java/com/github/juliarn/npclib/api/settings/NpcTrackingRule.java
+++ b/api/src/main/java/com/github/juliarn/npclib/api/settings/NpcTrackingRule.java
@@ -43,5 +43,5 @@ public interface NpcTrackingRule {
return (npc, player) -> npc.includedPlayers().contains(player);
}
- boolean shouldTrack(@NotNull Npc, P, ?> npc, @NotNull P player);
+ boolean shouldTrack(@NotNull Npc, P, ?, ?> npc, @NotNull P player);
}
diff --git a/build.gradle.kts b/build.gradle.kts
index 25d7f0b..6cc6b2b 100644
--- a/build.gradle.kts
+++ b/build.gradle.kts
@@ -35,8 +35,8 @@ allprojects {
repositories {
mavenCentral()
- maven("https://jitpack.io")
- maven("https://papermc.io/repo/repository/maven-public/")
+ maven("https://jitpack.io/")
+ maven("https://repo.papermc.io/repository/maven-public/")
maven("https://s01.oss.sonatype.org/content/repositories/snapshots/")
}
}
@@ -64,15 +64,19 @@ subprojects {
}
tasks.withType().configureEach {
- sourceCompatibility = JavaVersion.VERSION_1_8.toString()
- targetCompatibility = JavaVersion.VERSION_1_8.toString()
// options
+ options.release.set(8)
options.encoding = "UTF-8"
options.isIncremental = true
// we are aware that those are there, but we only do that if there is no other way we can use - so please keep the terminal clean!
options.compilerArgs = mutableListOf("-Xlint:-deprecation,-unchecked")
}
+ extensions.configure {
+ disableAutoTargetJvm()
+ toolchain.languageVersion.set(JavaLanguageVersion.of(17))
+ }
+
tasks.withType {
maxErrors = 0
maxWarnings = 0
diff --git a/bukkit/build.gradle.kts b/bukkit/build.gradle.kts
new file mode 100644
index 0000000..f86b281
--- /dev/null
+++ b/bukkit/build.gradle.kts
@@ -0,0 +1,46 @@
+/*
+ * This file is part of npc-lib, licensed under the MIT License (MIT).
+ *
+ * Copyright (c) 2022 Julian M., Pasqual K. and contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+import com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar
+
+dependencies {
+ api(projects.api)
+ implementation(libs.paperLib)
+ implementation(projects.common)
+ implementation(libs.packetEvents)
+
+ compileOnly(libs.netty)
+ compileOnly(libs.paper)
+ compileOnly(libs.protocolLib)
+}
+
+tasks.withType().configureEach {
+ minimize()
+
+ relocate("net.kyori", "com.github.juliarn.npclib.relocate.kyori")
+ relocate("com.google.gson", "com.github.juliarn.npclib.relocate.gson")
+ relocate("io.papermc.lib", "com.github.juliarn.npclib.relocate.paperlib")
+ relocate("io.github.retrooper", "com.github.juliarn.npclib.relocate.io.packetevents")
+ relocate("com.github.retrooper", "com.github.juliarn.npclib.relocate.com.packetevents")
+}
diff --git a/bukkit/src/main/java/com/github/juliarn/npclib/bukkit/BukkitActionController.java b/bukkit/src/main/java/com/github/juliarn/npclib/bukkit/BukkitActionController.java
new file mode 100644
index 0000000..b09eacd
--- /dev/null
+++ b/bukkit/src/main/java/com/github/juliarn/npclib/bukkit/BukkitActionController.java
@@ -0,0 +1,219 @@
+/*
+ * This file is part of npc-lib, licensed under the MIT License (MIT).
+ *
+ * Copyright (c) 2022 Julian M., Pasqual K. and contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+package com.github.juliarn.npclib.bukkit;
+
+import com.github.juliarn.npclib.api.Npc;
+import com.github.juliarn.npclib.api.NpcActionController;
+import com.github.juliarn.npclib.api.NpcTracker;
+import com.github.juliarn.npclib.api.Position;
+import com.github.juliarn.npclib.api.event.NpcEvent;
+import com.github.juliarn.npclib.api.event.ShowNpcEvent;
+import com.github.juliarn.npclib.api.flag.NpcFlag;
+import com.github.juliarn.npclib.api.protocol.enums.EntityAnimation;
+import com.github.juliarn.npclib.api.protocol.enums.PlayerInfoAction;
+import com.github.juliarn.npclib.api.protocol.meta.EntityMetadataFactory;
+import com.github.juliarn.npclib.bukkit.util.BukkitPlatformUtil;
+import com.github.juliarn.npclib.common.CommonNpcActionController;
+import com.github.juliarn.npclib.common.flag.CommonNpcFlaggedBuilder;
+import java.util.Map;
+import java.util.Objects;
+import java.util.Optional;
+import net.kyori.event.EventBus;
+import org.bukkit.Location;
+import org.bukkit.World;
+import org.bukkit.entity.Player;
+import org.bukkit.event.EventHandler;
+import org.bukkit.event.EventPriority;
+import org.bukkit.event.Listener;
+import org.bukkit.event.player.PlayerInteractEntityEvent;
+import org.bukkit.event.player.PlayerMoveEvent;
+import org.bukkit.event.player.PlayerQuitEvent;
+import org.bukkit.event.player.PlayerToggleSneakEvent;
+import org.bukkit.inventory.ItemStack;
+import org.bukkit.plugin.Plugin;
+import org.jetbrains.annotations.NotNull;
+
+public final class BukkitActionController extends CommonNpcActionController implements Listener {
+
+ private final NpcTracker npcTracker;
+
+ // based on the given flags
+ private final int spawnDistance;
+ private final int imitateDistance;
+
+ public BukkitActionController(
+ @NotNull Map, Optional>> flags,
+ @NotNull Plugin plugin,
+ @NotNull EventBus eventBus,
+ @NotNull NpcTracker tracker
+ ) {
+ super(flags);
+ this.npcTracker = tracker;
+
+ // add all listeners
+ plugin.getServer().getPluginManager().registerEvents(this, plugin);
+ eventBus.subscribe(ShowNpcEvent.Post.class, event -> {
+ // remove the npc from the tab list after the given amount of time (never smaller than 0 because of validation)
+ int tabRemovalTicks = this.flagValueOrDefault(TAB_REMOVAL_TICKS);
+ plugin.getServer().getScheduler().runTaskLaterAsynchronously(plugin, () -> {
+ // schedule the removal of the player from the tab list, can be done async
+ Player player = event.player();
+ event.npc().platform().packetFactory()
+ .createPlayerInfoPacket(PlayerInfoAction.REMOVE_PLAYER)
+ .schedule(player, event.npc());
+ }, tabRemovalTicks);
+ });
+
+ // pre-calculate flag values
+ int spawnDistance = this.flagValueOrDefault(SPAWN_DISTANCE);
+ this.spawnDistance = spawnDistance * spawnDistance;
+
+ int imitateDistance = this.flagValueOrDefault(IMITATE_DISTANCE);
+ this.imitateDistance = imitateDistance * imitateDistance;
+ }
+
+ static @NotNull NpcActionController.Builder actionControllerBuilder(
+ @NotNull Plugin plugin,
+ @NotNull EventBus eventBus,
+ @NotNull NpcTracker npcTracker
+ ) {
+ Objects.requireNonNull(plugin, "plugin");
+ Objects.requireNonNull(eventBus, "eventBus");
+ Objects.requireNonNull(npcTracker, "npcTracker");
+
+ return new BukkitActionControllerBuilder(plugin, eventBus, npcTracker);
+ }
+
+ @EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true)
+ public void handleMove(@NotNull PlayerMoveEvent event) {
+ Location to = event.getTo();
+ Location from = event.getFrom();
+
+ boolean changedWorld = !Objects.equals(from.getWorld(), to.getWorld());
+ boolean changedOrientation = from.getYaw() != to.getYaw() || from.getPitch() != to.getPitch();
+ boolean changedPosition = from.getX() != to.getX() || from.getY() != to.getY() || from.getZ() != to.getZ();
+
+ // check if any movement happened (event is also called when standing still)
+ if (changedPosition || changedOrientation || changedWorld) {
+ Player player = event.getPlayer();
+ for (Npc npc : this.npcTracker.trackedNpcs()) {
+ // check if the player is still in the same world as the npc
+ Position pos = npc.position();
+ if (!npc.world().equals(player.getWorld()) || !npc.world().isChunkLoaded(pos.chunkX(), pos.chunkZ())) {
+ // if the player is tracked by the npc, stop that
+ npc.stopTrackingPlayer(player);
+ continue;
+ }
+
+ // check if the player moved in / out of any npc tracking distance
+ double distance = BukkitPlatformUtil.distance(npc, to);
+ if (distance > this.spawnDistance) {
+ // this will only do something if the player is already tracked by the npc
+ npc.stopTrackingPlayer(player);
+ continue;
+ } else {
+ // this will only do something if the player is not already tracked by the npc
+ npc.trackPlayer(player);
+ }
+
+ // check if we should rotate the npc towards the player
+ if (changedOrientation
+ && npc.tracksPlayer(player)
+ && distance <= this.imitateDistance
+ && npc.flagValueOrDefault(Npc.LOOK_AT_PLAYER)) {
+ npc.lookAt(BukkitPlatformUtil.positionFromBukkit(to)).schedule(player);
+ }
+ }
+ }
+ }
+
+ @EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true)
+ public void handleSneak(@NotNull PlayerToggleSneakEvent event) {
+ Player player = event.getPlayer();
+ for (Npc npc : this.npcTracker.trackedNpcs()) {
+ double distance = BukkitPlatformUtil.distance(npc, player.getLocation());
+
+ // check if we should imitate the action
+ if (Objects.equals(player.getWorld(), npc.world())
+ && npc.tracksPlayer(player)
+ && distance <= this.imitateDistance
+ && npc.flagValueOrDefault(Npc.SNEAK_WHEN_PLAYER_SNEAKS)) {
+ // let the npc sneak as well
+ npc.platform().packetFactory()
+ .createEntityMetaPacket(event.isSneaking(), EntityMetadataFactory.sneakingMetaFactory())
+ .schedule(player, npc);
+ }
+ }
+ }
+
+ @EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true)
+ public void handleLeftClick(@NotNull PlayerInteractEntityEvent event) {
+ Player player = event.getPlayer();
+ for (Npc npc : this.npcTracker.trackedNpcs()) {
+ double distance = BukkitPlatformUtil.distance(npc, player.getLocation());
+
+ // check if we should imitate the action
+ if (Objects.equals(player.getWorld(), npc.world())
+ && npc.tracksPlayer(player)
+ && distance <= this.imitateDistance
+ && npc.flagValueOrDefault(Npc.HIT_WHEN_PLAYER_HITS)) {
+ // let the npc left click as well
+ npc.platform().packetFactory().createAnimationPacket(EntityAnimation.SWING_MAIN_ARM).schedule(player, npc);
+ }
+ }
+ }
+
+ @EventHandler(priority = EventPriority.MONITOR)
+ public void handleQuit(@NotNull PlayerQuitEvent event) {
+ for (Npc npc : this.npcTracker.trackedNpcs()) {
+ // check if the npc tracks the player which disconnected and stop tracking him if so
+ npc.stopTrackingPlayer(event.getPlayer());
+ }
+ }
+
+ private static final class BukkitActionControllerBuilder
+ extends CommonNpcFlaggedBuilder
+ implements NpcActionController.Builder {
+
+ private final Plugin plugin;
+ private final EventBus eventBus;
+ private final NpcTracker npcTracker;
+
+ public BukkitActionControllerBuilder(
+ @NotNull Plugin plugin,
+ @NotNull EventBus eventBus,
+ @NotNull NpcTracker npcTracker
+ ) {
+ this.plugin = plugin;
+ this.eventBus = eventBus;
+ this.npcTracker = npcTracker;
+ }
+
+ @Override
+ public @NotNull NpcActionController build() {
+ return new BukkitActionController(this.flags, this.plugin, this.eventBus, this.npcTracker);
+ }
+ }
+}
diff --git a/bukkit/src/main/java/com/github/juliarn/npclib/bukkit/BukkitPlatform.java b/bukkit/src/main/java/com/github/juliarn/npclib/bukkit/BukkitPlatform.java
new file mode 100644
index 0000000..ad1a93a
--- /dev/null
+++ b/bukkit/src/main/java/com/github/juliarn/npclib/bukkit/BukkitPlatform.java
@@ -0,0 +1,101 @@
+/*
+ * This file is part of npc-lib, licensed under the MIT License (MIT).
+ *
+ * Copyright (c) 2022 Julian M., Pasqual K. and contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+package com.github.juliarn.npclib.bukkit;
+
+import com.github.juliarn.npclib.api.NpcActionController;
+import com.github.juliarn.npclib.api.Platform;
+import com.github.juliarn.npclib.bukkit.protocol.BukkitProtocolAdapter;
+import com.github.juliarn.npclib.common.platform.CommonPlatform;
+import com.github.juliarn.npclib.common.platform.CommonPlatformBuilder;
+import org.bukkit.World;
+import org.bukkit.entity.Player;
+import org.bukkit.inventory.ItemStack;
+import org.bukkit.plugin.Plugin;
+import org.jetbrains.annotations.NotNull;
+
+public final class BukkitPlatform extends CommonPlatformBuilder {
+
+ private BukkitPlatform() {
+ }
+
+ public static @NotNull BukkitPlatform bukkitNpcPlatformBuilder() {
+ return new BukkitPlatform();
+ }
+
+ @Override
+ protected void prepareBuild() {
+ // set the profile resolver to a native platform one if not given
+ if (this.profileResolver == null) {
+ this.profileResolver = BukkitProfileResolver.profileResolver();
+ }
+
+ // set the default task manager
+ if (this.taskManager == null) {
+ this.taskManager = BukkitPlatformTaskManager.taskManager(this.extension);
+ }
+
+ // set the default version accessor
+ if (this.versionAccessor == null) {
+ this.versionAccessor = BukkitVersionAccessor.versionAccessor();
+ }
+
+ // set the default world accessor
+ if (this.worldAccessor == null) {
+ this.worldAccessor = BukkitWorldAccessor.worldAccessor();
+ }
+
+ // set the default packet adapter
+ if (this.packetAdapter == null) {
+ this.packetAdapter = BukkitProtocolAdapter.packetAdapter();
+ }
+ }
+
+ @Override
+ protected @NotNull Platform doBuild() {
+ // check if we need an action controller
+ NpcActionController actionController = null;
+ if (this.actionControllerDecorator != null) {
+ NpcActionController.Builder builder = BukkitActionController.actionControllerBuilder(
+ this.extension,
+ this.eventBus,
+ this.npcTracker);
+ this.actionControllerDecorator.accept(builder);
+ actionController = builder.build();
+ }
+
+ // build the platform
+ return new CommonPlatform<>(
+ this.debug,
+ this.extension,
+ this.npcTracker,
+ this.profileResolver,
+ this.taskManager,
+ actionController,
+ this.versionAccessor,
+ this.eventBus,
+ this.worldAccessor,
+ this.packetAdapter);
+ }
+}
diff --git a/bukkit/src/main/java/com/github/juliarn/npclib/bukkit/BukkitPlatformTaskManager.java b/bukkit/src/main/java/com/github/juliarn/npclib/bukkit/BukkitPlatformTaskManager.java
new file mode 100644
index 0000000..3ed25d5
--- /dev/null
+++ b/bukkit/src/main/java/com/github/juliarn/npclib/bukkit/BukkitPlatformTaskManager.java
@@ -0,0 +1,64 @@
+/*
+ * This file is part of npc-lib, licensed under the MIT License (MIT).
+ *
+ * Copyright (c) 2022 Julian M., Pasqual K. and contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+package com.github.juliarn.npclib.bukkit;
+
+import com.github.juliarn.npclib.api.PlatformTaskManager;
+import java.util.Objects;
+import org.bukkit.plugin.Plugin;
+import org.jetbrains.annotations.NotNull;
+
+public final class BukkitPlatformTaskManager implements PlatformTaskManager {
+
+ private final Plugin plugin;
+
+ private BukkitPlatformTaskManager(Plugin plugin) {
+ this.plugin = plugin;
+ }
+
+ public static @NotNull PlatformTaskManager taskManager(@NotNull Plugin plugin) {
+ Objects.requireNonNull(plugin, "plugin");
+ return new BukkitPlatformTaskManager(plugin);
+ }
+
+ @Override
+ public void scheduleSync(@NotNull Runnable task) {
+ this.plugin.getServer().getScheduler().runTask(this.plugin, task);
+ }
+
+ @Override
+ public void scheduleDelayedSync(@NotNull Runnable task, int delayTicks) {
+ this.plugin.getServer().getScheduler().runTaskLater(this.plugin, task, delayTicks);
+ }
+
+ @Override
+ public void scheduleAsync(@NotNull Runnable task) {
+ this.plugin.getServer().getScheduler().runTaskAsynchronously(this.plugin, task);
+ }
+
+ @Override
+ public void scheduleDelayedAsync(@NotNull Runnable task, int delayTicks) {
+ this.plugin.getServer().getScheduler().runTaskLaterAsynchronously(this.plugin, task, delayTicks);
+ }
+}
diff --git a/bukkit/src/main/java/com/github/juliarn/npclib/bukkit/BukkitProfileResolver.java b/bukkit/src/main/java/com/github/juliarn/npclib/bukkit/BukkitProfileResolver.java
new file mode 100644
index 0000000..c1b8137
--- /dev/null
+++ b/bukkit/src/main/java/com/github/juliarn/npclib/bukkit/BukkitProfileResolver.java
@@ -0,0 +1,125 @@
+/*
+ * This file is part of npc-lib, licensed under the MIT License (MIT).
+ *
+ * Copyright (c) 2022 Julian M., Pasqual K. and contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+package com.github.juliarn.npclib.bukkit;
+
+import com.destroystokyo.paper.profile.PlayerProfile;
+import com.github.juliarn.npclib.api.profile.Profile;
+import com.github.juliarn.npclib.api.profile.ProfileProperty;
+import com.github.juliarn.npclib.api.profile.ProfileResolver;
+import io.papermc.lib.PaperLib;
+import java.nio.charset.StandardCharsets;
+import java.util.Base64;
+import java.util.Collections;
+import java.util.Set;
+import java.util.concurrent.CompletableFuture;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+import java.util.stream.Collectors;
+import org.bukkit.Bukkit;
+import org.jetbrains.annotations.NotNull;
+
+public final class BukkitProfileResolver {
+
+ private BukkitProfileResolver() {
+ throw new UnsupportedOperationException();
+ }
+
+ public static @NotNull ProfileResolver profileResolver() {
+ // check if we're on paper and newer than 1.12 (when the profile API was introduced)
+ if (PaperLib.isPaper() && PaperLib.isVersion(12)) {
+ return PaperProfileResolver.INSTANCE;
+ }
+
+ // check if we're on spigot and newer than 1.18.2 (when the profile API was introduced)
+ if (PaperLib.isSpigot() && PaperLib.isVersion(18, 2)) {
+ return SpigotProfileResolver.INSTANCE;
+ }
+
+ // use fallback resolver
+ return LegacyResolver.INSTANCE;
+ }
+
+ private static final class PaperProfileResolver implements ProfileResolver {
+
+ private static final ProfileResolver INSTANCE = new PaperProfileResolver();
+
+ @Override
+ public @NotNull CompletableFuture resolveProfile(@NotNull Profile profile) {
+ return CompletableFuture.supplyAsync(() -> {
+ // create a profile from the given one and try to complete it
+ PlayerProfile playerProfile = Bukkit.createProfile(profile.uniqueId(), profile.name());
+ playerProfile.complete(true, true);
+
+ // convert the profile properties to the wrapper one
+ Set properties = playerProfile.getProperties()
+ .stream()
+ .map(prop -> ProfileProperty.property(prop.getName(), prop.getValue(), prop.getSignature()))
+ .collect(Collectors.toSet());
+
+ // create the resolved profile
+ //noinspection ConstantConditions
+ return Profile.resolved(playerProfile.getName(), playerProfile.getId(), properties);
+ });
+ }
+ }
+
+ private static final class SpigotProfileResolver implements ProfileResolver {
+
+ private static final ProfileResolver INSTANCE = new SpigotProfileResolver();
+ private static final Pattern DATA_EXTRACT_PATTERN = Pattern.compile("^CraftPlayerTextures \\[data=(.*)]$");
+
+ @Override
+ @SuppressWarnings("deprecation") // deprecated by paper, but we only use this on spigot
+ public @NotNull CompletableFuture resolveProfile(@NotNull Profile profile) {
+ // create the profile and fill in the empty values
+ org.bukkit.profile.PlayerProfile playerProfile = Bukkit.createPlayerProfile(profile.uniqueId(), profile.name());
+ return playerProfile.update().thenApply(resolvedProfile -> {
+ // hack to get the data from the profile
+ Matcher matcher = DATA_EXTRACT_PATTERN.matcher(resolvedProfile.getTextures().toString());
+ if (matcher.matches()) {
+ // encode the raw texture data
+ byte[] rawTextureData = matcher.group(1).getBytes(StandardCharsets.UTF_8);
+ String encodedTextureData = Base64.getEncoder().encodeToString(rawTextureData);
+
+ // create the profile from the spigot one
+ ProfileProperty textureProperty = ProfileProperty.property("textures", encodedTextureData);
+ Set properties = Collections.singleton(textureProperty);
+
+ // create the resolved profile
+ //noinspection ConstantConditions
+ return Profile.resolved(playerProfile.getName(), playerProfile.getUniqueId(), properties);
+ }
+
+ // unable to complete the profile
+ throw new IllegalArgumentException("Profile texture input: " + resolvedProfile.getTextures() + " is invalid");
+ });
+ }
+ }
+
+ private static final class LegacyResolver {
+
+ private static final ProfileResolver INSTANCE = ProfileResolver.caching(ProfileResolver.mojang());
+ }
+}
diff --git a/bukkit/src/main/java/com/github/juliarn/npclib/bukkit/BukkitVersionAccessor.java b/bukkit/src/main/java/com/github/juliarn/npclib/bukkit/BukkitVersionAccessor.java
new file mode 100644
index 0000000..5419953
--- /dev/null
+++ b/bukkit/src/main/java/com/github/juliarn/npclib/bukkit/BukkitVersionAccessor.java
@@ -0,0 +1,64 @@
+/*
+ * This file is part of npc-lib, licensed under the MIT License (MIT).
+ *
+ * Copyright (c) 2022 Julian M., Pasqual K. and contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+package com.github.juliarn.npclib.bukkit;
+
+import com.github.juliarn.npclib.api.PlatformVersionAccessor;
+import io.papermc.lib.PaperLib;
+
+public class BukkitVersionAccessor {
+
+ private BukkitVersionAccessor() {
+ throw new UnsupportedOperationException();
+ }
+
+ public static PlatformVersionAccessor versionAccessor() {
+ return PaperLibPlatformVersionAccessor.INSTANCE;
+ }
+
+ private static final class PaperLibPlatformVersionAccessor implements PlatformVersionAccessor {
+
+ private static final PaperLibPlatformVersionAccessor INSTANCE = new PaperLibPlatformVersionAccessor();
+
+ @Override
+ public int major() {
+ return 1;
+ }
+
+ @Override
+ public int minor() {
+ return PaperLib.getMinecraftVersion();
+ }
+
+ @Override
+ public int patch() {
+ return PaperLib.getMinecraftPatchVersion();
+ }
+
+ @Override
+ public boolean atLeast(int major, int minor, int patch) {
+ return PaperLib.isVersion(minor, patch);
+ }
+ }
+}
diff --git a/bukkit/src/main/java/com/github/juliarn/npclib/bukkit/BukkitWorldAccessor.java b/bukkit/src/main/java/com/github/juliarn/npclib/bukkit/BukkitWorldAccessor.java
new file mode 100644
index 0000000..e793e63
--- /dev/null
+++ b/bukkit/src/main/java/com/github/juliarn/npclib/bukkit/BukkitWorldAccessor.java
@@ -0,0 +1,88 @@
+/*
+ * This file is part of npc-lib, licensed under the MIT License (MIT).
+ *
+ * Copyright (c) 2022 Julian M., Pasqual K. and contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+package com.github.juliarn.npclib.bukkit;
+
+import com.github.juliarn.npclib.api.PlatformWorldAccessor;
+import io.papermc.lib.PaperLib;
+import org.bukkit.Bukkit;
+import org.bukkit.NamespacedKey;
+import org.bukkit.World;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+public final class BukkitWorldAccessor {
+
+ private BukkitWorldAccessor() {
+ throw new UnsupportedOperationException();
+ }
+
+ public static @NotNull PlatformWorldAccessor worldAccessor() {
+ // check if we are on paper and newer (or equal) to 1.16.5
+ if (PaperLib.isPaper() && PaperLib.isVersion(16, 5)) {
+ return ModernAccessor.INSTANCE;
+ } else {
+ return LegacyAccessor.INSTANCE;
+ }
+ }
+
+ public static @NotNull PlatformWorldAccessor nameBasedAccessor() {
+ return LegacyAccessor.INSTANCE;
+ }
+
+ public static @NotNull PlatformWorldAccessor keyBasedAccessor() {
+ return ModernAccessor.INSTANCE;
+ }
+
+ private static final class LegacyAccessor implements PlatformWorldAccessor {
+
+ private static final PlatformWorldAccessor INSTANCE = new LegacyAccessor();
+
+ @Override
+ public @NotNull String extractWorldIdentifier(@NotNull World world) {
+ return world.getName();
+ }
+
+ @Override
+ public @Nullable World resolveWorldFromIdentifier(@NotNull String identifier) {
+ return Bukkit.getWorld(identifier);
+ }
+ }
+
+ private static final class ModernAccessor implements PlatformWorldAccessor {
+
+ private static final PlatformWorldAccessor INSTANCE = new ModernAccessor();
+
+ @Override
+ public @NotNull String extractWorldIdentifier(@NotNull World world) {
+ return world.getKey().asString();
+ }
+
+ @Override
+ public @Nullable World resolveWorldFromIdentifier(@NotNull String identifier) {
+ NamespacedKey key = NamespacedKey.fromString(identifier);
+ return key == null ? null : Bukkit.getWorld(key);
+ }
+ }
+}
diff --git a/bukkit/src/main/java/com/github/juliarn/npclib/bukkit/protocol/BukkitProtocolAdapter.java b/bukkit/src/main/java/com/github/juliarn/npclib/bukkit/protocol/BukkitProtocolAdapter.java
new file mode 100644
index 0000000..52cba9e
--- /dev/null
+++ b/bukkit/src/main/java/com/github/juliarn/npclib/bukkit/protocol/BukkitProtocolAdapter.java
@@ -0,0 +1,58 @@
+/*
+ * This file is part of npc-lib, licensed under the MIT License (MIT).
+ *
+ * Copyright (c) 2022 Julian M., Pasqual K. and contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+package com.github.juliarn.npclib.bukkit.protocol;
+
+import com.github.juliarn.npclib.api.protocol.PlatformPacketAdapter;
+import org.bukkit.Bukkit;
+import org.bukkit.World;
+import org.bukkit.entity.Player;
+import org.bukkit.inventory.ItemStack;
+import org.bukkit.plugin.Plugin;
+import org.jetbrains.annotations.NotNull;
+
+public final class BukkitProtocolAdapter {
+
+ private BukkitProtocolAdapter() {
+ throw new UnsupportedOperationException();
+ }
+
+ public static @NotNull PlatformPacketAdapter packetAdapter() {
+ // check if protocol lib is available
+ if (Bukkit.getPluginManager().getPlugin("ProtocolLib") != null) {
+ return ProtocolLibPacketAdapter.INSTANCE;
+ }
+
+ // fallback
+ return PacketEventsPacketAdapter.INSTANCE;
+ }
+
+ public static @NotNull PlatformPacketAdapter protocolLib() {
+ return ProtocolLibPacketAdapter.INSTANCE;
+ }
+
+ public static @NotNull PlatformPacketAdapter packetEvents() {
+ return PacketEventsPacketAdapter.INSTANCE;
+ }
+}
diff --git a/bukkit/src/main/java/com/github/juliarn/npclib/bukkit/protocol/PacketEventsPacketAdapter.java b/bukkit/src/main/java/com/github/juliarn/npclib/bukkit/protocol/PacketEventsPacketAdapter.java
new file mode 100644
index 0000000..1eb8878
--- /dev/null
+++ b/bukkit/src/main/java/com/github/juliarn/npclib/bukkit/protocol/PacketEventsPacketAdapter.java
@@ -0,0 +1,435 @@
+/*
+ * This file is part of npc-lib, licensed under the MIT License (MIT).
+ *
+ * Copyright (c) 2022 Julian M., Pasqual K. and contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+package com.github.juliarn.npclib.bukkit.protocol;
+
+import com.github.juliarn.npclib.api.Npc;
+import com.github.juliarn.npclib.api.Platform;
+import com.github.juliarn.npclib.api.PlatformVersionAccessor;
+import com.github.juliarn.npclib.api.Position;
+import com.github.juliarn.npclib.api.event.InteractNpcEvent;
+import com.github.juliarn.npclib.api.profile.ProfileProperty;
+import com.github.juliarn.npclib.api.protocol.OutboundPacket;
+import com.github.juliarn.npclib.api.protocol.PlatformPacketAdapter;
+import com.github.juliarn.npclib.api.protocol.enums.EntityAnimation;
+import com.github.juliarn.npclib.api.protocol.enums.EntityPose;
+import com.github.juliarn.npclib.api.protocol.enums.ItemSlot;
+import com.github.juliarn.npclib.api.protocol.enums.PlayerInfoAction;
+import com.github.juliarn.npclib.api.protocol.meta.EntityMetadata;
+import com.github.juliarn.npclib.api.protocol.meta.EntityMetadataFactory;
+import com.github.juliarn.npclib.common.event.DefaultAttackNpcEvent;
+import com.github.juliarn.npclib.common.event.DefaultInteractNpcEvent;
+import com.github.juliarn.npclib.common.util.EventDispatcher;
+import com.github.retrooper.packetevents.PacketEventsAPI;
+import com.github.retrooper.packetevents.event.PacketListenerPriority;
+import com.github.retrooper.packetevents.event.SimplePacketListenerAbstract;
+import com.github.retrooper.packetevents.event.simple.PacketPlayReceiveEvent;
+import com.github.retrooper.packetevents.manager.player.PlayerManager;
+import com.github.retrooper.packetevents.manager.server.ServerVersion;
+import com.github.retrooper.packetevents.protocol.entity.data.EntityData;
+import com.github.retrooper.packetevents.protocol.entity.data.EntityDataType;
+import com.github.retrooper.packetevents.protocol.entity.data.EntityDataTypes;
+import com.github.retrooper.packetevents.protocol.packettype.PacketType;
+import com.github.retrooper.packetevents.protocol.player.Equipment;
+import com.github.retrooper.packetevents.protocol.player.EquipmentSlot;
+import com.github.retrooper.packetevents.protocol.player.GameMode;
+import com.github.retrooper.packetevents.protocol.player.InteractionHand;
+import com.github.retrooper.packetevents.protocol.player.TextureProperty;
+import com.github.retrooper.packetevents.protocol.player.UserProfile;
+import com.github.retrooper.packetevents.protocol.world.Location;
+import com.github.retrooper.packetevents.settings.PacketEventsSettings;
+import com.github.retrooper.packetevents.util.TimeStampMode;
+import com.github.retrooper.packetevents.wrapper.PacketWrapper;
+import com.github.retrooper.packetevents.wrapper.play.client.WrapperPlayClientInteractEntity;
+import com.github.retrooper.packetevents.wrapper.play.server.WrapperPlayServerDestroyEntities;
+import com.github.retrooper.packetevents.wrapper.play.server.WrapperPlayServerEntityAnimation;
+import com.github.retrooper.packetevents.wrapper.play.server.WrapperPlayServerEntityEquipment;
+import com.github.retrooper.packetevents.wrapper.play.server.WrapperPlayServerEntityHeadLook;
+import com.github.retrooper.packetevents.wrapper.play.server.WrapperPlayServerEntityMetadata;
+import com.github.retrooper.packetevents.wrapper.play.server.WrapperPlayServerEntityRotation;
+import com.github.retrooper.packetevents.wrapper.play.server.WrapperPlayServerEntityTeleport;
+import com.github.retrooper.packetevents.wrapper.play.server.WrapperPlayServerPlayerInfo;
+import com.github.retrooper.packetevents.wrapper.play.server.WrapperPlayServerPluginMessage;
+import com.github.retrooper.packetevents.wrapper.play.server.WrapperPlayServerSpawnPlayer;
+import com.google.common.collect.ImmutableMap;
+import io.github.retrooper.packetevents.factory.spigot.SpigotPacketEventsBuilder;
+import io.github.retrooper.packetevents.util.SpigotReflectionUtil;
+import java.lang.reflect.ParameterizedType;
+import java.lang.reflect.Type;
+import java.util.ArrayList;
+import java.util.EnumMap;
+import java.util.List;
+import java.util.Map;
+import java.util.function.Function;
+import net.kyori.adventure.text.Component;
+import org.bukkit.World;
+import org.bukkit.entity.Player;
+import org.bukkit.inventory.ItemStack;
+import org.bukkit.plugin.Plugin;
+import org.jetbrains.annotations.NotNull;
+
+final class PacketEventsPacketAdapter implements PlatformPacketAdapter