diff --git a/.idea/workspace.xml b/.idea/workspace.xml index 0c3487f..cd26f29 100644 --- a/.idea/workspace.xml +++ b/.idea/workspace.xml @@ -5,11 +5,20 @@ - - - + + + + + + + + + + - + + + - { + "keyToString": { + "Downloaded.Files.Path.Enabled": "false", + "Gradle.Build EntityLib.executor": "Run", + "Gradle.EntityLib:test-plugin [runServer].executor": "Run", + "Repository.Attach.Annotations": "false", + "Repository.Attach.JavaDocs": "false", + "Repository.Attach.Sources": "false", + "RunOnceActivity.OpenProjectViewOnStart": "true", + "RunOnceActivity.ShowReadmeOnStart": "true", + "WebServerToolWindowFactoryState": "false", + "git-widget-placeholder": "feat/platform-api", + "ignore.virus.scanning.warn.message": "true", + "jdk.selected.JAVA_MODULE": "corretto-17", + "kotlin-language-version-configured": "true", + "last_opened_file_path": "D:/Github/EntityLib", + "node.js.detected.package.eslint": "true", + "node.js.detected.package.tslint": "true", + "node.js.selected.package.eslint": "(autodetect)", + "node.js.selected.package.tslint": "(autodetect)", + "nodejs_package_manager_path": "npm", + "project.structure.last.edited": "Modules", + "project.structure.proportion": "0.0", + "project.structure.side.proportion": "0.0", + "settings.editor.selected.configurable": "preferences.pluginManager", + "vue.rearranger.settings.migration": "true" } -}]]> +} @@ -216,6 +229,10 @@ + + + + diff --git a/api/build.gradle b/api/build.gradle index 18d60d6..3e5d186 100644 --- a/api/build.gradle +++ b/api/build.gradle @@ -1,5 +1,6 @@ plugins { id 'java' + id 'java-library' } group = 'me.tofaa.entitylib' @@ -10,10 +11,8 @@ repositories { } dependencies { - testImplementation platform('org.junit:junit-bom:5.9.1') - testImplementation 'org.junit.jupiter:junit-jupiter' -} + api 'org.jetbrains:annotations:24.0.0' -test { - useJUnitPlatform() -} \ No newline at end of file + compileOnlyApi(adventureDependencies) + compileOnlyApi 'com.github.retrooper.packetevents:api:2.2.0' +} diff --git a/api/src/main/java/me/tofaa/entitylib/APISettings.java b/api/src/main/java/me/tofaa/entitylib/APISettings.java new file mode 100644 index 0000000..01b5d17 --- /dev/null +++ b/api/src/main/java/me/tofaa/entitylib/APISettings.java @@ -0,0 +1,100 @@ +package me.tofaa.entitylib; + +import com.github.retrooper.packetevents.PacketEventsAPI; +import com.google.gson.JsonArray; +import com.google.gson.JsonParser; +import org.jetbrains.annotations.NotNull; + +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.net.URL; + +public final class APISettings { + + private boolean debugMode = false; + private boolean checkForUpdates = false; + private boolean tickTickables = false; + private PacketEventsAPI packetEvents; + private boolean platformLogger = false; + + public APISettings(PacketEventsAPI packetEvents) { + this.packetEvents = packetEvents; + } + + public boolean requiresUpdate() throws IOException { + if (!checkForUpdates) return false; + String urlString = "https://api.github.com/repos/Tofaa2/EntityLib/releases/latest"; + String version = EntityLib.getVersion(); + + URL url = new URL(urlString); + JsonParser json = new JsonParser(); + InputStream stream = url.openStream(); + JsonArray array = json.parse(new InputStreamReader(stream)).getAsJsonArray(); + String latest = array.get(0).getAsJsonObject().get("tag_name").getAsString(); + + stream.close(); + return !version.equalsIgnoreCase(latest); + } + + public @NotNull APISettings usePlatformLogger() { + this.platformLogger = true; + return this; + } + + public @NotNull APISettings usePlatformLogger(boolean platformLogger) { + this.platformLogger = platformLogger; + return this; + } + + public @NotNull APISettings debugMode(boolean debugMode) { + this.debugMode = debugMode; + return this; + } + + public @NotNull APISettings checkForUpdates() { + this.checkForUpdates = true; + return this; + } + + public @NotNull APISettings tickTickables() { + this.tickTickables = true; + return this; + } + + public @NotNull APISettings debugMode() { + this.debugMode = true; + return this; + } + + public @NotNull APISettings checkForUpdates(boolean checkForUpdates) { + this.checkForUpdates = checkForUpdates; + return this; + } + + public @NotNull APISettings tickTickables(boolean tickTickables) { + this.tickTickables = tickTickables; + return this; + } + + public boolean isDebugMode() { + return debugMode; + } + + public boolean shouldCheckForUpdate() { + return checkForUpdates; + } + + public boolean shouldTickTickables() { + return tickTickables; + } + + public PacketEventsAPI getPacketEvents() { + return packetEvents; + } + + public boolean shouldUsePlatformLogger() { + return platformLogger; + } + +} diff --git a/api/src/main/java/me/tofaa/entitylib/EntityLib.java b/api/src/main/java/me/tofaa/entitylib/EntityLib.java new file mode 100644 index 0000000..453b99e --- /dev/null +++ b/api/src/main/java/me/tofaa/entitylib/EntityLib.java @@ -0,0 +1,28 @@ +package me.tofaa.entitylib; + +public final class EntityLib { + + private EntityLib() {} + + private static String version = "1.2.0-SNAPSHOT"; + private static Platform platform; + private static EntityLibAPI api; + + public static void init(Platform platform, APISettings settings) { + EntityLib.platform = platform; + platform.setupApi(settings); + api = platform.getAPI(); + } + + public static EntityLibAPI getApi() { + return api; + } + + public static Platform getPlatform() { + return platform; + } + + public static String getVersion() { + return version; + } +} diff --git a/api/src/main/java/me/tofaa/entitylib/EntityLibAPI.java b/api/src/main/java/me/tofaa/entitylib/EntityLibAPI.java new file mode 100644 index 0000000..6355aba --- /dev/null +++ b/api/src/main/java/me/tofaa/entitylib/EntityLibAPI.java @@ -0,0 +1,43 @@ +package me.tofaa.entitylib; + +import com.github.retrooper.packetevents.PacketEventsAPI; +import me.tofaa.entitylib.tick.TickContainer; +import org.jetbrains.annotations.NotNull; + +import java.util.Collection; + +/** + * Represents the API for EntityLib. + * Handles the loading, enabling, and disabling of the API. And handles platform specific creation of EntityLib content. + * @param The {@link WorldWrapper}'s param type for the platform specific World. + * @param The {@link TickContainer}'s param type for the platform specific TickContainer. + */ +public interface EntityLibAPI { + + PacketEventsAPI getPacketEvents(); + + void onLoad(); + + void onEnable(); + + @NotNull APISettings getSettings(); + + + /** + * If a platform supports ticking + * this method should be responsible for setting up the {@link me.tofaa.entitylib.tick.TickContainer}'s. + */ + void setupTickingContainers(); + + + /** + * @return An unmodifiable collection of TickContainers. + */ + @NotNull Collection> getTickContainers(); + + /** + * Adds a TickContainer to the API. Automatically starts ticking it. + * @param tickContainer the TickContainer to add. + */ + void addTickContainer(@NotNull TickContainer tickContainer); +} diff --git a/api/src/main/java/me/tofaa/entitylib/Platform.java b/api/src/main/java/me/tofaa/entitylib/Platform.java new file mode 100644 index 0000000..caa366a --- /dev/null +++ b/api/src/main/java/me/tofaa/entitylib/Platform.java @@ -0,0 +1,53 @@ +package me.tofaa.entitylib; + +import me.tofaa.entitylib.event.EntityLibEvent; +import org.jetbrains.annotations.NotNull; + +import java.util.function.Consumer; +import java.util.logging.Logger; + +/** + * A generic representation of a platform that EntityLib is running on. + * @param

The platform handle, for Spigot this would be a JavaPlugin. etc etc. + */ +public interface Platform

