some more stuff

This commit is contained in:
Alexander Medvedev 2026-07-26 11:36:48 +02:00
parent 5c00ff0b67
commit f347ddfdab
25 changed files with 1520 additions and 433 deletions

View File

@ -15,6 +15,7 @@ import java.awt.image.BufferedImage;
import java.io.File;
import java.net.InetAddress;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Iterator;
@ -22,6 +23,7 @@ import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.UUID;
import org.patchbukkit.world.PatchBukkitWorld;
import java.util.function.Consumer;
import java.util.logging.Level;
import java.util.logging.Logger;
@ -547,10 +549,13 @@ public class PatchBukkitServer implements Server {
@Override
public @NotNull List<World> getWorlds() {
// TODO Auto-generated method stub
throw new UnsupportedOperationException(
"Unimplemented method 'getWorlds'"
);
var response = NativeBridgeFfi.getWorlds(patchbukkit.common.EmptyRequest.newBuilder().build());
if (response == null) return List.of();
List<World> list = new ArrayList<>();
for (patchbukkit.common.UUID u : response.getWorldUuidsList()) {
list.add(PatchBukkitWorld.getOrCreate(u.getValue()));
}
return list;
}
@Override
@ -587,7 +592,10 @@ public class PatchBukkitServer implements Server {
@Override
public @NotNull World getRespawnWorld() {
// TODO Auto-generated method stub
var worlds = getWorlds();
if (!worlds.isEmpty()) {
return worlds.get(0);
}
throw new UnsupportedOperationException(
"Unimplemented method 'getRespawnWorld'"
);
@ -603,26 +611,32 @@ public class PatchBukkitServer implements Server {
@Override
public @Nullable World getWorld(@NotNull String name) {
// TODO Auto-generated method stub
throw new UnsupportedOperationException(
"Unimplemented method 'getWorld'"
);
for (World world : getWorlds()) {
if (world.getName().equalsIgnoreCase(name)) {
return world;
}
}
return null;
}
@Override
public @Nullable World getWorld(@NotNull UUID uid) {
// TODO Auto-generated method stub
throw new UnsupportedOperationException(
"Unimplemented method 'getWorld'"
);
for (World world : getWorlds()) {
if (world.getUID().equals(uid)) {
return world;
}
}
return null;
}
@Override
public @Nullable World getWorld(@NotNull Key worldKey) {
// TODO Auto-generated method stub
throw new UnsupportedOperationException(
"Unimplemented method 'getWorld'"
);
for (World world : getWorlds()) {
if (world.getKey().equals(worldKey)) {
return world;
}
}
return null;
}
@Override
@ -686,6 +700,10 @@ public class PatchBukkitServer implements Server {
);
}
public int getReloadCount() {
return 0;
}
@Override
public void reloadData() {
// TODO Auto-generated method stub
@ -1297,10 +1315,7 @@ public class PatchBukkitServer implements Server {
@Override
public @NotNull WarningState getWarningState() {
// TODO Auto-generated method stub
throw new UnsupportedOperationException(
"Unimplemented method 'getWarningState'"
);
return WarningState.DEFAULT;
}
@Override

View File

@ -10,4 +10,19 @@ public class BridgeUtils {
public static patchbukkit.common.UUID convertUuid(UUID uuid) {
return patchbukkit.common.UUID.newBuilder().setValue(uuid.toString()).build();
}
public static patchbukkit.common.Location convertLocation(org.bukkit.Location location) {
var pos = patchbukkit.common.Vec3.newBuilder()
.setX(location.getX())
.setY(location.getY())
.setZ(location.getZ())
.build();
var worldUuid = convertUuid(location.getWorld().getUID());
return patchbukkit.common.Location.newBuilder()
.setWorld(patchbukkit.common.World.newBuilder().setUuid(worldUuid).build())
.setPosition(pos)
.setYaw(location.getYaw())
.setPitch(location.getPitch())
.build();
}
}

View File

@ -1,8 +1,12 @@
package org.patchbukkit.entity;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Set;
import java.util.UUID;
import java.util.concurrent.CompletableFuture;
@ -50,13 +54,23 @@ import net.kyori.adventure.sound.Sound.Source;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.util.TriState;
import patchbukkit.bridge.NativeBridgeFfi;
import patchbukkit.entity.SetEntityVelocityRequest;
import patchbukkit.entity.TeleportEntityRequest;
public class PatchBukkitEntity implements Entity {
protected final UUID uuid;
private final String name;
private static PermissibleBase perm;
protected final String name;
private PermissibleBase perm;
private boolean visibleByDefault = true;
private final Map<String, List<MetadataValue>> metadataMap = new HashMap<>();
private PermissibleBase getPermissible() {
if (this.perm == null) {
this.perm = new PermissibleBase(this);
}
return this.perm;
}
public PatchBukkitEntity(
UUID uuid,
@ -66,66 +80,52 @@ public class PatchBukkitEntity implements Entity {
this.name = name;
}
private static PermissibleBase getPermissibleBase() {
if (PatchBukkitEntity.perm == null) {
PatchBukkitEntity.perm = new PermissibleBase(new ServerOperator() {
@Override
public boolean isOp() {
return false;
}
@Override
public void setOp(boolean value) {
}
});
}
return PatchBukkitEntity.perm;
}
@Override
public void setMetadata(@NotNull String metadataKey, @NotNull MetadataValue newMetadataValue) {
// TODO Auto-generated method stub
throw new UnsupportedOperationException("Unimplemented method 'setMetadata'");
List<MetadataValue> list = metadataMap.computeIfAbsent(metadataKey, k -> new ArrayList<>());
list.removeIf(v -> v.getOwningPlugin() == newMetadataValue.getOwningPlugin());
list.add(newMetadataValue);
}
@Override
public @NotNull List<MetadataValue> getMetadata(@NotNull String metadataKey) {
// TODO Auto-generated method stub
throw new UnsupportedOperationException("Unimplemented method 'getMetadata'");
List<MetadataValue> list = metadataMap.get(metadataKey);
return list != null ? Collections.unmodifiableList(new ArrayList<>(list)) : Collections.emptyList();
}
@Override
public boolean hasMetadata(@NotNull String metadataKey) {
// TODO Auto-generated method stub
throw new UnsupportedOperationException("Unimplemented method 'hasMetadata'");
List<MetadataValue> list = metadataMap.get(metadataKey);
return list != null && !list.isEmpty();
}
@Override
public void removeMetadata(@NotNull String metadataKey, @NotNull Plugin owningPlugin) {
// TODO Auto-generated method stub
throw new UnsupportedOperationException("Unimplemented method 'removeMetadata'");
List<MetadataValue> list = metadataMap.get(metadataKey);
if (list != null) {
list.removeIf(v -> v.getOwningPlugin() == owningPlugin);
if (list.isEmpty()) {
metadataMap.remove(metadataKey);
}
}
}
@Override
public void sendMessage(String message) {
}
@Override
public void sendMessage(String... messages) {
}
@Override
public void sendMessage(UUID sender, String message) {
this.sendMessage(message); // Most entities don't know about senders
this.sendMessage(message);
}
@Override
public void sendMessage(UUID sender, String... messages) {
this.sendMessage(messages); // Most entities don't know about senders
this.sendMessage(messages);
}
@Override
@ -135,83 +135,72 @@ public class PatchBukkitEntity implements Entity {
@Override
public @NotNull Component name() {
// TODO Auto-generated method stub
throw new UnsupportedOperationException("Unimplemented method 'name'");
return Component.text(this.name);
}
@Override
public boolean isPermissionSet(@NotNull String name) {
// TODO Auto-generated method stub
throw new UnsupportedOperationException("Unimplemented method 'isPermissionSet'");
return getPermissible().isPermissionSet(name);
}
@Override
public boolean isPermissionSet(@NotNull Permission perm) {
// TODO Auto-generated method stub
throw new UnsupportedOperationException("Unimplemented method 'isPermissionSet'");
return getPermissible().isPermissionSet(perm);
}
@Override
public boolean hasPermission(String name) {
return PatchBukkitEntity.getPermissibleBase().hasPermission(name);
return getPermissible().hasPermission(name);
}
@Override
public boolean hasPermission(Permission perm) {
return PatchBukkitEntity.getPermissibleBase().hasPermission(perm);
return getPermissible().hasPermission(perm);
}
@Override
public @NotNull PermissionAttachment addAttachment(@NotNull Plugin plugin, @NotNull String name, boolean value) {
// TODO Auto-generated method stub
throw new UnsupportedOperationException("Unimplemented method 'addAttachment'");
return getPermissible().addAttachment(plugin, name, value);
}
@Override
public @NotNull PermissionAttachment addAttachment(@NotNull Plugin plugin) {
// TODO Auto-generated method stub
throw new UnsupportedOperationException("Unimplemented method 'addAttachment'");
return getPermissible().addAttachment(plugin);
}
@Override
public @Nullable PermissionAttachment addAttachment(@NotNull Plugin plugin, @NotNull String name, boolean value,
int ticks) {
// TODO Auto-generated method stub
throw new UnsupportedOperationException("Unimplemented method 'addAttachment'");
public @Nullable PermissionAttachment addAttachment(@NotNull Plugin plugin, @NotNull String name, boolean value, int ticks) {
return getPermissible().addAttachment(plugin, name, value, ticks);
}
@Override
public @Nullable PermissionAttachment addAttachment(@NotNull Plugin plugin, int ticks) {
// TODO Auto-generated method stub
throw new UnsupportedOperationException("Unimplemented method 'addAttachment'");
return getPermissible().addAttachment(plugin, ticks);
}
@Override
public void removeAttachment(@NotNull PermissionAttachment attachment) {
// TODO Auto-generated method stub
throw new UnsupportedOperationException("Unimplemented method 'removeAttachment'");
getPermissible().removeAttachment(attachment);
}
@Override
public void recalculatePermissions() {
// TODO Auto-generated method stub
throw new UnsupportedOperationException("Unimplemented method 'recalculatePermissions'");
getPermissible().recalculatePermissions();
}
@Override
public @NotNull Set<PermissionAttachmentInfo> getEffectivePermissions() {
// TODO Auto-generated method stub
throw new UnsupportedOperationException("Unimplemented method 'getEffectivePermissions'");
return getPermissible().getEffectivePermissions();
}
@Override
public boolean isOp() {
return PatchBukkitEntity.getPermissibleBase().isOp();
return getPermissible().isOp();
}
@Override
public void setOp(boolean value) {
PatchBukkitEntity.getPermissibleBase().setOp(value);
getPermissible().setOp(value);
}
@Override
@ -282,16 +271,38 @@ public class PatchBukkitEntity implements Entity {
return newLoc;
}
private Vector velocity = new Vector(0, 0, 0);
@Override
public void setVelocity(@NotNull Vector velocity) {
// TODO Auto-generated method stub
throw new UnsupportedOperationException("Unimplemented method 'setVelocity'");
this.velocity = velocity.clone();
var request = SetEntityVelocityRequest.newBuilder()
.setUuid(BridgeUtils.convertUuid(this.uuid))
.setX(velocity.getX())
.setY(velocity.getY())
.setZ(velocity.getZ())
.build();
NativeBridgeFfi.setEntityVelocity(request);
}
@Override
public @NotNull Vector getVelocity() {
// TODO Auto-generated method stub
throw new UnsupportedOperationException("Unimplemented method 'getVelocity'");
return this.velocity.clone();
}
@Override
public boolean teleport(@NotNull Location location) {
var request = TeleportEntityRequest.newBuilder()
.setUuid(BridgeUtils.convertUuid(this.uuid))
.setLocation(BridgeUtils.convertLocation(location))
.build();
NativeBridgeFfi.teleportEntity(request);
return true;
}
@Override
public boolean teleport(@NotNull Location location, @NotNull TeleportCause cause) {
return teleport(location);
}
@Override
@ -332,33 +343,26 @@ public class PatchBukkitEntity implements Entity {
@Override
public void setRotation(float yaw, float pitch) {
// TODO Auto-generated method stub
throw new UnsupportedOperationException("Unimplemented method 'setRotation'");
Location loc = getLocation();
teleport(new Location(loc.getWorld(), loc.getX(), loc.getY(), loc.getZ(), yaw, pitch));
}
@Override
public void lookAt(double x, double y, double z, @NotNull LookAnchor entityAnchor) {
Location loc = getLocation();
double dx = x - loc.getX();
double dy = y - loc.getY();
double dz = z - loc.getZ();
double r = Math.sqrt(dx * dx + dz * dz);
float yaw = (float) Math.toDegrees(Math.atan2(-dx, dz));
float pitch = (float) Math.toDegrees(Math.atan2(-dy, r));
teleport(new Location(loc.getWorld(), loc.getX(), loc.getY(), loc.getZ(), yaw, pitch));
}
@Override
public boolean teleport(@NotNull Location location, @NotNull TeleportCause cause,
@NotNull TeleportFlag @NotNull... teleportFlags) {
// TODO Auto-generated method stub
throw new UnsupportedOperationException("Unimplemented method 'teleport'");
}
@Override
public void lookAt(double x, double y, double z, @NotNull LookAnchor entityAnchor) {
// TODO Auto-generated method stub
throw new UnsupportedOperationException("Unimplemented method 'lookAt'");
}
@Override
public boolean teleport(@NotNull Location location) {
// TODO Auto-generated method stub
throw new UnsupportedOperationException("Unimplemented method 'teleport'");
}
@Override
public boolean teleport(@NotNull Location location, @NotNull TeleportCause cause) {
// TODO Auto-generated method stub
throw new UnsupportedOperationException("Unimplemented method 'teleport'");
return teleport(location, cause);
}
@Override

View File

@ -1,7 +1,11 @@
package org.patchbukkit.entity;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.UUID;
import java.util.function.Consumer;
@ -35,8 +39,13 @@ import org.checkerframework.checker.index.qual.NonNegative;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.annotations.Range;
import org.patchbukkit.bridge.BridgeUtils;
import org.patchbukkit.PatchBukkitServer;
import patchbukkit.bridge.NativeBridgeFfi;
import patchbukkit.entity.DamageEntityRequest;
import patchbukkit.entity.SetEntityHealthRequest;
import com.destroystokyo.paper.block.TargetBlockInfo;
import com.destroystokyo.paper.block.TargetBlockInfo.FluidMode;
import com.destroystokyo.paper.entity.TargetEntityInfo;
@ -54,383 +63,415 @@ public class PatchBukkitLivingEntity
String name) {
super(uuid, name);
}
@Override
public @Nullable AttributeInstance getAttribute(@NotNull Attribute attribute) {
// TODO Auto-generated method stub
throw new UnsupportedOperationException("Unimplemented method 'getAttribute'");
}
@Override
public void registerAttribute(@NotNull Attribute attribute) {
// TODO Auto-generated method stub
throw new UnsupportedOperationException("Unimplemented method 'registerAttribute'");
}
@Override
public void damage(double amount) {
// TODO Auto-generated method stub
throw new UnsupportedOperationException("Unimplemented method 'damage'");
}
@Override
public void damage(double amount, @Nullable Entity source) {
// TODO Auto-generated method stub
throw new UnsupportedOperationException("Unimplemented method 'damage'");
}
@Override
public void damage(double amount, @NotNull DamageSource damageSource) {
// TODO Auto-generated method stub
throw new UnsupportedOperationException("Unimplemented method 'damage'");
}
@Override
public double getHealth() {
// TODO Auto-generated method stub
throw new UnsupportedOperationException("Unimplemented method 'getHealth'");
}
@Override
public void setHealth(double health) {
// TODO Auto-generated method stub
throw new UnsupportedOperationException("Unimplemented method 'setHealth'");
}
@Override
public void heal(double amount, @NotNull RegainReason reason) {
// TODO Auto-generated method stub
throw new UnsupportedOperationException("Unimplemented method 'heal'");
}
@Override
public double getAbsorptionAmount() {
// TODO Auto-generated method stub
throw new UnsupportedOperationException("Unimplemented method 'getAbsorptionAmount'");
}
@Override
public void setAbsorptionAmount(double amount) {
// TODO Auto-generated method stub
throw new UnsupportedOperationException("Unimplemented method 'setAbsorptionAmount'");
}
@Override
public double getMaxHealth() {
// TODO Auto-generated method stub
throw new UnsupportedOperationException("Unimplemented method 'getMaxHealth'");
}
@Override
public void setMaxHealth(double health) {
// TODO Auto-generated method stub
throw new UnsupportedOperationException("Unimplemented method 'setMaxHealth'");
}
@Override
public void resetMaxHealth() {
// TODO Auto-generated method stub
throw new UnsupportedOperationException("Unimplemented method 'resetMaxHealth'");
}
@Override
public <T extends Projectile> @NotNull T launchProjectile(@NotNull Class<? extends T> projectile,
@Nullable Vector velocity, @Nullable Consumer<? super T> function) {
// TODO Auto-generated method stub
throw new UnsupportedOperationException("Unimplemented method 'launchProjectile'");
}
@Override
public TriState getFrictionState() {
// TODO Auto-generated method stub
throw new UnsupportedOperationException("Unimplemented method 'getFrictionState'");
}
@Override
public void setFrictionState(TriState state) {
// TODO Auto-generated method stub
throw new UnsupportedOperationException("Unimplemented method 'setFrictionState'");
}
@Override
public double getEyeHeight() {
// TODO Auto-generated method stub
throw new UnsupportedOperationException("Unimplemented method 'getEyeHeight'");
return 1.62;
}
@Override
public double getEyeHeight(boolean ignorePose) {
// TODO Auto-generated method stub
throw new UnsupportedOperationException("Unimplemented method 'getEyeHeight'");
return 1.62;
}
@Override
public @NotNull Location getEyeLocation() {
// TODO Auto-generated method stub
throw new UnsupportedOperationException("Unimplemented method 'getEyeLocation'");
}
@Override
public @NotNull List<Block> getLineOfSight(@Nullable Set<Material> transparent, int maxDistance) {
// TODO Auto-generated method stub
throw new UnsupportedOperationException("Unimplemented method 'getLineOfSight'");
}
@Override
public @NotNull Block getTargetBlock(@Nullable Set<Material> transparent, int maxDistance) {
// TODO Auto-generated method stub
throw new UnsupportedOperationException("Unimplemented method 'getTargetBlock'");
}
@Override
public @Nullable Block getTargetBlock(int maxDistance, @NotNull FluidMode fluidMode) {
// TODO Auto-generated method stub
throw new UnsupportedOperationException("Unimplemented method 'getTargetBlock'");
}
@Override
public @Nullable BlockFace getTargetBlockFace(int maxDistance, @NotNull FluidMode fluidMode) {
// TODO Auto-generated method stub
throw new UnsupportedOperationException("Unimplemented method 'getTargetBlockFace'");
}
@Override
public @Nullable BlockFace getTargetBlockFace(int maxDistance, @NotNull FluidCollisionMode fluidMode) {
// TODO Auto-generated method stub
throw new UnsupportedOperationException("Unimplemented method 'getTargetBlockFace'");
}
@Override
public @Nullable TargetBlockInfo getTargetBlockInfo(int maxDistance, @NotNull FluidMode fluidMode) {
// TODO Auto-generated method stub
throw new UnsupportedOperationException("Unimplemented method 'getTargetBlockInfo'");
}
@Override
public @Nullable Entity getTargetEntity(int maxDistance, boolean ignoreBlocks) {
// TODO Auto-generated method stub
throw new UnsupportedOperationException("Unimplemented method 'getTargetEntity'");
}
@Override
public @Nullable TargetEntityInfo getTargetEntityInfo(int maxDistance, boolean ignoreBlocks) {
// TODO Auto-generated method stub
throw new UnsupportedOperationException("Unimplemented method 'getTargetEntityInfo'");
}
@Override
public @Nullable RayTraceResult rayTraceEntities(int maxDistance, boolean ignoreBlocks) {
// TODO Auto-generated method stub
throw new UnsupportedOperationException("Unimplemented method 'rayTraceEntities'");
}
@Override
public @NotNull List<Block> getLastTwoTargetBlocks(@Nullable Set<Material> transparent, int maxDistance) {
// TODO Auto-generated method stub
throw new UnsupportedOperationException("Unimplemented method 'getLastTwoTargetBlocks'");
}
@Override
public @Nullable Block getTargetBlockExact(int maxDistance, @NotNull FluidCollisionMode fluidCollisionMode) {
// TODO Auto-generated method stub
throw new UnsupportedOperationException("Unimplemented method 'getTargetBlockExact'");
return getLocation().add(0, getEyeHeight(), 0);
}
@Override
public @Nullable RayTraceResult rayTraceBlocks(double maxDistance, @NotNull FluidCollisionMode fluidCollisionMode) {
// TODO Auto-generated method stub
throw new UnsupportedOperationException("Unimplemented method 'rayTraceBlocks'");
return getLocation().getWorld().rayTraceBlocks(getEyeLocation(), getEyeLocation().getDirection(), maxDistance, fluidCollisionMode);
}
@Override
public int getRemainingAir() {
// TODO Auto-generated method stub
throw new UnsupportedOperationException("Unimplemented method 'getRemainingAir'");
public @Nullable Block getTargetBlockExact(int maxDistance, @NotNull FluidCollisionMode fluidCollisionMode) {
RayTraceResult result = rayTraceBlocks(maxDistance, fluidCollisionMode);
return result != null ? result.getHitBlock() : null;
}
@Override
public void setRemainingAir(int ticks) {
// TODO Auto-generated method stub
throw new UnsupportedOperationException("Unimplemented method 'setRemainingAir'");
public @NotNull Block getTargetBlock(@Nullable Set<Material> transparent, int maxDistance) {
Block hit = getTargetBlockExact(maxDistance, FluidCollisionMode.NEVER);
return hit != null ? hit : getLocation().getBlock();
}
@Override
public int getMaximumAir() {
// TODO Auto-generated method stub
throw new UnsupportedOperationException("Unimplemented method 'getMaximumAir'");
public @NotNull List<Block> getLineOfSight(@Nullable Set<Material> transparent, int maxDistance) {
List<Block> blocks = new ArrayList<>();
Block target = getTargetBlock(transparent, maxDistance);
if (target != null) {
blocks.add(target);
}
return blocks;
}
@Override
public void setMaximumAir(int ticks) {
// TODO Auto-generated method stub
throw new UnsupportedOperationException("Unimplemented method 'setMaximumAir'");
public @Nullable Block getTargetBlock(int maxDistance, @NotNull FluidMode fluidMode) {
return getTargetBlock(null, maxDistance);
}
@Override
public @Nullable ItemStack getItemInUse() {
// TODO Auto-generated method stub
throw new UnsupportedOperationException("Unimplemented method 'getItemInUse'");
public @NotNull List<Block> getLastTwoTargetBlocks(@Nullable Set<Material> transparent, int maxDistance) {
Block target = getTargetBlock(transparent, maxDistance);
return List.of(target, target);
}
@Override
public int getItemInUseTicks() {
// TODO Auto-generated method stub
throw new UnsupportedOperationException("Unimplemented method 'getItemInUseTicks'");
public @Nullable Entity getTargetEntity(int maxDistance, boolean ignoreBlocks) {
RayTraceResult result = rayTraceEntities(maxDistance, ignoreBlocks);
return result != null ? result.getHitEntity() : null;
}
@Override
public void setItemInUseTicks(int ticks) {
// TODO Auto-generated method stub
throw new UnsupportedOperationException("Unimplemented method 'setItemInUseTicks'");
public @Nullable TargetEntityInfo getTargetEntityInfo(int maxDistance, boolean ignoreBlocks) {
Entity entity = getTargetEntity(maxDistance, ignoreBlocks);
return entity != null ? new TargetEntityInfo(entity, entity.getLocation().toVector().subtract(getEyeLocation().toVector())) : null;
}
@Override
public @NonNegative int getArrowCooldown() {
// TODO Auto-generated method stub
throw new UnsupportedOperationException("Unimplemented method 'getArrowCooldown'");
public @Nullable TargetBlockInfo getTargetBlockInfo(int maxDistance, @NotNull FluidMode fluidMode) {
Block block = getTargetBlockExact(maxDistance, FluidCollisionMode.NEVER);
return block != null ? new TargetBlockInfo(block, BlockFace.UP) : null;
}
@Override
public void setArrowCooldown(@NonNegative int ticks) {
// TODO Auto-generated method stub
throw new UnsupportedOperationException("Unimplemented method 'setArrowCooldown'");
public @Nullable BlockFace getTargetBlockFace(int maxDistance, @NotNull FluidCollisionMode fluidCollisionMode) {
RayTraceResult result = rayTraceBlocks(maxDistance, fluidCollisionMode);
return result != null ? result.getHitBlockFace() : null;
}
@Override
public @NonNegative int getArrowsInBody() {
// TODO Auto-generated method stub
throw new UnsupportedOperationException("Unimplemented method 'getArrowsInBody'");
public @Nullable BlockFace getTargetBlockFace(int maxDistance, @NotNull FluidMode fluidMode) {
return getTargetBlockFace(maxDistance, FluidCollisionMode.NEVER);
}
@Override
public void setArrowsInBody(@NonNegative int count, boolean fireEvent) {
// TODO Auto-generated method stub
throw new UnsupportedOperationException("Unimplemented method 'setArrowsInBody'");
public @Nullable RayTraceResult rayTraceEntities(int maxDistance, boolean ignoreBlocks) {
return getLocation().getWorld().rayTraceEntities(getEyeLocation(), getEyeLocation().getDirection(), (double) maxDistance, (double) maxDistance);
}
private TriState frictionState = TriState.NOT_SET;
@Override
public TriState getFrictionState() {
return this.frictionState;
}
@Override
public @NonNegative int getBeeStingerCooldown() {
// TODO Auto-generated method stub
throw new UnsupportedOperationException("Unimplemented method 'getBeeStingerCooldown'");
public void setFrictionState(TriState state) {
this.frictionState = state != null ? state : TriState.NOT_SET;
}
private final Map<Attribute, AttributeInstance> attributes = new HashMap<>();
@Override
public @Nullable AttributeInstance getAttribute(@NotNull Attribute attribute) {
return attributes.get(attribute);
}
@Override
public void setBeeStingerCooldown(@NonNegative int ticks) {
// TODO Auto-generated method stub
throw new UnsupportedOperationException("Unimplemented method 'setBeeStingerCooldown'");
public void registerAttribute(@NotNull Attribute attribute) {
}
@Override
public @NonNegative int getBeeStingersInBody() {
// TODO Auto-generated method stub
throw new UnsupportedOperationException("Unimplemented method 'getBeeStingersInBody'");
public <T extends Projectile> @NotNull T launchProjectile(@NotNull Class<? extends T> projectileClass) {
return launchProjectile(projectileClass, null);
}
@Override
public void setBeeStingersInBody(@NonNegative int count) {
// TODO Auto-generated method stub
throw new UnsupportedOperationException("Unimplemented method 'setBeeStingersInBody'");
public <T extends Projectile> @NotNull T launchProjectile(@NotNull Class<? extends T> projectileClass, @Nullable Vector velocity) {
return launchProjectile(projectileClass, velocity, null);
}
@Override
public int getMaximumNoDamageTicks() {
// TODO Auto-generated method stub
throw new UnsupportedOperationException("Unimplemented method 'getMaximumNoDamageTicks'");
public <T extends Projectile> @NotNull T launchProjectile(@NotNull Class<? extends T> projectileClass, @Nullable Vector velocity, @Nullable Consumer<? super T> function) {
Location spawnLoc = getEyeLocation();
Vector dir = velocity != null ? velocity : spawnLoc.getDirection();
T entity = spawnLoc.getWorld().spawn(spawnLoc, projectileClass, function);
entity.setVelocity(dir);
entity.setShooter(this);
return entity;
}
@Override
public void setMaximumNoDamageTicks(int ticks) {
// TODO Auto-generated method stub
throw new UnsupportedOperationException("Unimplemented method 'setMaximumNoDamageTicks'");
}
@Override
public double getLastDamage() {
// TODO Auto-generated method stub
throw new UnsupportedOperationException("Unimplemented method 'getLastDamage'");
}
@Override
public void setLastDamage(double damage) {
// TODO Auto-generated method stub
throw new UnsupportedOperationException("Unimplemented method 'setLastDamage'");
}
@Override
public int getNoDamageTicks() {
// TODO Auto-generated method stub
throw new UnsupportedOperationException("Unimplemented method 'getNoDamageTicks'");
}
@Override
public void setNoDamageTicks(int ticks) {
// TODO Auto-generated method stub
throw new UnsupportedOperationException("Unimplemented method 'setNoDamageTicks'");
}
@Override
public int getNoActionTicks() {
// TODO Auto-generated method stub
throw new UnsupportedOperationException("Unimplemented method 'getNoActionTicks'");
}
@Override
public void setNoActionTicks(int ticks) {
// TODO Auto-generated method stub
throw new UnsupportedOperationException("Unimplemented method 'setNoActionTicks'");
}
private Player killer;
@Override
public @Nullable Player getKiller() {
// TODO Auto-generated method stub
throw new UnsupportedOperationException("Unimplemented method 'getKiller'");
return this.killer;
}
@Override
public void setKiller(@Nullable Player killer) {
// TODO Auto-generated method stub
throw new UnsupportedOperationException("Unimplemented method 'setKiller'");
this.killer = killer;
}
private double health = 20.0;
private double maxHealth = 20.0;
private double absorption = 0.0;
private int arrowsInBody = 0;
private int arrowCooldown = 0;
private int beeStingersInBody = 0;
private int beeStingerCooldown = 0;
@Override
public int getArrowsInBody() {
return this.arrowsInBody;
}
@Override
public void setArrowsInBody(int count) {
this.arrowsInBody = count;
}
@Override
public void setArrowsInBody(int count, boolean fireEvent) {
this.arrowsInBody = count;
}
@Override
public int getArrowCooldown() {
return this.arrowCooldown;
}
@Override
public void setArrowCooldown(int ticks) {
this.arrowCooldown = ticks;
}
@Override
public int getBeeStingersInBody() {
return this.beeStingersInBody;
}
@Override
public void setBeeStingersInBody(int count) {
this.beeStingersInBody = count;
}
@Override
public int getBeeStingerCooldown() {
return this.beeStingerCooldown;
}
@Override
public void setBeeStingerCooldown(int ticks) {
this.beeStingerCooldown = ticks;
}
private int noActionTicks = 0;
@Override
public int getNoActionTicks() {
return this.noActionTicks;
}
@Override
public void setNoActionTicks(int ticks) {
this.noActionTicks = ticks;
}
private int itemInUseTicks = 0;
private ItemStack itemInUse = null;
@Override
public @Nullable ItemStack getItemInUse() {
return this.itemInUse;
}
@Override
public int getItemInUseTicks() {
return this.itemInUseTicks;
}
@Override
public void setItemInUseTicks(int ticks) {
this.itemInUseTicks = ticks;
}
private int remainingAir = 300;
private int maximumAir = 300;
private int noDamageTicks = 0;
private int maximumNoDamageTicks = 20;
private double lastDamage = 0.0;
private final Map<PotionEffectType, PotionEffect> potionEffects = new HashMap<>();
@Override
public double getHealth() {
try {
var response = NativeBridgeFfi.getEntityHealth(BridgeUtils.convertUuid(this.getUniqueId()));
if (response != null) {
return response.getHealth();
}
} catch (Throwable ignored) {}
return this.health;
}
@Override
public void setHealth(double health) {
this.health = Math.max(0.0, Math.min(health, getMaxHealth()));
var request = SetEntityHealthRequest.newBuilder()
.setUuid(BridgeUtils.convertUuid(this.getUniqueId()))
.setHealth(this.health)
.build();
NativeBridgeFfi.setEntityHealth(request);
}
@Override
public void damage(double amount) {
var request = DamageEntityRequest.newBuilder()
.setUuid(BridgeUtils.convertUuid(this.getUniqueId()))
.setAmount(amount)
.build();
NativeBridgeFfi.damageEntity(request);
}
@Override
public void damage(double amount, @Nullable Entity source) {
setHealth(getHealth() - amount);
}
@Override
public void damage(double amount, @NotNull DamageSource damageSource) {
setHealth(getHealth() - amount);
}
@Override
public void heal(double amount, @NotNull RegainReason reason) {
setHealth(getHealth() + amount);
}
@Override
public double getMaxHealth() {
return this.maxHealth;
}
@Override
public void setMaxHealth(double health) {
this.maxHealth = Math.max(0.1, health);
if (this.health > this.maxHealth) {
this.health = this.maxHealth;
}
}
@Override
public void resetMaxHealth() {
setMaxHealth(20.0);
}
@Override
public double getAbsorptionAmount() {
return this.absorption;
}
@Override
public void setAbsorptionAmount(double amount) {
this.absorption = Math.max(0.0, amount);
}
@Override
public int getRemainingAir() {
return this.remainingAir;
}
@Override
public void setRemainingAir(int ticks) {
this.remainingAir = ticks;
}
@Override
public int getMaximumAir() {
return this.maximumAir;
}
@Override
public void setMaximumAir(int ticks) {
this.maximumAir = ticks;
}
@Override
public int getNoDamageTicks() {
return this.noDamageTicks;
}
@Override
public void setNoDamageTicks(int ticks) {
this.noDamageTicks = ticks;
}
@Override
public int getMaximumNoDamageTicks() {
return this.maximumNoDamageTicks;
}
@Override
public void setMaximumNoDamageTicks(int ticks) {
this.maximumNoDamageTicks = ticks;
}
@Override
public double getLastDamage() {
return this.lastDamage;
}
@Override
public void setLastDamage(double damage) {
this.lastDamage = damage;
}
@Override
public boolean addPotionEffect(@NotNull PotionEffect effect) {
return addPotionEffect(effect, false);
}
@Override
public boolean addPotionEffect(@NotNull PotionEffect effect, boolean force) {
// TODO Auto-generated method stub
throw new UnsupportedOperationException("Unimplemented method 'addPotionEffect'");
PotionEffectType type = effect.getType();
PotionEffect existing = potionEffects.get(type);
if (existing == null || force || effect.getAmplifier() > existing.getAmplifier()
|| (effect.getAmplifier() == existing.getAmplifier() && effect.getDuration() > existing.getDuration())) {
potionEffects.put(type, effect);
return true;
}
return false;
}
@Override
public boolean addPotionEffects(@NotNull Collection<PotionEffect> effects) {
// TODO Auto-generated method stub
throw new UnsupportedOperationException("Unimplemented method 'addPotionEffects'");
boolean success = false;
for (PotionEffect effect : effects) {
if (addPotionEffect(effect)) {
success = true;
}
}
return success;
}
@Override
public boolean hasPotionEffect(@NotNull PotionEffectType type) {
// TODO Auto-generated method stub
throw new UnsupportedOperationException("Unimplemented method 'hasPotionEffect'");
return potionEffects.containsKey(type);
}
@Override
public @Nullable PotionEffect getPotionEffect(@NotNull PotionEffectType type) {
// TODO Auto-generated method stub
throw new UnsupportedOperationException("Unimplemented method 'getPotionEffect'");
return potionEffects.get(type);
}
@Override
public void removePotionEffect(@NotNull PotionEffectType type) {
// TODO Auto-generated method stub
throw new UnsupportedOperationException("Unimplemented method 'removePotionEffect'");
potionEffects.remove(type);
}
@Override
public @NotNull Collection<PotionEffect> getActivePotionEffects() {
// TODO Auto-generated method stub
throw new UnsupportedOperationException("Unimplemented method 'getActivePotionEffects'");
return Collections.unmodifiableCollection(potionEffects.values());
}
@Override
public boolean clearActivePotionEffects() {
// TODO Auto-generated method stub
throw new UnsupportedOperationException("Unimplemented method 'clearActivePotionEffects'");
boolean hadEffects = !potionEffects.isEmpty();
potionEffects.clear();
return hadEffects;
}
@Override
@ -846,11 +887,6 @@ public class PatchBukkitLivingEntity
throw new UnsupportedOperationException("Unimplemented method 'kill'");
}
@Override
public boolean addPotionEffect(@NotNull PotionEffect effect) {
// TODO Auto-generated method stub
throw new UnsupportedOperationException("Unimplemented method 'addPotionEffect'");
}
@Override
public float getSoundVolume() {
// TODO Auto-generated method stub
throw new UnsupportedOperationException("Unimplemented method 'getSoundVolume'");

View File

@ -46,6 +46,7 @@ public class PatchBukkitEventFactory {
LOGGER.warning("EventFactory: Received Event with no data");
yield null;
}
default -> null;
};
}

View File

@ -0,0 +1,437 @@
package org.patchbukkit.world;
import org.bukkit.Location;
import org.bukkit.Material;
import org.bukkit.World;
import org.bukkit.inventory.ItemStack;
import org.bukkit.entity.Entity;
import org.bukkit.entity.Player;
import org.bukkit.block.Biome;
import org.bukkit.block.Block;
import org.bukkit.block.BlockFace;
import org.bukkit.block.BlockState;
import org.bukkit.block.PistonMoveReaction;
import org.bukkit.block.data.BlockData;
import org.bukkit.SoundGroup;
import org.bukkit.Chunk;
import org.bukkit.FluidCollisionMode;
import org.bukkit.util.BoundingBox;
import org.bukkit.util.RayTraceResult;
import org.bukkit.util.Vector;
import org.bukkit.util.VoxelShape;
import org.bukkit.metadata.MetadataValue;
import org.bukkit.plugin.Plugin;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class PatchBukkitBlock implements Block {
private final World world;
private final int x;
private final int y;
private final int z;
private final Map<String, List<MetadataValue>> metadataMap = new HashMap<>();
public PatchBukkitBlock(World world, int x, int y, int z) {
this.world = world;
this.x = x;
this.y = y;
this.z = z;
}
@Override
public @NotNull World getWorld() {
return this.world;
}
@Override
public int getX() {
return this.x;
}
@Override
public int getY() {
return this.y;
}
@Override
public int getZ() {
return this.z;
}
@Override
public @NotNull Location getLocation() {
return new Location(this.world, this.x, this.y, this.z);
}
@Override
public @Nullable Location getLocation(@Nullable Location loc) {
if (loc != null) {
loc.setWorld(this.world);
loc.setX(this.x);
loc.setY(this.y);
loc.setZ(this.z);
return loc;
}
return getLocation();
}
@Override
public @NotNull BlockData getBlockData() {
return this.world.getBlockData(this.x, this.y, this.z);
}
@Override
public void setBlockData(@NotNull BlockData data) {
this.world.setBlockData(this.x, this.y, this.z, data);
}
@Override
public void setBlockData(@NotNull BlockData data, boolean applyPhysics) {
this.world.setBlockData(this.x, this.y, this.z, data);
}
@Override
public boolean isEmpty() {
return getType().isAir();
}
@Override
public boolean isLiquid() {
Material type = getType();
return type == Material.WATER || type == Material.LAVA;
}
@Override
public boolean isPassable() {
return isEmpty() || isLiquid();
}
@Override
public boolean isSolid() {
return getType().isSolid();
}
@Override
public boolean isReplaceable() {
return isEmpty() || getType() == Material.TALL_GRASS || getType() == Material.SHORT_GRASS;
}
@Override
public boolean isBuildable() {
return !isEmpty();
}
@Override
public int getBlockPower(@NotNull BlockFace face) {
return 0;
}
@Override
public int getBlockPower() {
return 0;
}
@Override
public @NotNull BlockState getState() {
return getState(true);
}
@Override
public @NotNull BlockState getState(boolean useSnapshot) {
return new PatchBukkitBlockState(this);
}
@Override
public boolean isBlockPowered() {
return false;
}
@Override
public boolean isBlockIndirectlyPowered() {
return false;
}
@Override
public boolean isBlockFacePowered(@NotNull BlockFace face) {
return false;
}
@Override
public boolean isBlockFaceIndirectlyPowered(@NotNull BlockFace face) {
return false;
}
@Override
public boolean isSuffocating() {
return !isPassable();
}
@Override
public boolean isBurnable() {
return getType().isBurnable();
}
@Override
public boolean isCollidable() {
return !isPassable();
}
@Override
public @NotNull String getTranslationKey() {
return getType().getTranslationKey();
}
@Override
public @NotNull com.destroystokyo.paper.block.BlockSoundGroup getSoundGroup() {
return (com.destroystokyo.paper.block.BlockSoundGroup) (Object) getBlockData().getSoundGroup();
}
@Override
public @NotNull SoundGroup getBlockSoundGroup() {
return getBlockData().getSoundGroup();
}
@Override
public boolean canPlace(@NotNull BlockData data) {
return true;
}
@Override
public @NotNull BoundingBox getBoundingBox() {
return BoundingBox.of(this, this);
}
@Override
public float getBreakSpeed(@NotNull Player player) {
return 1.0f;
}
@Override
public boolean isPreferredTool(@NotNull ItemStack tool) {
return false;
}
@Override
public @NotNull Collection<ItemStack> getDrops() {
return getDrops(null);
}
@Override
public @NotNull Collection<ItemStack> getDrops(@Nullable ItemStack tool) {
return getDrops(tool, null);
}
@Override
public @NotNull Collection<ItemStack> getDrops(@Nullable ItemStack tool, @Nullable Entity entity) {
return List.of();
}
@Override
public boolean applyBoneMeal(@NotNull BlockFace targetFace) {
return false;
}
@Override
public void randomTick() {
}
@Override
public boolean breakNaturally() {
return breakNaturally(null);
}
@Override
public boolean breakNaturally(@Nullable ItemStack tool) {
return breakNaturally(tool, true);
}
@Override
public boolean breakNaturally(boolean triggerEffect) {
return breakNaturally(null, triggerEffect);
}
@Override
public boolean breakNaturally(boolean triggerEffect, boolean dropExperience) {
return breakNaturally(null, triggerEffect, dropExperience);
}
@Override
public boolean breakNaturally(@Nullable ItemStack tool, boolean triggerEffect) {
return breakNaturally(tool, triggerEffect, true);
}
@Override
public boolean breakNaturally(@Nullable ItemStack tool, boolean triggerEffect, boolean dropExperience) {
return breakNaturally(tool, triggerEffect, dropExperience, true);
}
@Override
public boolean breakNaturally(@Nullable ItemStack tool, boolean triggerEffect, boolean dropExperience, boolean dropAsItem) {
setType(Material.AIR);
return true;
}
@Override
public @NotNull Biome getBiome() {
return Biome.PLAINS;
}
@Override
public @NotNull Biome getComputedBiome() {
return getBiome();
}
@Override
public void setBiome(@NotNull Biome bio) {
}
@Override
public double getTemperature() {
return 0.8;
}
@Override
public double getHumidity() {
return 0.4;
}
@Override
public @NotNull PistonMoveReaction getPistonMoveReaction() {
return PistonMoveReaction.MOVE;
}
@Override
public void fluidTick() {
}
@Override
public void tick() {
}
@Override
public @Nullable RayTraceResult rayTrace(@NotNull Location start, @NotNull Vector direction, double maxDistance, @NotNull FluidCollisionMode fluidCollisionMode) {
return getBoundingBox().rayTrace(start.toVector(), direction, maxDistance);
}
@Override
public @NotNull VoxelShape getCollisionShape() {
return getBlockData().getCollisionShape(getLocation());
}
@Override
public @NotNull Material getType() {
return getBlockData().getMaterial();
}
@Override
public void setType(@NotNull Material type) {
setBlockData(org.bukkit.Bukkit.createBlockData(type));
}
@Override
public void setType(@NotNull Material type, boolean applyPhysics) {
setBlockData(org.bukkit.Bukkit.createBlockData(type), applyPhysics);
}
@Override
public @NotNull Block getRelative(int modX, int modY, int modZ) {
return this.world.getBlockAt(this.x + modX, this.y + modY, this.z + modZ);
}
@Override
public @NotNull Block getRelative(@NotNull BlockFace face) {
return getRelative(face.getModX(), face.getModY(), face.getModZ());
}
@Override
public @NotNull Block getRelative(@NotNull BlockFace face, int distance) {
return getRelative(face.getModX() * distance, face.getModY() * distance, face.getModZ() * distance);
}
@Override
public @Nullable BlockFace getFace(@NotNull Block block) {
int modX = block.getX() - this.x;
int modY = block.getY() - this.y;
int modZ = block.getZ() - this.z;
for (BlockFace face : BlockFace.values()) {
if (face.getModX() == modX && face.getModY() == modY && face.getModZ() == modZ) {
return face;
}
}
return null;
}
@Override
public void setMetadata(@NotNull String metadataKey, @NotNull MetadataValue newMetadataValue) {
metadataMap.computeIfAbsent(metadataKey, k -> new ArrayList<>()).add(newMetadataValue);
}
@Override
public @NotNull List<MetadataValue> getMetadata(@NotNull String metadataKey) {
return metadataMap.getOrDefault(metadataKey, List.of());
}
@Override
public boolean hasMetadata(@NotNull String metadataKey) {
return metadataMap.containsKey(metadataKey);
}
@Override
public void removeMetadata(@NotNull String metadataKey, @NotNull Plugin owningPlugin) {
List<MetadataValue> list = metadataMap.get(metadataKey);
if (list != null) {
list.removeIf(v -> v.getOwningPlugin() == owningPlugin);
if (list.isEmpty()) {
metadataMap.remove(metadataKey);
}
}
}
@Override
public String translationKey() {
// TODO Auto-generated method stub
throw new UnsupportedOperationException("Unimplemented method 'translationKey'");
}
@Override
public byte getData() {
// TODO Auto-generated method stub
throw new UnsupportedOperationException("Unimplemented method 'getData'");
}
@Override
public byte getLightLevel() {
// TODO Auto-generated method stub
throw new UnsupportedOperationException("Unimplemented method 'getLightLevel'");
}
@Override
public byte getLightFromSky() {
// TODO Auto-generated method stub
throw new UnsupportedOperationException("Unimplemented method 'getLightFromSky'");
}
@Override
public byte getLightFromBlocks() {
// TODO Auto-generated method stub
throw new UnsupportedOperationException("Unimplemented method 'getLightFromBlocks'");
}
@Override
public boolean isValidTool(@NotNull ItemStack tool) {
// TODO Auto-generated method stub
throw new UnsupportedOperationException("Unimplemented method 'isValidTool'");
}
@Override
public @NotNull Chunk getChunk() {
// TODO Auto-generated method stub
throw new UnsupportedOperationException("Unimplemented method 'getChunk'");
}
}

View File

@ -0,0 +1,180 @@
package org.patchbukkit.world;
import org.bukkit.Chunk;
import org.bukkit.Location;
import org.bukkit.Material;
import org.bukkit.World;
import org.bukkit.block.Block;
import org.bukkit.block.BlockState;
import org.bukkit.block.data.BlockData;
import org.bukkit.material.MaterialData;
import org.bukkit.metadata.MetadataValue;
import org.bukkit.plugin.Plugin;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.annotations.Unmodifiable;
import org.bukkit.entity.Entity;
import org.bukkit.inventory.ItemStack;
import java.util.Collection;
import java.util.List;
public class PatchBukkitBlockState implements BlockState {
private final Block block;
private BlockData data;
public PatchBukkitBlockState(Block block) {
this.block = block;
this.data = block.getBlockData().clone();
}
@Override
public @NotNull Block getBlock() {
return this.block;
}
@Override
public @NotNull Material getType() {
return this.data.getMaterial();
}
@Override
public void setType(@NotNull Material type) {
this.data = org.bukkit.Bukkit.createBlockData(type);
}
@Override
public @NotNull BlockData getBlockData() {
return this.data;
}
@Override
public void setBlockData(@NotNull BlockData data) {
this.data = data.clone();
}
@Override
public byte getRawData() {
return 0;
}
@Override
public void setRawData(byte data) {
}
@Override
public boolean update() {
return update(false);
}
@Override
public boolean update(boolean force) {
return update(force, true);
}
@Override
public boolean update(boolean force, boolean applyPhysics) {
this.block.setBlockData(this.data, applyPhysics);
return true;
}
@Override
public @NotNull World getWorld() {
return this.block.getWorld();
}
@Override
public int getX() {
return this.block.getX();
}
@Override
public int getY() {
return this.block.getY();
}
@Override
public int getZ() {
return this.block.getZ();
}
@Override
public @NotNull Location getLocation() {
return this.block.getLocation();
}
@Override
public @Nullable Location getLocation(@Nullable Location loc) {
return this.block.getLocation(loc);
}
@Override
public @NotNull Chunk getChunk() {
return this.block.getChunk();
}
@Override
public void setMetadata(@NotNull String metadataKey, @NotNull MetadataValue newMetadataValue) {
this.block.setMetadata(metadataKey, newMetadataValue);
}
@Override
public @NotNull List<MetadataValue> getMetadata(@NotNull String metadataKey) {
return this.block.getMetadata(metadataKey);
}
@Override
public boolean hasMetadata(@NotNull String metadataKey) {
return this.block.hasMetadata(metadataKey);
}
@Override
public void removeMetadata(@NotNull String metadataKey, @NotNull Plugin owningPlugin) {
this.block.removeMetadata(metadataKey, owningPlugin);
}
@Override
public boolean isPlaced() {
return true;
}
@Override
public boolean isCollidable() {
return this.block.isCollidable();
}
@Override
public boolean isSuffocating() {
return this.block.isSuffocating();
}
@Override
public @NotNull MaterialData getData() {
return new MaterialData(getType());
}
@Override
public void setData(@NotNull MaterialData data) {
}
@Override
public @NotNull BlockState copy() {
return new PatchBukkitBlockState(this.block);
}
@Override
public @NotNull BlockState copy(@NotNull Location location) {
return new PatchBukkitBlockState(location.getBlock());
}
@Override
public byte getLightLevel() {
// TODO Auto-generated method stub
throw new UnsupportedOperationException("Unimplemented method 'getLightLevel'");
}
@Override
public @NotNull @Unmodifiable Collection<ItemStack> getDrops(@Nullable ItemStack tool, @Nullable Entity entity) {
// TODO Auto-generated method stub
throw new UnsupportedOperationException("Unimplemented method 'getDrops'");
}
}

View File

@ -21,6 +21,10 @@ import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.annotations.Unmodifiable;
import org.jspecify.annotations.NonNull;
import org.patchbukkit.bridge.BridgeUtils;
import patchbukkit.bridge.NativeBridgeFfi;
import patchbukkit.world.GetBlockDataRequest;
import patchbukkit.world.SetBlockDataRequest;
public class PatchBukkitRegionAccessor implements RegionAccessor {
@ -65,32 +69,42 @@ public class PatchBukkitRegionAccessor implements RegionAccessor {
}
@Override
public @NotNull BlockData getBlockData(int x, int y, int z) {
// TODO Auto-generated method stub
throw new UnsupportedOperationException(
"Unimplemented method 'getBlockData'"
);
public @org.jetbrains.annotations.NotNull BlockData getBlockData(int x, int y, int z) {
if (this instanceof PatchBukkitWorld world) {
var request = GetBlockDataRequest.newBuilder()
.setWorldUuid(BridgeUtils.convertUuid(world.getUID()))
.setX(x)
.setY(y)
.setZ(z)
.build();
try {
var response = NativeBridgeFfi.getBlockData(request);
if (response != null && !response.getBlockState().isEmpty()) {
return Bukkit.createBlockData(Material.STONE);
}
} catch (Throwable ignored) {}
}
return Bukkit.createBlockData(Material.AIR);
}
@Override
public @NotNull Material getType(int x, int y, int z) {
// TODO Auto-generated method stub
throw new UnsupportedOperationException(
"Unimplemented method 'getType'"
);
public @org.jetbrains.annotations.NotNull Material getType(int x, int y, int z) {
return getBlockData(x, y, z).getMaterial();
}
@Override
public void setBlockData(
int x,
int y,
int z,
@NotNull BlockData blockData
) {
// TODO Auto-generated method stub
throw new UnsupportedOperationException(
"Unimplemented method 'setBlockData'"
);
public void setBlockData(int x, int y, int z, @org.jetbrains.annotations.NotNull BlockData blockData) {
if (this instanceof PatchBukkitWorld world) {
var request = SetBlockDataRequest.newBuilder()
.setWorldUuid(BridgeUtils.convertUuid(world.getUID()))
.setX(x)
.setY(y)
.setZ(z)
.setBlockState(blockData.getAsString())
.setApplyPhysics(true)
.build();
NativeBridgeFfi.setBlockData(request);
}
}
@Override

View File

@ -6,7 +6,14 @@ import io.papermc.paper.entity.poi.PoiType.Occupancy;
import io.papermc.paper.math.Position;
import io.papermc.paper.raytracing.PositionedRayTraceConfigurationBuilder;
import java.nio.file.Path;
import java.util.*;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.UUID;
import java.util.function.Consumer;
import java.util.function.Predicate;
import org.bukkit.*;
@ -27,7 +34,11 @@ import org.bukkit.metadata.MetadataValue;
import org.bukkit.persistence.PersistentDataContainer;
import org.bukkit.plugin.Plugin;
import org.bukkit.util.*;
import org.bukkit.util.Vector;
import org.patchbukkit.bridge.BridgeUtils;
import patchbukkit.bridge.NativeBridgeFfi;
import patchbukkit.world.GetBlockDataRequest;
import patchbukkit.world.SetBlockDataRequest;
import patchbukkit.world.SpawnParticleRequest;
import org.checkerframework.checker.index.qual.Positive;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@ -153,10 +164,7 @@ public class PatchBukkitWorld
@Override
public @NotNull Block getBlockAt(int x, int y, int z) {
// TODO Auto-generated method stub
throw new UnsupportedOperationException(
"Unimplemented method 'getBlockAt'"
);
return new PatchBukkitBlock(this, x, y, z);
}
@Override
@ -1585,10 +1593,7 @@ public class PatchBukkitWorld
@Override
public @NotNull String getName() {
// TODO Auto-generated method stub
throw new UnsupportedOperationException(
"Unimplemented method 'getName'"
);
return "world";
}
@Override

View File

@ -6,16 +6,29 @@ import "google/protobuf/wrappers.proto";
import "patchbukkit/abilities.proto";
import "patchbukkit/common/types.proto";
import "patchbukkit/config.proto";
import "patchbukkit/entity.proto";
import "patchbukkit/events/event.proto";
import "patchbukkit/log.proto";
import "patchbukkit/message.proto";
import "patchbukkit/registry.proto";
import "patchbukkit/sound.proto";
import "patchbukkit/world.proto";
service NativeBridge {
rpc GetAbilities(patchbukkit.common.UUID) returns (patchbukkit.abilities.Abilities);
rpc SetAbilities(patchbukkit.abilities.SetAbilitiesRequest) returns (patchbukkit.abilities.SetAbilitiesResponse);
rpc GetEntityHealth(patchbukkit.common.UUID) returns (patchbukkit.entity.EntityHealthResponse);
rpc SetEntityHealth(patchbukkit.entity.SetEntityHealthRequest) returns (google.protobuf.Empty);
rpc DamageEntity(patchbukkit.entity.DamageEntityRequest) returns (google.protobuf.Empty);
rpc SetEntityVelocity(patchbukkit.entity.SetEntityVelocityRequest) returns (google.protobuf.Empty);
rpc TeleportEntity(patchbukkit.entity.TeleportEntityRequest) returns (google.protobuf.Empty);
rpc GetBlockData(patchbukkit.world.GetBlockDataRequest) returns (patchbukkit.world.GetBlockDataResponse);
rpc SetBlockData(patchbukkit.world.SetBlockDataRequest) returns (google.protobuf.Empty);
rpc SpawnParticle(patchbukkit.world.SpawnParticleRequest) returns (google.protobuf.Empty);
rpc GetWorlds(patchbukkit.common.EmptyRequest) returns (patchbukkit.world.GetWorldsResponse);
rpc RegisterEvent(patchbukkit.events.RegisterEventRequest) returns (google.protobuf.Empty);
rpc CallEvent(patchbukkit.events.CallEventRequest) returns (patchbukkit.events.CallEventResponse);

View File

@ -0,0 +1,41 @@
syntax = "proto3";
package patchbukkit.entity;
import "patchbukkit/common/types.proto";
option java_multiple_files = true;
option java_package = "patchbukkit.entity";
message EntityHealthResponse {
double health = 1;
double max_health = 2;
}
message SetEntityHealthRequest {
patchbukkit.common.UUID uuid = 1;
double health = 2;
}
message DamageEntityRequest {
patchbukkit.common.UUID uuid = 1;
double amount = 2;
}
message SetEntityVelocityRequest {
patchbukkit.common.UUID uuid = 1;
double x = 2;
double y = 3;
double z = 4;
}
message TeleportEntityRequest {
patchbukkit.common.UUID uuid = 1;
patchbukkit.common.Location location = 2;
}
message SetEntityPoseRequest {
patchbukkit.common.UUID uuid = 1;
string pose = 2;
}

View File

@ -4,6 +4,7 @@ package patchbukkit.events;
import "patchbukkit/common/types.proto";
import "patchbukkit/events/player_events.proto";
import "patchbukkit/events/server_events.proto";
option java_multiple_files = true;
option java_package = "patchbukkit.events";
@ -11,6 +12,11 @@ option java_package = "patchbukkit.events";
message Event {
oneof data {
PlayerJoinEvent player_join = 1;
PluginEnableEvent plugin_enable = 2;
PluginDisableEvent plugin_disable = 3;
PlayerQuitEvent player_quit = 4;
PlayerInteractEvent player_interact = 5;
PlayerGameModeChangeEvent player_gamemode_change = 6;
}
}

View File

@ -10,3 +10,24 @@ message PlayerJoinEvent {
patchbukkit.common.UUID player_uuid = 1;
string join_message = 2;
}
message PlayerQuitEvent {
patchbukkit.common.UUID player_uuid = 1;
string quit_message = 2;
}
message PlayerInteractEvent {
patchbukkit.common.UUID player_uuid = 1;
string action = 2;
int32 clicked_x = 3;
int32 clicked_y = 4;
int32 clicked_z = 5;
string block_face = 6;
string hand = 7;
}
message PlayerGameModeChangeEvent {
patchbukkit.common.UUID player_uuid = 1;
string new_gamemode = 2;
}

View File

@ -0,0 +1,15 @@
syntax = "proto3";
package patchbukkit.events;
import "patchbukkit/common/types.proto";
option java_multiple_files = true;
option java_package = "patchbukkit.events";
message PluginEnableEvent {
string plugin_name = 1;
}
message PluginDisableEvent {
string plugin_name = 1;
}

View File

@ -0,0 +1,46 @@
syntax = "proto3";
package patchbukkit.world;
import "patchbukkit/common/types.proto";
option java_multiple_files = true;
option java_package = "patchbukkit.world";
message GetBlockDataRequest {
patchbukkit.common.UUID world_uuid = 1;
int32 x = 2;
int32 y = 3;
int32 z = 4;
}
message GetBlockDataResponse {
string block_state = 1;
}
message SetBlockDataRequest {
patchbukkit.common.UUID world_uuid = 1;
int32 x = 2;
int32 y = 3;
int32 z = 4;
string block_state = 5;
bool apply_physics = 6;
}
message SpawnParticleRequest {
patchbukkit.common.UUID world_uuid = 1;
string particle = 2;
double x = 3;
double y = 4;
double z = 5;
int32 count = 6;
double offset_x = 7;
double offset_y = 8;
double offset_z = 9;
double extra = 10;
}
message GetWorldsResponse {
repeated patchbukkit.common.UUID world_uuids = 1;
}

View File

@ -107,12 +107,16 @@ pub unsafe extern "C" fn {fn_name}(
) -> *mut u8 {{
use prost::Message;
tracing::debug!("{fn_name}: called with input_ptr={{:?}}, input_len={{}}", input_ptr, input_len);
if input_ptr.is_null() || input_len == 0 {{
tracing::warn!("{fn_name}: null or empty input (ptr={{:?}}, len={{}})", input_ptr, input_len);
if input_ptr.is_null() {{
tracing::warn!("{fn_name}: null input (ptr={{:?}}, len={{}})", input_ptr, input_len);
unsafe {{ *output_len = 0 }};
return std::ptr::null_mut();
}}
let input_slice = unsafe {{ std::slice::from_raw_parts(input_ptr, input_len) }};
let input_slice = if input_len == 0 {{
&[]
}} else {{
unsafe {{ std::slice::from_raw_parts(input_ptr, input_len) }}
}};
let Ok(request) = {input_type}::decode(input_slice) else {{
tracing::warn!("{fn_name}: failed to decode request");
unsafe {{ *output_len = 0 }};

View File

@ -145,7 +145,11 @@ pub struct SpigotPluginYml {
#[serde(default)]
pub default_permission: Option<DefaultPermission>,
// Paper-specific fields
// Paper/Folia-specific fields
/// Folia region threading support flag
#[serde(rename = "folia-supported")]
#[serde(default)]
pub folia_supported: Option<bool>,
/// Paper plugin loader class
#[serde(rename = "paper-plugin-loader")]
#[serde(default)]

View File

@ -47,11 +47,9 @@ impl PatchBukkitEvent for pumpkin::plugin::player::player_join::PlayerJoinEvent
}
fn apply_modifications(&mut self, server: &Arc<Server>, data: Data) -> Option<()> {
match data {
Data::PlayerJoin(event) => {
self.join_message = serde_json::from_str(&event.join_message).ok()?;
server.get_player_by_uuid(uuid::Uuid::from_str(&event.player_uuid?.value).ok()?)?;
}
if let Data::PlayerJoin(event) = data {
self.join_message = serde_json::from_str(&event.join_message).ok()?;
server.get_player_by_uuid(uuid::Uuid::from_str(&event.player_uuid?.value).ok()?)?;
}
Some(())

View File

@ -0,0 +1,76 @@
use pumpkin::entity::EntityBase;
use crate::{
java::native_callbacks::{CALLBACK_CONTEXT, utils::with_player},
proto::patchbukkit::{
common::Uuid,
entity::{
DamageEntityRequest, EntityHealthResponse, SetEntityHealthRequest,
SetEntityVelocityRequest, TeleportEntityRequest,
},
},
};
pub fn ffi_native_bridge_get_entity_health_impl(request: Uuid) -> Option<EntityHealthResponse> {
let ctx = CALLBACK_CONTEXT.get()?;
with_player(Some(&request), |player| {
let health = tokio::task::block_in_place(|| {
ctx.runtime
.block_on(async { f64::from(player.living_entity.health.load()) })
});
EntityHealthResponse {
health,
max_health: 20.0,
}
})
}
pub fn ffi_native_bridge_set_entity_health_impl(request: SetEntityHealthRequest) -> Option<()> {
let ctx = CALLBACK_CONTEXT.get()?;
with_player(request.uuid.as_ref(), |player| {
let health = request.health as f32;
let player = player.clone();
ctx.runtime.spawn(async move {
player.set_health(health).await;
});
})
}
pub fn ffi_native_bridge_damage_entity_impl(request: DamageEntityRequest) -> Option<()> {
let ctx = CALLBACK_CONTEXT.get()?;
with_player(request.uuid.as_ref(), |player| {
let amount = request.amount as f32;
let player = player.clone();
ctx.runtime.spawn(async move {
let current = player.living_entity.health.load();
player.set_health((current - amount).max(0.0)).await;
});
})
}
pub fn ffi_native_bridge_set_entity_velocity_impl(request: SetEntityVelocityRequest) -> Option<()> {
with_player(request.uuid.as_ref(), |player| {
let velocity = pumpkin_util::math::vector3::Vector3::new(request.x, request.y, request.z);
player.living_entity.entity.set_velocity(velocity);
})
}
pub fn ffi_native_bridge_teleport_entity_impl(request: TeleportEntityRequest) -> Option<()> {
let ctx = CALLBACK_CONTEXT.get()?;
let loc = request.location?;
let pos = loc.position?;
let yaw = loc.yaw;
let pitch = loc.pitch;
with_player(request.uuid.as_ref(), |player| {
let position = pumpkin_util::math::vector3::Vector3::new(pos.x, pos.y, pos.z);
let world = player.living_entity.entity.world.load_full();
let player = player.clone();
ctx.runtime.spawn(async move {
player
.teleport(position, Some(yaw), Some(pitch), world)
.await;
});
})
}

View File

@ -84,6 +84,11 @@ pub fn ffi_native_bridge_call_event_impl(request: CallEventRequest) -> Option<Ca
context.server.plugin_manager.fire(pumpkin_event).await;
Some(true)
}
Data::PluginEnable(_plugin_enable_event) => todo!(),
Data::PluginDisable(_plugin_disable_event) => todo!(),
Data::PlayerQuit(_player_quit_event) => todo!(),
Data::PlayerInteract(_player_interact_event) => todo!(),
Data::PlayerGamemodeChange(_player_game_mode_change_event) => todo!(),
}
})
})?;

View File

@ -36,6 +36,12 @@ pub use log::*;
pub mod config;
pub use config::*;
pub mod entity;
pub use entity::*;
pub mod world;
pub use world::*;
pub mod itemstack;
static CALLBACK_CONTEXT: OnceLock<CallbackContext> = OnceLock::new();

View File

@ -1,6 +1,37 @@
use super::CALLBACK_CONTEXT;
use crate::proto::patchbukkit::common::Uuid as ProtoUuid;
use pumpkin::entity::player::Player;
use std::collections::HashMap;
use std::ffi::{CStr, c_char};
use std::sync::{Arc, RwLock};
static PLAYER_HANDLE_CACHE: RwLock<Option<HashMap<uuid::Uuid, Arc<Player>>>> = RwLock::new(None);
#[must_use]
pub fn get_string(str_ptr: *const c_char) -> String {
unsafe { CStr::from_ptr(str_ptr).to_string_lossy().into_owned() }
}
pub fn with_player<F, R>(proto_uuid: Option<&ProtoUuid>, f: F) -> Option<R>
where
F: FnOnce(Arc<Player>) -> R,
{
let ctx = CALLBACK_CONTEXT.get()?;
let uuid_str = &proto_uuid?.value;
let player_uuid = uuid::Uuid::parse_str(uuid_str).ok()?;
if let Ok(read_guard) = PLAYER_HANDLE_CACHE.read()
&& let Some(ref cache) = *read_guard
&& let Some(player) = cache.get(&player_uuid)
{
return Some(f(player.clone()));
}
let player = ctx.plugin_context.server.get_player_by_uuid(player_uuid)?;
if let Ok(mut write_guard) = PLAYER_HANDLE_CACHE.write() {
let cache = write_guard.get_or_insert_with(HashMap::new);
cache.insert(player_uuid, player.clone());
}
Some(f(player))
}

View File

@ -0,0 +1,73 @@
use crate::{
java::native_callbacks::CALLBACK_CONTEXT,
proto::patchbukkit::world::{
GetBlockDataRequest, GetBlockDataResponse, SetBlockDataRequest, SpawnParticleRequest,
},
};
pub fn ffi_native_bridge_get_block_data_impl(
request: GetBlockDataRequest,
) -> Option<GetBlockDataResponse> {
let ctx = CALLBACK_CONTEXT.get()?;
let uuid_str = &request.world_uuid.as_ref()?.value;
let world_uuid = uuid::Uuid::parse_str(uuid_str).ok()?;
let worlds = ctx.plugin_context.server.worlds.load_full();
let world = worlds
.iter()
.find(|w| w.uuid == world_uuid)
.cloned()
.or_else(|| worlds.first().cloned())?;
let pos = pumpkin_util::math::position::BlockPos::new(request.x, request.y, request.z);
let state_id = world.get_block_state(&pos).id;
Some(GetBlockDataResponse {
block_state: format!("minecraft:block_{state_id}"),
})
}
pub fn ffi_native_bridge_set_block_data_impl(request: SetBlockDataRequest) -> Option<()> {
let ctx = CALLBACK_CONTEXT.get()?;
let uuid_str = &request.world_uuid.as_ref()?.value;
let world_uuid = uuid::Uuid::parse_str(uuid_str).ok()?;
let worlds = ctx.plugin_context.server.worlds.load_full();
let world = worlds
.iter()
.find(|w| w.uuid == world_uuid)
.cloned()
.or_else(|| worlds.first().cloned())?;
let pos = pumpkin_util::math::position::BlockPos::new(request.x, request.y, request.z);
ctx.runtime.spawn(async move {
world
.set_block_state(
&pos,
pumpkin_data::BlockStateId::new_or_air(1),
pumpkin::world::BlockFlags::NOTIFY_ALL,
)
.await;
});
Some(())
}
pub fn ffi_native_bridge_spawn_particle_impl(_request: SpawnParticleRequest) -> Option<()> {
Some(())
}
pub fn ffi_native_bridge_get_worlds_impl(
_request: crate::proto::patchbukkit::common::EmptyRequest,
) -> Option<crate::proto::patchbukkit::world::GetWorldsResponse> {
let ctx = CALLBACK_CONTEXT.get()?;
let worlds = ctx.plugin_context.server.worlds.load_full();
let world_uuids = worlds
.iter()
.map(|w| crate::proto::patchbukkit::common::Uuid {
value: w.uuid.to_string(),
})
.collect();
Some(crate::proto::patchbukkit::world::GetWorldsResponse { world_uuids })
}

View File

@ -169,7 +169,10 @@ impl CommandManager {
&[InvocationArg::try_from(&cmd_name)?, j_plugin_arg],
)?));
tracing::info!("Registering Bukkit command: {}", &cmd_name);
let cmd_aliases: Vec<String> = cmd_data.aliases.as_ref().map_or_else(Vec::new, |a| a.to_vec());
let cmd_aliases: Vec<String> = cmd_data
.aliases
.as_ref()
.map_or_else(Vec::new, |a| a.to_vec());
let mut names: Vec<String> = vec![cmd_name.clone()];
names.extend(cmd_aliases.clone());
@ -178,21 +181,51 @@ impl CommandManager {
let j_plugin_cmd_owned = jvm.clone_instance(&cmd_lock)?;
if let Some(ref desc) = cmd_data.description {
let _ = jvm.invoke(&j_plugin_cmd_owned, "setDescription", &[InvocationArg::try_from(desc)?]);
let _ = jvm.invoke(
&j_plugin_cmd_owned,
"setDescription",
&[InvocationArg::try_from(desc)?],
);
}
if let Some(ref usage) = cmd_data.usage {
let _ = jvm.invoke(&j_plugin_cmd_owned, "setUsage", &[InvocationArg::try_from(usage)?]);
let _ = jvm.invoke(
&j_plugin_cmd_owned,
"setUsage",
&[InvocationArg::try_from(usage)?],
);
}
if let Some(ref perm_msg) = cmd_data.permission_message {
let _ = jvm.invoke(&j_plugin_cmd_owned, "setPermissionMessage", &[InvocationArg::try_from(perm_msg)?]);
let _ = jvm.invoke(
&j_plugin_cmd_owned,
"setPermissionMessage",
&[InvocationArg::try_from(perm_msg)?],
);
}
if let Some(ref perm) = cmd_data.permission {
let _ = jvm.invoke(&j_plugin_cmd_owned, "setPermission", &[InvocationArg::try_from(perm)?]);
let _ = jvm.invoke(
&j_plugin_cmd_owned,
"setPermission",
&[InvocationArg::try_from(perm)?],
);
}
if !cmd_aliases.is_empty() {
let j_aliases = jvm.create_java_array("java.lang.String", &cmd_aliases.iter().map(|a| InvocationArg::try_from(a).unwrap()).collect::<Vec<_>>())?;
let j_list = jvm.invoke_static("java.util.Arrays", "asList", &[InvocationArg::from(j_aliases)])?;
let _ = jvm.invoke(&j_plugin_cmd_owned, "setAliases", &[InvocationArg::from(j_list)]);
let j_aliases = jvm.create_java_array(
"java.lang.String",
&cmd_aliases
.iter()
.map(|a| InvocationArg::try_from(a).unwrap())
.collect::<Vec<_>>(),
)?;
let j_list = jvm.invoke_static(
"java.util.Arrays",
"asList",
&[InvocationArg::from(j_aliases)],
)?;
let _ = jvm.invoke(
&j_plugin_cmd_owned,
"setAliases",
&[InvocationArg::from(j_list)],
);
}
jvm.invoke(

View File

@ -38,6 +38,14 @@ pub mod patchbukkit {
pub mod itemstack {
include!(concat!(env!("OUT_DIR"), "/patchbukkit.itemstack.rs"));
}
pub mod entity {
include!(concat!(env!("OUT_DIR"), "/patchbukkit.entity.rs"));
}
pub mod world {
include!(concat!(env!("OUT_DIR"), "/patchbukkit.world.rs"));
}
}
include!(concat!(env!("OUT_DIR"), "/ffi_init.rs"));