diff --git a/nmsutils-plugin/pom.xml b/nmsutils-plugin/pom.xml index 60cc40e..3e9e343 100644 --- a/nmsutils-plugin/pom.xml +++ b/nmsutils-plugin/pom.xml @@ -56,6 +56,7 @@ de.cubeside.nmsutils:nmsutils-v1_19_R1_1 de.cubeside.nmsutils:nmsutils-v1_19_R2 de.cubeside.nmsutils:nmsutils-v1_19_R3 + de.cubeside.nmsutils:nmsutils-v1_20_R1 diff --git a/nmsutils-v1_20_R1/pom.xml b/nmsutils-v1_20_R1/pom.xml new file mode 100644 index 0000000..2f1ea61 --- /dev/null +++ b/nmsutils-v1_20_R1/pom.xml @@ -0,0 +1,100 @@ + + 4.0.0 + nmsutils-v1_20_R1 + + de.cubeside.nmsutils + nmsutils-parent + 0.0.1-SNAPSHOT + + + + io.papermc.paper + paper-api + 1.20-R0.1-SNAPSHOT + provided + + + io.papermc.paper + paper-server + 1.20-R0.1-SNAPSHOT + mojang-mapped + provided + + + ${project.groupId} + nmsutils-core + ${project.version} + provided + + + net.fabricmc + mapping-io + 0.3.0 + provided + + + com.mojang + datafixerupper + 5.0.28 + provided + + + + + minecraft-repo + https://libraries.minecraft.net/ + + + spigot-repo + https://hub.spigotmc.org/nexus/content/groups/public/ + + + fabric-repo + https://maven.fabricmc.net/ + + + + + + maven-compiler-plugin + 3.8.1 + + 17 + + + + net.md-5 + specialsource-maven-plugin + 1.2.3 + + + package + + remap + + remap-obf + + org.spigotmc:minecraft-server:1.20-R0.1-SNAPSHOT:txt:maps-mojang + true + org.spigotmc:spigot:1.20-R0.1-SNAPSHOT:jar:remapped-mojang + true + remapped-obf + + + + package + + remap + + remap-spigot + + ${project.build.directory}/${project.artifactId}-${project.version}-remapped-obf.jar + org.spigotmc:minecraft-server:1.20-R0.1-SNAPSHOT:csrg:maps-spigot + org.spigotmc:spigot:1.20-R0.1-SNAPSHOT:jar:remapped-obf + + + + + + + \ No newline at end of file diff --git a/nmsutils-v1_20_R1/src/main/java/de/cubeside/nmsutils/v1_20_R1/BiomeUtilsImpl.java b/nmsutils-v1_20_R1/src/main/java/de/cubeside/nmsutils/v1_20_R1/BiomeUtilsImpl.java new file mode 100644 index 0000000..8cbeda8 --- /dev/null +++ b/nmsutils-v1_20_R1/src/main/java/de/cubeside/nmsutils/v1_20_R1/BiomeUtilsImpl.java @@ -0,0 +1,154 @@ +package de.cubeside.nmsutils.v1_20_R1; + +import com.mojang.serialization.Lifecycle; +import de.cubeside.nmsutils.BiomeUtils; +import de.cubeside.nmsutils.NMSUtils; +import de.cubeside.nmsutils.biome.CustomBiome; +import de.cubeside.nmsutils.util.ReobfHelper; +import java.lang.reflect.Field; +import java.util.Collection; +import java.util.Collections; +import java.util.HashMap; +import java.util.IdentityHashMap; +import net.minecraft.core.BlockPos; +import net.minecraft.core.Holder; +import net.minecraft.core.Holder.Reference; +import net.minecraft.core.MappedRegistry; +import net.minecraft.core.WritableRegistry; +import net.minecraft.core.registries.Registries; +import net.minecraft.resources.ResourceKey; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.server.dedicated.DedicatedServer; +import net.minecraft.world.level.Level; +import net.minecraft.world.level.biome.AmbientMoodSettings; +import net.minecraft.world.level.biome.Biome; +import net.minecraft.world.level.biome.Biome.BiomeBuilder; +import net.minecraft.world.level.biome.Biome.TemperatureModifier; +import net.minecraft.world.level.biome.BiomeSpecialEffects; +import net.minecraft.world.level.biome.BiomeSpecialEffects.Builder; +import net.minecraft.world.level.biome.Biomes; +import net.minecraft.world.level.chunk.LevelChunk; +import org.bukkit.Location; +import org.bukkit.NamespacedKey; +import org.bukkit.Server; +import org.bukkit.craftbukkit.v1_20_R1.CraftServer; +import org.bukkit.craftbukkit.v1_20_R1.CraftWorld; + +public class BiomeUtilsImpl implements BiomeUtils { + private final NMSUtilsImpl nmsUtils; + + private HashMap customBiomes; + private HashMap customBiomesByBiome; + private Collection unmodifiableCustomBiomes; + + private static final Field FIELD_MAPPED_REGISTRY_FROZEN = ReobfHelper.getFieldByMojangName(MappedRegistry.class, "frozen"); + private static final Field FIELD_MAPPED_REGISTRY_UNREGISTERED_INTRUSIVE_HOLDERS = ReobfHelper.getFieldByMojangName(MappedRegistry.class, "unregisteredIntrusiveHolders"); + + public BiomeUtilsImpl(NMSUtilsImpl nmsUtils) { + this.nmsUtils = nmsUtils; + this.customBiomes = new HashMap<>(); + this.customBiomesByBiome = new HashMap<>(); + this.unmodifiableCustomBiomes = Collections.unmodifiableCollection(customBiomes.values()); + } + + public NMSUtils getNMSUtils() { + return nmsUtils; + } + + @Override + public CustomBiome registerCustomBiome(NamespacedKey id, float downfall, float temperature, de.cubeside.nmsutils.biome.Precipitation precipitation, Integer fogColor, Integer waterColor, Integer waterFogColor, Integer skyColor, Integer foliageColor, Integer grassColor) { + Server server = nmsUtils.getPlugin().getServer(); + CraftServer craftserver = (CraftServer) server; + DedicatedServer dedicatedserver = craftserver.getServer(); + ResourceKey newKey = ResourceKey.create(Registries.BIOME, new ResourceLocation(id.getNamespace(), id.getKey())); + + ResourceKey oldKey = Biomes.FOREST; + WritableRegistry registrywritable = (WritableRegistry) dedicatedserver.registryAccess().registryOrThrow(Registries.BIOME); + + try { + FIELD_MAPPED_REGISTRY_FROZEN.set(registrywritable, false); + FIELD_MAPPED_REGISTRY_UNREGISTERED_INTRUSIVE_HOLDERS.set(registrywritable, new IdentityHashMap<>()); + } catch (IllegalArgumentException | IllegalAccessException e) { + throw new RuntimeException(e); + } + + Biome forestbiome = registrywritable.get(oldKey); + + BiomeBuilder builder = new Biome.BiomeBuilder(); + builder.downfall(downfall); + builder.temperature(temperature); + boolean mojangPrecipitation = false; + + if (precipitation == de.cubeside.nmsutils.biome.Precipitation.RAIN) { + mojangPrecipitation = true; + } else if (precipitation == de.cubeside.nmsutils.biome.Precipitation.SNOW) { + mojangPrecipitation = true; + } + builder.hasPrecipitation(mojangPrecipitation); + builder.mobSpawnSettings(forestbiome.getMobSettings()); + builder.generationSettings(forestbiome.getGenerationSettings()); + builder.temperatureAdjustment(TemperatureModifier.NONE); + Builder effects = new BiomeSpecialEffects.Builder(); + effects.waterColor(waterColor == null ? forestbiome.getWaterColor() : waterColor); + effects.waterFogColor(waterFogColor == null ? forestbiome.getWaterFogColor() : waterFogColor); + effects.fogColor(fogColor == null ? forestbiome.getFogColor() : fogColor); + effects.skyColor(skyColor == null ? forestbiome.getSkyColor() : skyColor); + if (foliageColor != null) { + effects.foliageColorOverride(foliageColor); + } + if (grassColor != null) { + effects.grassColorOverride(grassColor); + } + effects.ambientMoodSound(AmbientMoodSettings.LEGACY_CAVE_SETTINGS); + builder.specialEffects(effects.build()); + + Biome newbiome = builder.build(); + + registrywritable.createIntrusiveHolder(newbiome); + Reference biomeHolder = registrywritable.register(newKey, newbiome, Lifecycle.stable()); + + try { + FIELD_MAPPED_REGISTRY_UNREGISTERED_INTRUSIVE_HOLDERS.set(registrywritable, null); + } catch (IllegalArgumentException | IllegalAccessException e) { + throw new RuntimeException(e); + } + CustomBiomeImpl impl = new CustomBiomeImpl(id, newKey, newbiome, biomeHolder); + customBiomes.put(id, impl); + customBiomesByBiome.put(newbiome, impl); + + return new CustomBiomeImpl(id, newKey, newbiome, biomeHolder); + } + + @Override + public Collection getAllCustomBiomes() { + return unmodifiableCustomBiomes; + } + + @Override + public CustomBiome getCustomBiome(NamespacedKey id) { + return customBiomes.get(id); + } + + @Override + public CustomBiome getCustomBiomeAt(Location location) { + location.getWorld().getChunkAt(location); + Level level = ((CraftWorld) location.getWorld()).getHandle(); + int x = location.getBlockX(); + int y = location.getBlockY(); + int z = location.getBlockZ(); + + BlockPos pos = new BlockPos(x, 0, z); + if (level.isLoaded(pos)) { + LevelChunk chunk = level.getChunkAt(pos); + if (chunk != null) { + + Holder biomeHolder = chunk.getNoiseBiome(x >> 2, y >> 2, z >> 2); + if (biomeHolder.isBound()) { + Biome biome = biomeHolder.value(); + return customBiomesByBiome.get(biome); + } + } + } + return null; + } +} diff --git a/nmsutils-v1_20_R1/src/main/java/de/cubeside/nmsutils/v1_20_R1/CustomBiomeImpl.java b/nmsutils-v1_20_R1/src/main/java/de/cubeside/nmsutils/v1_20_R1/CustomBiomeImpl.java new file mode 100644 index 0000000..593db26 --- /dev/null +++ b/nmsutils-v1_20_R1/src/main/java/de/cubeside/nmsutils/v1_20_R1/CustomBiomeImpl.java @@ -0,0 +1,49 @@ +package de.cubeside.nmsutils.v1_20_R1; + +import de.cubeside.nmsutils.biome.CustomBiome; +import net.minecraft.core.BlockPos.MutableBlockPos; +import net.minecraft.core.Holder.Reference; +import net.minecraft.resources.ResourceKey; +import net.minecraft.world.level.Level; +import net.minecraft.world.level.biome.Biome; +import net.minecraft.world.level.chunk.LevelChunk; +import org.bukkit.Location; +import org.bukkit.NamespacedKey; +import org.bukkit.craftbukkit.v1_20_R1.CraftWorld; + +public class CustomBiomeImpl implements CustomBiome { + private NamespacedKey bukkitKey; + private Reference biomeHolder; + private MutableBlockPos pos = new MutableBlockPos(); + + public CustomBiomeImpl(NamespacedKey bukkitKey, ResourceKey key, Biome biome, Reference biomeHolder) { + this.bukkitKey = bukkitKey; + this.biomeHolder = biomeHolder; + } + + @Override + public NamespacedKey getId() { + return bukkitKey; + } + + @Override + public boolean setBiome(Location location) { + location.getWorld().getChunkAt(location); + Level level = ((CraftWorld) location.getWorld()).getHandle(); + int x = location.getBlockX(); + int y = location.getBlockY(); + int z = location.getBlockZ(); + + pos.set(x, 0, z); + if (level.isLoaded(pos)) { + LevelChunk chunk = level.getChunkAt(pos); + if (chunk != null) { + + chunk.setBiome(x >> 2, y >> 2, z >> 2, biomeHolder); + chunk.setUnsaved(true); + return true; + } + } + return false; + } +} diff --git a/nmsutils-v1_20_R1/src/main/java/de/cubeside/nmsutils/v1_20_R1/EntityUtilsImpl.java b/nmsutils-v1_20_R1/src/main/java/de/cubeside/nmsutils/v1_20_R1/EntityUtilsImpl.java new file mode 100644 index 0000000..e5c60f6 --- /dev/null +++ b/nmsutils-v1_20_R1/src/main/java/de/cubeside/nmsutils/v1_20_R1/EntityUtilsImpl.java @@ -0,0 +1,345 @@ +package de.cubeside.nmsutils.v1_20_R1; + +import com.destroystokyo.paper.entity.ai.VanillaGoal; +import de.cubeside.nmsutils.EntityUtils; +import de.cubeside.nmsutils.NMSUtils; +import java.util.function.Function; +import java.util.logging.Level; +import net.minecraft.core.BlockPos; +import net.minecraft.network.protocol.game.ClientboundTeleportEntityPacket; +import net.minecraft.server.level.ChunkMap; +import net.minecraft.world.entity.Entity; +import net.minecraft.world.entity.LivingEntity; +import net.minecraft.world.entity.MoverType; +import net.minecraft.world.entity.PathfinderMob; +import net.minecraft.world.entity.ai.goal.FloatGoal; +import net.minecraft.world.entity.animal.Wolf; +import net.minecraft.world.entity.animal.camel.Camel; +import net.minecraft.world.level.ChunkPos; +import net.minecraft.world.phys.Vec3; +import org.bukkit.Bukkit; +import org.bukkit.Chunk; +import org.bukkit.Location; +import org.bukkit.World; +import org.bukkit.craftbukkit.v1_20_R1.CraftWorld; +import org.bukkit.craftbukkit.v1_20_R1.entity.CraftBat; +import org.bukkit.craftbukkit.v1_20_R1.entity.CraftCamel; +import org.bukkit.craftbukkit.v1_20_R1.entity.CraftCreature; +import org.bukkit.craftbukkit.v1_20_R1.entity.CraftEntity; +import org.bukkit.craftbukkit.v1_20_R1.entity.CraftMob; +import org.bukkit.craftbukkit.v1_20_R1.entity.CraftPiglin; +import org.bukkit.craftbukkit.v1_20_R1.entity.CraftRaider; +import org.bukkit.craftbukkit.v1_20_R1.entity.CraftShulker; +import org.bukkit.craftbukkit.v1_20_R1.entity.CraftVex; +import org.bukkit.craftbukkit.v1_20_R1.entity.CraftWolf; +import org.bukkit.entity.Bat; +import org.bukkit.entity.Creature; +import org.bukkit.entity.EntityType; +import org.bukkit.entity.Mob; +import org.bukkit.entity.Pose; +import org.bukkit.entity.Vex; +import org.bukkit.util.Vector; + +public class EntityUtilsImpl implements EntityUtils { + private final NMSUtilsImpl nmsUtils; + + public EntityUtilsImpl(NMSUtilsImpl nmsUtils) { + this.nmsUtils = nmsUtils; + } + + @Override + public NMSUtils getNMSUtils() { + return nmsUtils; + } + + @Override + public void clearAI(org.bukkit.entity.Entity entity) { + if (entity instanceof Mob) { + nmsUtils.getPlugin().getServer().getMobGoals().removeAllGoals((Mob) entity); + } + } + + @Override + public void addGoalFloat(Mob mob) { + if (!Bukkit.getMobGoals().hasGoal(mob, VanillaGoal.FLOAT)) { + net.minecraft.world.entity.Mob h = ((CraftMob) mob).getHandle(); + h.goalSelector.addGoal(1, new FloatGoal(h)); + } + } + + @Override + public void addGoalLimitedStrollLand(Creature mob, double velocity, Function checkTargetFunction) { + PathfinderMob h = ((CraftCreature) mob).getHandle(); + h.goalSelector.addGoal(7, new PathfinderGoalLimitedRandomStrollLand(h, velocity, checkTargetFunction)); + } + + @Override + public int getShulkerOpenState(org.bukkit.entity.Entity shulker) { + try { + if (shulker.getType() != EntityType.SHULKER) { + return 0; + } + CraftShulker cs = (CraftShulker) shulker; + return cs.getHandle().getRawPeekAmount(); + } catch (Exception e) { + nmsUtils.getPlugin().getLogger().log(Level.SEVERE, "Could not get shulker open state", e); + } + return 0; + } + + @Override + public void setShulkerOpenState(org.bukkit.entity.Entity shulker, int state) { + try { + if (shulker.getType() != EntityType.SHULKER) { + return; + } + state = Math.max(0, Math.min(Byte.MAX_VALUE, state)); + CraftShulker cs = (CraftShulker) shulker; + cs.getHandle().setRawPeekAmount(state); + } catch (Exception e) { + nmsUtils.getPlugin().getLogger().log(Level.SEVERE, "Could not set shulker open state", e); + } + } + + @Override + public boolean isPiglinDancing(org.bukkit.entity.Entity piglin) { + try { + if (piglin.getType() != EntityType.PIGLIN) { + return false; + } + CraftPiglin cs = (CraftPiglin) piglin; + return cs.getHandle().isDancing(); + } catch (Exception e) { + nmsUtils.getPlugin().getLogger().log(Level.SEVERE, "Could not get piglin dancing state", e); + } + return false; + } + + @Override + public void setPiglinDancing(org.bukkit.entity.Entity piglin, boolean dancing) { + try { + if (piglin.getType() != EntityType.PIGLIN) { + return; + } + CraftPiglin cs = (CraftPiglin) piglin; + cs.getHandle().setDancing(dancing); + } catch (Exception e) { + nmsUtils.getPlugin().getLogger().log(Level.SEVERE, "Could not set piglin dancing state", e); + } + } + + @Override + public void sendEntityPositionUpdate(org.bukkit.entity.Entity entity) { + Entity handle = ((CraftEntity) entity).getHandle(); + ChunkMap.TrackedEntity ete = handle.tracker; + if (ete != null) { + ClientboundTeleportEntityPacket positionPacket = new ClientboundTeleportEntityPacket(handle); + ete.seenBy.stream().forEach(viewer -> { + viewer.send(positionPacket); + }); + } + } + + @Override + public void moveEntity(org.bukkit.entity.Entity e, double x, double y, double z) { + Entity handle = ((CraftEntity) e).getHandle(); + handle.move(MoverType.SELF, new Vec3(x, y, z)); + } + + @Override + public void moveEntity(org.bukkit.entity.Entity e, Vector v) { + moveEntity(e, v.getX(), v.getY(), v.getZ()); + } + + @Override + public void setEntityHeadRotation(org.bukkit.entity.Entity e, float headRotation) { + Entity handle = ((CraftEntity) e).getHandle(); + if (handle instanceof LivingEntity) { + // required for goats + ((LivingEntity) handle).yHeadRot = headRotation; + } else { + handle.setYHeadRot(headRotation); + } + } + + @Override + public float getEntityHeadRotation(org.bukkit.entity.Entity e) { + Entity handle = ((CraftEntity) e).getHandle(); + return handle.getYHeadRot(); + } + + @Override + public void setEntityYaw(org.bukkit.entity.Entity e, float yaw) { + Entity handle = ((CraftEntity) e).getHandle(); + handle.setYRot(yaw); + } + + @Override + public float getEntityYaw(org.bukkit.entity.Entity e) { + Entity handle = ((CraftEntity) e).getHandle(); + return handle.getYRot(); + } + + @Override + public void setEntityPitch(org.bukkit.entity.Entity e, float pitch) { + Entity handle = ((CraftEntity) e).getHandle(); + handle.setXRot(pitch); + } + + @Override + public float getEntityPitch(org.bukkit.entity.Entity e) { + Entity handle = ((CraftEntity) e).getHandle(); + return handle.getXRot(); + } + + @Override + public void setEntityNavigationTarget(org.bukkit.entity.Entity entity, Location target, double speed) { + if (entity instanceof Bat) { + ((CraftBat) entity).getHandle().targetPosition = new BlockPos(target.getBlockX(), target.getBlockY(), target.getBlockZ()); + } else if (entity instanceof Vex) { + net.minecraft.world.entity.monster.Vex entityVex = ((CraftVex) entity).getHandle(); + entityVex.getMoveControl().setWantedPosition(target.getX(), target.getY(), target.getZ(), speed); + if (entityVex.getTarget() == null) { + entityVex.getLookControl().setLookAt(target.getX(), target.getY(), target.getZ(), 180, 20); + } + } else if (entity instanceof CraftMob) { + ((CraftMob) entity).getHandle().getNavigation().moveTo(target.getX(), target.getY(), target.getZ(), speed); + } else { + throw new IllegalArgumentException("Cannot set the navigation target for this mob: " + (entity == null ? "null" : entity.getType())); + } + } + + @Override + public void setEntityLeftHanded(org.bukkit.entity.Entity ent, boolean left) { + Entity nmsEntity = ((CraftEntity) ent).getHandle(); + if (nmsEntity instanceof net.minecraft.world.entity.Mob mob) { + mob.setLeftHanded(left); + } + } + + @Override + public boolean isEntityLeftHanded(org.bukkit.entity.Entity ent) { + Entity nmsEntity = ((CraftEntity) ent).getHandle(); + if (nmsEntity instanceof net.minecraft.world.entity.Mob mob) { + return mob.isLeftHanded(); + } + return false; + } + + @Override + public boolean isEntityInvisible(org.bukkit.entity.Entity entity) { + Entity nmsEntity = ((CraftEntity) entity).getHandle(); + return nmsEntity.isInvisible(); + } + + @Override + public void setEntityInvisible(org.bukkit.entity.Entity entity, boolean invisible) { + Entity nmsEntity = ((CraftEntity) entity).getHandle(); + nmsEntity.setInvisible(invisible); + } + + @Override + public boolean hasEntityNoClip(org.bukkit.entity.Entity entity) { + Entity nmsEntity = ((CraftEntity) entity).getHandle(); + return nmsEntity.noPhysics; + } + + @Override + public void setEntityNoClip(org.bukkit.entity.Entity entity, boolean noClip) { + Entity nmsEntity = ((CraftEntity) entity).getHandle(); + nmsEntity.noPhysics = noClip; + } + + @Override + public boolean areChunkEntitiesLoaded(Chunk c) { + return ((CraftWorld) c.getWorld()).getHandle().areEntitiesLoaded(ChunkPos.asLong(c.getX(), c.getZ())); + } + + @Override + public void loadChunkEntities(Chunk c) { + nmsUtils.getPlugin().getLogger().log(Level.SEVERE, "Call to unimplemented method", new RuntimeException()); + // int x = c.getX(); + // int z = c.getZ(); + // World world = c.getWorld(); + // if (!world.isChunkLoaded(x, z)) { + // world.getChunkAt(x, z); + // } + // + // if (areChunkEntitiesLoaded(c)) { + // return; + // } + // ServerLevel serverLevel = ((CraftWorld) world).getHandle(); + // serverLevel.entityManager.updateChunkStatus(new ChunkPos(x, z), Visibility.TRACKED); + // // now wait until entities are loaded + // if (!areChunkEntitiesLoaded(c)) { + // serverLevel.getServer().managedBlock(() -> { + // if (areChunkEntitiesLoaded(c)) { + // return true; + // } + // // process chunk inbox + // serverLevel.entityManager.tick(); + // return areChunkEntitiesLoaded(c); + // }); + // } + } + + @Override + public void setOnGround(org.bukkit.entity.Entity entity, boolean onGround) { + Entity nmsEntity = ((CraftEntity) entity).getHandle(); + nmsEntity.setOnGround(onGround); + } + + @Override + public org.bukkit.entity.Entity getEntityById(World world, int id) { + Entity entity = ((CraftWorld) world).getHandle().getEntity(id); + return entity == null ? null : entity.getBukkitEntity(); + } + + @Override + public void setPose(org.bukkit.entity.Entity entity, Pose pose) { + Entity nmsEntity = ((CraftEntity) entity).getHandle(); + nmsEntity.setPose(net.minecraft.world.entity.Pose.values()[pose.ordinal()]); + } + + @Override + public void setWolfAngerTime(org.bukkit.entity.Wolf entity, int timeInTicks) { + Wolf nmsEntity = ((CraftWolf) entity).getHandle(); + nmsEntity.setRemainingPersistentAngerTime(timeInTicks); + } + + @Override + public void setAggressive(org.bukkit.entity.Mob entity, boolean aggressive) { + net.minecraft.world.entity.Mob nmsEntity = ((CraftMob) entity).getHandle(); + nmsEntity.setAggressive(aggressive); + } + + @Override + public boolean isAggressive(org.bukkit.entity.Mob entity) { + net.minecraft.world.entity.Mob nmsEntity = ((CraftMob) entity).getHandle(); + return nmsEntity.isAggressive(); + } + + @Override + public boolean isCelebrating(org.bukkit.entity.Raider entity) { + net.minecraft.world.entity.raid.Raider nmsEntity = ((CraftRaider) entity).getHandle(); + return nmsEntity.isCelebrating(); + } + + @Override + public void setCelebrating(org.bukkit.entity.Raider entity, boolean celebrating) { + net.minecraft.world.entity.raid.Raider nmsEntity = ((CraftRaider) entity).getHandle(); + nmsEntity.setCelebrating(celebrating); + } + + @Override + public void setCamelLastPoseChange(org.bukkit.entity.Camel entity, long tick) { + Camel nmsEntity = ((CraftCamel) entity).getHandle(); + nmsEntity.resetLastPoseChangeTick(nmsEntity.level().getGameTime() - tick); + } + + @Override + public long getCamelLastPoseChange(org.bukkit.entity.Camel entity) { + Camel nmsEntity = ((CraftCamel) entity).getHandle(); + return nmsEntity.getPoseTime(); + } +} diff --git a/nmsutils-v1_20_R1/src/main/java/de/cubeside/nmsutils/v1_20_R1/MiscUtilsImpl.java b/nmsutils-v1_20_R1/src/main/java/de/cubeside/nmsutils/v1_20_R1/MiscUtilsImpl.java new file mode 100644 index 0000000..f023b96 --- /dev/null +++ b/nmsutils-v1_20_R1/src/main/java/de/cubeside/nmsutils/v1_20_R1/MiscUtilsImpl.java @@ -0,0 +1,82 @@ +package de.cubeside.nmsutils.v1_20_R1; + +import de.cubeside.nmsutils.MiscUtils; +import de.cubeside.nmsutils.NMSUtils; +import java.lang.reflect.Constructor; +import java.lang.reflect.Field; +import net.minecraft.world.level.block.Block; +import net.minecraft.world.level.block.state.BlockBehaviour; +import net.minecraft.world.level.block.state.BlockBehaviour.BlockStateBase; +import net.minecraft.world.level.block.state.BlockBehaviour.Properties; +import net.minecraft.world.level.block.state.BlockState; +import net.minecraft.world.level.material.MapColor; +import org.bukkit.Material; +import org.bukkit.craftbukkit.v1_20_R1.util.CraftMagicNumbers; + +public class MiscUtilsImpl implements MiscUtils { + private final NMSUtilsImpl nmsUtils; + + private Field fieldBlockBehaviour_properties; + private MapColor transparentColor; + private Field fieldBlockStateBase_materialColor; + + public MiscUtilsImpl(NMSUtilsImpl nmsUtils) { + this.nmsUtils = nmsUtils; + } + + @Override + public NMSUtils getNMSUtils() { + return nmsUtils; + } + + @Override + public void setBlockMapColorTransparent(Material m) { + if (fieldBlockBehaviour_properties == null) { + Class classBlockProperties = BlockBehaviour.Properties.class; + Class classBlockBehaviour = BlockBehaviour.class; + fieldBlockBehaviour_properties = null; + for (Field f : classBlockBehaviour.getDeclaredFields()) { + if (f.getType() == classBlockProperties) { + fieldBlockBehaviour_properties = f; + fieldBlockBehaviour_properties.setAccessible(true); + } + } + if (fieldBlockBehaviour_properties == null) { + throw new IllegalStateException("Could not find block properties field!"); + } + + Class classBlockStateBase = BlockBehaviour.BlockStateBase.class; + for (Field f : classBlockStateBase.getDeclaredFields()) { + if (f.getType() == MapColor.class) { + fieldBlockStateBase_materialColor = f; + fieldBlockStateBase_materialColor.setAccessible(true); + } + } + if (fieldBlockStateBase_materialColor == null) { + throw new IllegalStateException("Could not find BlockStateBase materialColor field!"); + } + + try { + Constructor constructorMaterialColor = MapColor.class.getDeclaredConstructor(int.class, int.class); + constructorMaterialColor.setAccessible(true); + transparentColor = constructorMaterialColor.newInstance(0, 0); + } catch (ReflectiveOperationException e) { + throw new IllegalStateException("Could not create custom transparent MaterialColor!"); + } + } + + if (!m.isBlock()) { + throw new IllegalArgumentException("Material must be a block"); + } + Block b = CraftMagicNumbers.getBlock(m); + try { + BlockBehaviour.Properties properties = (Properties) fieldBlockBehaviour_properties.get(b); + properties.mapColor(transparentColor); + for (BlockState state : b.getStateDefinition().getPossibleStates()) { + fieldBlockStateBase_materialColor.set(state, transparentColor); + } + } catch (IllegalAccessException e) { + throw new IllegalStateException("Could not set the MaterialColor!"); + } + } +} diff --git a/nmsutils-v1_20_R1/src/main/java/de/cubeside/nmsutils/v1_20_R1/NMSUtilsImpl.java b/nmsutils-v1_20_R1/src/main/java/de/cubeside/nmsutils/v1_20_R1/NMSUtilsImpl.java new file mode 100644 index 0000000..510f881 --- /dev/null +++ b/nmsutils-v1_20_R1/src/main/java/de/cubeside/nmsutils/v1_20_R1/NMSUtilsImpl.java @@ -0,0 +1,49 @@ +package de.cubeside.nmsutils.v1_20_R1; + +import de.cubeside.nmsutils.BiomeUtils; +import de.cubeside.nmsutils.EntityUtils; +import de.cubeside.nmsutils.MiscUtils; +import de.cubeside.nmsutils.NMSUtils; +import de.cubeside.nmsutils.WorldUtils; +import org.bukkit.plugin.Plugin; + +public class NMSUtilsImpl implements NMSUtils { + private final Plugin plugin; + private EntityUtils entityUtilsImpl; + private WorldUtilsImpl worldUtilsImpl; + private MiscUtilsImpl miscUtilsImpl; + private BiomeUtilsImpl biomeUtils; + + public NMSUtilsImpl(Plugin plugin) { + this.plugin = plugin; + this.entityUtilsImpl = new EntityUtilsImpl(this); + this.worldUtilsImpl = new WorldUtilsImpl(this); + this.miscUtilsImpl = new MiscUtilsImpl(this); + this.biomeUtils = new BiomeUtilsImpl(this); + } + + @Override + public Plugin getPlugin() { + return plugin; + } + + @Override + public EntityUtils getEntityUtils() { + return entityUtilsImpl; + } + + @Override + public WorldUtils getWorldUtils() { + return worldUtilsImpl; + } + + @Override + public MiscUtils getMiscUtils() { + return miscUtilsImpl; + } + + @Override + public BiomeUtils getBiomeUtils() { + return biomeUtils; + } +} diff --git a/nmsutils-v1_20_R1/src/main/java/de/cubeside/nmsutils/v1_20_R1/PathfinderGoalLimitedRandomStrollLand.java b/nmsutils-v1_20_R1/src/main/java/de/cubeside/nmsutils/v1_20_R1/PathfinderGoalLimitedRandomStrollLand.java new file mode 100644 index 0000000..589fee8 --- /dev/null +++ b/nmsutils-v1_20_R1/src/main/java/de/cubeside/nmsutils/v1_20_R1/PathfinderGoalLimitedRandomStrollLand.java @@ -0,0 +1,28 @@ +package de.cubeside.nmsutils.v1_20_R1; + +import java.util.function.Function; +import net.minecraft.world.entity.PathfinderMob; +import net.minecraft.world.entity.ai.goal.RandomStrollGoal; +import net.minecraft.world.phys.Vec3; +import org.bukkit.util.Vector; + +public class PathfinderGoalLimitedRandomStrollLand extends RandomStrollGoal { + + private Function checkVectorFunction; + // private EntityCreature entity; + + public PathfinderGoalLimitedRandomStrollLand(PathfinderMob entity, double velocity, Function checkVectorFunction) { + super(entity, velocity, 1, false); + // this.entity = entity; + this.checkVectorFunction = checkVectorFunction; + } + + @Override + protected Vec3 getPosition() { + Vec3 base = super.getPosition(); + if (base == null || checkVectorFunction.apply(org.bukkit.craftbukkit.v1_20_R1.util.CraftVector.toBukkit(base)) != Boolean.TRUE) { + base = null; + } + return base; + } +} diff --git a/nmsutils-v1_20_R1/src/main/java/de/cubeside/nmsutils/v1_20_R1/WorldUtilsImpl.java b/nmsutils-v1_20_R1/src/main/java/de/cubeside/nmsutils/v1_20_R1/WorldUtilsImpl.java new file mode 100644 index 0000000..39e294d --- /dev/null +++ b/nmsutils-v1_20_R1/src/main/java/de/cubeside/nmsutils/v1_20_R1/WorldUtilsImpl.java @@ -0,0 +1,98 @@ +package de.cubeside.nmsutils.v1_20_R1; + +import de.cubeside.nmsutils.WorldUtils; +import io.papermc.paper.chunk.system.io.RegionFileIOThread; +import io.papermc.paper.chunk.system.scheduling.NewChunkHolder; +import java.util.ArrayList; +import java.util.logging.Level; +import net.minecraft.server.level.ServerLevel; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.world.level.chunk.ChunkStatus; +import net.minecraft.world.level.chunk.LevelChunk; +import org.bukkit.Bukkit; +import org.bukkit.Chunk; +import org.bukkit.Location; +import org.bukkit.World; +import org.bukkit.craftbukkit.v1_20_R1.CraftChunk; +import org.bukkit.craftbukkit.v1_20_R1.CraftWorld; +import org.bukkit.entity.Player; + +public class WorldUtilsImpl implements WorldUtils { + private final NMSUtilsImpl nmsUtils; + + public WorldUtilsImpl(NMSUtilsImpl nmsUtils) { + this.nmsUtils = nmsUtils; + } + + @Override + public void saveWorldNow(World world) { + world.save(); + + CraftWorld craftWorld = (CraftWorld) world; + ServerLevel handle = craftWorld.getHandle(); + try { + handle.save(null, true, false); + } catch (Exception e) { + Bukkit.getLogger().log(Level.SEVERE, "Exception while saving world", e); + } + } + + @Override + public void forceUnloadWorldWithoutSaving(World world, Location playerTarget) { + // final long t0 = System.currentTimeMillis(); + // String worldName = world.getName(); + + if (playerTarget.getWorld() == null || playerTarget.getWorld() == world) { + throw new IllegalArgumentException("Valid target world required"); + } + // move players out of this world + try { + for (Player p : world.getPlayers()) { + if (p.isDead()) { + p.spigot().respawn(); + } + if (p.getWorld() == world) { + p.teleport(playerTarget); + } + } + } catch (Exception e) { + e.printStackTrace(); + } + // remove broken players + CraftWorld craftWorld = (CraftWorld) world; + ServerLevel handle = craftWorld.getHandle(); + if (!handle.players().isEmpty()) { + for (ServerPlayer human : new ArrayList<>(handle.players())) { + kickPlayer((human.getBukkitEntity()), "Connection lost"); + } + handle.players().clear(); + } + + // unload the now empty world from the server + if (!nmsUtils.getPlugin().getServer().unloadWorld(world, false)) { + throw new IllegalStateException("Could not unload world"); + } + + // long t = System.currentTimeMillis(); + // nmsUtils.getPlugin().getLogger().info("Unloading world " + worldName + " completed in " + (t - t0) + "ms."); + } + + @Override + public void saveChunkNow(Chunk chunk) { + CraftChunk craftChunk = (CraftChunk) chunk; + if (craftChunk.getHandle(ChunkStatus.FULL) instanceof LevelChunk levelChunk) { + NewChunkHolder holder = levelChunk.getChunkHolder(); + holder.save(false, false); + } + } + + @Override + public void flushChunkSaves() { + RegionFileIOThread.flush(); + } + + @SuppressWarnings("deprecation") + private void kickPlayer(Player player, String message) { + player.kickPlayer(message); + } +} diff --git a/nmsutils/pom.xml b/nmsutils/pom.xml index c9575bc..adc4771 100644 --- a/nmsutils/pom.xml +++ b/nmsutils/pom.xml @@ -15,6 +15,22 @@ + + v1_20 + + true + + all + + + + + ${project.groupId} + nmsutils-v1_20_R1 + ${project.version} + + + v1_19_4 @@ -34,7 +50,6 @@ v1_19 - true all diff --git a/pom.xml b/pom.xml index a22d02b..685d16f 100644 --- a/pom.xml +++ b/pom.xml @@ -24,6 +24,18 @@ + + v1_20 + + true + + all + + + + nmsutils-v1_20_R1 + + v1_19_4 @@ -39,7 +51,6 @@ v1_19 - true all