Compare commits

...

44 commits

Author SHA1 Message Date
25823e4cf2 upstream
Some checks failed
Build / build (push) Has been cancelled
2025-07-27 00:13:02 +02:00
b2d6cd6d93 Merge remote-tracking branch 'upstream/master'
# Conflicts:
#	.github/workflows/dev-build-release.yml
#	api/build.gradle.kts
#	platforms/spigot/src/main/java/me/tofaa/entitylib/spigot/SpigotEntityLibPlatform.java
2025-07-27 00:12:43 +02:00
Tofaa
bff89d2fac
Merge pull request #40 from huanmeng-qwq/feat/translation-render
Add support for GlobalTranslator
2025-07-21 20:21:03 +04:00
huanmeng-qwq
acbb438325
feat: Introduce UserLocaleProvider implementation for Spigot, Velocity, and Standalone platforms 2025-07-21 22:50:10 +08:00
huanmeng-qwq
9ea8e9c1b4
feat: Add user locale handling and metadata translation support 2025-07-21 22:26:51 +08:00
Tofaa
d6a3cb72a4
Merge pull request #39 from huanmeng-qwq/blockstate
Add entity meta support for WrappedBlockState.
2025-07-20 15:09:22 +04:00
huanmeng-qwq
64021bb0dc
update 2025-07-20 18:57:01 +08:00
huanmeng-qwq
56e64cde5b
feat: Add entity meta support for WrappedBlockState. 2025-07-20 18:51:40 +08:00
Tofaa
9dec1374d6
Merge pull request #38 from fantasyorg/master
feat(meta): add TntMeta class and register in MetaConverterRegistry
2025-07-20 12:07:53 +04:00
Felipe Paschoal Bergamo
3d803fd4d7 feat: add missing metadata converters for entities
```
- Implemented converters for ALLAY, CREAKING, ENDER_PEARL,
  EVOKER_FANGS, GHAST, HAPPY_GHAST, ITEM, MOOSHROOM, PHANTOM,
  POTION, SNOWBALL, and SQUID.
- Verified and updated converters for COW, and added TODO notes
  for correctness checks on certain entities.
```
2025-07-20 05:03:06 -03:00
Felipe Paschoal Bergamo
d69eeb3932 feat(meta): add TntMeta class and register in MetaConverterRegistry 2025-07-20 02:54:16 -03:00
Felipe Paschoal Bergamo
38d858f5b3 Merge remote-tracking branch 'origin/master'
# Conflicts:
#	api/src/main/java/me/tofaa/entitylib/meta/MetaConverterRegistry.java
2025-07-20 02:53:44 -03:00
Felipe Paschoal Bergamo
7ea339a69e feat(meta): add TntMeta class and register in MetaConverterRegistry 2025-07-20 02:53:31 -03:00
Tofaa
fe6161613f
Merge pull request #37 from OakLoaf/master
MetaConverterRegistry changes
2025-07-18 19:25:50 +04:00
OakLoaf
6af48b98e7 Cleaned up MetaConverterRegistry, added newer entities and implemented unused meta classes 2025-07-18 15:50:18 +01:00
Tofaa
4682ddd581
Merge pull request #36 from fantasyorg/master
feat: packet-events 2.9.1
2025-07-16 10:31:03 +04:00
Felipe Paschoal Bergamo
a4ce05886d fix(meta): update EntityData type to be generic in EntityMeta and Metadata 2025-07-15 22:40:24 -03:00
Tofaa
49943c9515
Merge pull request #35 from huanmeng-qwq/master
fix: correct player spawn in 1.20.1-
2025-07-07 22:38:50 +04:00
huanmeng-qwq
8bc8bfb76a
feat: Add rotation packet handling after player spawn. 2025-07-08 02:35:47 +08:00
huanmeng-qwq
8478e87a38
fix: correct player spawn in 1.20.1- 2025-07-08 02:00:05 +08:00
Tofaa
34db96d434
Merge pull request #31 from fantasyorg/master
fix(meta): remove ThrownTridentMeta registration from registry POTION
2025-06-15 07:57:12 +04:00
Felipe Paschoal Bergamo
239f736d9d
Merge branch 'Tofaa2:master' into master 2025-06-14 02:12:57 -03:00
Felipe Paschoal Bergamo
e89c025545 fix(meta): remove ThrownTridentMeta registration from registry POTION 2025-06-14 02:12:43 -03:00
Tofaa
2a15c882db
Merge pull request #30 from fantasyorg/master
fix: packets send after entity spawned
2025-06-04 01:55:20 +04:00
Felipe Paschoal Bergamo
26cccce9a8 fix(wrapper): refactor spawn packet sending method 2025-06-03 18:54:38 -03:00
Felipe Paschoal Bergamo
1fc7a00ec7 fix: errors 2025-06-03 18:53:19 -03:00
Felipe Paschoal Bergamo
b5481f783d
Merge branch 'Tofaa2:master' into master 2025-06-03 18:38:12 -03:00
Felipe Paschoal Bergamo
2e05530f36 Cleaned up the WrapperEntityPotionEffect class by removing 2025-06-03 18:37:58 -03:00
Tofaa
630837fc06
Merge pull request #29 from steveb05/fix/wrong-horse-parent-class
Fix client disconnections caused by incorrect metadata offsets in horse packets
2025-05-01 13:43:33 +04:00
Tofaa2
9032d33058 GCI make create release step only available on head 2025-05-01 13:41:36 +04:00
Bram
a9fc4125da
Merge remote-tracking branch 'origin/master' into fork/steveb05/fix/wrong-horse-parent-class 2025-05-01 11:21:44 +02:00
Bram
74871b38bb
Fixed: Compile errors due to PacketEvents not being present during the test build phase 2025-05-01 11:20:38 +02:00
steve
5a5c7c8dc1
fix: correct BaseHorseMeta inheritance and remove non-functional owner methods
Inheriting from MobMeta caused incorrect metadata offsets for horses, leading to client disconnections when metadata packets were sent. Vanilla horses don't synchronize Owner UUID via metadata, making the previous owner methods non-functional client-side.

Changing inheritance to AgeableMeta also enables baby horse functionality.
2025-04-30 13:54:54 +02:00
Tofaa2
d9aae45571 idk how to fix this 2025-04-09 21:35:48 +04:00
Felipe Paschoal Bergamo
9e7ecf310e feat(api): add setPacketDispatcher method to EntityLibAPI 2025-04-03 00:03:40 -03:00
Felipe Paschoal Bergamo
d7eda34611 feat: enhance EntityLib with packet dispatcher and server version check 2025-04-03 00:02:49 -03:00
Felipe Paschoal Bergamo
35111c0149 fix(wrapper): streamline potion effect handling and packet creation 2025-04-01 11:25:33 -03:00
Felipe Paschoal Bergamo
0868a75617 feat: potion effect controller system 2025-04-01 10:43:55 -03:00
Tofaa2
53e4d917cd Fix build error 2025-03-31 21:35:44 +04:00
Tofaa2
7298de0bb7 Use dyecolor and byte, remove old method 2025-03-31 21:27:58 +04:00
Tofaa
e181b979a0
publicize standalone platform constructor 2025-03-25 20:29:05 +04:00
Tofaa2
6fba8ea5fd fix: ViewerEngine#untrack impl 2025-03-05 14:23:36 +04:00
Tofaa
244145e07a
Merge pull request #26 from LoJoSho/hurt_animation_fix
Fix PlayHurtAnimation not working on modern Minecraft verions
2025-03-02 13:52:15 +04:00
LoJoSho
f819cdb893 fix: playHurtAnimation not working on 1.19.4+ versions 2025-03-01 22:19:40 -06:00
30 changed files with 958 additions and 186 deletions

47
.github/workflows/dev-build-release.yml vendored Normal file
View file

@ -0,0 +1,47 @@
name: Build
on:
workflow_dispatch:
push:
branches: ['*']
pull_request:
branches: ['*']
permissions:
contents: write
jobs:
build:
runs-on: ubuntu-latest
env:
TYCOONS_REPO_USER: ${{ secrets.EVOKE_REPO_USERNAME }}
TYCOONS_REPO_PASS: ${{ secrets.EVOKE_REPO_PASSWORD }}
steps:
- name: Clone project
uses: actions/checkout@v4
- name: Install JDK 8
uses: actions/setup-java@v4
with:
java-version: '8'
distribution: 'temurin'
check-latest: true
- name: Setup gradle
uses: gradle/actions/setup-gradle@v4
- name: Run build & publish with Gradle Wrapper
if: github.ref == 'refs/heads/master'
run: chmod +x ./gradlew && ./gradlew publishAllPublicationsToMavenRepository
- name: Run build with Gradle Wrapper
if: github.ref != 'refs/heads/master'
run: chmod +x ./gradlew && ./gradlew build
- name: Create Release
if: github.ref != 'refs/heads/master'
uses: softprops/action-gh-release@v2
with:
name: '${{ github.ref_name }}: ${{ github.event.head_commit.message }} (${{ github.sha }})'
prerelease: ${{ github.ref != 'refs/heads/master' }}
tag_name: ${{ github.ref_name }}-${{ github.sha }}

View file

@ -1,6 +1,7 @@
plugins {
entitylib.`shadow-conventions`
entitylib.`library-conventions`
`el-version`
}
dependencies {
@ -8,4 +9,23 @@ dependencies {
compileOnly(libs.bundles.adventure)
compileOnly(libs.packetevents.api)
}
testCompileOnly(libs.packetevents.api)
}
tasks {
javadoc {
mustRunAfter(generateVersionsFile)
}
sourcesJar {
mustRunAfter(generateVersionsFile)
}
withType<JavaCompile> {
dependsOn(generateVersionsFile)
}
generateVersionsFile {
packageName = "me.tofaa.entitylib.utils"
}
}

View file

@ -1,14 +1,9 @@
package me.tofaa.entitylib;
import com.github.retrooper.packetevents.PacketEventsAPI;
import com.github.retrooper.packetevents.protocol.entity.type.EntityType;
import com.github.retrooper.packetevents.protocol.player.UserProfile;
import com.github.retrooper.packetevents.protocol.world.Location;
import me.tofaa.entitylib.container.EntityContainer;
import me.tofaa.entitylib.tick.TickContainer;
import me.tofaa.entitylib.wrapper.WrapperEntity;
import me.tofaa.entitylib.wrapper.WrapperPlayer;
import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@ -58,4 +53,6 @@ public interface EntityLibAPI<T> {
@NotNull
EntityContainer getDefaultContainer();
@NotNull
UserLocaleProvider getUserLocaleProvider();
}

View file