{ + + + /** + * @return the logger EntityLib uses internally. + */ + @NotNull Logger getLogger(); + + /** + * Sends an event to the platform. Platform implementations should handle the event accordingly. + * @param event the event to send. + */ + void sendEvent(EntityLibEvent event); + + /** + * Registers a listener for the given event class, the handle will be called when the event is sent. + * @param eventClass the event class to listen for. + * @param handle the handle to call when the event is sent. + * @param the event type. + */ + void registerListener(Class eventClass, Consumer handle); + + /** + * Sets up the API for the platform. This method should be called automatically by the platform. Don't call it yourself. + * @param settings + */ + void setupApi(@NotNull APISettings settings); + + /** + * @return The API instance. + */ + EntityLibAPI getAPI(); + + /** + * @return the platforms name. + */ + String getName(); + + @NotNull P getHandle(); + +} diff --git a/api/src/main/java/me/tofaa/entitylib/WorldWrapper.java b/api/src/main/java/me/tofaa/entitylib/WorldWrapper.java new file mode 100644 index 0000000..1cfdb45 --- /dev/null +++ b/api/src/main/java/me/tofaa/entitylib/WorldWrapper.java @@ -0,0 +1,19 @@ +package me.tofaa.entitylib; + +import com.github.retrooper.packetevents.protocol.world.states.WrappedBlockState; +import com.github.retrooper.packetevents.resources.ResourceLocation; + +/** + * Represents a platform specific world. + * These are not needed at all times, and should exclusively be used when an Entity needs to be + * aware of its surroundings. + * @param The platform specific World type. + */ +public interface WorldWrapper { + + ResourceLocation getPacketEventsWorld(); + + WrappedBlockState getBlock(int x, int y, int z); + + W getHandle(); +} diff --git a/api/src/main/java/me/tofaa/entitylib/event/EntityLibEvent.java b/api/src/main/java/me/tofaa/entitylib/event/EntityLibEvent.java new file mode 100644 index 0000000..8f3e66c --- /dev/null +++ b/api/src/main/java/me/tofaa/entitylib/event/EntityLibEvent.java @@ -0,0 +1,13 @@ +package me.tofaa.entitylib.event; + +public interface EntityLibEvent { + + boolean isCancelled(); + + void setCancelled(boolean cancelled); + + default boolean isAsync() { + return false; + } + +} diff --git a/api/src/main/java/me/tofaa/entitylib/event/UserReceiveMetaUpdateEvent.java b/api/src/main/java/me/tofaa/entitylib/event/UserReceiveMetaUpdateEvent.java new file mode 100644 index 0000000..40df9cd --- /dev/null +++ b/api/src/main/java/me/tofaa/entitylib/event/UserReceiveMetaUpdateEvent.java @@ -0,0 +1,29 @@ +package me.tofaa.entitylib.event; + +import com.github.retrooper.packetevents.protocol.player.User; +import me.tofaa.entitylib.meta.EntityMeta; + +public final class UserReceiveMetaUpdateEvent implements EntityLibEvent { + + private final User user; + private boolean cancelled; + private EntityMeta meta; + + public UserReceiveMetaUpdateEvent(User user) { + this.user = user; + } + + public User getUser() { + return user; + } + + @Override + public boolean isCancelled() { + return false; + } + + @Override + public void setCancelled(boolean cancelled) { + + } +} diff --git a/api/src/main/java/me/tofaa/entitylib/extras/InvalidVersionException.java b/api/src/main/java/me/tofaa/entitylib/extras/InvalidVersionException.java new file mode 100644 index 0000000..a6d0964 --- /dev/null +++ b/api/src/main/java/me/tofaa/entitylib/extras/InvalidVersionException.java @@ -0,0 +1,23 @@ +package me.tofaa.entitylib.extras; + +public class InvalidVersionException extends RuntimeException { + + public InvalidVersionException() { + } + + public InvalidVersionException(String message) { + super(message); + } + + public InvalidVersionException(String message, Throwable cause) { + super(message, cause); + } + + public InvalidVersionException(Throwable cause) { + super(cause); + } + + public InvalidVersionException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) { + super(message, cause, enableSuppression, writableStackTrace); + } +} diff --git a/api/src/main/java/me/tofaa/entitylib/meta/EntityMeta.java b/api/src/main/java/me/tofaa/entitylib/meta/EntityMeta.java new file mode 100644 index 0000000..bab0c11 --- /dev/null +++ b/api/src/main/java/me/tofaa/entitylib/meta/EntityMeta.java @@ -0,0 +1,221 @@ +package me.tofaa.entitylib.meta; + +import com.github.retrooper.packetevents.manager.server.ServerVersion; +import com.github.retrooper.packetevents.manager.server.VersionComparison; +import com.github.retrooper.packetevents.protocol.entity.data.EntityData; +import com.github.retrooper.packetevents.protocol.entity.data.EntityDataTypes; +import com.github.retrooper.packetevents.protocol.entity.data.EntityMetadataProvider; +import com.github.retrooper.packetevents.protocol.entity.pose.EntityPose; +import com.github.retrooper.packetevents.protocol.player.ClientVersion; +import com.github.retrooper.packetevents.wrapper.play.server.WrapperPlayServerEntityMetadata; +import me.tofaa.entitylib.EntityLib; +import me.tofaa.entitylib.extras.InvalidVersionException; +import net.kyori.adventure.text.Component; + +import java.util.List; + +public class EntityMeta implements EntityMetadataProvider { + + public static final byte OFFSET = 0; + public static final byte MAX_OFFSET = OFFSET + 8; + + private final static byte ON_FIRE_BIT = 0x01; + private final static byte CROUCHING_BIT = 0x02; + private final static byte SPRINTING_BIT = 0x08; + private final static byte SWIMMING_BIT = 0x10; + private final static byte INVISIBLE_BIT = 0x20; + private final static byte HAS_GLOWING_EFFECT_BIT = 0x40; + private final static byte FLYING_WITH_ELYTRA_BIT = (byte) 0x80; + + protected final int entityId; + protected final Metadata metadata; + + public EntityMeta(int entityId, Metadata metadata) { + this.entityId = entityId; + this.metadata = metadata; + } + + public boolean isOnFire() { + return getMaskBit(OFFSET, ON_FIRE_BIT); + } + + public void setOnFire(boolean value) { + setMaskBit(OFFSET, ON_FIRE_BIT, value); + } + + public boolean isSneaking() { + return getMaskBit(OFFSET, CROUCHING_BIT); + } + + public void setSneaking(boolean value) { + setMaskBit(OFFSET, CROUCHING_BIT, value); + } + + public boolean isSprinting() { + return getMaskBit(OFFSET, SPRINTING_BIT); + } + + public void setSprinting(boolean value) { + setMaskBit(OFFSET, SPRINTING_BIT, value); + } + + public boolean isInvisible() { + return getMaskBit(OFFSET, INVISIBLE_BIT); + } + + public void setInvisible(boolean value) { + setMaskBit(OFFSET, INVISIBLE_BIT, value); + } + + public short getAirTicks() { + return this.metadata.getIndex((byte) 1, (short) 300); + } + + public void setAirTicks(short value) { + this.metadata.setIndex((byte) 1, EntityDataTypes.SHORT, value); + } + + public Component getCustomName() { + return this.metadata.getIndex(offset(OFFSET, 2), null); + } + + public void setCustomName(Component value) { + this.metadata.setIndex(offset(OFFSET, 2), EntityDataTypes.ADV_COMPONENT, value); + } + + public boolean isCustomNameVisible() { + return this.metadata.getIndex(offset(OFFSET, 3), false); + } + + public void setCustomNameVisible(boolean value) { + this.metadata.setIndex(offset(OFFSET, 3), EntityDataTypes.BOOLEAN, value); + } + + public boolean hasGlowingEffect() { + return getMaskBit(OFFSET, HAS_GLOWING_EFFECT_BIT); + } + + public boolean isSwimming() { + return getMaskBit(OFFSET, SWIMMING_BIT); + } + + public void setSwimming(boolean value) { + setMaskBit(OFFSET, SWIMMING_BIT, value); + } + + public void setHasGlowingEffect(boolean value) { + setMaskBit(OFFSET, HAS_GLOWING_EFFECT_BIT, value); + } + + public boolean isFlyingWithElytra() { + return getMaskBit(OFFSET, FLYING_WITH_ELYTRA_BIT); + } + + public void setFlyingWithElytra(boolean value) { + setMaskBit(OFFSET, FLYING_WITH_ELYTRA_BIT, value); + } + + public boolean isSilent() { + return this.metadata.getIndex((byte) 4, false); + } + + public void setSilent(boolean value) { + this.metadata.setIndex((byte) 4, EntityDataTypes.BOOLEAN, value); + } + + public boolean isHasNoGravity() { + return this.metadata.getIndex(offset(OFFSET, 5), true); + } + + public void setHasNoGravity(boolean value) { + this.metadata.setIndex(offset(OFFSET, 5), EntityDataTypes.BOOLEAN, value); + } + + public EntityPose getPose() { + return this.metadata.getIndex(offset(OFFSET, 6), EntityPose.STANDING); + } + + public void setPose(EntityPose value) { + this.metadata.setIndex(offset(OFFSET, 6), EntityDataTypes.ENTITY_POSE, value); + } + + public int getTicksFrozenInPowderedSnow() { + return this.metadata.getIndex(offset(OFFSET, 7), 0); + } + + public void setTicksFrozenInPowderedSnow(int value) { + this.metadata.setIndex(offset(OFFSET, 7), EntityDataTypes.INT, value); + } + + public WrapperPlayServerEntityMetadata createPacket() { + return metadata.createPacket(); + } + + protected static void isVersionOlder(ServerVersion version) { + if (!EntityLib.getApi().getPacketEvents().getServerManager().getVersion().is(VersionComparison.OLDER_THAN, version)) { + throw new InvalidVersionException("This method is only available for versions older than " + version.name() + "."); + } + } + + protected static void isVersionNewer(ServerVersion version) { + if (!EntityLib.getApi().getPacketEvents().getServerManager().getVersion().is(VersionComparison.NEWER_THAN, version)) { + throw new InvalidVersionException("This method is only available for versions newer than " + version.name() + "."); + } + } + + protected static boolean isVersion(ServerVersion version, VersionComparison comparison) { + return EntityLib.getApi().getPacketEvents().getServerManager().getVersion().is(comparison, version); + } + + protected static boolean isVersion(ServerVersion version) { + return EntityLib.getApi().getPacketEvents().getServerManager().getVersion().is(VersionComparison.EQUALS, version); + } + + /** + * Annoying java 8 not letting me do OFFSET + amount in the method call so this is a workaround + * @param value the value to offset + * @param amount the amount to offset by + * @return the offset value + */ + protected static byte offset(byte value, int amount) { + return (byte) (value + amount); + } + + protected byte getMask(byte index) { + return this.metadata.getIndex(index, (byte) 0); + } + + protected void setMask(byte index, byte mask) { + this.metadata.setIndex(index, EntityDataTypes.BYTE, mask); + } + + protected boolean getMaskBit(byte index, byte bit) { + return (getMask(index) & bit) == bit; + } + + protected void setMaskBit(int index, byte bit, boolean value) { + byte mask = getMask((byte)index); + boolean currentValue = (mask & bit) == bit; + if (currentValue == value) { + return; + } + if (value) { + mask |= bit; + } else { + mask &= (byte) ~bit; + } + setMask((byte)index, mask); + } + + @Override + public List entityData(ClientVersion clientVersion) { + return metadata.getEntries(); // TODO: Atm this is useless cause of the way the api works. Might change in the future + } + + @Override + public List entityData() { + return metadata.getEntries(); + } + + +} diff --git a/api/src/main/java/me/tofaa/entitylib/meta/Metadata.java b/api/src/main/java/me/tofaa/entitylib/meta/Metadata.java new file mode 100644 index 0000000..cc4c681 --- /dev/null +++ b/api/src/main/java/me/tofaa/entitylib/meta/Metadata.java @@ -0,0 +1,45 @@ +package me.tofaa.entitylib.meta; + +import com.github.retrooper.packetevents.protocol.entity.data.EntityData; +import com.github.retrooper.packetevents.protocol.entity.data.EntityDataType; +import com.github.retrooper.packetevents.wrapper.play.server.WrapperPlayServerEntityMetadata; +import me.tofaa.entitylib.EntityLib; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +@SuppressWarnings("unchecked") +public class Metadata { + + private final Map metadataMap = new ConcurrentHashMap<>(); + private final int entityId; + + public Metadata(int entityId) { + this.entityId = entityId; + } + + public T getIndex(byte index, @Nullable T defaultValue) { + EntityData entityData = metadataMap.get(index); + if (entityData == null) return defaultValue; + if (entityData.getValue() == null) return defaultValue; + return (T) entityData.getValue(); + } + + public void setIndex(byte index, @NotNull EntityDataType dataType, T value) { + EntityData data = new EntityData(index, dataType, value); + this.metadataMap.put(index, data); + } + + @NotNull List getEntries() { + return new ArrayList<>(metadataMap.values()); + } + + public WrapperPlayServerEntityMetadata createPacket() { + return new WrapperPlayServerEntityMetadata(entityId, getEntries()); + } + +} \ No newline at end of file diff --git a/api/src/main/java/me/tofaa/entitylib/tick/TickContainer.java b/api/src/main/java/me/tofaa/entitylib/tick/TickContainer.java new file mode 100644 index 0000000..f1b856f --- /dev/null +++ b/api/src/main/java/me/tofaa/entitylib/tick/TickContainer.java @@ -0,0 +1,72 @@ +package me.tofaa.entitylib.tick; + +import org.jetbrains.annotations.ApiStatus; +import org.jetbrains.annotations.NotNull; + +import java.util.Collection; +import java.util.Collections; +import java.util.HashSet; +import java.util.Set; + +/** + * Represents a storage/container for {@link Tickable}s. + * @param The type of {@link Tickable} to store. + * @param If a platform enforces a specific method of ticking, this type represents the handler for that method. + */ +public abstract class TickContainer { + + private final Set tickables = new HashSet<>(); + private H handle; + + /** + * @return The {@link Tickable}s stored in this container. This collection is immutable + */ + public @NotNull Collection getTickables() { + return Collections.unmodifiableCollection(tickables); + } + + /** + * Adds a {@link Tickable} to this container. + * @param tickable The {@link Tickable} to add. + * @return {@code true} if the {@link Tickable} was added, {@code false} otherwise. + */ + public boolean addTickable(@NotNull T tickable) { + return tickables.add(tickable); + } + + /** + * Removes a {@link Tickable} from this container. + * @param tickable The {@link Tickable} to remove. + * @return {@code true} if the {@link Tickable} was removed, {@code false} otherwise. + */ + public boolean removeTickable(T tickable) { + return tickables.remove(tickable); + } + + /** + * Ticks all {@link Tickable}s in this container, this method can be overriden to provide a custom implementation. + * @param time The current time in milliseconds, incase the {@link Tickable} needs to know the current time. + */ + public void tick(long time) { + for (T tickable : tickables) { + tickable.tick(time); + } + } + + /** + * @return The handler for this container. + */ + public @NotNull H getHandle() { + return handle; + } + + /** + * Sets the handler for this container. This method is only used internally. + * @param handle The handler to set. + */ + @ApiStatus.Internal + public void setHandle(@NotNull H handle) { + this.handle = handle; + } + +} diff --git a/api/src/main/java/me/tofaa/entitylib/tick/Tickable.java b/api/src/main/java/me/tofaa/entitylib/tick/Tickable.java new file mode 100644 index 0000000..11dd41b --- /dev/null +++ b/api/src/main/java/me/tofaa/entitylib/tick/Tickable.java @@ -0,0 +1,7 @@ +package me.tofaa.entitylib.tick; + +public interface Tickable { + + void tick(long time); + +} diff --git a/build.gradle b/build.gradle index 1958f2c..555b4ab 100644 --- a/build.gradle +++ b/build.gradle @@ -5,6 +5,12 @@ plugins { allprojects { + project.ext.adventureVersion = '4.15.0' + project.ext.adventureDependencies=["net.kyori:adventure-api:${adventureVersion}", + "net.kyori:adventure-text-serializer-gson:${adventureVersion}", + "net.kyori:adventure-text-serializer-legacy:${adventureVersion}", + "net.kyori:adventure-nbt:${adventureVersion}"] + apply plugin: 'java' group = 'me.tofaa.entitylib' diff --git a/platforms/spigot/build.gradle b/platforms/spigot/build.gradle new file mode 100644 index 0000000..90169d6 --- /dev/null +++ b/platforms/spigot/build.gradle @@ -0,0 +1,15 @@ +plugins { + id 'java' +} + +group = 'me.tofaa.entitylib' +version = '1.0-SNAPSHOT' + +repositories { + mavenCentral() +} + +dependencies { + implementation(project(":api")) + implementation('org.spigotmc:spigot-api:1.20.1-R0.1-SNAPSHOT') +} diff --git a/platforms/spigot/src/main/java/me/tofaa/entitylib/spigot/SpigotEntityLibAPI.java b/platforms/spigot/src/main/java/me/tofaa/entitylib/spigot/SpigotEntityLibAPI.java new file mode 100644 index 0000000..5f2222e --- /dev/null +++ b/platforms/spigot/src/main/java/me/tofaa/entitylib/spigot/SpigotEntityLibAPI.java @@ -0,0 +1,98 @@ +package me.tofaa.entitylib.spigot; + +import com.github.retrooper.packetevents.PacketEventsAPI; +import me.tofaa.entitylib.APISettings; +import me.tofaa.entitylib.EntityLibAPI; +import me.tofaa.entitylib.tick.TickContainer; +import org.bukkit.Bukkit; +import org.bukkit.World; +import org.bukkit.scheduler.BukkitTask; +import org.jetbrains.annotations.NotNull; + +import java.util.Collection; +import java.util.Collections; +import java.util.HashSet; +import java.util.logging.Level; + +public class SpigotEntityLibAPI implements EntityLibAPI { + + private final SpigotEntityLibPlatform platform; + private final PacketEventsAPI packetEvents; + private final APISettings settings; + private Collection> tickContainers; + + SpigotEntityLibAPI(SpigotEntityLibPlatform platform, APISettings settings) { + this.platform = platform; + this.packetEvents = settings.getPacketEvents(); + this.settings = settings; + } + + @Override + public void onLoad() { + this.tickContainers = settings.shouldTickTickables() ? new HashSet<>() : Collections.EMPTY_SET; + } + + @Override + public void onEnable() { + + } + + @NotNull @Override + public APISettings getSettings() { + return settings; + } + + @Override + public void setupTickingContainers() { + if (!getSettings().shouldTickTickables()) { + if (getSettings().isDebugMode()) { + platform.getLogger().log(Level.CONFIG, "Skipping ticking containers as it is disabled in the settings."); + } + return; + } + + if (getSettings().isDebugMode()) { + platform.getLogger().log(Level.CONFIG, "Setting up ticking containers..."); + } + if (tickContainers.isEmpty()) { + if (getSettings().isDebugMode()) { + platform.getLogger().log(Level.CONFIG, "No tick containers found."); + } + return; + } + + if (getSettings().isDebugMode()) { + platform.getLogger().log(Level.CONFIG, "Found " + tickContainers.size() + " tick containers."); + } + + tickContainers.forEach(this::registerNewTickContainer); + } + + @Override + public PacketEventsAPI getPacketEvents() { + return packetEvents; + } + + @Override + public Collection> getTickContainers() { + return tickContainers; + } + + @Override + public void addTickContainer(@NotNull TickContainer tickContainer) { + tickContainers.add(tickContainer); + registerNewTickContainer(tickContainer); + } + + public void registerNewTickContainer(TickContainer tickContainer) { + + if (getSettings().isDebugMode()) { + platform.getLogger().log(Level.CONFIG, "Registering new tick container..."); + } + getTickContainers().add(tickContainer); + BukkitTask task = Bukkit.getScheduler().runTaskTimerAsynchronously(platform.getHandle(), () -> tickContainer.tick(System.currentTimeMillis()), 1L, 1L); + tickContainer.setHandle(task); + } + + +} diff --git a/platforms/spigot/src/main/java/me/tofaa/entitylib/spigot/SpigotEntityLibPlatform.java b/platforms/spigot/src/main/java/me/tofaa/entitylib/spigot/SpigotEntityLibPlatform.java new file mode 100644 index 0000000..c719df0 --- /dev/null +++ b/platforms/spigot/src/main/java/me/tofaa/entitylib/spigot/SpigotEntityLibPlatform.java @@ -0,0 +1,65 @@ +package me.tofaa.entitylib.spigot; + +import me.tofaa.entitylib.APISettings; +import me.tofaa.entitylib.EntityLibAPI; +import me.tofaa.entitylib.Platform; +import me.tofaa.entitylib.event.EntityLibEvent; +import me.tofaa.entitylib.tick.TickContainer; +import org.bukkit.Bukkit; +import org.bukkit.plugin.java.JavaPlugin; +import org.bukkit.scheduler.BukkitTask; +import org.jetbrains.annotations.NotNull; + +import java.util.Collection; +import java.util.function.Consumer; +import java.util.logging.Level; +import java.util.logging.Logger; + +public class SpigotEntityLibPlatform implements Platform { + + private final JavaPlugin plugin; + private SpigotEntityLibAPI api; + private Logger logger; + + public SpigotEntityLibPlatform(@NotNull JavaPlugin plugin) { + this.plugin = plugin; + } + + @Override + public void setupApi(@NotNull APISettings settings) { + this.logger = settings.shouldUsePlatformLogger() ? plugin.getLogger() : Logger.getLogger("EntityLib"); + this.api = new SpigotEntityLibAPI(this, settings); + this.api.onLoad(); + this.api.onEnable(); + } + + @Override + public @NotNull Logger getLogger() { + return logger; + } + + @Override + public void sendEvent(EntityLibEvent event) { + + } + + @Override + public void registerListener(Class eventClass, Consumer handle) { + + } + + @Override + public EntityLibAPI getAPI() { + return api; + } + + @Override + public @NotNull JavaPlugin getHandle() { + return plugin; + } + + @Override + public String getName() { + return "Spigot"; + } +} diff --git a/settings.gradle b/settings.gradle index 81e87e9..5426790 100644 --- a/settings.gradle +++ b/settings.gradle @@ -1,4 +1,6 @@ rootProject.name = 'EntityLib' include 'test-plugin' include 'api' +include 'platforms:spigot' +findProject(':platforms:spigot')?.name = 'spigot' diff --git a/src/main/java/me/tofaa/entitylib/meta/Metadata.java b/src/main/java/me/tofaa/entitylib/meta/Metadata.java index 8dd0d57..bcb0f7f 100644 --- a/src/main/java/me/tofaa/entitylib/meta/Metadata.java +++ b/src/main/java/me/tofaa/entitylib/meta/Metadata.java @@ -9,7 +9,6 @@ import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import java.util.ArrayList; -import java.util.Collections; import java.util.List; import java.util.Map; import java.util.concurrent.ConcurrentHashMap;