@ -26,6 +26,13 @@ public interface Platform<P> {
*/
@NotNull EntityUuidProvider getEntityUuidProvider();
/**
* Gets the provider responsible for retrieving the locale of a user.
*
* @return a non-null {@link UserLocaleProvider} instance.
*/
@NotNull UserLocaleProvider getUserLocaleProvider();
/**
* Sets the entityId integer provider. This can be provided by a platform if needed.
* @param provider the entityId integer provider.
@ -38,6 +45,12 @@ public interface Platform<P> {
*/
void setEntityUuidProvider(@NotNull EntityUuidProvider provider);
/**
* Sets the provider responsible for retrieving the locale of a user.
*
* @param provider the {@link UserLocaleProvider} instance to be set. Must not be null.
*/
void setUserLocaleProvider(@NotNull UserLocaleProvider provider);
/**
* @return the logger EntityLib uses internally.

View file

@ -0,0 +1,8 @@
package me.tofaa.entitylib;
import java.util.Locale;
import java.util.UUID;
public interface UserLocaleProvider {
Locale locale(UUID user);
}

View file

@ -228,6 +228,7 @@ public class EntityMeta implements EntityMetadataProvider {
/**
* 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
@ -271,12 +272,12 @@ public class EntityMeta implements EntityMetadataProvider {
}
@Override
public List<EntityData> entityData(ClientVersion clientVersion) {
public @NotNull List<EntityData<?>> entityData(@NotNull 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> entityData() {
public @NotNull List<EntityData<?>> entityData() {
return metadata.getEntries();
}

View file

@ -1,7 +1,6 @@
package me.tofaa.entitylib.meta;
import com.github.retrooper.packetevents.protocol.entity.type.EntityType;
import com.github.retrooper.packetevents.protocol.entity.type.EntityTypes;
import me.tofaa.entitylib.meta.display.BlockDisplayMeta;
import me.tofaa.entitylib.meta.display.ItemDisplayMeta;
import me.tofaa.entitylib.meta.display.TextDisplayMeta;
@ -50,9 +49,9 @@ final class MetaConverterRegistry {
MetaConverterRegistry() {
put(ABSTRACT_WIND_CHARGE, SmallFireballMeta.class, SmallFireballMeta::new); // TODO: Verify correctness
put(AREA_EFFECT_CLOUD, AreaEffectCloudMeta.class, AreaEffectCloudMeta::new);
put(ALLAY, LivingEntityMeta.class, LivingEntityMeta::new); // TODO: Implement
put(ARMADILLO, ArmadilloMeta.class, ArmadilloMeta::new); // TODO: Verify correctness
put(ARMOR_STAND, ArmorStandMeta.class, ArmorStandMeta::new);
put(ALLAY, LivingEntityMeta.class, LivingEntityMeta::new); // TODO: Implement
put(ARROW, ArrowMeta.class, ArrowMeta::new);
put(AXOLOTL, AxolotlMeta.class, AxolotlMeta::new);
put(BAT, BatMeta.class, BatMeta::new);
@ -69,8 +68,9 @@ final class MetaConverterRegistry {
put(CHEST_MINECART, ChestMinecartMeta.class, ChestMinecartMeta::new);
put(CHICKEN, ChickenMeta.class, ChickenMeta::new);
put(COD, CodMeta.class, CodMeta::new);
put(COW, CowMeta.class, CowMeta::new);
put(COMMAND_BLOCK_MINECART, CommandBlockMinecartMeta.class, CommandBlockMinecartMeta::new);
put(COW, CowMeta.class, CowMeta::new);
put(CREAKING, LivingEntityMeta.class, LivingEntityMeta::new); // TODO: Implement
put(CREEPER, CreeperMeta.class, CreeperMeta::new);
put(DOLPHIN, DolphinMeta.class, DolphinMeta::new);
put(DONKEY, DonkeyMeta.class, DonkeyMeta::new);
@ -80,11 +80,12 @@ final class MetaConverterRegistry {
put(ELDER_GUARDIAN, ElderGuardianMeta.class, ElderGuardianMeta::new);
put(END_CRYSTAL, EndCrystalMeta.class, EndCrystalMeta::new);
put(ENDER_DRAGON, EnderDragonMeta.class, EnderDragonMeta::new);
put(ENDER_PEARL, ThrownEnderPearlMeta.class, ThrownEnderPearlMeta::new);
put(ENDERMAN, EndermanMeta.class, EndermanMeta::new);
put(ENDERMITE, EndermiteMeta.class, EndermiteMeta::new);
put(EVOKER, EvokerMeta.class, EvokerMeta::new);
put(EYE_OF_ENDER, EyeOfEnderMeta.class, EyeOfEnderMeta::new);
put(EVOKER_FANGS, EvokerFangsMeta.class, EvokerFangsMeta::new);
put(EYE_OF_ENDER, EyeOfEnderMeta.class, EyeOfEnderMeta::new);
put(FALLING_BLOCK, FallingBlockMeta.class, FallingBlockMeta::new);
put(FIREBALL, LargeFireballMeta.class, LargeFireballMeta::new); // TODO: Verify correctness
put(FIREWORK_ROCKET, FireworkRocketMeta.class, FireworkRocketMeta::new);
@ -92,11 +93,13 @@ final class MetaConverterRegistry {
put(FOX, FoxMeta.class, FoxMeta::new);
put(FROG, FrogMeta.class, FrogMeta::new);
put(FURNACE_MINECART, FurnaceMinecartMeta.class, FurnaceMinecartMeta::new);
put(GHAST, GhastMeta.class, GhastMeta::new);
put(GIANT, GiantMeta.class, GiantMeta::new);
put(GLOW_ITEM_FRAME, GlowItemFrameMeta.class, GlowItemFrameMeta::new);
put(GLOW_SQUID, GlowSquidMeta.class, GlowSquidMeta::new);
put(GOAT, GoatMeta.class, GoatMeta::new);
put(GUARDIAN, GuardianMeta.class, GuardianMeta::new);
put(HAPPY_GHAST, GhastMeta.class, GhastMeta::new); // TODO: Implement
put(HOGLIN, HoglinMeta.class, HoglinMeta::new);
put(HOPPER_MINECART, FurnaceMinecartMeta.class, FurnaceMinecartMeta::new);
put(HORSE, HorseMeta.class, HorseMeta::new);
@ -104,35 +107,35 @@ final class MetaConverterRegistry {
put(ILLUSIONER, IllusionerMeta.class, IllusionerMeta::new);
put(INTERACTION, InteractionMeta.class, InteractionMeta::new);
put(IRON_GOLEM, IronGolemMeta.class, IronGolemMeta::new);
put(ITEM, ItemEntityMeta.class, ItemEntityMeta::new);
put(ITEM_DISPLAY, ItemDisplayMeta.class, ItemDisplayMeta::new);
put(ITEM_FRAME, ItemFrameMeta.class, ItemFrameMeta::new);
put(ITEM, ItemEntityMeta.class, ItemEntityMeta::new);
put(LEASH_KNOT, LeashKnotMeta.class, LeashKnotMeta::new);
put(LIGHTNING_BOLT, LightningBoltMeta.class, LightningBoltMeta::new);
put(LLAMA, LlamaMeta.class, LlamaMeta::new);
put(LLAMA_SPIT, LlamaSpitMeta.class, LlamaSpitMeta::new);
put(MAGMA_CUBE, MagmaCubeMeta.class, MagmaCubeMeta::new);
put(MARKER, MarkerMeta.class, MarkerMeta::new);
put(MOOSHROOM, MooshroomMeta.class, MooshroomMeta::new);
put(MULE, MuleMeta.class, MuleMeta::new);
put(OCELOT, OcelotMeta.class, OcelotMeta::new);
put(PAINTING, PaintingMeta.class, PaintingMeta::new);
put(PANDA, PandaMeta.class, PandaMeta::new);
put(POTION, ThrownPotionMeta.class, ThrownPotionMeta::new);
put(PARROT, ParrotMeta.class, ParrotMeta::new);
put(PHANTOM, PhantomMeta.class, PhantomMeta::new);
put(PIG, PigMeta.class, PigMeta::new);
put(PIGLIN, PiglinMeta.class, PiglinMeta::new);
put(PIGLIN_BRUTE, PiglinBruteMeta.class, PiglinBruteMeta::new);
put(PILLAGER, PillagerMeta.class, PillagerMeta::new);
put(PLAYER, PlayerMeta.class, PlayerMeta::new);
put(POLAR_BEAR, PolarBearMeta.class, PolarBearMeta::new);
put(POTION, ThrownTridentMeta.class, ThrownTridentMeta::new);
put(POTION, ThrownPotionMeta.class, ThrownPotionMeta::new);
put(PRIMED_TNT, PrimedTntMeta.class, PrimedTntMeta::new);
put(PUFFERFISH, PufferFishMeta.class, PufferFishMeta::new);
put(RABBIT, RabbitMeta.class, RabbitMeta::new);
put(RAVAGER, RavagerMeta.class, RavagerMeta::new);
put(SALMON, SalmonMeta.class, SalmonMeta::new);
put(SHEEP, SheepMeta.class, SheepMeta::new);
put(SNOWBALL, SnowballMeta.class, SnowballMeta::new);
put(SHULKER, ShulkerMeta.class, ShulkerMeta::new);
put(SHULKER_BULLET, ShulkerBulletMeta.class, ShulkerBulletMeta::new);
put(SILVERFISH, SilverfishMeta.class, SilverfishMeta::new);
@ -142,14 +145,16 @@ final class MetaConverterRegistry {
put(SMALL_FIREBALL, SmallFireballMeta.class, SmallFireballMeta::new);
put(SNIFFER, SnifferMeta.class, SnifferMeta::new);
put(SNOW_GOLEM, SnowGolemMeta.class, SnowGolemMeta::new);
put(SNOWBALL, SnowballMeta.class, SnowballMeta::new);
put(SPAWNER_MINECART, SpawnerMinecartMeta.class, SpawnerMinecartMeta::new);
put(SPIDER, SpiderMeta.class, SpiderMeta::new);
put(SQUID, SquidMeta.class, SquidMeta::new);
put(STRAY, StrayMeta.class, StrayMeta::new);
put(STRIDER, StriderMeta.class, StriderMeta::new);
put(TADPOLE, LivingEntityMeta.class, LivingEntityMeta::new); // TODO: Implement
put(TEXT_DISPLAY, TextDisplayMeta.class, TextDisplayMeta::new);
put(THROWN_EXP_BOTTLE, ThrownExpBottleMeta.class, ThrownExpBottleMeta::new);
put(ENDER_PEARL, ThrownEnderPearlMeta.class, ThrownEnderPearlMeta::new);
put(TNT, TntMeta.class, TntMeta::new);
put(TNT_MINECART, TntMinecartMeta.class, TntMinecartMeta::new);
put(TRADER_LLAMA, TraderLlamaMeta.class, TraderLlamaMeta::new);
put(TRIDENT, ThrownTridentMeta.class, ThrownTridentMeta::new);
@ -186,4 +191,4 @@ final class MetaConverterRegistry {
return converters.getOrDefault(entityType, EntityMeta::new);
}
}
}

View file

@ -19,8 +19,8 @@ public class Metadata {
private final int entityId;
private volatile boolean notifyAboutChanges = true;
private final HashMap<Byte, EntityData> notNotifiedChanges = new HashMap<>();
private final ConcurrentHashMap<Byte, EntityData> metadataMap = new ConcurrentHashMap<>();
private final HashMap<Byte, EntityData<?>> notNotifiedChanges = new HashMap<>();
private final ConcurrentHashMap<Byte, EntityData<?>> metadataMap = new ConcurrentHashMap<>();
public Metadata(int entityId) {
this.entityId = entityId;
@ -47,13 +47,13 @@ public class Metadata {
}
public <T> T getIndex(byte index, @Nullable T defaultValue) {
EntityData value = this.metadataMap.get(index);
EntityData<?> value = this.metadataMap.get(index);
return value != null ? (T) value.getValue() : defaultValue;
}
public <T> void setIndex(byte index, @NotNull EntityDataType<T> dataType, T value) {
final EntityData entry = new EntityData(index, dataType, value);
final EntityData<?> entry = new EntityData<>(index, dataType, value);
this.metadataMap.put(index, entry);
final Optional<EntityLibAPI<?>> optionalApi = EntityLib.getOptionalApi();
@ -75,7 +75,7 @@ public class Metadata {
return;
}
List<EntityData> entries = null;
List<EntityData<?>> entries = null;
synchronized (this.notNotifiedChanges) {
this.notifyAboutChanges = notifyAboutChanges;
if (notifyAboutChanges) {
@ -96,7 +96,7 @@ public class Metadata {
}
public void setMetaFromPacket(WrapperPlayServerEntityMetadata wrapper) {
for (EntityData data : wrapper.getEntityMetadata()) {
for (EntityData<?> data : wrapper.getEntityMetadata()) {
metadataMap.put((byte) data.getIndex(), data);
}
}
@ -105,7 +105,7 @@ public class Metadata {
return notifyAboutChanges;
}
@NotNull List<EntityData> getEntries() {
@NotNull List<EntityData<?>> getEntries() {
return Collections.unmodifiableList(new ArrayList<>(metadataMap.values()));
}

View file

@ -1,6 +1,8 @@
package me.tofaa.entitylib.meta.display;
import com.github.retrooper.packetevents.PacketEvents;
import com.github.retrooper.packetevents.protocol.entity.data.EntityDataTypes;
import com.github.retrooper.packetevents.protocol.world.states.WrappedBlockState;
import me.tofaa.entitylib.meta.Metadata;
public class BlockDisplayMeta extends AbstractDisplayMeta {
@ -20,4 +22,11 @@ public class BlockDisplayMeta extends AbstractDisplayMeta {
super.metadata.setIndex(OFFSET, EntityDataTypes.BLOCK_STATE, blockId);
}
public WrappedBlockState getBlockState() {
return WrappedBlockState.getByGlobalId(PacketEvents.getAPI().getServerManager().getVersion().toClientVersion(), getBlockId());
}
public void setBlockState(WrappedBlockState blockState) {
setBlockId(blockState.getGlobalId());
}
}

View file

@ -1,10 +1,12 @@
package me.tofaa.entitylib.meta.mobs.golem;
import com.github.retrooper.packetevents.protocol.color.DyeColor;
import com.github.retrooper.packetevents.protocol.entity.data.EntityDataTypes;
import com.github.retrooper.packetevents.protocol.world.Direction;
import com.github.retrooper.packetevents.util.Vector3i;
import me.tofaa.entitylib.meta.Metadata;
import me.tofaa.entitylib.meta.types.MobMeta;
import net.kyori.adventure.text.format.NamedTextColor;
import java.util.Optional;
@ -13,42 +15,43 @@ public class ShulkerMeta extends MobMeta {
public static final byte OFFSET = MobMeta.MAX_OFFSET;
public static final byte MAX_OFFSET = OFFSET + 1;
private static final DyeColor[] DYE_COLORS = DyeColor.values();
public ShulkerMeta(int entityId, Metadata metadata) {
super(entityId, metadata);
}
public Direction getAttachFace() {
return super.metadata.getIndex(OFFSET, Direction.DOWN);
return super.metadata.getIndex((byte)16, Direction.DOWN);
}
public void setAttachFace(Direction value) {
super.metadata.setIndex(OFFSET, EntityDataTypes.INT, value.ordinal());
}
public Optional<Vector3i> getAttachmentPosition() {
return super.metadata.getIndex(offset(OFFSET, 1), Optional.empty());
}
public void setAttachmentPosition(Vector3i value) {
super.metadata.setIndex(offset(OFFSET, 1), EntityDataTypes.OPTIONAL_BLOCK_POSITION, Optional.of(value));
super.metadata.setIndex((byte)16, EntityDataTypes.INT, value.ordinal());
}
public byte getShieldHeight() {
return super.metadata.getIndex(offset(OFFSET, 2), (byte) 0);
return super.metadata.getIndex(offset(OFFSET, 1), (byte) 0);
}
public void setShieldHeight(byte value) {
super.metadata.setIndex(offset(OFFSET, 2), EntityDataTypes.BYTE, value);
super.metadata.setIndex(offset(OFFSET, 1), EntityDataTypes.BYTE, value);
}
public byte getColor() {
return super.metadata.getIndex(offset(OFFSET, 3), (byte) 10);
return super.metadata.getIndex(offset(OFFSET, 2), (byte) 16);
}
public DyeColor getColorEnum() {
return DYE_COLORS[getColor()];
}
public void setColor(byte value) {
super.metadata.setIndex(offset(OFFSET, 3), EntityDataTypes.BYTE, value);
super.metadata.setIndex(offset(OFFSET, 2), EntityDataTypes.BYTE, value);
}
public void setColor(DyeColor color) {
setColor((byte)color.ordinal());
}
}

View file

@ -1,16 +1,12 @@
package me.tofaa.entitylib.meta.mobs.horse;
import com.github.retrooper.packetevents.protocol.entity.data.EntityDataTypes;
import me.tofaa.entitylib.meta.Metadata;
import me.tofaa.entitylib.meta.types.MobMeta;
import me.tofaa.entitylib.meta.types.AgeableMeta;
import java.util.Optional;
import java.util.UUID;
public abstract class BaseHorseMeta extends AgeableMeta {
public abstract class BaseHorseMeta extends MobMeta {
public static final byte OFFSET = MobMeta.MAX_OFFSET;
public static final byte MAX_OFFSET = OFFSET + 2;
public static final byte OFFSET = AgeableMeta.MAX_OFFSET;
public static final byte MAX_OFFSET = OFFSET + 1;
private final static byte TAMED_BIT = 0x02;
private final static byte SADDLED_BIT = 0x04;
@ -70,13 +66,4 @@ public abstract class BaseHorseMeta extends MobMeta {
public void setMouthOpen(boolean value) {
setMaskBit(OFFSET, MOUTH_OPEN_BIT, value);
}
public Optional<UUID> getOwner() {
return super.metadata.getIndex(offset(OFFSET, 1), Optional.empty());
}
public void setOwner(UUID value) {
super.metadata.setIndex(offset(OFFSET, 1), EntityDataTypes.OPTIONAL_UUID, Optional.of(value));
}
}

View file

@ -1,11 +1,12 @@
package me.tofaa.entitylib.meta.mobs.monster;
import com.github.retrooper.packetevents.PacketEvents;
import com.github.retrooper.packetevents.protocol.entity.data.EntityDataTypes;
import com.github.retrooper.packetevents.protocol.world.states.WrappedBlockState;
import me.tofaa.entitylib.meta.Metadata;
import me.tofaa.entitylib.meta.types.MobMeta;
import org.jetbrains.annotations.Nullable;
import java.util.Optional;
import org.jetbrains.annotations.Nullable;
public class EndermanMeta extends MobMeta {
@ -24,6 +25,20 @@ public class EndermanMeta extends MobMeta {
super.metadata.setIndex(OFFSET, EntityDataTypes.OPTIONAL_INT, Optional.ofNullable(value));
}
public WrappedBlockState getCarriedBlockState() {
Integer carriedBlockID = getCarriedBlockID();
if (carriedBlockID == null) return null;
return WrappedBlockState.getByGlobalId(PacketEvents.getAPI().getServerManager().getVersion().toClientVersion(), carriedBlockID);
}
public void setCarriedBlockState(WrappedBlockState blockState) {
if (blockState == null) {
setCarriedBlockID(null);
return;
}
setCarriedBlockID(blockState.getGlobalId());
}
public boolean isScreaming() {
return super.metadata.getIndex(offset(OFFSET, 1), false);
}
@ -33,7 +48,7 @@ public class EndermanMeta extends MobMeta {
}
public boolean isStaring() {
return super.metadata.getIndex(offset(OFFSET, 2), false);
return super.metadata.getIndex(offset(OFFSET, 2), false);
}
public void setStaring(boolean value) {

View file

@ -1,6 +1,8 @@
package me.tofaa.entitylib.meta.other;
import com.github.retrooper.packetevents.PacketEvents;
import com.github.retrooper.packetevents.protocol.entity.data.EntityDataTypes;
import com.github.retrooper.packetevents.protocol.world.states.WrappedBlockState;
import com.github.retrooper.packetevents.util.Vector3i;
import me.tofaa.entitylib.meta.EntityMeta;
import me.tofaa.entitylib.meta.Metadata;
@ -34,6 +36,14 @@ public class FallingBlockMeta extends EntityMeta implements ObjectData {
this.blockStateId = blockStateId;
}
public WrappedBlockState getBlockState() {
return WrappedBlockState.getByGlobalId(PacketEvents.getAPI().getServerManager().getVersion().toClientVersion(), getBlockStateId());
}
public void setBlockState(WrappedBlockState blockState) {
setBlockStateId(blockState.getGlobalId());
}
@Override
public int getObjectData() {
return blockStateId;

View file

@ -0,0 +1,42 @@
package me.tofaa.entitylib.meta.other;
import com.github.retrooper.packetevents.PacketEvents;
import com.github.retrooper.packetevents.protocol.entity.data.EntityDataTypes;
import com.github.retrooper.packetevents.protocol.world.states.WrappedBlockState;
import com.github.retrooper.packetevents.protocol.world.states.type.StateTypes;
import me.tofaa.entitylib.meta.EntityMeta;
import me.tofaa.entitylib.meta.Metadata;
public class TntMeta extends EntityMeta {
public static final byte OFFSET = EntityMeta.MAX_OFFSET;
public static final byte MAX_OFFSET = OFFSET + 2;
public TntMeta(int entityId, Metadata metadata) {
super(entityId, metadata);
}
public int getFuseTime() {
return super.metadata.getIndex(OFFSET, 80);
}
public void setFuseTime(int value) {
super.metadata.setIndex(OFFSET, EntityDataTypes.INT, value);
}
public int getBlockData() {
return super.metadata.getIndex(offset(OFFSET, 1), StateTypes.TNT.createBlockState().getGlobalId());
}
public void setBlockData(int blockData) {
super.metadata.setIndex(offset(OFFSET, 1), EntityDataTypes.BLOCK_STATE, blockData);
}
public WrappedBlockState getBlockState() {
return WrappedBlockState.getByGlobalId(PacketEvents.getAPI().getServerManager().getVersion().toClientVersion(), getBlockData());
}
public void setBlockState(WrappedBlockState blockState) {
setBlockData(blockState.getGlobalId());
}
}

View file

@ -0,0 +1,34 @@
package me.tofaa.entitylib.utils;
import com.github.retrooper.packetevents.protocol.entity.data.EntityData;
import com.github.retrooper.packetevents.protocol.entity.data.EntityDataTypes;
import com.github.retrooper.packetevents.wrapper.play.server.WrapperPlayServerEntityMetadata;
import me.tofaa.entitylib.EntityLib;
import java.util.Locale;
import java.util.Optional;
import java.util.UUID;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.translation.GlobalTranslator;
public class PacketUtil {
private PacketUtil() {
}
public static void renderPacket(UUID user, WrapperPlayServerEntityMetadata metadata) {
Locale locale = EntityLib.getApi().getUserLocaleProvider().locale(user);
for (final EntityData<?> entityData : metadata.getEntityMetadata()) {
if (entityData.getType() == EntityDataTypes.ADV_COMPONENT) {
Component component = (Component) entityData.getValue();
final Component rendered = GlobalTranslator.render(component, locale);
((EntityData<Component>) entityData).setValue(rendered);
} else if (entityData.getType() == EntityDataTypes.OPTIONAL_ADV_COMPONENT) {
final Optional<Component> optional = (Optional<Component>) entityData.getValue();
if (optional.isPresent()) {
final Component component = optional.get();
final Component rendered = GlobalTranslator.render(component, locale);
((EntityData<Optional<Component>>) entityData).setValue(Optional.of(rendered));
}
}
}
}
}

View file

@ -25,21 +25,57 @@ public class ViewerEngine {
private final Set<WrapperEntity> tracked;
private final ViewerEngineListener listener;
private Executor executor;
private boolean enabled = false;
/**
* Creates an instance of ViewerEngine
* It is recommended to specify explicitly the Executor that should be used.
*/
public ViewerEngine() {
this(Executors.newSingleThreadExecutor());
}
/**
* Creates an instance of ViewerEngine with a specific executor. Depending on your rules one thread might not be enough
* @param executor The executor that is used to process entity tracking.
*/
public ViewerEngine(Executor executor) {
this.globalRules = new CopyOnWriteArrayList<>();
this.tracked = Collections.newSetFromMap(new WeakHashMap<>());
this.executor = Executors.newSingleThreadExecutor();
this.executor = executor;
this.listener = new ViewerEngineListener(this);
}
/**
* Enables this viewer engine.
* Registers a viewer engine listener to handle tracking
*/
public void enable() {
if (enabled) {
return;
}
enabled = true;
EntityLib.getApi().getPacketEvents().getEventManager().registerListener(listener);
}
/**
* Disables this viewer engine.
* Unregisters the viewer engine listener that handles tracking.
*/
public void disable() {
if (!enabled) {
return;
}
enabled = false;
EntityLib.getApi().getPacketEvents().getEventManager().unregisterListener(listener);
}
/**
* Refreshes and updates every tracked by this viewer engine entities viewers to see if they follow the spawning rules.
* If they do not they will no longer see the entity;
*/
public void refresh() {
getTracked0().forEach(entity -> {
for (UUID viewer : entity.getViewers()) {
@ -58,19 +94,40 @@ public class ViewerEngine {
this.executor = executor;
}
/**
* Tells this ViewerEngine to begin tracking a specific {@link WrapperEntity}
* @param entity the entity to begin tracking.
*/
public void track(@NotNull WrapperEntity entity) {
tracked.add(entity);
}
/**
* Tells this ViewerEngine to stop tracking a specific {@link WrapperEntity}
* @param entity the entity to stop tracking.
*/
public void untrack(@NotNull WrapperEntity entity) {
tracked.remove(entity);
}
public void clearTracked() {
tracked.clear();
}
/**
* Checks if a viewer/user validates every viewer rule handled by this viewer engine or not.
* @param user The user to check
* @param entity The entity that is getting its own viewer rules checked as well as the global defined one with {@link ViewerEngine#addViewerRule(ViewerRule)}
* @return true if the user passed and did not fail any rules, false otherwise
*/
public boolean canSpawnFor(User user, WrapperEntity entity) {
if (entity.getViewerRules().stream().anyMatch(rule -> rule.shouldSee(user))) return true;
return globalRules.stream().anyMatch(rule -> rule.shouldSee(user));
}
/**
* Same as {@link ViewerEngine#canSpawnFor(User, WrapperEntity)} but with UUID instead of User
*/
public boolean canSpawnFor(UUID userId, WrapperEntity entity) {
User user = EntityLib.getApi().getPacketEvents().getProtocolManager().getUser(
EntityLib.getApi().getPacketEvents().getProtocolManager().getChannel(userId)
@ -79,6 +136,9 @@ public class ViewerEngine {
return canSpawnFor(user, entity);
}
/**
* @return An unmodifiable view of the entities that are being tracked.
*/
public @UnmodifiableView Collection<WrapperEntity> getTracked() {
return Collections.unmodifiableCollection(tracked);
}

View file

@ -5,31 +5,44 @@ import com.github.retrooper.packetevents.protocol.player.User;
import com.github.retrooper.packetevents.protocol.world.Location;
import com.github.retrooper.packetevents.util.Vector3d;
import com.github.retrooper.packetevents.wrapper.PacketWrapper;
import com.github.retrooper.packetevents.wrapper.play.server.*;
import com.github.retrooper.packetevents.wrapper.play.server.WrapperPlayServerBundle;
import com.github.retrooper.packetevents.wrapper.play.server.WrapperPlayServerDestroyEntities;
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.WrapperPlayServerEntityVelocity;
import com.github.retrooper.packetevents.wrapper.play.server.WrapperPlayServerSetPassengers;
import com.github.retrooper.packetevents.wrapper.play.server.WrapperPlayServerSpawnEntity;
import com.github.retrooper.packetevents.wrapper.play.server.WrapperPlayServerSystemChatMessage;
import me.tofaa.entitylib.EntityLib;
import me.tofaa.entitylib.container.EntityContainer;
import me.tofaa.entitylib.meta.EntityMeta;
import me.tofaa.entitylib.meta.types.ObjectData;
import me.tofaa.entitylib.tick.Tickable;
import me.tofaa.entitylib.utils.PacketUtil;
import me.tofaa.entitylib.ve.ViewerRule;
import me.tofaa.entitylib.wrapper.spawning.SpawnPacketProvider;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Optional;
import java.util.Set;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.function.Consumer;
import net.kyori.adventure.text.Component;
import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.annotations.UnmodifiableView;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.function.Consumer;
public class WrapperEntity implements Tickable {
private final UUID uuid;
private final int entityId;
private EntityType entityType;
private EntityMeta entityMeta;
private final EntityType entityType;
private final EntityMeta entityMeta;
private boolean ticking;
protected Location location;
private Location preRidingLocation;
@ -62,6 +75,7 @@ public class WrapperEntity implements Tickable {
public WrapperEntity(UUID uuid, EntityType entityType) {
this(EntityLib.getPlatform().getEntityIdProvider().provide(uuid, entityType), uuid, entityType);
}
public WrapperEntity(EntityType entityType) {
this(EntityLib.getPlatform().getEntityUuidProvider().provide(entityType), entityType);
}
@ -72,25 +86,20 @@ public class WrapperEntity implements Tickable {
public boolean spawn(Location location, EntityContainer parent) {
if (spawned) return false;
this.location = location;
this.spawned = true;
sendPacketsToViewers(
new WrapperPlayServerSpawnEntity(
entityId,
Optional.of(this.uuid),
entityType,
location.getPosition(),
location.getPitch(),
location.getYaw(),
location.getYaw(),
getObjectData(),
createVeloPacket()
),
createSpawnPacket(),
entityMeta.createPacket()
);
if (this instanceof WrapperLivingEntity) {
sendPacketsToViewers(((WrapperLivingEntity)this).getAttributes().createPacket());
WrapperLivingEntity wrapperLivingEntity = (WrapperLivingEntity) this;
wrapperLivingEntity.createSpawnPackets().forEach(this::sendPacketsToViewers);
}
this.parent = parent;
parent.addEntity(this);
return true;
@ -101,7 +110,7 @@ public class WrapperEntity implements Tickable {
return SpawnPacketProvider.GENERAL.provide(this);
}
public boolean spawn(Location location) {
public boolean spawn(@NotNull Location location) {
return spawn(location, EntityLib.getApi().getDefaultContainer());
}
@ -110,6 +119,7 @@ public class WrapperEntity implements Tickable {
if (entityMeta instanceof ObjectData) {
return ((ObjectData) entityMeta).getObjectData();
}
return 0;
}
@ -117,6 +127,7 @@ public class WrapperEntity implements Tickable {
public Optional<Vector3d> createVeloPacket() {
Optional<Vector3d> velocity;
double veloX = 0, veloY = 0, veloZ = 0;
if (entityMeta instanceof ObjectData) {
ObjectData od = (ObjectData) entityMeta;
if (od.requiresVelocityPacketAtSpawn()) {
@ -126,11 +137,13 @@ public class WrapperEntity implements Tickable {
veloZ = veloPacket.getVelocity().getZ();
}
}
if (veloX == 0 && veloY == 0 && veloZ == 0) {
velocity = Optional.of(Vector3d.zero());
} else {
velocity = Optional.of(new Vector3d(veloX, veloY, veloZ));
}
return velocity;
}
@ -141,37 +154,37 @@ public class WrapperEntity implements Tickable {
public void remove() {
if (parent != null) {
parent.removeEntity(this, true);
}
else {
} else {
despawn();
}
}
public void despawn() {
if (!spawned) return;
spawned = false;
if (this instanceof WrapperPlayer) {
WrapperPlayer p = (WrapperPlayer) this;
sendPacketsToViewers(p.tabListRemovePacket());
}
sendPacketToViewers(new WrapperPlayServerDestroyEntities(entityId));
}
public void teleport(@NotNull Location location, boolean onGround) {
if (!spawned) {
return;
}
if (!spawned) return;
this.location = location;
this.onGround = onGround;
sendPacketToViewers(
new WrapperPlayServerEntityTeleport(
entityId,
location.getPosition(),
location.getYaw(),
location.getPitch(),
onGround
)
);
sendPacketToViewers(new WrapperPlayServerEntityTeleport(
entityId,
location.getPosition(),
location.getYaw(),
location.getPitch(),
onGround
));
}
public void teleport(@NotNull Location location) {
@ -180,30 +193,38 @@ public class WrapperEntity implements Tickable {
/**
* Adds a viewer to the viewers set. The viewer will receive all packets and be informed of this addition
*
* @param uuid the uuid of the user to add
*/
public void addViewer(UUID uuid) {
public void addViewer(@NotNull UUID uuid) {
if (!viewers.add(uuid)) {
return;
}
if (location == null) {
if (EntityLib.getApi().getSettings().isDebugMode()) {
EntityLib.getPlatform().getLogger().warning("Location is null for entity " + entityId + ". Cannot spawn.");
}
return;
}
if (spawned) {
if (this instanceof WrapperPlayer) {
WrapperPlayer p = (WrapperPlayer) this;
sendPacket(uuid, p.tabListPacket());
}
sendPacket(uuid, createSpawnPacket());
sendPacket(uuid, entityMeta.createPacket());
sendPacket(uuid, createPassengerPacket());
if (this instanceof WrapperLivingEntity) {
sendPacket(uuid, ((WrapperLivingEntity)this).getAttributes().createPacket());
WrapperLivingEntity wrapperLivingEntity = (WrapperLivingEntity) this;
wrapperLivingEntity.createSpawnPackets().forEach(packetWrapper -> sendPacket(uuid, packetWrapper));
}
}
if (EntityLib.getApi().getSettings().isDebugMode()) {
EntityLib.getPlatform().getLogger().info("Added viewer " + uuid + " to entity " + entityId);
}
@ -221,7 +242,7 @@ public class WrapperEntity implements Tickable {
sendPacketToViewers(new WrapperPlayServerSystemChatMessage(true, message));
}
protected WrapperPlayServerSpawnEntity createSpawnPacket() {
protected PacketWrapper<?> createSpawnPacket() {
return new WrapperPlayServerSpawnEntity(
entityId,
Optional.of(this.uuid),
@ -235,62 +256,70 @@ public class WrapperEntity implements Tickable {
);
}
public void addViewer(User user) {
public void addViewer(@NotNull User user) {
addViewer(user.getUUID());
}
/**
* Adds a viewer silently into the viewers set. The viewer will not receive any packets or be informed of this addition
*
* @param uuid the uuid of the user to add
*/
public void addViewerSilently(UUID uuid) {
public void addViewerSilently(@NotNull UUID uuid) {
viewers.add(uuid);
}
/**
* Adds a viewer silently into the viewers set. The viewer will not receive any packets or be informed of this addition
*
* @param user the user to add
*/
public void addViewerSilently(User user) {
public void addViewerSilently(@NotNull User user) {
addViewerSilently(user.getUUID());
}
/**
* Removes a viewer from the viewers set of this entity. The viewer will be informed of this removal and will no longer receive any packets
*
* @param uuid the uuid of the user to remove
*/
public void removeViewer(UUID uuid) {
public void removeViewer(@NotNull UUID uuid) {
if (!viewers.remove(uuid)) {
return;
}
if (this instanceof WrapperPlayer) {
WrapperPlayer p = (WrapperPlayer) this;
sendPacket(uuid, p.tabListRemovePacket());
}
sendPacket(uuid, new WrapperPlayServerDestroyEntities(entityId));
}
/**
* Removes a viewer from the viewers set of this entity. The viewer will be informed of this removal and will no longer receive any packets
*
* @param user the user to remove
*/
public void removeViewer(User user) {
public void removeViewer(@NotNull User user) {
removeViewer(user.getUUID());
}
/**
* removes a viewer silently into the viewers set. The viewer will not receive any packets or be informed of this removal
*
* @param uuid of the user to remove
*/
public void removeViewerSilently(UUID uuid) {
public void removeViewerSilently(@NotNull UUID uuid) {
viewers.remove(uuid);
}
/**
* removes a viewer silently into the viewers set. The viewer will not receive any packets or be informed of this removal
*
* @param user the user to remove
*/
public void removeViewerSilently(User user) {
public void removeViewerSilently(@NotNull User user) {
removeViewerSilently(user.getUUID());
}
@ -298,11 +327,11 @@ public class WrapperEntity implements Tickable {
return onGround;
}
public Vector3d getVelocity() {
public @NotNull Vector3d getVelocity() {
return velocity;
}
public void setVelocity(Vector3d velocity) {
public void setVelocity(@NotNull Vector3d velocity) {
this.velocity = velocity;
sendPacketToViewers(getVelocityPacket());
}
@ -331,7 +360,7 @@ public class WrapperEntity implements Tickable {
return entityId;
}
public EntityMeta getEntityMeta() {
public @NotNull EntityMeta getEntityMeta() {
return entityMeta;
}
@ -339,12 +368,12 @@ public class WrapperEntity implements Tickable {
return metaClass.cast(entityMeta);
}
public <T extends EntityMeta> void consumeEntityMeta(@NotNull Class<T> metaClass, Consumer<T> consumer) {
public <T extends EntityMeta> void consumeEntityMeta(@NotNull Class<T> metaClass, @NotNull Consumer<T> consumer) {
T meta = getEntityMeta(metaClass);
consumer.accept(meta);
}
public void consumeMeta(Consumer<EntityMeta> consumer) {
public void consumeMeta(@NotNull Consumer<EntityMeta> consumer) {
consumer.accept(entityMeta);
}
@ -358,13 +387,14 @@ public class WrapperEntity implements Tickable {
/**
* Returns an unmodifiable set of the passengers of the entity.
*
* @return the passengers of the entity
*/
public Set<Integer> getPassengers() {
public @NotNull Set<Integer> getPassengers() {
return Collections.unmodifiableSet(passengers);
}
public WrapperEntity getRiding() {
public @Nullable WrapperEntity getRiding() {
return EntityLib.getApi().getEntity(riding);
}
@ -433,20 +463,22 @@ public class WrapperEntity implements Tickable {
new WrapperPlayServerEntityRotation(entityId, yaw, pitch, onGround),
new WrapperPlayServerEntityHeadLook(entityId, yaw)
);
this.location.setYaw(yaw);
this.location.setPitch(pitch);
}
public void rotateHead(Location location) {
public void rotateHead(@NotNull Location location) {
rotateHead(location.getYaw(), location.getPitch());
}
public void rotateHead(WrapperEntity entity) {
public void rotateHead(@NotNull WrapperEntity entity) {
rotateHead(entity.getLocation());
}
public void refresh() {
if (!spawned) return;
sendPacketsToViewers(entityMeta.createPacket(), createPassengerPacket());
}
@ -457,13 +489,12 @@ public class WrapperEntity implements Tickable {
sendPacket(uuid, packet);
sendPacket(uuid, new WrapperPlayServerBundle());
});
}
else {
} else {
viewers.forEach(uuid -> sendPacket(uuid, packet));
}
}
public void sendPacketsToViewers(PacketWrapper<?>... wrappers) {
public void sendPacketsToViewers(PacketWrapper<?> @NotNull ... wrappers) {
for (PacketWrapper<?> wrapper : wrappers) {
sendPacketToViewers(wrapper);
}
@ -483,13 +514,21 @@ public class WrapperEntity implements Tickable {
private static void sendPacket(UUID user, PacketWrapper<?> wrapper) {
if (wrapper == null) return;
Object channel = EntityLib.getApi().getPacketEvents().getProtocolManager().getChannel(user);
if (channel == null) {
if (EntityLib.getApi().getSettings().isDebugMode()) {
EntityLib.getPlatform().getLogger().warning("Failed to send packet to " + user + " because the channel was null. They may be disconnected/not online.");
}
return;
}
// Special handling for entity metadata packets to support `GlobalTranslator` functionality and component rendering
if (wrapper instanceof WrapperPlayServerEntityMetadata) {
PacketUtil.renderPacket(user, (WrapperPlayServerEntityMetadata) wrapper);
}
EntityLib.getApi().getPacketEvents().getProtocolManager().sendPacket(channel, wrapper);
}
@ -504,12 +543,14 @@ public class WrapperEntity implements Tickable {
/**
* Adds a passenger to the entity. The passenger will be visible to all viewers of the entity.
*
* @param passenger the entity id of the passenger
*/
public void addPassenger(int passenger) {
if (passengers.contains(passenger)) {
throw new IllegalArgumentException("Passenger already exists");
}
passengers.add(passenger);
sendPacketToViewers(createPassengerPacket());
WrapperEntity e = EntityLib.getApi().getEntity(passenger);
@ -533,9 +574,10 @@ public class WrapperEntity implements Tickable {
/**
* Adds multiple passengers to the entity. The passengers will be visible to all viewers of the entity.
*
* @param passengers the entity ids of the passengers
*/
public void addPassengers(int... passengers) {
public void addPassengers(int @NotNull ... passengers) {
for (int passenger : passengers) {
addPassenger(passenger);
}
@ -543,17 +585,19 @@ public class WrapperEntity implements Tickable {
/**
* Adds a passenger to the entity. The passenger will be visible to all viewers of the entity.
*
* @param passenger the wrapper entity passenger
*/
public void addPassenger(WrapperEntity passenger) {
public void addPassenger(@NotNull WrapperEntity passenger) {
addPassenger(passenger.getEntityId());
}
/**
* Adds multiple passengers to the entity. The passengers will be visible to all viewers of the entity.
*
* @param passengers the wrapper entity passengers
*/
public void addPassengers(WrapperEntity... passengers) {
public void addPassengers(WrapperEntity @NotNull ... passengers) {
for (WrapperEntity passenger : passengers) {
addPassenger(passenger);
}
@ -561,12 +605,14 @@ public class WrapperEntity implements Tickable {
/**
* Removes a passenger from the entity. The passenger will be removed from the view of all viewers of the entity.
*
* @param passenger the entity id of the passenger
*/
public void removePassenger(int passenger) {
if (!passengers.contains(passenger)) {
throw new IllegalArgumentException("Passenger does not exist");
}
passengers.remove(passenger);
sendPacketToViewers(createPassengerPacket());
WrapperEntity e = EntityLib.getApi().getEntity(passenger);
@ -588,15 +634,16 @@ public class WrapperEntity implements Tickable {
* @param passenger the passenger wrapper entity
* @return true if the entity has the passenger, false otherwise
*/
public boolean hasPassenger(WrapperEntity passenger) {
public boolean hasPassenger(@NotNull WrapperEntity passenger) {
return hasPassenger(passenger.getEntityId());
}
/**
* Removes multiple passengers from the entity. The passengers will be removed from the view of all viewers of the entity.
*
* @param passengers the entity ids of the passengers
*/
public void removePassengers(int... passengers) {
public void removePassengers(int @NotNull ... passengers) {
for (int passenger : passengers) {
removePassenger(passenger);
}
@ -604,17 +651,19 @@ public class WrapperEntity implements Tickable {
/**
* Removes a passenger from the entity. The passenger will be removed from the view of all viewers of the entity.
*
* @param passenger the wrapper entity passenger
*/
public void removePassenger(WrapperEntity passenger) {
public void removePassenger(@NotNull WrapperEntity passenger) {
removePassenger(passenger.getEntityId());
}
/**
* Removes multiple passengers from the entity. The passengers will be removed from the view of all viewers of the entity.
*
* @param passengers the wrapper entity passengers
*/
public void removePassengers(WrapperEntity... passengers) {
public void removePassengers(WrapperEntity @NotNull ... passengers) {
for (WrapperEntity passenger : passengers) {
removePassenger(passenger);
}
@ -631,11 +680,11 @@ public class WrapperEntity implements Tickable {
return Collections.unmodifiableSet(viewers);
}
public boolean hasViewer(UUID uuid) {
public boolean hasViewer(@NotNull UUID uuid) {
return viewers.contains(uuid);
}
public boolean hasViewer(User user) {
public boolean hasViewer(@NotNull User user) {
return hasViewer(user.getUUID());
}

View file

@ -0,0 +1,229 @@
package me.tofaa.entitylib.wrapper;
import com.github.retrooper.packetevents.protocol.nbt.NBTCompound;
import com.github.retrooper.packetevents.protocol.potion.PotionType;
import com.github.retrooper.packetevents.wrapper.play.server.WrapperPlayServerEntityEffect;
import com.github.retrooper.packetevents.wrapper.play.server.WrapperPlayServerRemoveEntityEffect;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
public class WrapperEntityPotionEffect {
private final WrapperLivingEntity entity;
private final Map<PotionType, WrapperPotionEffect> effects = new ConcurrentHashMap<>();
private boolean notifyChanges = true;
public WrapperEntityPotionEffect(WrapperLivingEntity entity) {
this.entity = entity;
}
/**
* @param type The type of the potion effect {@link com.github.retrooper.packetevents.protocol.potion.PotionTypes}
* @param amplifier The amplifier of the potion effect. The notchian client displays the number as (amplifier + 1)
* @param duration The duration of the potion effect in ticks, if set to -1 the client will display the effect as infinite
* @param ambient A
* @param visible A
* @param showIcons A
* @param factorData The factor data of the potion effect, if hasFactorData is false this will be ignored
*/
public void addPotionEffect(
PotionType type,
int amplifier,
int duration,
boolean ambient,
boolean visible,
boolean showIcons,
@Nullable NBTCompound factorData
) {
WrapperPotionEffect effect = new WrapperPotionEffect(type, amplifier, duration, ambient, visible, showIcons, factorData);
this.effects.put(type, effect);
this.entity.sendPacketToViewers(createEffectPacket(effect));
}
/**
* @param type The type of the potion effect {@link com.github.retrooper.packetevents.protocol.potion.PotionTypes}
* @param amplifier The amplifier of the potion effect. The notchian client displays the number as (amplifier + 1)
* @param duration The duration of the potion effect in ticks, if set to -1 the client will display the effect as infinite
* @param flags The bit flags of the potion effect see: <a href="https://minecraft.wiki/w/Java_Edition_protocol#Entity_Effect">https://minecraft.wiki/w/Java_Edition_protocol#Entity_Effect</a>
* @param factorData The factor data of the potion effect, if hasFactorData is false this will be ignored
*/
public void addPotionEffect(
PotionType type,
int amplifier,
int duration,
byte flags,
@Nullable NBTCompound factorData
) {
BitSet flagsBitSet = BitSet.valueOf(new byte[]{flags});
boolean ambient = flagsBitSet.get(0);
boolean visible = flagsBitSet.get(1);
boolean icons = flagsBitSet.get(2);
addPotionEffect(type, amplifier, duration, ambient, visible, icons, factorData);
}
/**
* @param type The type of the potion effect {@link com.github.retrooper.packetevents.protocol.potion.PotionTypes}
* @param amplifier The amplifier of the potion effect. The notchian client displays the number as (amplifier + 1)
* @param duration The duration of the potion effect in ticks, if set to -1 the client will display the effect as infinite
* @param flags The bit flags of the potion effect see: <a href="https://minecraft.wiki/w/Java_Edition_protocol#Entity_Effect">https://minecraft.wiki/w/Java_Edition_protocol#Entity_Effect</a>
* @param hasFactorData Whether the potion effect has factor data
* @param factorData The factor data of the potion effect, if hasFactorData is false this will be ignored
*/
public void addPotionEffect(
PotionType type,
int amplifier,
int duration,
byte flags,
boolean hasFactorData,
@Nullable NBTCompound factorData
) {
addPotionEffect(type, amplifier, duration, flags, hasFactorData ? factorData : null);
}
/**
* Adds a potion effect to the entity.
* EntityLib will not keep track of the potions you give or what you do with them,
* this simply sends the packet to the viewers of the entity.
*
* @param type The type of the potion effect {@link com.github.retrooper.packetevents.protocol.potion.PotionTypes}
* @param amplifier The amplifier of the potion effect. The notchian client displays the number as (amplifier + 1)
* @param duration The duration of the potion effect in ticks, if set to -1 the client will display the effect as infinite
* @param flags The bit flags of the potion effect see: <a href="https://minecraft.wiki/w/Java_Edition_protocol#Entity_Effect">https://minecraft.wiki/w/Java_Edition_protocol#Entity_Effect</a>
*/
public void addPotionEffect(
PotionType type,
int amplifier,
int duration,
byte flags
) {
addPotionEffect(type, amplifier, duration, flags, false, null);
}
public void removePotionEffect(@NotNull PotionType potionType) {
if (this.effects.remove(potionType) != null) {
this.entity.sendPacketsToViewers(createRemoveEffectPacket(potionType));
}
}
public void clearPotionEffects() {
new ArrayList<>(this.effects.keySet()).forEach(this::removePotionEffect);
}
public @NotNull List<WrapperPlayServerEntityEffect> createEffectPackets() {
List<WrapperPlayServerEntityEffect> packets = new ArrayList<>();
this.effects.forEach((potionType, effect) -> packets.add(createEffectPacket(effect)));
return packets;
}
public @NotNull WrapperPlayServerEntityEffect createEffectPacket(@NotNull WrapperPotionEffect effect) {
PotionType potionType = effect.getPotionType();
int amplifier = effect.getAmplifier();
int duration = effect.getDuration();
boolean ambient = effect.isAmbient();
boolean visible = effect.isVisible();
boolean icons = effect.hasIcons();
NBTCompound factorData = effect.getFactorData();
int flags = 0;
flags |= ambient ? 1 : 0; // Bit 0 para ambient
flags |= visible ? (1 << 1) : 0; // Bit 1 para visible
flags |= icons ? (1 << 2) : 0; // Bit 2 para icons
WrapperPlayServerEntityEffect wrapperPlayServerEntityEffect = new WrapperPlayServerEntityEffect(
0,
null,
0,
0,
(byte) flags
);
wrapperPlayServerEntityEffect.setEntityId(this.entity.getEntityId());
wrapperPlayServerEntityEffect.setPotionType(potionType);
wrapperPlayServerEntityEffect.setEffectAmplifier(amplifier);
wrapperPlayServerEntityEffect.setEffectDurationTicks(duration);
wrapperPlayServerEntityEffect.setFactorData(factorData);
return wrapperPlayServerEntityEffect;
}
public @NotNull WrapperPlayServerRemoveEntityEffect createRemoveEffectPacket(@NotNull PotionType potionType) {
return new WrapperPlayServerRemoveEntityEffect(this.entity.getEntityId(), potionType);
}
public void refresh() {
if (notifyChanges) {
new ArrayList<>(this.effects.values()).forEach(effect -> {
WrapperPlayServerEntityEffect wrapperPlayServerEntityEffect = createEffectPacket(effect);
this.entity.sendPacketToViewers(wrapperPlayServerEntityEffect);
});
}
}
public boolean isNotifyingChanges() {
return notifyChanges;
}
public void setNotifyChanges(boolean notifyChanges) {
this.notifyChanges = notifyChanges;
refresh();
}
public static class WrapperPotionEffect {
private final PotionType potionType;
private final int amplifier;
private final int duration;
private final boolean ambient;
private final boolean visible;
private final boolean icons;
private final @Nullable NBTCompound factorData;
private WrapperPotionEffect(PotionType potionType, int amplifier, int duration, boolean ambient, boolean visible, boolean icons, @Nullable NBTCompound factorData) {
this.potionType = potionType;
this.amplifier = amplifier;
this.duration = duration;
this.ambient = ambient;
this.visible = visible;
this.icons = icons;
this.factorData = factorData;
}
public PotionType getPotionType() {
return potionType;
}
public int getAmplifier() {
return amplifier;
}
public int getDuration() {
return duration;
}
public boolean isAmbient() {
return ambient;
}
public boolean isVisible() {
return visible;
}
public boolean hasIcons() {
return icons;
}
public @Nullable NBTCompound getFactorData() {
return factorData;
}
}
}

View file

@ -1,25 +1,37 @@
package me.tofaa.entitylib.wrapper;
import com.github.retrooper.packetevents.manager.server.ServerVersion;
import com.github.retrooper.packetevents.protocol.entity.type.EntityType;
import com.github.retrooper.packetevents.protocol.nbt.NBTCompound;
import com.github.retrooper.packetevents.protocol.potion.PotionType;
import com.github.retrooper.packetevents.protocol.world.Location;
import com.github.retrooper.packetevents.wrapper.PacketWrapper;
import com.github.retrooper.packetevents.wrapper.play.server.WrapperPlayServerEntityAnimation;
import com.github.retrooper.packetevents.wrapper.play.server.WrapperPlayServerEntityEffect;
import com.github.retrooper.packetevents.wrapper.play.server.WrapperPlayServerEntityEquipment;
import com.github.retrooper.packetevents.wrapper.play.server.WrapperPlayServerHurtAnimation;
import me.tofaa.entitylib.EntityLib;
import me.tofaa.entitylib.container.EntityContainer;
import me.tofaa.entitylib.meta.EntityMeta;
import me.tofaa.entitylib.utils.VersionUtil;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
public class WrapperLivingEntity extends WrapperEntity{
public class WrapperLivingEntity extends WrapperEntity {
private final WrapperEntityEquipment equipment;
private final WrapperEntityAttributes attributes;
private final WrapperEntityPotionEffect potionEffect;
public WrapperLivingEntity(int entityId, UUID uuid, EntityType entityType, EntityMeta entityMeta) {
super(entityId, uuid, entityType, entityMeta);
this.equipment = new WrapperEntityEquipment(this);
this.attributes = new WrapperEntityAttributes(this);
this.potionEffect = new WrapperEntityPotionEffect(this);
}
public WrapperLivingEntity(int entityId, UUID uuid, EntityType entityType) {
@ -33,6 +45,7 @@ public class WrapperLivingEntity extends WrapperEntity{
public WrapperLivingEntity(UUID uuid, EntityType entityType) {
this(EntityLib.getPlatform().getEntityIdProvider().provide(uuid, entityType), uuid, entityType);
}
public WrapperLivingEntity(EntityType entityType) {
this(EntityLib.getPlatform().getEntityUuidProvider().provide(entityType), entityType);
}
@ -40,60 +53,74 @@ public class WrapperLivingEntity extends WrapperEntity{
@Override
public void refresh() {
super.refresh();
equipment.refresh();
attributes.refresh();
this.equipment.refresh();
this.potionEffect.refresh();
this.attributes.refresh();
}
public WrapperEntityAttributes getAttributes() {
return attributes;
return this.attributes;
}
public WrapperEntityEquipment getEquipment() {
return this.equipment;
}
public WrapperEntityPotionEffect getPotionEffect() {
return this.potionEffect;
}
public @NotNull List<PacketWrapper<?>> createSpawnPackets() {
List<PacketWrapper<?>> packets = new ArrayList<>();
packets.add(getAttributes().createPacket());
packets.add(getEquipment().createPacket());
packets.addAll(getPotionEffect().createEffectPackets());
return packets;
}
/**
* Adds a potion effect to the entity.
* EntityLib will not keep track of the potions you give or what you do with them,
* this simply sends the packet to the viewers of the entity.
* @param type The type of the potion effect {@link com.github.retrooper.packetevents.protocol.potion.PotionTypes}
* @param amplifier The amplifier of the potion effect. The notchian client displays the number as (amplifier + 1)
* @param duration The duration of the potion effect in ticks, if set to -1 the client will display the effect as infinite
* @param flags The bit flags of the potion effect see: https://wiki.vg/Protocol#Entity_Effect
*
* @param type The type of the potion effect {@link com.github.retrooper.packetevents.protocol.potion.PotionTypes}
* @param amplifier The amplifier of the potion effect. The notchian client displays the number as (amplifier + 1)
* @param duration The duration of the potion effect in ticks, if set to -1 the client will display the effect as infinite
* @param flags The bit flags of the potion effect see:
* <a href="https://minecraft.wiki/w/Java_Edition_protocol#Entity_Effect">https://minecraft.wiki/w/Java_Edition_protocol#Entity_Effect</a>
* @param hasFactorData Whether the potion effect has factor data
* @param factorData The factor data of the potion effect, if hasFactorData is false this will be ignored
* @param factorData The factor data of the potion effect, if hasFactorData is false this will be ignored
*/
public void addPotionEffect(
PotionType type,
int amplifier,
int duration,
byte flags,
boolean hasFactorData,
@Nullable NBTCompound factorData
PotionType type,
int amplifier,
int duration,
byte flags,
boolean hasFactorData,
@Nullable NBTCompound factorData
) {
sendPacketToViewers(
new WrapperPlayServerEntityEffect(
getEntityId(),
type,
amplifier,
duration,
flags
)
);
this.potionEffect.addPotionEffect(type, amplifier, duration, flags, hasFactorData, factorData);
}
/**
* Adds a potion effect to the entity.
* EntityLib will not keep track of the potions you give or what you do with them,
* this simply sends the packet to the viewers of the entity.
* @param type The type of the potion effect {@link com.github.retrooper.packetevents.protocol.potion.PotionTypes}
*
* @param type The type of the potion effect {@link com.github.retrooper.packetevents.protocol.potion.PotionTypes}
* @param amplifier The amplifier of the potion effect. The notchian client displays the number as (amplifier + 1)
* @param duration The duration of the potion effect in ticks, if set to -1 the client will display the effect as infinite
* @param flags The bit flags of the potion effect see: https://wiki.vg/Protocol#Entity_Effect
* @param duration The duration of the potion effect in ticks, if set to -1 the client will display the effect as infinite
* @param flags The bit flags of the potion effect see:
* <a href="https://minecraft.wiki/w/Java_Edition_protocol#Entity_Effect">https://minecraft.wiki/w/Java_Edition_protocol#Entity_Effect</a>
*/
public void addPotionEffect(
PotionType type,
int amplifier,
int duration,
byte flags
PotionType type,
int amplifier,
int duration,
byte flags
) {
addPotionEffect(type, amplifier, duration, flags, false, null);
}
@ -110,7 +137,30 @@ public class WrapperLivingEntity extends WrapperEntity{
sendAnimation(WrapperPlayServerEntityAnimation.EntityAnimationType.WAKE_UP);
}
/**
* Plays the hurt animation of the entity.
* This method is deprecated and should use {@link #playHurtAnimation(int)} instead.
*/
@Deprecated
public void playHurtAnimation() {
playHurtAnimation(0);
}
/**
* Plays the hurt animation of the entity.
*
* @param yaw The yaw of the entity when the hurt animation is played.
* For any entity other than a player it's safe to simply put 0, as it's not used.
* The yaw is only used on 1.19.4+.
*/
public void playHurtAnimation(int yaw) {
// 1.19.4+ uses a different packet for hurt animation than previous versions
if (VersionUtil.isNewerThan(ServerVersion.V_1_19_4)) {
sendPacketToViewers(
new WrapperPlayServerHurtAnimation(getEntityId(), yaw)
);
return;
}
sendAnimation(WrapperPlayServerEntityAnimation.EntityAnimationType.HURT);
}
@ -127,8 +177,4 @@ public class WrapperLivingEntity extends WrapperEntity{
new WrapperPlayServerEntityAnimation(getEntityId(), type)
);
}
public WrapperEntityEquipment getEquipment() {
return equipment;
}
}

View file

@ -1,16 +1,24 @@
package me.tofaa.entitylib.wrapper;
import com.github.retrooper.packetevents.manager.server.ServerVersion;
import com.github.retrooper.packetevents.protocol.entity.type.EntityTypes;
import com.github.retrooper.packetevents.protocol.player.*;
import com.github.retrooper.packetevents.protocol.world.Location;
import com.github.retrooper.packetevents.util.Vector3d;
import com.github.retrooper.packetevents.protocol.player.GameMode;
import com.github.retrooper.packetevents.protocol.player.TextureProperty;
import com.github.retrooper.packetevents.protocol.player.UserProfile;
import com.github.retrooper.packetevents.wrapper.PacketWrapper;
import com.github.retrooper.packetevents.wrapper.play.server.*;
import com.github.retrooper.packetevents.wrapper.play.server.WrapperPlayServerDestroyEntities;
import com.github.retrooper.packetevents.wrapper.play.server.WrapperPlayServerEntityHeadLook;
import com.github.retrooper.packetevents.wrapper.play.server.WrapperPlayServerEntityRotation;
import com.github.retrooper.packetevents.wrapper.play.server.WrapperPlayServerPlayerInfoRemove;
import com.github.retrooper.packetevents.wrapper.play.server.WrapperPlayServerPlayerInfoUpdate;
import com.github.retrooper.packetevents.wrapper.play.server.WrapperPlayServerSpawnPlayer;
import me.tofaa.entitylib.EntityLib;
import me.tofaa.entitylib.meta.EntityMeta;
import java.util.EnumSet;
import java.util.List;
import java.util.UUID;
import net.kyori.adventure.text.Component;
import java.util.*;
import org.jetbrains.annotations.NotNull;
public class WrapperPlayer extends WrapperLivingEntity {
@ -25,6 +33,27 @@ public class WrapperPlayer extends WrapperLivingEntity {
this.profile = profile;
}
@Override
protected PacketWrapper<?> createSpawnPacket() {
if (EntityLib.getApi().getPacketEvents().getServerManager().getVersion().isOlderThanOrEquals(ServerVersion.V_1_20_1)) {
return new WrapperPlayServerSpawnPlayer(
getEntityId(),
profile.getUUID(),
getLocation(),
getEntityMeta().entityData()
);
}
return super.createSpawnPacket();
}
@Override
public @NotNull List<PacketWrapper<?>> createSpawnPackets() {
final List<PacketWrapper<?>> packets = super.createSpawnPackets();
packets.add(new WrapperPlayServerEntityRotation(getEntityId(), getYaw(), getPitch(), isOnGround()));
packets.add(new WrapperPlayServerEntityHeadLook(getEntityId(), getYaw()));
return packets;
}
public WrapperPlayServerPlayerInfoUpdate tabListPacket() {
EnumSet<WrapperPlayServerPlayerInfoUpdate.Action> actions = EnumSet.of(
WrapperPlayServerPlayerInfoUpdate.Action.ADD_PLAYER,

View file

@ -40,7 +40,8 @@ abstract class ELVersionTask : DefaultTask() {
* This file is generated by the auto-version task. Modifying it will have no effect.
*/
package $packageName;
import java.text.DateFormat;
import com.github.retrooper.packetevents.util.PEVersion;
public final class ELVersions {
@ -52,6 +53,29 @@ abstract class ELVersionTask : DefaultTask() {
private ELVersions() {
throw new IllegalStateException();
}
public static class Version {
private final long timestamp;
public Version(long timestamp) {
this.timestamp = timestamp;
}
public long getTimestamp() {
return timestamp;
}
public String getTimestampFormatted() {
return DateFormat.getDateTimeInstance().format(new java.util.Date(timestamp));
}
public boolean isOlderThan(Version version) {
return this.timestamp < version.timestamp;
}
}
}
""".trimIndent())
}

View file

@ -4,17 +4,19 @@ import com.github.retrooper.packetevents.PacketEventsAPI;
import me.tofaa.entitylib.APIConfig;
import me.tofaa.entitylib.EntityLibAPI;
import me.tofaa.entitylib.Platform;
import me.tofaa.entitylib.UserLocaleProvider;
import me.tofaa.entitylib.container.EntityContainer;
import me.tofaa.entitylib.tick.TickContainer;
import me.tofaa.entitylib.wrapper.WrapperEntity;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.*;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.UUID;
public abstract class AbstractEntityLibAPI<P, T> implements EntityLibAPI<T> {
protected final Platform<P> platform;
protected final PacketEventsAPI<?> packetEvents;
protected final APIConfig settings;
@ -36,7 +38,6 @@ public abstract class AbstractEntityLibAPI<P, T> implements EntityLibAPI<T> {
@Override
public @Nullable WrapperEntity getEntity(@NotNull UUID uuid) {
return defaultEntityContainer.getEntity(uuid);
}
@Override
@ -49,9 +50,8 @@ public abstract class AbstractEntityLibAPI<P, T> implements EntityLibAPI<T> {
return defaultEntityContainer;
}
@NotNull
@Override
public APIConfig getSettings() {
public @NotNull APIConfig getSettings() {
return settings;
}
@ -60,9 +60,13 @@ public abstract class AbstractEntityLibAPI<P, T> implements EntityLibAPI<T> {
return packetEvents;
}
@NotNull
@Override
public Collection<TickContainer<?, T>> getTickContainers() {
public @NotNull Collection<TickContainer<?, T>> getTickContainers() {
return tickContainers;
}
@Override
public @NotNull UserLocaleProvider getUserLocaleProvider() {
return platform.getUserLocaleProvider();
}
}

View file

@ -1,8 +1,8 @@
[versions]
adventure = "4.16.0"
jetbrains-annotations = "24.0.0"
gson = "2.11.0"
packetevents = "2.7.0"
adventure = "4.22.0"
jetbrains-annotations = "26.0.2"
gson = "2.13.1"
packetevents = "2.9.1"
paper = "1.21-R0.1-SNAPSHOT"
velocity = "3.3.0-SNAPSHOT"
run-paper = "2.3.0"

View file

@ -1,15 +1,21 @@
package me.tofaa.entitylib.spigot;
import com.github.retrooper.packetevents.PacketEventsAPI;
import io.github.retrooper.packetevents.bstats.bukkit.Metrics;
import io.github.retrooper.packetevents.bstats.charts.SimplePie;
import me.tofaa.entitylib.APIConfig;
import me.tofaa.entitylib.EntityLib;
import me.tofaa.entitylib.UserLocaleProvider;
import me.tofaa.entitylib.common.AbstractPlatform;
import java.util.logging.Logger;
import org.bukkit.plugin.Plugin;
import org.bukkit.plugin.java.JavaPlugin;
import org.jetbrains.annotations.NotNull;
import java.util.logging.Logger;
public class SpigotEntityLibPlatform extends AbstractPlatform<JavaPlugin> {
private SpigotEntityLibAPI api;
private UserLocaleProvider userLocaleProvider = new SpigotPlayerLocaleProvider();
public SpigotEntityLibPlatform(@NotNull JavaPlugin plugin) {
super(plugin);
@ -23,6 +29,10 @@ public class SpigotEntityLibPlatform extends AbstractPlatform<JavaPlugin> {
this.setEntityIdProvider(new SpigotEntityIdProvider(this));
this.api.onLoad();
this.api.onEnable();
if (settings.shouldUseBstats()) {
PacketEventsAPI<Plugin> pe = (PacketEventsAPI<Plugin>) api.getPacketEvents();
}
}
@Override
@ -34,4 +44,14 @@ public class SpigotEntityLibPlatform extends AbstractPlatform<JavaPlugin> {
public String getName() {
return "Spigot";
}
@Override
public @NotNull UserLocaleProvider getUserLocaleProvider() {
return userLocaleProvider;
}
@Override
public void setUserLocaleProvider(@NotNull final UserLocaleProvider provider) {
this.userLocaleProvider = provider;
}
}

View file

@ -0,0 +1,73 @@
package me.tofaa.entitylib.spigot;
import me.tofaa.entitylib.UserLocaleProvider;
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodType;
import java.util.Locale;
import java.util.UUID;
import java.util.function.Function;
import net.kyori.adventure.translation.Translator;
import org.bukkit.Bukkit;
import org.bukkit.entity.Player;
import org.jetbrains.annotations.NotNull;
/**
* This implementation is based on code from the scoreboard-library project:
* <a href="https://github.com/MegavexNetwork/scoreboard-library/blob/bc8e3c2d2ecf9973ec0d6bb8ae4af94ed008b360/commons/src/main/java/net/megavex/scoreboardlibrary/implementation/commons/LocaleProvider.java">LocaleProvider</a>
* Modified and adapted for use in EntityLib.
*/
public class SpigotPlayerLocaleProvider implements UserLocaleProvider {
private static final Locale DEFAULT_LOCALE = Locale.US;
private static final Function<Player, Locale> provider = get();
@Override
public Locale locale(final UUID user) {
final Player player = Bukkit.getPlayer(user);
return player == null ? DEFAULT_LOCALE : provider.apply(player);
}
private static @NotNull Function<Player, Locale> get() {
MethodHandles.Lookup lookup = MethodHandles.publicLookup();
try {
MethodHandle adventureMethod = lookup.findVirtual(Player.class, "locale", MethodType.methodType(Locale.class));
return player -> {
try {
return (Locale) adventureMethod.invokeExact(player);
} catch (Throwable e) {
throw new RuntimeException(e);
}
};
} catch (IllegalAccessException | NoSuchMethodException ignored) {
}
MethodType methodType = MethodType.methodType(String.class);
try {
MethodHandle legacySpigotMethod = lookup.findVirtual(Player.Spigot.class, "getLocale", methodType);
return player -> {
try {
Locale locale = Translator.parseLocale((String) legacySpigotMethod.invokeExact(player.spigot()));
return locale == null ? DEFAULT_LOCALE : locale;
} catch (Throwable e) {
throw new RuntimeException(e);
}
};
} catch (IllegalAccessException | NoSuchMethodException ignored) {
}
try {
MethodHandle legacyMethod = lookup.findVirtual(Player.class, "getLocale", methodType);
return player -> {
try {
Locale locale = Translator.parseLocale((String) legacyMethod.invokeExact(player));
return locale == null ? DEFAULT_LOCALE : locale;
} catch (Throwable e) {
throw new RuntimeException(e);
}
};
} catch (IllegalAccessException | NoSuchMethodException ignored) {
throw new RuntimeException("No way to get players locale found");
}
}
}

View file

@ -2,15 +2,18 @@ package me.tofaa.entitylib.standalone;
import me.tofaa.entitylib.APIConfig;
import me.tofaa.entitylib.EntityLibAPI;
import me.tofaa.entitylib.UserLocaleProvider;
import me.tofaa.entitylib.common.AbstractPlatform;
import java.util.Locale;
import org.jetbrains.annotations.NotNull;
public class StandaloneEntityLibPlatform extends AbstractPlatform<Object> {
private StandaloneEntityLibApi api;
private UserLocaleProvider userLocaleProvider = (user) -> Locale.US;
private StandaloneEntityLibPlatform() {
public StandaloneEntityLibPlatform() {
super(null);
}
@ -34,4 +37,14 @@ public class StandaloneEntityLibPlatform extends AbstractPlatform<Object> {
public String getName() {
return "Standalone";
}
@Override
public @NotNull UserLocaleProvider getUserLocaleProvider() {
return userLocaleProvider;
}
@Override
public void setUserLocaleProvider(@NotNull final UserLocaleProvider provider) {
this.userLocaleProvider = provider;
}
}

View file

@ -9,6 +9,7 @@ import com.velocitypowered.api.proxy.ProxyServer;
import io.github.retrooper.packetevents.velocity.factory.VelocityPacketEventsBuilder;
import me.tofaa.entitylib.APIConfig;
import me.tofaa.entitylib.EntityLibAPI;
import me.tofaa.entitylib.UserLocaleProvider;
import me.tofaa.entitylib.common.AbstractPlatform;
import org.jetbrains.annotations.NotNull;
@ -16,6 +17,7 @@ import java.util.logging.Logger;
public class VelocityEntityLibPlatform extends AbstractPlatform<ProxyServer> {
private VelocityEntityLibAPI api;
private UserLocaleProvider userLocaleProvider;
private Object plugin;
public VelocityEntityLibPlatform(Object plugin, ProxyServer handle) {
@ -54,4 +56,14 @@ public class VelocityEntityLibPlatform extends AbstractPlatform<ProxyServer> {
public String getName() {
return "Velocity";
}
@Override
public @NotNull UserLocaleProvider getUserLocaleProvider() {
return userLocaleProvider;
}
@Override
public void setUserLocaleProvider(final UserLocaleProvider userLocaleProvider) {
this.userLocaleProvider = userLocaleProvider;
}
}

View file

@ -0,0 +1,20 @@
package me.tofaa.entitylib.velocity;
import com.velocitypowered.api.proxy.Player;
import com.velocitypowered.api.proxy.ProxyServer;
import me.tofaa.entitylib.UserLocaleProvider;
import java.util.Locale;
import java.util.UUID;
public class VelocityPlayerLocaleProvider implements UserLocaleProvider {
private final ProxyServer proxyServer;
public VelocityPlayerLocaleProvider(final ProxyServer proxyServer) {
this.proxyServer = proxyServer;
}
@Override
public Locale locale(final UUID user) {
return proxyServer.getPlayer(user).map(Player::getEffectiveLocale).orElse(Locale.US);
}
}

View file

@ -34,7 +34,7 @@ include(":platforms:spigot")
include(":platforms:velocity")
include(":platforms:standalone")
if (!System.getenv("JITPACK").toBoolean()) {
if (System.getenv("PRIVATE").toBoolean()) {
include("discord-bot")
include(":code-gen")
include(":test-plugin")

View file

@ -9,11 +9,13 @@ import org.bukkit.command.CommandMap;
import org.bukkit.plugin.java.JavaPlugin;
import java.lang.reflect.InvocationTargetException;
import java.text.DateFormat;
public class TestEntityLibPlugin extends JavaPlugin {
@Override
public void onEnable() {
DateFormat.getDateTimeInstance().format(new java.util.Date(timestamp));
SpigotEntityLibPlatform platform = new SpigotEntityLibPlatform(this);
APIConfig settings = new APIConfig(PacketEvents.getAPI())