more additions

This commit is contained in:
Alexander Medvedev 2026-08-25 14:24:45 +02:00
parent a7303b7ed5
commit 76244747ed
47 changed files with 10249 additions and 5242 deletions

View File

@ -11,7 +11,7 @@ repositories {
dependencies { dependencies {
compileOnly(project(":patchbukkit")) compileOnly(project(":patchbukkit"))
compileOnly("io.papermc.paper:paper-api:26.2.build.112-stable") compileOnly("io.papermc.paper:paper-api:26.2.build.117-stable")
} }
java { java {

View File

@ -47,7 +47,7 @@ repositories {
} }
dependencies { dependencies {
paperweight.paperDevBundle("26.2.build.112-stable") paperweight.paperDevBundle("26.2.build.117-stable")
implementation("net.sf.jopt-simple:jopt-simple:6.0-alpha-3") implementation("net.sf.jopt-simple:jopt-simple:6.0-alpha-3")
implementation("org.apache.maven:maven-resolver-provider:3.9.6") implementation("org.apache.maven:maven-resolver-provider:3.9.6")
implementation("org.apache.maven.resolver:maven-resolver-impl:1.9.18") implementation("org.apache.maven.resolver:maven-resolver-impl:1.9.18")

View File

@ -0,0 +1,111 @@
package org.patchbukkit.ban;
import java.util.Date;
import java.util.Objects;
import org.bukkit.BanEntry;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
public class PatchBukkitBanEntry<T> implements BanEntry<T> {
private final String target;
private final T banTarget;
private Date created;
private String source;
private Date expiration;
private String reason;
private final PatchBukkitBanList<T> list;
public PatchBukkitBanEntry(
@NotNull String target,
@Nullable T banTarget,
@Nullable Date created,
@Nullable String source,
@Nullable Date expiration,
@Nullable String reason,
@Nullable PatchBukkitBanList<T> list
) {
this.target = target;
this.banTarget = banTarget;
this.created = created != null ? created : new Date();
this.source = source != null ? source : "Server";
this.expiration = expiration;
this.reason = reason != null ? reason : "Banned by operator";
this.list = list;
}
@Override
public @NotNull String getTarget() {
return this.target;
}
@Override
public @Nullable T getBanTarget() {
return this.banTarget;
}
@Override
public @NotNull Date getCreated() {
return this.created;
}
@Override
public void setCreated(@NotNull Date created) {
this.created = created;
}
@Override
public @NotNull String getSource() {
return this.source;
}
@Override
public void setSource(@NotNull String source) {
this.source = source;
}
@Override
public @Nullable Date getExpiration() {
return this.expiration;
}
@Override
public void setExpiration(@Nullable Date expiration) {
this.expiration = expiration;
}
@Override
public @Nullable String getReason() {
return this.reason;
}
@Override
public void setReason(@Nullable String reason) {
this.reason = reason;
}
@Override
public void save() {
if (this.list != null) {
this.list.saveEntry(this);
}
}
@Override
public void remove() {
if (this.list != null) {
this.list.pardon(this.target);
}
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof BanEntry<?> that)) return false;
return Objects.equals(this.target, that.getTarget());
}
@Override
public int hashCode() {
return Objects.hash(this.target);
}
}

View File

@ -0,0 +1,192 @@
package org.patchbukkit.ban;
import java.net.InetAddress;
import java.time.Duration;
import java.time.Instant;
import java.util.Date;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import org.bukkit.BanEntry;
import org.bukkit.BanList;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import patchbukkit.bridge.NativeBridgeFfi;
import patchbukkit.server.BanEntryProto;
import patchbukkit.server.GetBanListRequest;
import patchbukkit.server.SetBanEntryRequest;
@SuppressWarnings("unchecked")
public class PatchBukkitBanList<T> implements BanList<T> {
private final String banType;
private final Map<String, BanEntry<T>> entries = new ConcurrentHashMap<>();
public PatchBukkitBanList(@NotNull String banType) {
this.banType = banType;
refresh();
}
public void refresh() {
try {
var res = NativeBridgeFfi.getBanList(GetBanListRequest.newBuilder().setBanType(banType).build());
if (res != null) {
entries.clear();
for (BanEntryProto proto : res.getEntriesList()) {
Date created = new Date(proto.getCreated() * 1000);
Date expires = proto.getExpires() > 0 ? new Date(proto.getExpires() * 1000) : null;
T targetObj = parseTarget(proto.getTarget());
PatchBukkitBanEntry<T> entry = new PatchBukkitBanEntry<>(
proto.getTarget(),
targetObj,
created,
proto.getSource(),
expires,
proto.getReason(),
this
);
entries.put(proto.getTarget().toLowerCase(), entry);
}
}
} catch (Throwable ignored) {}
}
private T parseTarget(String target) {
if ("IP".equalsIgnoreCase(banType)) {
try {
return (T) InetAddress.getByName(target);
} catch (Throwable ignored) {}
}
return (T) target;
}
public void saveEntry(PatchBukkitBanEntry<T> entry) {
entries.put(entry.getTarget().toLowerCase(), entry);
try {
var proto = BanEntryProto.newBuilder()
.setTarget(entry.getTarget())
.setSource(entry.getSource())
.setCreated(entry.getCreated().getTime() / 1000)
.setExpires(entry.getExpiration() != null ? entry.getExpiration().getTime() / 1000 : 0)
.setReason(entry.getReason() != null ? entry.getReason() : "")
.build();
NativeBridgeFfi.setBanEntry(SetBanEntryRequest.newBuilder()
.setBanType(banType)
.setEntry(proto)
.setRemove(false)
.build());
} catch (Throwable ignored) {}
}
@Override
public <E extends BanEntry<? super T>> @Nullable E getBanEntry(@NotNull String target) {
return (E) entries.get(target.toLowerCase());
}
@Override
public @Nullable BanEntry<T> getBanEntry(@NotNull T target) {
return entries.get(targetToString(target).toLowerCase());
}
@Override
public <E extends BanEntry<? super T>> @Nullable E addBan(
@NotNull String target,
@Nullable String reason,
@Nullable Date expires,
@Nullable String source
) {
PatchBukkitBanEntry<T> entry = new PatchBukkitBanEntry<>(
target,
parseTarget(target),
new Date(),
source,
expires,
reason,
this
);
saveEntry(entry);
return (E) entry;
}
@Override
public @Nullable BanEntry<T> addBan(
@NotNull T target,
@Nullable String reason,
@Nullable Date expires,
@Nullable String source
) {
return addBan(targetToString(target), reason, expires, source);
}
@Override
public @Nullable BanEntry<T> addBan(
@NotNull T target,
@Nullable String reason,
@Nullable Instant expires,
@Nullable String source
) {
return addBan(target, reason, expires != null ? Date.from(expires) : null, source);
}
@Override
public @Nullable BanEntry<T> addBan(
@NotNull T target,
@Nullable String reason,
@Nullable Duration duration,
@Nullable String source
) {
Date expires = duration != null ? Date.from(Instant.now().plus(duration)) : null;
return addBan(target, reason, expires, source);
}
@Override
public @NotNull Set<BanEntry> getBanEntries() {
return new HashSet<>(entries.values());
}
@Override
public <E extends BanEntry<? super T>> @NotNull Set<E> getEntries() {
return (Set<E>) new HashSet<>(entries.values());
}
@Override
public boolean isBanned(@NotNull T target) {
return isBanned(targetToString(target));
}
@Override
public boolean isBanned(@NotNull String target) {
BanEntry<T> entry = entries.get(target.toLowerCase());
if (entry == null) return false;
if (entry.getExpiration() != null && entry.getExpiration().before(new Date())) {
pardon(target);
return false;
}
return true;
}
@Override
public void pardon(@NotNull T target) {
pardon(targetToString(target));
}
@Override
public void pardon(@NotNull String target) {
entries.remove(target.toLowerCase());
try {
var proto = BanEntryProto.newBuilder().setTarget(target).build();
NativeBridgeFfi.setBanEntry(SetBanEntryRequest.newBuilder()
.setBanType(banType)
.setEntry(proto)
.setRemove(true)
.build());
} catch (Throwable ignored) {}
}
private String targetToString(T target) {
if (target instanceof InetAddress addr) {
return addr.getHostAddress();
}
return String.valueOf(target);
}
}

View File

@ -0,0 +1,136 @@
package org.patchbukkit.boss;
import java.util.ArrayList;
import java.util.Collections;
import java.util.EnumSet;
import java.util.List;
import java.util.Set;
import java.util.concurrent.CopyOnWriteArrayList;
import org.bukkit.boss.BarColor;
import org.bukkit.boss.BarFlag;
import org.bukkit.boss.BarStyle;
import org.bukkit.boss.BossBar;
import org.bukkit.entity.Player;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
public class PatchBukkitBossBar implements BossBar {
private String title;
private BarColor color;
private BarStyle style;
private final Set<BarFlag> flags = EnumSet.noneOf(BarFlag.class);
private double progress = 1.0;
private boolean visible = true;
private final List<Player> players = new CopyOnWriteArrayList<>();
public PatchBukkitBossBar(
@Nullable String title,
@NotNull BarColor color,
@NotNull BarStyle style,
@NotNull BarFlag... flags
) {
this.title = title != null ? title : "";
this.color = color;
this.style = style;
if (flags != null) {
Collections.addAll(this.flags, flags);
}
}
@Override
public @NotNull String getTitle() {
return this.title;
}
@Override
public void setTitle(@Nullable String title) {
this.title = title != null ? title : "";
}
@Override
public @NotNull BarColor getColor() {
return this.color;
}
@Override
public void setColor(@NotNull BarColor color) {
this.color = color;
}
@Override
public @NotNull BarStyle getStyle() {
return this.style;
}
@Override
public void setStyle(@NotNull BarStyle style) {
this.style = style;
}
@Override
public void removeFlag(@NotNull BarFlag flag) {
this.flags.remove(flag);
}
@Override
public void addFlag(@NotNull BarFlag flag) {
this.flags.add(flag);
}
@Override
public boolean hasFlag(@NotNull BarFlag flag) {
return this.flags.contains(flag);
}
@Override
public void setProgress(double progress) {
this.progress = Math.max(0.0, Math.min(1.0, progress));
}
@Override
public double getProgress() {
return this.progress;
}
@Override
public void addPlayer(@NotNull Player player) {
if (!this.players.contains(player)) {
this.players.add(player);
}
}
@Override
public void removePlayer(@NotNull Player player) {
this.players.remove(player);
}
@Override
public void removeAll() {
this.players.clear();
}
@Override
public @NotNull List<Player> getPlayers() {
return Collections.unmodifiableList(new ArrayList<>(this.players));
}
@Override
public void setVisible(boolean visible) {
this.visible = visible;
}
@Override
public boolean isVisible() {
return this.visible;
}
@Override
public void show() {
setVisible(true);
}
@Override
public void hide() {
setVisible(false);
}
}

View File

@ -0,0 +1,29 @@
package org.patchbukkit.boss;
import org.bukkit.NamespacedKey;
import org.bukkit.boss.BarColor;
import org.bukkit.boss.BarFlag;
import org.bukkit.boss.BarStyle;
import org.bukkit.boss.KeyedBossBar;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
public class PatchBukkitKeyedBossBar extends PatchBukkitBossBar implements KeyedBossBar {
private final NamespacedKey key;
public PatchBukkitKeyedBossBar(
@NotNull NamespacedKey key,
@Nullable String title,
@NotNull BarColor color,
@NotNull BarStyle style,
@NotNull BarFlag... flags
) {
super(title, color, style, flags);
this.key = key;
}
@Override
public @NotNull NamespacedKey getKey() {
return this.key;
}
}

View File

@ -0,0 +1,83 @@
package org.patchbukkit.datapack;
import io.papermc.paper.datapack.Datapack;
import io.papermc.paper.datapack.DatapackManager;
import io.papermc.paper.datapack.DatapackSource;
import java.util.Collection;
import java.util.Collections;
import java.util.Set;
import net.kyori.adventure.text.Component;
import org.bukkit.FeatureFlag;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
public class PatchBukkitDatapackManager implements DatapackManager {
private static final Datapack VANILLA = new Datapack() {
@Override
public @NotNull String getName() {
return "vanilla";
}
@Override
public @NotNull Component getTitle() {
return Component.text("Vanilla");
}
@Override
public @NotNull Component getDescription() {
return Component.text("The default data pack");
}
@Override
public boolean isRequired() {
return true;
}
@Override
public boolean isEnabled() {
return true;
}
@Override
public void setEnabled(boolean enabled) {}
@Override
public @NotNull Component computeDisplayName() {
return Component.text("Vanilla");
}
@Override
public @NotNull Compatibility getCompatibility() {
return Compatibility.COMPATIBLE;
}
@Override
public @NotNull Set<FeatureFlag> getRequiredFeatures() {
return Collections.emptySet();
}
@Override
public @NotNull DatapackSource getSource() {
return DatapackSource.DEFAULT;
}
};
@Override
public void refreshPacks() {}
@Override
public @Nullable Datapack getPack(@NotNull String name) {
if ("vanilla".equalsIgnoreCase(name)) return VANILLA;
return null;
}
@Override
public @NotNull Collection<Datapack> getPacks() {
return Collections.singletonList(VANILLA);
}
@Override
public @NotNull Collection<Datapack> getEnabledPacks() {
return Collections.singletonList(VANILLA);
}
}

View File

@ -83,6 +83,16 @@ public class PatchBukkitEntity implements Entity {
return this.perm; return this.perm;
} }
private Location cachedLocation;
private EntityType entityType = EntityType.UNKNOWN;
public static Entity create(UUID uuid, EntityType type, Location loc) {
PatchBukkitEntity entity = new PatchBukkitEntity(uuid, type != null ? type.name() : "entity");
entity.entityType = type != null ? type : EntityType.UNKNOWN;
entity.cachedLocation = loc != null ? loc.clone() : new Location(null, 0, 0, 0);
return entity;
}
public PatchBukkitEntity( public PatchBukkitEntity(
UUID uuid, UUID uuid,
String name String name
@ -263,10 +273,15 @@ public class PatchBukkitEntity implements Entity {
@Override @Override
public @NotNull Location getLocation() { public @NotNull Location getLocation() {
var location = NativeBridgeFfi.getLocation(BridgeUtils.convertUuid(this.uuid)); try {
var world = PatchBukkitWorld.getOrCreate(BridgeUtils.convertUuid(location.getWorld().getUuid())); var location = NativeBridgeFfi.getLocation(BridgeUtils.convertUuid(this.uuid));
var position = location.getPosition(); if (location != null && location.hasWorld() && location.hasPosition()) {
return new Location(world, position.getX(), position.getY(), position.getZ(), location.getYaw(), location.getPitch()); var world = PatchBukkitWorld.getOrCreate(BridgeUtils.convertUuid(location.getWorld().getUuid()));
var position = location.getPosition();
return new Location(world, position.getX(), position.getY(), position.getZ(), location.getYaw(), location.getPitch());
}
} catch (Throwable ignored) {}
return this.cachedLocation != null ? this.cachedLocation.clone() : new Location(null, 0, 0, 0);
} }
@Override @Override
@ -646,8 +661,7 @@ public class PatchBukkitEntity implements Entity {
@Override @Override
public @NotNull EntityType getType() { public @NotNull EntityType getType() {
// TODO Auto-generated method stub return this.entityType != null ? this.entityType : EntityType.UNKNOWN;
throw new UnsupportedOperationException("Unimplemented method 'getType'");
} }
@Override @Override

View File

@ -0,0 +1,42 @@
package org.patchbukkit.entity;
import org.bukkit.Location;
import org.bukkit.World;
import org.bukkit.entity.Entity;
import org.bukkit.entity.EntityFactory;
import org.bukkit.entity.EntitySnapshot;
import org.bukkit.entity.EntityType;
import org.jetbrains.annotations.NotNull;
public class PatchBukkitEntityFactory implements EntityFactory {
public static final PatchBukkitEntityFactory INSTANCE = new PatchBukkitEntityFactory();
@Override
public @NotNull EntitySnapshot createEntitySnapshot(@NotNull String input) throws IllegalArgumentException {
return new EntitySnapshot() {
@Override
public @NotNull Entity createEntity(@NotNull World world) {
return world.spawnEntity(world.getSpawnLocation(), getEntityType());
}
@Override
public @NotNull Entity createEntity(@NotNull Location location) {
return location.getWorld().spawnEntity(location, getEntityType());
}
@Override
public @NotNull EntityType getEntityType() {
try {
return EntityType.valueOf(input.toUpperCase());
} catch (Throwable e) {
return EntityType.PIG;
}
}
@Override
public @NotNull String getAsString() {
return input;
}
};
}
}

View File

@ -4,6 +4,7 @@ import java.util.ArrayList;
import java.util.Collection; import java.util.Collection;
import java.util.Collections; import java.util.Collections;
import java.util.HashMap; import java.util.HashMap;
import java.util.HashSet;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Set; import java.util.Set;
@ -474,387 +475,421 @@ public class PatchBukkitLivingEntity
return hadEffects; return hadEffects;
} }
private boolean removeWhenFarAway = false;
private boolean canPickupItems = false;
private boolean leashed = false;
private Entity leashHolder = null;
private boolean gliding = false;
private boolean swimming = false;
private boolean riptiding = false;
private boolean sleeping = false;
private boolean climbing = false;
private boolean hasAI = true;
private boolean collidable = true;
private boolean jumping = false;
private float hurtDirection = 0.0f;
private float bodyYaw = 0.0f;
private final Set<UUID> collidableExemptions = new HashSet<>();
private final Map<MemoryKey<?>, Object> memories = new HashMap<>();
private ItemStack activeItem = ItemStack.empty();
private EquipmentSlot activeItemHand = EquipmentSlot.HAND;
private int activeItemRemainingTime = 0;
private int activeItemUsedTime = 0;
private Key waypointStyle;
private Color waypointColor;
@Override @Override
public boolean hasLineOfSight(@NotNull Entity other) { public boolean hasLineOfSight(@NotNull Entity other) {
// TODO Auto-generated method stub if (other == null || other.getWorld() != getWorld()) return false;
throw new UnsupportedOperationException("Unimplemented method 'hasLineOfSight'"); RayTraceResult result = getWorld().rayTraceBlocks(getEyeLocation(), other.getLocation().toVector().subtract(getEyeLocation().toVector()), getEyeLocation().distance(other.getLocation()), FluidCollisionMode.NEVER);
return result == null || result.getHitBlock() == null;
} }
@Override @Override
public boolean hasLineOfSight(@NotNull Location location) { public boolean hasLineOfSight(@NotNull Location location) {
// TODO Auto-generated method stub if (location == null || location.getWorld() != getWorld()) return false;
throw new UnsupportedOperationException("Unimplemented method 'hasLineOfSight'"); RayTraceResult result = getWorld().rayTraceBlocks(getEyeLocation(), location.toVector().subtract(getEyeLocation().toVector()), getEyeLocation().distance(location), FluidCollisionMode.NEVER);
return result == null || result.getHitBlock() == null;
} }
@Override @Override
public boolean getRemoveWhenFarAway() { public boolean getRemoveWhenFarAway() {
// TODO Auto-generated method stub return this.removeWhenFarAway;
throw new UnsupportedOperationException("Unimplemented method 'getRemoveWhenFarAway'");
} }
@Override @Override
public void setRemoveWhenFarAway(boolean remove) { public void setRemoveWhenFarAway(boolean remove) {
// TODO Auto-generated method stub this.removeWhenFarAway = remove;
throw new UnsupportedOperationException("Unimplemented method 'setRemoveWhenFarAway'");
} }
@Override @Override
public @Nullable EntityEquipment getEquipment() { public @Nullable EntityEquipment getEquipment() {
// TODO Auto-generated method stub return null;
throw new UnsupportedOperationException("Unimplemented method 'getEquipment'");
} }
@Override @Override
public void setCanPickupItems(boolean pickup) { public void setCanPickupItems(boolean pickup) {
// TODO Auto-generated method stub this.canPickupItems = pickup;
throw new UnsupportedOperationException("Unimplemented method 'setCanPickupItems'");
} }
@Override @Override
public boolean getCanPickupItems() { public boolean getCanPickupItems() {
// TODO Auto-generated method stub return this.canPickupItems;
throw new UnsupportedOperationException("Unimplemented method 'getCanPickupItems'");
} }
@Override @Override
public boolean isLeashed() { public boolean isLeashed() {
// TODO Auto-generated method stub return this.leashed;
throw new UnsupportedOperationException("Unimplemented method 'isLeashed'");
} }
@Override @Override
public @NotNull Entity getLeashHolder() throws IllegalStateException { public @NotNull Entity getLeashHolder() throws IllegalStateException {
// TODO Auto-generated method stub if (!this.leashed || this.leashHolder == null) {
throw new UnsupportedOperationException("Unimplemented method 'getLeashHolder'"); throw new IllegalStateException("Entity is not leashed");
}
return this.leashHolder;
} }
@Override @Override
public boolean setLeashHolder(@Nullable Entity holder) { public boolean setLeashHolder(@Nullable Entity holder) {
// TODO Auto-generated method stub this.leashHolder = holder;
throw new UnsupportedOperationException("Unimplemented method 'setLeashHolder'"); this.leashed = holder != null;
return true;
} }
@Override @Override
public boolean isGliding() { public boolean isGliding() {
// TODO Auto-generated method stub return this.gliding;
throw new UnsupportedOperationException("Unimplemented method 'isGliding'");
} }
@Override @Override
public void setGliding(boolean gliding) { public void setGliding(boolean gliding) {
// TODO Auto-generated method stub this.gliding = gliding;
throw new UnsupportedOperationException("Unimplemented method 'setGliding'");
} }
@Override @Override
public boolean isSwimming() { public boolean isSwimming() {
// TODO Auto-generated method stub return this.swimming;
throw new UnsupportedOperationException("Unimplemented method 'isSwimming'");
} }
@Override @Override
public void setSwimming(boolean swimming) { public void setSwimming(boolean swimming) {
// TODO Auto-generated method stub this.swimming = swimming;
throw new UnsupportedOperationException("Unimplemented method 'setSwimming'");
} }
@Override @Override
public boolean isRiptiding() { public boolean isRiptiding() {
// TODO Auto-generated method stub return this.riptiding;
throw new UnsupportedOperationException("Unimplemented method 'isRiptiding'");
} }
@Override @Override
public void setRiptiding(boolean riptiding) { public void setRiptiding(boolean riptiding) {
// TODO Auto-generated method stub this.riptiding = riptiding;
throw new UnsupportedOperationException("Unimplemented method 'setRiptiding'");
} }
@Override @Override
public boolean isSleeping() { public boolean isSleeping() {
// TODO Auto-generated method stub return this.sleeping;
throw new UnsupportedOperationException("Unimplemented method 'isSleeping'");
} }
@Override @Override
public boolean isClimbing() { public boolean isClimbing() {
// TODO Auto-generated method stub return this.climbing;
throw new UnsupportedOperationException("Unimplemented method 'isClimbing'");
} }
@Override @Override
public void setAI(boolean ai) { public void setAI(boolean ai) {
// TODO Auto-generated method stub this.hasAI = ai;
throw new UnsupportedOperationException("Unimplemented method 'setAI'");
} }
@Override @Override
public boolean hasAI() { public boolean hasAI() {
// TODO Auto-generated method stub return this.hasAI;
throw new UnsupportedOperationException("Unimplemented method 'hasAI'");
} }
@Override @Override
public void attack(@NotNull Entity target) { public void attack(@NotNull Entity target) {
// TODO Auto-generated method stub if (target instanceof LivingEntity living) {
throw new UnsupportedOperationException("Unimplemented method 'attack'"); living.damage(1.0, this);
}
} }
@Override @Override
public void swingMainHand() { public void swingMainHand() {
// TODO Auto-generated method stub
throw new UnsupportedOperationException("Unimplemented method 'swingMainHand'");
} }
@Override @Override
public void swingOffHand() { public void swingOffHand() {
// TODO Auto-generated method stub
throw new UnsupportedOperationException("Unimplemented method 'swingOffHand'");
} }
@Override @Override
public void playHurtAnimation(float yaw) { public void playHurtAnimation(float yaw) {
// TODO Auto-generated method stub
throw new UnsupportedOperationException("Unimplemented method 'playHurtAnimation'");
} }
@Override @Override
public void setCollidable(boolean collidable) { public void setCollidable(boolean collidable) {
// TODO Auto-generated method stub this.collidable = collidable;
throw new UnsupportedOperationException("Unimplemented method 'setCollidable'");
} }
@Override @Override
public boolean isCollidable() { public boolean isCollidable() {
// TODO Auto-generated method stub return this.collidable;
throw new UnsupportedOperationException("Unimplemented method 'isCollidable'");
} }
@Override @Override
public @NotNull Set<UUID> getCollidableExemptions() { public @NotNull Set<UUID> getCollidableExemptions() {
// TODO Auto-generated method stub return this.collidableExemptions;
throw new UnsupportedOperationException("Unimplemented method 'getCollidableExemptions'");
} }
@Override @Override
public <T> @Nullable T getMemory(@NotNull MemoryKey<T> memoryKey) { public <T> @Nullable T getMemory(@NotNull MemoryKey<T> memoryKey) {
// TODO Auto-generated method stub return (T) this.memories.get(memoryKey);
throw new UnsupportedOperationException("Unimplemented method 'getMemory'");
} }
@Override @Override
public <T> void setMemory(@NotNull MemoryKey<T> memoryKey, @Nullable T memoryValue) { public <T> void setMemory(@NotNull MemoryKey<T> memoryKey, @Nullable T memoryValue) {
// TODO Auto-generated method stub if (memoryValue == null) {
throw new UnsupportedOperationException("Unimplemented method 'setMemory'"); this.memories.remove(memoryKey);
} else {
this.memories.put(memoryKey, memoryValue);
}
} }
@Override @Override
public @Nullable Sound getHurtSound() { public @Nullable Sound getHurtSound() {
// TODO Auto-generated method stub return Sound.ENTITY_GENERIC_HURT;
throw new UnsupportedOperationException("Unimplemented method 'getHurtSound'");
} }
@Override @Override
public @Nullable Sound getDeathSound() { public @Nullable Sound getDeathSound() {
// TODO Auto-generated method stub return Sound.ENTITY_GENERIC_DEATH;
throw new UnsupportedOperationException("Unimplemented method 'getDeathSound'");
} }
@Override @Override
public @NotNull Sound getFallDamageSound(int fallHeight) { public @NotNull Sound getFallDamageSound(int fallHeight) {
// TODO Auto-generated method stub return fallHeight > 4 ? Sound.ENTITY_GENERIC_BIG_FALL : Sound.ENTITY_GENERIC_SMALL_FALL;
throw new UnsupportedOperationException("Unimplemented method 'getFallDamageSound'");
} }
@Override @Override
public @NotNull Sound getFallDamageSoundSmall() { public @NotNull Sound getFallDamageSoundSmall() {
// TODO Auto-generated method stub return Sound.ENTITY_GENERIC_SMALL_FALL;
throw new UnsupportedOperationException("Unimplemented method 'getFallDamageSoundSmall'");
} }
@Override @Override
public @NotNull Sound getFallDamageSoundBig() { public @NotNull Sound getFallDamageSoundBig() {
// TODO Auto-generated method stub return Sound.ENTITY_GENERIC_BIG_FALL;
throw new UnsupportedOperationException("Unimplemented method 'getFallDamageSoundBig'");
} }
@Override @Override
public @NotNull Sound getDrinkingSound(@NotNull ItemStack itemStack) { public @NotNull Sound getDrinkingSound(@NotNull ItemStack itemStack) {
// TODO Auto-generated method stub return Sound.ENTITY_GENERIC_DRINK;
throw new UnsupportedOperationException("Unimplemented method 'getDrinkingSound'");
} }
@Override @Override
public @NotNull Sound getEatingSound(@NotNull ItemStack itemStack) { public @NotNull Sound getEatingSound(@NotNull ItemStack itemStack) {
// TODO Auto-generated method stub return Sound.ENTITY_GENERIC_EAT;
throw new UnsupportedOperationException("Unimplemented method 'getEatingSound'");
} }
@Override @Override
public boolean canBreatheUnderwater() { public boolean canBreatheUnderwater() {
// TODO Auto-generated method stub return hasPotionEffect(PotionEffectType.WATER_BREATHING);
throw new UnsupportedOperationException("Unimplemented method 'canBreatheUnderwater'");
} }
@Override @Override
public @NotNull EntityCategory getCategory() { public @NotNull EntityCategory getCategory() {
// TODO Auto-generated method stub return EntityCategory.NONE;
throw new UnsupportedOperationException("Unimplemented method 'getCategory'");
} }
@Override @Override
public float getSidewaysMovement() { public float getSidewaysMovement() {
// TODO Auto-generated method stub return 0.0f;
throw new UnsupportedOperationException("Unimplemented method 'getSidewaysMovement'");
} }
@Override @Override
public float getUpwardsMovement() { public float getUpwardsMovement() {
// TODO Auto-generated method stub return 0.0f;
throw new UnsupportedOperationException("Unimplemented method 'getUpwardsMovement'");
} }
@Override @Override
public float getForwardsMovement() { public float getForwardsMovement() {
// TODO Auto-generated method stub return 0.0f;
throw new UnsupportedOperationException("Unimplemented method 'getForwardsMovement'");
} }
@Override @Override
public void startUsingItem(@NotNull EquipmentSlot hand) { public void startUsingItem(@NotNull EquipmentSlot hand) {
// TODO Auto-generated method stub this.activeItemHand = hand;
throw new UnsupportedOperationException("Unimplemented method 'startUsingItem'"); this.activeItemRemainingTime = 72000;
this.activeItemUsedTime = 0;
} }
@Override @Override
public void completeUsingActiveItem() { public void completeUsingActiveItem() {
// TODO Auto-generated method stub this.activeItemRemainingTime = 0;
throw new UnsupportedOperationException("Unimplemented method 'completeUsingActiveItem'"); this.activeItem = ItemStack.empty();
} }
@Override @Override
public @NotNull ItemStack getActiveItem() { public @NotNull ItemStack getActiveItem() {
// TODO Auto-generated method stub return this.activeItem != null ? this.activeItem : ItemStack.empty();
throw new UnsupportedOperationException("Unimplemented method 'getActiveItem'");
} }
@Override @Override
public void clearActiveItem() { public void clearActiveItem() {
// TODO Auto-generated method stub this.activeItem = ItemStack.empty();
throw new UnsupportedOperationException("Unimplemented method 'clearActiveItem'"); this.activeItemRemainingTime = 0;
this.activeItemUsedTime = 0;
} }
@Override @Override
public int getActiveItemRemainingTime() { public int getActiveItemRemainingTime() {
// TODO Auto-generated method stub return this.activeItemRemainingTime;
throw new UnsupportedOperationException("Unimplemented method 'getActiveItemRemainingTime'");
} }
@Override @Override
public void setActiveItemRemainingTime(@Range(from = 0, to = 2147483647) int ticks) { public void setActiveItemRemainingTime(@Range(from = 0, to = 2147483647) int ticks) {
// TODO Auto-generated method stub this.activeItemRemainingTime = ticks;
throw new UnsupportedOperationException("Unimplemented method 'setActiveItemRemainingTime'");
} }
@Override @Override
public boolean hasActiveItem() { public boolean hasActiveItem() {
// TODO Auto-generated method stub return this.activeItemRemainingTime > 0;
throw new UnsupportedOperationException("Unimplemented method 'hasActiveItem'");
} }
@Override @Override
public int getActiveItemUsedTime() { public int getActiveItemUsedTime() {
// TODO Auto-generated method stub return this.activeItemUsedTime;
throw new UnsupportedOperationException("Unimplemented method 'getActiveItemUsedTime'");
} }
@Override @Override
public @NotNull EquipmentSlot getActiveItemHand() { public @NotNull EquipmentSlot getActiveItemHand() {
// TODO Auto-generated method stub return this.activeItemHand != null ? this.activeItemHand : EquipmentSlot.HAND;
throw new UnsupportedOperationException("Unimplemented method 'getActiveItemHand'");
} }
@Override @Override
public boolean isJumping() { public boolean isJumping() {
// TODO Auto-generated method stub return this.jumping;
throw new UnsupportedOperationException("Unimplemented method 'isJumping'");
} }
@Override @Override
public void setJumping(boolean jumping) { public void setJumping(boolean jumping) {
// TODO Auto-generated method stub this.jumping = jumping;
throw new UnsupportedOperationException("Unimplemented method 'setJumping'");
} }
@Override @Override
public void playPickupItemAnimation(@NotNull Item item, int quantity) { public void playPickupItemAnimation(@NotNull Item item, int quantity) {
// TODO Auto-generated method stub
throw new UnsupportedOperationException("Unimplemented method 'playPickupItemAnimation'");
} }
@Override @Override
public float getHurtDirection() { public float getHurtDirection() {
// TODO Auto-generated method stub return this.hurtDirection;
throw new UnsupportedOperationException("Unimplemented method 'getHurtDirection'");
} }
@Override @Override
public void setHurtDirection(float hurtDirection) { public void setHurtDirection(float hurtDirection) {
// TODO Auto-generated method stub this.hurtDirection = hurtDirection;
throw new UnsupportedOperationException("Unimplemented method 'setHurtDirection'");
} }
@Override @Override
public void knockback(double strength, double directionX, double directionZ) { public void knockback(double strength, double directionX, double directionZ) {
// TODO Auto-generated method stub Vector vel = getVelocity();
throw new UnsupportedOperationException("Unimplemented method 'knockback'"); vel.setX(vel.getX() + directionX * strength);
vel.setZ(vel.getZ() + directionZ * strength);
setVelocity(vel);
} }
@Override @Override
public void broadcastSlotBreak(@NotNull EquipmentSlot slot) { public void broadcastSlotBreak(@NotNull EquipmentSlot slot) {
// TODO Auto-generated method stub
throw new UnsupportedOperationException("Unimplemented method 'broadcastSlotBreak'");
} }
@Override @Override
public void broadcastSlotBreak(@NotNull EquipmentSlot slot, @NotNull Collection<Player> players) { public void broadcastSlotBreak(@NotNull EquipmentSlot slot, @NotNull Collection<Player> players) {
// TODO Auto-generated method stub
throw new UnsupportedOperationException("Unimplemented method 'broadcastSlotBreak'");
} }
@Override @Override
public @NotNull ItemStack damageItemStack(@NotNull ItemStack stack, int amount) { public @NotNull ItemStack damageItemStack(@NotNull ItemStack stack, int amount) {
// TODO Auto-generated method stub if (stack == null || stack.isEmpty()) return ItemStack.empty();
throw new UnsupportedOperationException("Unimplemented method 'damageItemStack'"); return stack;
} }
@Override @Override
public void damageItemStack(@NotNull EquipmentSlot slot, int amount) { public void damageItemStack(@NotNull EquipmentSlot slot, int amount) {
// TODO Auto-generated method stub
throw new UnsupportedOperationException("Unimplemented method 'damageItemStack'");
} }
@Override @Override
public float getBodyYaw() { public float getBodyYaw() {
// TODO Auto-generated method stub return this.bodyYaw;
throw new UnsupportedOperationException("Unimplemented method 'getBodyYaw'");
} }
@Override @Override
public void setBodyYaw(float bodyYaw) { public void setBodyYaw(float bodyYaw) {
// TODO Auto-generated method stub this.bodyYaw = bodyYaw;
throw new UnsupportedOperationException("Unimplemented method 'setBodyYaw'");
} }
@Override @Override
public boolean canUseEquipmentSlot(@NotNull EquipmentSlot slot) { public boolean canUseEquipmentSlot(@NotNull EquipmentSlot slot) {
// TODO Auto-generated method stub return true;
throw new UnsupportedOperationException("Unimplemented method 'canUseEquipmentSlot'");
} }
public @NotNull CombatTracker getCombatTracker() { @Override
// TODO Auto-generated method stub public @NotNull io.papermc.paper.world.damagesource.CombatTracker getCombatTracker() {
throw new UnsupportedOperationException("Unimplemented method 'getCombatTracker'"); return new io.papermc.paper.world.damagesource.CombatTracker() {
@Override
public org.bukkit.entity.LivingEntity getEntity() {
return PatchBukkitLivingEntity.this;
}
@Override
public java.util.List<io.papermc.paper.world.damagesource.CombatEntry> getCombatEntries() {
return Collections.emptyList();
}
@Override
public void setCombatEntries(java.util.List<io.papermc.paper.world.damagesource.CombatEntry> entries) {
}
@Override
public io.papermc.paper.world.damagesource.CombatEntry computeMostSignificantFall() {
return null;
}
@Override
public boolean isInCombat() {
return false;
}
@Override
public boolean isTakingDamage() {
return false;
}
@Override
public int getCombatDuration() {
return 0;
}
@Override
public void addCombatEntry(io.papermc.paper.world.damagesource.CombatEntry entry) {
}
@Override
public net.kyori.adventure.text.Component getDeathMessage() {
return net.kyori.adventure.text.Component.empty();
}
@Override
public void resetCombatState() {
}
@Override
public io.papermc.paper.world.damagesource.FallLocationType calculateFallLocationType() {
return null;
}
@Override
public int getLastDamageTime() {
return 0;
}
};
} }
@Override @Override
@ -867,41 +902,33 @@ public class PatchBukkitLivingEntity
} }
public void setWaypointStyle(@Nullable Key key) { public void setWaypointStyle(@Nullable Key key) {
// TODO Auto-generated method stub this.waypointStyle = key;
throw new UnsupportedOperationException("Unimplemented method 'setWaypointStyle'");
} }
public void setWaypointColor(@Nullable Color color) { public void setWaypointColor(@Nullable Color color) {
// TODO Auto-generated method stub this.waypointColor = color;
throw new UnsupportedOperationException("Unimplemented method 'setWaypointColor'");
} }
public @NotNull Key getWaypointStyle() { public @NotNull Key getWaypointStyle() {
// TODO Auto-generated method stub return this.waypointStyle != null ? this.waypointStyle : Key.key("minecraft", "default");
throw new UnsupportedOperationException("Unimplemented method 'getWaypointStyle'");
} }
public @Nullable Color getWaypointColor() { public @Nullable Color getWaypointColor() {
// TODO Auto-generated method stub return this.waypointColor;
throw new UnsupportedOperationException("Unimplemented method 'getWaypointColor'");
} }
public void kill(DamageSource damageSource) { public void kill(DamageSource damageSource) {
// TODO Auto-generated method stub setHealth(0.0);
throw new UnsupportedOperationException("Unimplemented method 'kill'");
} }
public float getSoundVolume() { public float getSoundVolume() {
// TODO Auto-generated method stub return 1.0f;
throw new UnsupportedOperationException("Unimplemented method 'getSoundVolume'");
} }
public float getSoundPitch() { public float getSoundPitch() {
// TODO Auto-generated method stub return 1.0f;
throw new UnsupportedOperationException("Unimplemented method 'getSoundPitch'");
} }
public @Nullable Sound getHurtSound(@NotNull DamageSource damageSource) { public @Nullable Sound getHurtSound(@NotNull DamageSource damageSource) {
// TODO Auto-generated method stub return Sound.ENTITY_GENERIC_HURT;
throw new UnsupportedOperationException("Unimplemented method 'getHurtSound'");
}} }}

View File

@ -0,0 +1,75 @@
package org.patchbukkit.entity;
import com.destroystokyo.paper.entity.ai.Goal;
import com.destroystokyo.paper.entity.ai.GoalKey;
import com.destroystokyo.paper.entity.ai.GoalType;
import com.destroystokyo.paper.entity.ai.MobGoals;
import java.util.Collection;
import java.util.Collections;
import org.bukkit.entity.Mob;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
public class PatchBukkitMobGoals implements MobGoals {
public static final PatchBukkitMobGoals INSTANCE = new PatchBukkitMobGoals();
@Override
public <T extends Mob> void addGoal(@NotNull T mob, int priority, @NotNull Goal<T> goal) {}
@Override
public <T extends Mob> void removeGoal(@NotNull T mob, @NotNull Goal<T> goal) {}
@Override
public <T extends Mob> void removeAllGoals(@NotNull T mob) {}
@Override
public <T extends Mob> void removeAllGoals(@NotNull T mob, @NotNull GoalType type) {}
@Override
public <T extends Mob> void removeGoal(@NotNull T mob, @NotNull GoalKey<T> key) {}
@Override
public <T extends Mob> boolean hasGoal(@NotNull T mob, @NotNull GoalKey<T> key) {
return false;
}
@Override
public <T extends Mob> @Nullable Goal<T> getGoal(@NotNull T mob, @NotNull GoalKey<T> key) {
return null;
}
@Override
public <T extends Mob> @NotNull Collection<Goal<T>> getGoals(@NotNull T mob, @NotNull GoalKey<T> key) {
return Collections.emptyList();
}
@Override
public <T extends Mob> @NotNull Collection<Goal<T>> getAllGoals(@NotNull T mob) {
return Collections.emptyList();
}
@Override
public <T extends Mob> @NotNull Collection<Goal<T>> getAllGoals(@NotNull T mob, @NotNull GoalType type) {
return Collections.emptyList();
}
@Override
public <T extends Mob> @NotNull Collection<Goal<T>> getAllGoalsWithout(@NotNull T mob, @NotNull GoalType type) {
return Collections.emptyList();
}
@Override
public <T extends Mob> @NotNull Collection<Goal<T>> getRunningGoals(@NotNull T mob) {
return Collections.emptyList();
}
@Override
public <T extends Mob> @NotNull Collection<Goal<T>> getRunningGoals(@NotNull T mob, @NotNull GoalType type) {
return Collections.emptyList();
}
@Override
public <T extends Mob> @NotNull Collection<Goal<T>> getRunningGoalsWithout(@NotNull T mob, @NotNull GoalType type) {
return Collections.emptyList();
}
}

View File

@ -20,6 +20,14 @@ public class PatchBukkitOfflinePlayer implements OfflinePlayer {
this.name = name; this.name = name;
} }
public PatchBukkitOfflinePlayer(UUID uuid) {
this(uuid, null);
}
public PatchBukkitOfflinePlayer(String name) {
this(null, name);
}
@Override @Override
public boolean isOnline() { public boolean isOnline() {
return getPlayer() != null; return getPlayer() != null;

View File

@ -0,0 +1,358 @@
package org.patchbukkit.inventory;
import org.bukkit.Location;
import org.bukkit.Material;
import org.bukkit.entity.HumanEntity;
import org.bukkit.event.inventory.InventoryType;
import org.bukkit.inventory.Inventory;
import org.bukkit.inventory.InventoryHolder;
import org.bukkit.inventory.ItemStack;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.*;
public class PatchBukkitInventory implements Inventory {
private final InventoryHolder holder;
private final int size;
private final String title;
private final InventoryType type;
private final ItemStack[] contents;
private int maxStackSize = 64;
public PatchBukkitInventory(@Nullable InventoryHolder holder, int size, @NotNull String title) {
this(holder, size, title, InventoryType.CHEST);
}
public PatchBukkitInventory(@Nullable InventoryHolder holder, @NotNull InventoryType type) {
this(holder, type.getDefaultSize(), type.getDefaultTitle(), type);
}
public PatchBukkitInventory(@Nullable InventoryHolder holder, @NotNull InventoryType type, @NotNull String title) {
this(holder, type.getDefaultSize(), title, type);
}
public PatchBukkitInventory(@Nullable InventoryHolder holder, @NotNull InventoryType type, @NotNull net.kyori.adventure.text.Component title) {
this(holder, type.getDefaultSize(), net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer.legacySection().serialize(title), type);
}
public PatchBukkitInventory(@Nullable InventoryHolder holder, int size, @NotNull net.kyori.adventure.text.Component title) {
this(holder, size, net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer.legacySection().serialize(title), InventoryType.CHEST);
}
public PatchBukkitInventory(@Nullable InventoryHolder holder, int size) {
this(holder, size, "Inventory", InventoryType.CHEST);
}
public PatchBukkitInventory(@Nullable InventoryHolder holder, int size, @NotNull String title, @NotNull InventoryType type) {
this.holder = holder;
this.size = size;
this.title = title;
this.type = type;
this.contents = new ItemStack[size];
Arrays.fill(this.contents, ItemStack.empty());
}
@Override
public int getSize() {
return this.size;
}
@Override
public int getMaxStackSize() {
return this.maxStackSize;
}
@Override
public void setMaxStackSize(int size) {
this.maxStackSize = size;
}
@Override
public @Nullable ItemStack getItem(int index) {
if (index < 0 || index >= this.size) return null;
return this.contents[index];
}
@Override
public void setItem(int index, @Nullable ItemStack item) {
if (index >= 0 && index < this.size) {
this.contents[index] = (item != null) ? item.clone() : ItemStack.empty();
}
}
@Override
public @NotNull HashMap<Integer, ItemStack> addItem(@NotNull ItemStack... items) throws IllegalArgumentException {
HashMap<Integer, ItemStack> leftover = new HashMap<>();
if (items == null) return leftover;
for (int i = 0; i < items.length; i++) {
ItemStack item = items[i];
if (item == null || item.isEmpty()) continue;
ItemStack remaining = item.clone();
for (int slot = 0; slot < this.size; slot++) {
ItemStack current = this.contents[slot];
if (current != null && !current.isEmpty() && current.isSimilar(remaining)) {
int canAdd = Math.min(remaining.getAmount(), current.getMaxStackSize() - current.getAmount());
if (canAdd > 0) {
current.setAmount(current.getAmount() + canAdd);
remaining.setAmount(remaining.getAmount() - canAdd);
if (remaining.getAmount() <= 0) break;
}
}
}
if (remaining.getAmount() > 0) {
for (int slot = 0; slot < this.size; slot++) {
ItemStack current = this.contents[slot];
if (current == null || current.isEmpty()) {
this.contents[slot] = remaining.clone();
remaining.setAmount(0);
break;
}
}
}
if (remaining.getAmount() > 0) {
leftover.put(i, remaining);
}
}
return leftover;
}
@Override
public @NotNull HashMap<Integer, ItemStack> removeItem(@NotNull ItemStack... items) throws IllegalArgumentException {
HashMap<Integer, ItemStack> leftover = new HashMap<>();
if (items == null) return leftover;
for (int i = 0; i < items.length; i++) {
ItemStack item = items[i];
if (item == null || item.isEmpty()) continue;
int toRemove = item.getAmount();
for (int slot = 0; slot < this.size; slot++) {
ItemStack current = this.contents[slot];
if (current != null && current.isSimilar(item)) {
int remove = Math.min(toRemove, current.getAmount());
current.setAmount(current.getAmount() - remove);
toRemove -= remove;
if (current.getAmount() <= 0) {
this.contents[slot] = ItemStack.empty();
}
if (toRemove <= 0) break;
}
}
if (toRemove > 0) {
ItemStack rem = item.clone();
rem.setAmount(toRemove);
leftover.put(i, rem);
}
}
return leftover;
}
@Override
public @NotNull HashMap<Integer, ItemStack> removeItemAnySlot(@NotNull ItemStack... items) throws IllegalArgumentException {
return removeItem(items);
}
@Override
public @NotNull ItemStack[] getContents() {
return this.contents.clone();
}
@Override
public void setContents(@NotNull ItemStack[] items) throws IllegalArgumentException {
if (items == null) return;
for (int i = 0; i < this.size; i++) {
if (i < items.length && items[i] != null) {
this.contents[i] = items[i].clone();
} else {
this.contents[i] = ItemStack.empty();
}
}
}
@Override
public @NotNull ItemStack[] getStorageContents() {
return getContents();
}
@Override
public void setStorageContents(@NotNull ItemStack[] items) throws IllegalArgumentException {
setContents(items);
}
@Override
public boolean contains(@NotNull Material material) throws IllegalArgumentException {
return first(material) != -1;
}
@Override
public boolean contains(@Nullable ItemStack item) {
return first(item) != -1;
}
@Override
public boolean contains(@NotNull Material material, int amount) throws IllegalArgumentException {
int count = 0;
for (ItemStack is : this.contents) {
if (is != null && is.getType() == material) {
count += is.getAmount();
if (count >= amount) return true;
}
}
return false;
}
@Override
public boolean contains(@Nullable ItemStack item, int amount) {
if (item == null) return false;
int count = 0;
for (ItemStack is : this.contents) {
if (is != null && is.isSimilar(item)) {
count += is.getAmount();
if (count >= amount) return true;
}
}
return false;
}
@Override
public boolean containsAtLeast(@Nullable ItemStack item, int amount) {
return contains(item, amount);
}
@Override
public @NotNull HashMap<Integer, ? extends ItemStack> all(@NotNull Material material) throws IllegalArgumentException {
HashMap<Integer, ItemStack> map = new HashMap<>();
for (int i = 0; i < this.size; i++) {
if (this.contents[i] != null && this.contents[i].getType() == material) {
map.put(i, this.contents[i]);
}
}
return map;
}
@Override
public @NotNull HashMap<Integer, ? extends ItemStack> all(@Nullable ItemStack item) {
HashMap<Integer, ItemStack> map = new HashMap<>();
if (item == null) return map;
for (int i = 0; i < this.size; i++) {
if (this.contents[i] != null && this.contents[i].isSimilar(item)) {
map.put(i, this.contents[i]);
}
}
return map;
}
@Override
public int first(@NotNull Material material) throws IllegalArgumentException {
for (int i = 0; i < this.size; i++) {
if (this.contents[i] != null && this.contents[i].getType() == material) {
return i;
}
}
return -1;
}
@Override
public int first(@NotNull ItemStack item) {
if (item == null) return -1;
for (int i = 0; i < this.size; i++) {
if (this.contents[i] != null && this.contents[i].isSimilar(item)) {
return i;
}
}
return -1;
}
@Override
public int firstEmpty() {
for (int i = 0; i < this.size; i++) {
if (this.contents[i] == null || this.contents[i].isEmpty()) {
return i;
}
}
return -1;
}
@Override
public boolean isEmpty() {
for (ItemStack is : this.contents) {
if (is != null && !is.isEmpty()) return false;
}
return true;
}
@Override
public void remove(@NotNull Material material) throws IllegalArgumentException {
for (int i = 0; i < this.size; i++) {
if (this.contents[i] != null && this.contents[i].getType() == material) {
this.contents[i] = ItemStack.empty();
}
}
}
@Override
public void remove(@NotNull ItemStack item) {
if (item == null) return;
for (int i = 0; i < this.size; i++) {
if (this.contents[i] != null && this.contents[i].isSimilar(item)) {
this.contents[i] = ItemStack.empty();
}
}
}
@Override
public void clear(int index) {
setItem(index, ItemStack.empty());
}
@Override
public void clear() {
Arrays.fill(this.contents, ItemStack.empty());
}
@Override
public int close() {
return 0;
}
@Override
public @NotNull List<HumanEntity> getViewers() {
return Collections.emptyList();
}
@Override
public @NotNull InventoryType getType() {
return this.type;
}
@Override
public @Nullable InventoryHolder getHolder() {
return this.holder;
}
@Override
public @Nullable InventoryHolder getHolder(boolean useSnapshot) {
return this.holder;
}
@Override
public @NotNull ListIterator<ItemStack> iterator() {
return Arrays.asList(this.contents).listIterator();
}
@Override
public @NotNull ListIterator<ItemStack> iterator(int index) {
return Arrays.asList(this.contents).listIterator(index);
}
@Override
public @Nullable Location getLocation() {
return null;
}
}

View File

@ -0,0 +1,143 @@
package org.patchbukkit.inventory;
import org.bukkit.entity.HumanEntity;
import org.bukkit.event.inventory.InventoryType;
import org.bukkit.event.inventory.InventoryType.SlotType;
import org.bukkit.inventory.Inventory;
import org.bukkit.inventory.InventoryView;
import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.MenuType;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
public class PatchBukkitInventoryView implements InventoryView {
private final HumanEntity player;
private final Inventory topInventory;
private String title;
public PatchBukkitInventoryView(@NotNull HumanEntity player, @NotNull Inventory topInventory) {
this.player = player;
this.topInventory = topInventory;
this.title = topInventory.getType().getDefaultTitle();
}
public PatchBukkitInventoryView(@NotNull HumanEntity player, @NotNull Inventory topInventory, @NotNull String title) {
this.player = player;
this.topInventory = topInventory;
this.title = title;
}
@Override
public @NotNull Inventory getTopInventory() {
return this.topInventory;
}
@Override
public @NotNull Inventory getBottomInventory() {
return this.player.getInventory();
}
@Override
public @NotNull HumanEntity getPlayer() {
return this.player;
}
@Override
public @NotNull InventoryType getType() {
return this.topInventory.getType();
}
@Override
public void setItem(int slot, @Nullable ItemStack item) {
if (slot >= 0 && slot < getTopInventory().getSize()) {
getTopInventory().setItem(slot, item);
} else if (slot >= getTopInventory().getSize()) {
getBottomInventory().setItem(slot - getTopInventory().getSize(), item);
}
}
@Override
public @Nullable ItemStack getItem(int slot) {
if (slot >= 0 && slot < getTopInventory().getSize()) {
return getTopInventory().getItem(slot);
} else if (slot >= getTopInventory().getSize()) {
return getBottomInventory().getItem(slot - getTopInventory().getSize());
}
return null;
}
@Override
public void setCursor(@Nullable ItemStack item) {
getPlayer().setItemOnCursor(item);
}
@Override
public @Nullable ItemStack getCursor() {
return getPlayer().getItemOnCursor();
}
@Override
public @Nullable Inventory getInventory(int rawSlot) {
if (rawSlot >= 0 && rawSlot < getTopInventory().getSize()) {
return getTopInventory();
} else if (rawSlot >= getTopInventory().getSize()) {
return getBottomInventory();
}
return null;
}
@Override
public int convertSlot(int rawSlot) {
if (rawSlot >= getTopInventory().getSize()) {
return rawSlot - getTopInventory().getSize();
}
return rawSlot;
}
@Override
public @NotNull SlotType getSlotType(int slot) {
if (slot < getTopInventory().getSize()) {
return SlotType.CONTAINER;
}
return SlotType.QUICKBAR;
}
@Override
public void open() {
}
@Override
public void close() {
getPlayer().closeInventory();
}
@Override
public int countSlots() {
return getTopInventory().getSize() + getBottomInventory().getSize();
}
@Override
public boolean setProperty(@NotNull Property prop, int value) {
return false;
}
@Override
public @NotNull String getTitle() {
return this.title != null ? this.title : getType().getDefaultTitle();
}
@Override
public @NotNull String getOriginalTitle() {
return getType().getDefaultTitle();
}
@Override
public void setTitle(@NotNull String title) {
this.title = title;
}
@Override
public @NotNull MenuType getMenuType() {
return MenuType.GENERIC_9X3;
}
}

View File

@ -0,0 +1,56 @@
package org.patchbukkit.inventory;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import net.kyori.adventure.text.Component;
import org.bukkit.entity.HumanEntity;
import org.bukkit.inventory.Merchant;
import org.bukkit.inventory.MerchantRecipe;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
public class PatchBukkitMerchant implements Merchant {
private final Component title;
private List<MerchantRecipe> recipes = new ArrayList<>();
private HumanEntity trader;
public PatchBukkitMerchant(@Nullable Component title) {
this.title = title != null ? title : Component.text("Merchant");
}
@Override
public @NotNull List<MerchantRecipe> getRecipes() {
return Collections.unmodifiableList(this.recipes);
}
@Override
public void setRecipes(@NotNull List<MerchantRecipe> recipes) {
this.recipes = new ArrayList<>(recipes);
}
@Override
public @NotNull MerchantRecipe getRecipe(int i) throws IndexOutOfBoundsException {
return this.recipes.get(i);
}
@Override
public void setRecipe(int i, @NotNull MerchantRecipe recipe) throws IndexOutOfBoundsException {
this.recipes.set(i, recipe);
}
@Override
public int getRecipeCount() {
return this.recipes.size();
}
@Override
public boolean isTrading() {
return this.trader != null;
}
@Override
public @Nullable HumanEntity getTrader() {
return this.trader;
}
}

View File

@ -0,0 +1,122 @@
package org.patchbukkit.map;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import org.bukkit.World;
import org.bukkit.map.MapRenderer;
import org.bukkit.map.MapView;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
public class PatchBukkitMapView implements MapView {
private final int id;
private Scale scale = Scale.NORMAL;
private int centerX;
private int centerZ;
private World world;
private final List<MapRenderer> renderers = new ArrayList<>();
private boolean trackingPosition = true;
private boolean unlimitedTracking = false;
private boolean locked = false;
public PatchBukkitMapView(int id, @Nullable World world) {
this.id = id;
this.world = world;
}
@Override
public int getId() {
return this.id;
}
@Override
public boolean isVirtual() {
return false;
}
@Override
public @NotNull Scale getScale() {
return this.scale;
}
@Override
public void setScale(@NotNull Scale scale) {
this.scale = scale;
}
@Override
public int getCenterX() {
return this.centerX;
}
@Override
public int getCenterZ() {
return this.centerZ;
}
@Override
public void setCenterX(int x) {
this.centerX = x;
}
@Override
public void setCenterZ(int z) {
this.centerZ = z;
}
@Override
public @Nullable World getWorld() {
return this.world;
}
@Override
public void setWorld(@Nullable World world) {
this.world = world;
}
@Override
public @NotNull List<MapRenderer> getRenderers() {
return Collections.unmodifiableList(this.renderers);
}
@Override
public void addRenderer(@NotNull MapRenderer renderer) {
this.renderers.add(renderer);
}
@Override
public boolean removeRenderer(@Nullable MapRenderer renderer) {
return this.renderers.remove(renderer);
}
@Override
public boolean isTrackingPosition() {
return this.trackingPosition;
}
@Override
public void setTrackingPosition(boolean trackingPosition) {
this.trackingPosition = trackingPosition;
}
@Override
public boolean isUnlimitedTracking() {
return this.unlimitedTracking;
}
@Override
public void setUnlimitedTracking(boolean unlimitedTracking) {
this.unlimitedTracking = unlimitedTracking;
}
@Override
public boolean isLocked() {
return this.locked;
}
@Override
public void setLocked(boolean locked) {
this.locked = locked;
}
}

View File

@ -0,0 +1,28 @@
package org.patchbukkit.potion;
import io.papermc.paper.potion.PotionMix;
import java.util.Collection;
import java.util.Collections;
import org.bukkit.NamespacedKey;
import org.bukkit.potion.PotionBrewer;
import org.bukkit.potion.PotionEffect;
import org.bukkit.potion.PotionType;
import org.jetbrains.annotations.NotNull;
public class PatchBukkitPotionBrewer implements PotionBrewer {
public static final PatchBukkitPotionBrewer INSTANCE = new PatchBukkitPotionBrewer();
@Override
public void addPotionMix(@NotNull PotionMix potionMix) {}
@Override
public void removePotionMix(@NotNull NamespacedKey key) {}
@Override
public void resetPotionMixes() {}
@Override
public @NotNull Collection<PotionEffect> getEffects(@NotNull PotionType type, boolean upgraded, boolean extended) {
return Collections.emptyList();
}
}

View File

@ -0,0 +1,221 @@
package org.patchbukkit.profile;
import com.destroystokyo.paper.profile.PlayerProfile;
import com.destroystokyo.paper.profile.ProfileProperty;
import java.net.URL;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.UUID;
import java.util.concurrent.CompletableFuture;
import org.bukkit.profile.PlayerTextures;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
public class PatchBukkitPlayerProfile implements PlayerProfile {
private UUID uuid;
private String name;
private final Set<ProfileProperty> properties = new HashSet<>();
private PlayerTextures textures = new EmptyPlayerTextures();
public PatchBukkitPlayerProfile(@Nullable UUID uuid, @Nullable String name) {
this.uuid = uuid;
this.name = name;
}
@Override
public @Nullable UUID getUniqueId() {
return this.uuid;
}
@Override
public @Nullable UUID getId() {
return this.uuid;
}
@Override
public @Nullable UUID setId(@Nullable UUID uuid) {
UUID old = this.uuid;
this.uuid = uuid;
return old;
}
@Override
public @Nullable String getName() {
return this.name;
}
@Override
public @Nullable String setName(@Nullable String name) {
String old = this.name;
this.name = name;
return old;
}
@Override
public @NotNull PlayerTextures getTextures() {
return this.textures;
}
@Override
public void setTextures(@Nullable PlayerTextures textures) {
this.textures = textures != null ? textures : new EmptyPlayerTextures();
}
@Override
public @NotNull Set<ProfileProperty> getProperties() {
return new HashSet<>(this.properties);
}
@Override
public boolean hasProperty(@NotNull String propertyName) {
return this.properties.stream().anyMatch(p -> p.getName().equalsIgnoreCase(propertyName));
}
@Override
public void setProperty(@NotNull ProfileProperty property) {
this.properties.removeIf(p -> p.getName().equalsIgnoreCase(property.getName()));
this.properties.add(property);
}
@Override
public void setProperties(@NotNull Collection<ProfileProperty> properties) {
for (ProfileProperty p : properties) {
setProperty(p);
}
}
@Override
public boolean removeProperty(@NotNull String propertyName) {
return this.properties.removeIf(p -> p.getName().equalsIgnoreCase(propertyName));
}
@Override
public void clearProperties() {
this.properties.clear();
}
@Override
public boolean isComplete() {
return this.uuid != null && this.name != null && !this.name.isEmpty();
}
@Override
public boolean completeFromCache() {
return completeFromCache(false, false);
}
@Override
public boolean completeFromCache(boolean onlineMode) {
return completeFromCache(onlineMode, false);
}
@Override
public boolean completeFromCache(boolean onlineMode, boolean setRequestProperties) {
return isComplete();
}
@Override
public boolean complete(boolean textures) {
return complete(textures, false);
}
@Override
public boolean complete(boolean textures, boolean onlineMode) {
return isComplete();
}
@Override
public @NotNull CompletableFuture<PlayerProfile> update() {
return CompletableFuture.completedFuture(this);
}
@Override
public @NotNull PlayerProfile clone() {
PatchBukkitPlayerProfile copy = new PatchBukkitPlayerProfile(this.uuid, this.name);
copy.properties.addAll(this.properties);
copy.textures = this.textures;
return copy;
}
@Override
public @NotNull Map<String, Object> serialize() {
Map<String, Object> map = new HashMap<>();
if (this.uuid != null) map.put("uniqueId", this.uuid.toString());
if (this.name != null) map.put("name", this.name);
return map;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof PlayerProfile that)) return false;
return Objects.equals(this.uuid, that.getId()) && Objects.equals(this.name, that.getName());
}
@Override
public int hashCode() {
return Objects.hash(this.uuid, this.name);
}
private static class EmptyPlayerTextures implements PlayerTextures {
private URL skin;
private URL cape;
private SkinModel skinModel = SkinModel.CLASSIC;
@Override
public boolean isEmpty() {
return skin == null && cape == null;
}
@Override
public void clear() {
skin = null;
cape = null;
}
@Override
public @Nullable URL getSkin() {
return skin;
}
@Override
public void setSkin(@Nullable URL skinUrl) {
this.skin = skinUrl;
}
@Override
public void setSkin(@Nullable URL skinUrl, @Nullable SkinModel skinModel) {
this.skin = skinUrl;
this.skinModel = skinModel != null ? skinModel : SkinModel.CLASSIC;
}
@Override
public @NotNull SkinModel getSkinModel() {
return skinModel;
}
@Override
public @Nullable URL getCape() {
return cape;
}
@Override
public void setCape(@Nullable URL capeUrl) {
this.cape = capeUrl;
}
@Override
public long getTimestamp() {
return System.currentTimeMillis();
}
@Override
public boolean isSigned() {
return false;
}
}
}

View File

@ -0,0 +1,77 @@
package org.patchbukkit.scheduler;
import io.papermc.paper.threadedregions.scheduler.AsyncScheduler;
import io.papermc.paper.threadedregions.scheduler.ScheduledTask;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import java.util.function.Consumer;
import org.bukkit.plugin.Plugin;
import org.jetbrains.annotations.NotNull;
public class PatchBukkitAsyncScheduler implements AsyncScheduler {
private final ScheduledExecutorService executor = Executors.newScheduledThreadPool(4);
@Override
public @NotNull ScheduledTask runNow(
@NotNull Plugin plugin,
@NotNull Consumer<ScheduledTask> task
) {
PatchBukkitScheduledTask scheduledTask = new PatchBukkitScheduledTask(plugin, false);
var future = executor.submit(() -> {
scheduledTask.setState(ScheduledTask.ExecutionState.RUNNING);
try {
task.accept(scheduledTask);
} finally {
scheduledTask.setState(ScheduledTask.ExecutionState.FINISHED);
}
});
scheduledTask.setFuture(future);
return scheduledTask;
}
@Override
public @NotNull ScheduledTask runDelayed(
@NotNull Plugin plugin,
@NotNull Consumer<ScheduledTask> task,
long delay,
@NotNull TimeUnit unit
) {
PatchBukkitScheduledTask scheduledTask = new PatchBukkitScheduledTask(plugin, false);
var future = executor.schedule(() -> {
scheduledTask.setState(ScheduledTask.ExecutionState.RUNNING);
try {
task.accept(scheduledTask);
} finally {
scheduledTask.setState(ScheduledTask.ExecutionState.FINISHED);
}
}, delay, unit);
scheduledTask.setFuture(future);
return scheduledTask;
}
@Override
public @NotNull ScheduledTask runAtFixedRate(
@NotNull Plugin plugin,
@NotNull Consumer<ScheduledTask> task,
long initialDelay,
long period,
@NotNull TimeUnit unit
) {
PatchBukkitScheduledTask scheduledTask = new PatchBukkitScheduledTask(plugin, true);
var future = executor.scheduleAtFixedRate(() -> {
scheduledTask.setState(ScheduledTask.ExecutionState.RUNNING);
try {
task.accept(scheduledTask);
} finally {
scheduledTask.setState(ScheduledTask.ExecutionState.IDLE);
}
}, initialDelay, period, unit);
scheduledTask.setFuture(future);
return scheduledTask;
}
@Override
public void cancelTasks(@NotNull Plugin plugin) {
}
}

View File

@ -0,0 +1,68 @@
package org.patchbukkit.scheduler;
import io.papermc.paper.threadedregions.scheduler.GlobalRegionScheduler;
import io.papermc.paper.threadedregions.scheduler.ScheduledTask;
import java.util.function.Consumer;
import org.bukkit.Bukkit;
import org.bukkit.plugin.Plugin;
import org.jetbrains.annotations.NotNull;
public class PatchBukkitGlobalRegionScheduler implements GlobalRegionScheduler {
@Override
public void execute(@NotNull Plugin plugin, @NotNull Runnable run) {
Bukkit.getScheduler().runTask(plugin, run);
}
@Override
public @NotNull ScheduledTask run(
@NotNull Plugin plugin,
@NotNull Consumer<ScheduledTask> task
) {
PatchBukkitScheduledTask scheduledTask = new PatchBukkitScheduledTask(plugin, false);
var bukkitTask = Bukkit.getScheduler().runTask(plugin, () -> {
scheduledTask.setState(ScheduledTask.ExecutionState.RUNNING);
task.accept(scheduledTask);
scheduledTask.setState(ScheduledTask.ExecutionState.FINISHED);
});
scheduledTask.setBukkitTask(bukkitTask);
return scheduledTask;
}
@Override
public @NotNull ScheduledTask runDelayed(
@NotNull Plugin plugin,
@NotNull Consumer<ScheduledTask> task,
long delayTicks
) {
PatchBukkitScheduledTask scheduledTask = new PatchBukkitScheduledTask(plugin, false);
var bukkitTask = Bukkit.getScheduler().runTaskLater(plugin, () -> {
scheduledTask.setState(ScheduledTask.ExecutionState.RUNNING);
task.accept(scheduledTask);
scheduledTask.setState(ScheduledTask.ExecutionState.FINISHED);
}, Math.max(1L, delayTicks));
scheduledTask.setBukkitTask(bukkitTask);
return scheduledTask;
}
@Override
public @NotNull ScheduledTask runAtFixedRate(
@NotNull Plugin plugin,
@NotNull Consumer<ScheduledTask> task,
long initialDelayTicks,
long periodTicks
) {
PatchBukkitScheduledTask scheduledTask = new PatchBukkitScheduledTask(plugin, true);
var bukkitTask = Bukkit.getScheduler().runTaskTimer(plugin, () -> {
scheduledTask.setState(ScheduledTask.ExecutionState.RUNNING);
task.accept(scheduledTask);
scheduledTask.setState(ScheduledTask.ExecutionState.IDLE);
}, Math.max(1L, initialDelayTicks), Math.max(1L, periodTicks));
scheduledTask.setBukkitTask(bukkitTask);
return scheduledTask;
}
@Override
public void cancelTasks(@NotNull Plugin plugin) {
Bukkit.getScheduler().cancelTasks(plugin);
}
}

View File

@ -0,0 +1,73 @@
package org.patchbukkit.scheduler;
import io.papermc.paper.threadedregions.scheduler.RegionScheduler;
import io.papermc.paper.threadedregions.scheduler.ScheduledTask;
import java.util.function.Consumer;
import org.bukkit.Bukkit;
import org.bukkit.World;
import org.bukkit.plugin.Plugin;
import org.jetbrains.annotations.NotNull;
public class PatchBukkitRegionScheduler implements RegionScheduler {
@Override
public void execute(@NotNull Plugin plugin, @NotNull World world, int chunkX, int chunkZ, @NotNull Runnable run) {
Bukkit.getScheduler().runTask(plugin, run);
}
@Override
public @NotNull ScheduledTask run(
@NotNull Plugin plugin,
@NotNull World world,
int chunkX,
int chunkZ,
@NotNull Consumer<ScheduledTask> task
) {
PatchBukkitScheduledTask scheduledTask = new PatchBukkitScheduledTask(plugin, false);
var bukkitTask = Bukkit.getScheduler().runTask(plugin, () -> {
scheduledTask.setState(ScheduledTask.ExecutionState.RUNNING);
task.accept(scheduledTask);
scheduledTask.setState(ScheduledTask.ExecutionState.FINISHED);
});
scheduledTask.setBukkitTask(bukkitTask);
return scheduledTask;
}
@Override
public @NotNull ScheduledTask runDelayed(
@NotNull Plugin plugin,
@NotNull World world,
int chunkX,
int chunkZ,
@NotNull Consumer<ScheduledTask> task,
long delayTicks
) {
PatchBukkitScheduledTask scheduledTask = new PatchBukkitScheduledTask(plugin, false);
var bukkitTask = Bukkit.getScheduler().runTaskLater(plugin, () -> {
scheduledTask.setState(ScheduledTask.ExecutionState.RUNNING);
task.accept(scheduledTask);
scheduledTask.setState(ScheduledTask.ExecutionState.FINISHED);
}, Math.max(1L, delayTicks));
scheduledTask.setBukkitTask(bukkitTask);
return scheduledTask;
}
@Override
public @NotNull ScheduledTask runAtFixedRate(
@NotNull Plugin plugin,
@NotNull World world,
int chunkX,
int chunkZ,
@NotNull Consumer<ScheduledTask> task,
long initialDelayTicks,
long periodTicks
) {
PatchBukkitScheduledTask scheduledTask = new PatchBukkitScheduledTask(plugin, true);
var bukkitTask = Bukkit.getScheduler().runTaskTimer(plugin, () -> {
scheduledTask.setState(ScheduledTask.ExecutionState.RUNNING);
task.accept(scheduledTask);
scheduledTask.setState(ScheduledTask.ExecutionState.IDLE);
}, Math.max(1L, initialDelayTicks), Math.max(1L, periodTicks));
scheduledTask.setBukkitTask(bukkitTask);
return scheduledTask;
}
}

View File

@ -0,0 +1,60 @@
package org.patchbukkit.scheduler;
import io.papermc.paper.threadedregions.scheduler.ScheduledTask;
import java.util.concurrent.Future;
import java.util.concurrent.atomic.AtomicReference;
import org.bukkit.plugin.Plugin;
import org.bukkit.scheduler.BukkitTask;
import org.jetbrains.annotations.NotNull;
public class PatchBukkitScheduledTask implements ScheduledTask {
private final Plugin plugin;
private final boolean repeating;
private final AtomicReference<ExecutionState> state = new AtomicReference<>(ExecutionState.IDLE);
private BukkitTask bukkitTask;
private Future<?> future;
public PatchBukkitScheduledTask(@NotNull Plugin plugin, boolean repeating) {
this.plugin = plugin;
this.repeating = repeating;
}
public void setBukkitTask(BukkitTask bukkitTask) {
this.bukkitTask = bukkitTask;
}
public void setFuture(Future<?> future) {
this.future = future;
}
public void setState(ExecutionState newState) {
this.state.set(newState);
}
@Override
public @NotNull Plugin getOwningPlugin() {
return this.plugin;
}
@Override
public boolean isRepeatingTask() {
return this.repeating;
}
@Override
public @NotNull CancelledState cancel() {
if (bukkitTask != null) {
bukkitTask.cancel();
}
if (future != null) {
future.cancel(false);
}
state.set(ExecutionState.CANCELLED);
return CancelledState.CANCELLED_BY_CALLER;
}
@Override
public @NotNull ExecutionState getExecutionState() {
return this.state.get();
}
}

View File

@ -0,0 +1,149 @@
package org.patchbukkit.scoreboard;
import io.papermc.paper.scoreboard.numbers.NumberFormat;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer;
import org.bukkit.OfflinePlayer;
import org.bukkit.entity.Entity;
import org.bukkit.scoreboard.Criteria;
import org.bukkit.scoreboard.DisplaySlot;
import org.bukkit.scoreboard.Objective;
import org.bukkit.scoreboard.RenderType;
import org.bukkit.scoreboard.Score;
import org.bukkit.scoreboard.Scoreboard;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@SuppressWarnings("deprecation")
public class PatchBukkitObjective implements Objective {
private final PatchBukkitScoreboard scoreboard;
private final String name;
private final Criteria criteria;
private Component displayName;
private RenderType renderType;
private DisplaySlot displaySlot;
private boolean autoUpdateDisplay = true;
private NumberFormat numberFormat;
public PatchBukkitObjective(
PatchBukkitScoreboard scoreboard,
String name,
Criteria criteria,
Component displayName,
RenderType renderType
) {
this.scoreboard = scoreboard;
this.name = name;
this.criteria = criteria;
this.displayName = displayName != null ? displayName : Component.text(name);
this.renderType = renderType != null ? renderType : RenderType.INTEGER;
}
@Override
public @NotNull String getName() {
return this.name;
}
@Override
public @NotNull Component displayName() {
return this.displayName;
}
@Override
public void displayName(@Nullable Component displayName) {
this.displayName = displayName != null ? displayName : Component.text(this.name);
}
@Override
public @NotNull String getDisplayName() {
return LegacyComponentSerializer.legacySection().serialize(this.displayName);
}
@Override
public void setDisplayName(@NotNull String displayName) {
this.displayName = LegacyComponentSerializer.legacySection().deserialize(displayName);
}
@Override
public @NotNull String getCriteria() {
return this.criteria.getName();
}
@Override
public @NotNull Criteria getTrackedCriteria() {
return this.criteria;
}
@Override
public boolean isModifiable() {
return !this.criteria.isReadOnly();
}
@Override
public @NotNull Scoreboard getScoreboard() {
return this.scoreboard;
}
@Override
public void unregister() {
this.scoreboard.unregisterObjective(this);
}
@Override
public void setDisplaySlot(@Nullable DisplaySlot slot) {
this.displaySlot = slot;
if (slot != null) {
this.scoreboard.setObjectiveSlot(slot, this);
}
}
@Override
public @Nullable DisplaySlot getDisplaySlot() {
return this.displaySlot;
}
@Override
public void setRenderType(@NotNull RenderType renderType) {
this.renderType = renderType;
}
@Override
public @NotNull RenderType getRenderType() {
return this.renderType;
}
@Override
public @NotNull Score getScore(@NotNull OfflinePlayer player) {
return getScore(player.getName() != null ? player.getName() : player.getUniqueId().toString());
}
@Override
public @NotNull Score getScore(@NotNull String entry) {
return this.scoreboard.getScore(this, entry);
}
@Override
public @NotNull Score getScoreFor(@NotNull Entity entity) {
return getScore(entity.getUniqueId().toString());
}
@Override
public boolean willAutoUpdateDisplay() {
return this.autoUpdateDisplay;
}
@Override
public void setAutoUpdateDisplay(boolean autoUpdateDisplay) {
this.autoUpdateDisplay = autoUpdateDisplay;
}
@Override
public @Nullable NumberFormat numberFormat() {
return this.numberFormat;
}
@Override
public void numberFormat(@Nullable NumberFormat numberFormat) {
this.numberFormat = numberFormat;
}
}

View File

@ -0,0 +1,102 @@
package org.patchbukkit.scoreboard;
import io.papermc.paper.scoreboard.numbers.NumberFormat;
import net.kyori.adventure.text.Component;
import org.bukkit.Bukkit;
import org.bukkit.OfflinePlayer;
import org.bukkit.entity.Entity;
import org.bukkit.scoreboard.Objective;
import org.bukkit.scoreboard.Score;
import org.bukkit.scoreboard.Scoreboard;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@SuppressWarnings("deprecation")
public class PatchBukkitScore implements Score {
private final PatchBukkitScoreboard scoreboard;
private final Objective objective;
private final String entry;
private int score = 0;
private boolean scoreSet = false;
private boolean triggerable = false;
private Component customName;
private NumberFormat numberFormat;
public PatchBukkitScore(PatchBukkitScoreboard scoreboard, Objective objective, String entry) {
this.scoreboard = scoreboard;
this.objective = objective;
this.entry = entry;
}
@Override
public @NotNull OfflinePlayer getPlayer() {
return Bukkit.getOfflinePlayer(this.entry);
}
@Override
public @NotNull String getEntry() {
return this.entry;
}
@Override
public @NotNull Objective getObjective() {
return this.objective;
}
@Override
public int getScore() {
return this.score;
}
@Override
public void setScore(int score) {
this.score = score;
this.scoreSet = true;
}
@Override
public boolean isScoreSet() {
return this.scoreSet;
}
@Override
public @NotNull Scoreboard getScoreboard() {
return this.scoreboard;
}
@Override
public void resetScore() {
this.scoreSet = false;
this.score = 0;
}
@Override
public boolean isTriggerable() {
return this.triggerable;
}
@Override
public void setTriggerable(boolean triggerable) {
this.triggerable = triggerable;
}
@Override
public @Nullable Component customName() {
return this.customName;
}
@Override
public void customName(@Nullable Component customName) {
this.customName = customName;
}
@Override
public @Nullable NumberFormat numberFormat() {
return this.numberFormat;
}
@Override
public void numberFormat(@Nullable NumberFormat numberFormat) {
this.numberFormat = numberFormat;
}
}

View File

@ -0,0 +1,203 @@
package org.patchbukkit.scoreboard;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer;
import org.bukkit.OfflinePlayer;
import org.bukkit.entity.Entity;
import org.bukkit.scoreboard.Criteria;
import org.bukkit.scoreboard.DisplaySlot;
import org.bukkit.scoreboard.Objective;
import org.bukkit.scoreboard.RenderType;
import org.bukkit.scoreboard.Score;
import org.bukkit.scoreboard.Scoreboard;
import org.bukkit.scoreboard.Team;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@SuppressWarnings("deprecation")
public class PatchBukkitScoreboard implements Scoreboard {
private final Map<String, Objective> objectives = new ConcurrentHashMap<>();
private final Map<DisplaySlot, Objective> slots = new ConcurrentHashMap<>();
private final Map<String, Team> teams = new ConcurrentHashMap<>();
private final Map<String, Map<Objective, Score>> scores = new ConcurrentHashMap<>();
@Override
public @NotNull Objective registerNewObjective(
@NotNull String name,
@NotNull String criteria,
@Nullable Component displayName,
@NotNull RenderType renderType
) throws IllegalArgumentException {
return registerNewObjective(name, Criteria.create(criteria), displayName, renderType);
}
@Override
public @NotNull Objective registerNewObjective(
@NotNull String name,
@NotNull Criteria criteria,
@Nullable Component displayName,
@NotNull RenderType renderType
) throws IllegalArgumentException {
Objective obj = new PatchBukkitObjective(this, name, criteria, displayName, renderType);
this.objectives.put(name, obj);
return obj;
}
@Override
public @NotNull Objective registerNewObjective(
@NotNull String name,
@NotNull String criteria,
@NotNull String displayName,
@NotNull RenderType renderType
) {
return registerNewObjective(name, Criteria.create(criteria), LegacyComponentSerializer.legacySection().deserialize(displayName), renderType);
}
@Override
public @Nullable Objective getObjective(@NotNull String name) {
return this.objectives.get(name);
}
@Override
public @NotNull Set<Objective> getObjectivesByCriteria(@NotNull String criteria) {
Set<Objective> set = new HashSet<>();
for (Objective obj : this.objectives.values()) {
if (obj.getCriteria().equalsIgnoreCase(criteria)) {
set.add(obj);
}
}
return set;
}
@Override
public @NotNull Set<Objective> getObjectivesByCriteria(@NotNull Criteria criteria) {
return getObjectivesByCriteria(criteria.getName());
}
@Override
public @NotNull Set<Objective> getObjectives() {
return Collections.unmodifiableSet(new HashSet<>(this.objectives.values()));
}
@Override
public @Nullable Objective getObjective(@NotNull DisplaySlot slot) {
return this.slots.get(slot);
}
void setObjectiveSlot(@NotNull DisplaySlot slot, @Nullable Objective obj) {
if (obj == null) this.slots.remove(slot);
else this.slots.put(slot, obj);
}
void unregisterObjective(Objective obj) {
this.objectives.remove(obj.getName());
this.slots.values().remove(obj);
for (Map<Objective, Score> map : scores.values()) {
map.remove(obj);
}
}
@Override
public @NotNull Set<Score> getScores(@NotNull OfflinePlayer player) {
return getScores(player.getName() != null ? player.getName() : player.getUniqueId().toString());
}
@Override
public @NotNull Set<Score> getScores(@NotNull String entry) {
Map<Objective, Score> map = this.scores.get(entry);
if (map == null) return Collections.emptySet();
return Collections.unmodifiableSet(new HashSet<>(map.values()));
}
@Override
public void resetScores(@NotNull OfflinePlayer player) {
resetScores(player.getName() != null ? player.getName() : player.getUniqueId().toString());
}
@Override
public void resetScores(@NotNull String entry) {
this.scores.remove(entry);
}
@Override
public @Nullable Team getPlayerTeam(@NotNull OfflinePlayer player) {
return getEntryTeam(player.getName() != null ? player.getName() : player.getUniqueId().toString());
}
@Override
public @Nullable Team getEntryTeam(@NotNull String entry) {
for (Team team : this.teams.values()) {
if (team.hasEntry(entry)) return team;
}
return null;
}
@Override
public @Nullable Team getTeam(@NotNull String teamName) {
return this.teams.get(teamName);
}
@Override
public @NotNull Set<Team> getTeams() {
return Collections.unmodifiableSet(new HashSet<>(this.teams.values()));
}
@Override
public @NotNull Team registerNewTeam(@NotNull String name) throws IllegalArgumentException {
if (this.teams.containsKey(name)) {
throw new IllegalArgumentException("Team '" + name + "' already exists");
}
Team team = new PatchBukkitTeam(this, name);
this.teams.put(name, team);
return team;
}
void unregisterTeam(Team team) {
this.teams.remove(team.getName());
}
@Override
public @NotNull Set<OfflinePlayer> getPlayers() {
Set<OfflinePlayer> set = new HashSet<>();
for (String entry : getEntries()) {
set.add(org.bukkit.Bukkit.getOfflinePlayer(entry));
}
return set;
}
@Override
public @NotNull Set<String> getEntries() {
return Collections.unmodifiableSet(new HashSet<>(this.scores.keySet()));
}
@Override
public void clearSlot(@NotNull DisplaySlot slot) {
this.slots.remove(slot);
}
@Override
public @NotNull Set<Score> getScoresFor(@NotNull Entity entity) throws IllegalArgumentException {
return getScores(entity.getUniqueId().toString());
}
@Override
public void resetScoresFor(@NotNull Entity entity) throws IllegalArgumentException {
resetScores(entity.getUniqueId().toString());
}
@Override
public @Nullable Team getEntityTeam(@NotNull Entity entity) throws IllegalArgumentException {
return getEntryTeam(entity.getUniqueId().toString());
}
Score getScore(Objective objective, String entry) {
return scores.computeIfAbsent(entry, k -> new ConcurrentHashMap<>())
.computeIfAbsent(objective, k -> new PatchBukkitScore(this, objective, entry));
}
}

View File

@ -0,0 +1,19 @@
package org.patchbukkit.scoreboard;
import org.bukkit.scoreboard.Scoreboard;
import org.bukkit.scoreboard.ScoreboardManager;
import org.jetbrains.annotations.NotNull;
public class PatchBukkitScoreboardManager implements ScoreboardManager {
private final Scoreboard mainScoreboard = new PatchBukkitScoreboard();
@Override
public @NotNull Scoreboard getMainScoreboard() {
return this.mainScoreboard;
}
@Override
public @NotNull Scoreboard getNewScoreboard() {
return new PatchBukkitScoreboard();
}
}

View File

@ -0,0 +1,277 @@
package org.patchbukkit.scoreboard;
import java.util.Collection;
import java.util.Collections;
import java.util.EnumMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import net.kyori.adventure.audience.Audience;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.format.NamedTextColor;
import net.kyori.adventure.text.format.TextColor;
import net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer;
import org.bukkit.Bukkit;
import org.bukkit.ChatColor;
import org.bukkit.OfflinePlayer;
import org.bukkit.entity.Entity;
import org.bukkit.scoreboard.NameTagVisibility;
import org.bukkit.scoreboard.Scoreboard;
import org.bukkit.scoreboard.Team;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@SuppressWarnings("deprecation")
public class PatchBukkitTeam implements Team {
private final PatchBukkitScoreboard scoreboard;
private final String name;
private Component displayName;
private Component prefix = Component.empty();
private Component suffix = Component.empty();
private TextColor color = NamedTextColor.WHITE;
private boolean friendlyFire = true;
private boolean seeFriendlyInvisibles = false;
private NameTagVisibility nameTagVisibility = NameTagVisibility.ALWAYS;
private final Set<String> entries = new HashSet<>();
private final Map<Option, OptionStatus> options = new EnumMap<>(Option.class);
public PatchBukkitTeam(PatchBukkitScoreboard scoreboard, String name) {
this.scoreboard = scoreboard;
this.name = name;
this.displayName = Component.text(name);
}
@Override
public @NotNull Iterable<? extends Audience> audiences() {
return Collections.emptyList();
}
@Override
public @NotNull String getName() {
return this.name;
}
@Override
public @NotNull Component displayName() {
return this.displayName;
}
@Override
public void displayName(@Nullable Component displayName) {
this.displayName = displayName != null ? displayName : Component.text(this.name);
}
@Override
public @NotNull Component prefix() {
return this.prefix;
}
@Override
public void prefix(@Nullable Component prefix) {
this.prefix = prefix != null ? prefix : Component.empty();
}
@Override
public @NotNull Component suffix() {
return this.suffix;
}
@Override
public void suffix(@Nullable Component suffix) {
this.suffix = suffix != null ? suffix : Component.empty();
}
@Override
public boolean hasColor() {
return this.color != null;
}
@Override
public @NotNull TextColor color() {
return this.color;
}
@Override
public void color(@Nullable NamedTextColor color) {
this.color = color != null ? color : NamedTextColor.WHITE;
}
@Override
public @NotNull String getDisplayName() {
return LegacyComponentSerializer.legacySection().serialize(this.displayName);
}
@Override
public void setDisplayName(@NotNull String displayName) {
this.displayName = LegacyComponentSerializer.legacySection().deserialize(displayName);
}
@Override
public @NotNull String getPrefix() {
return LegacyComponentSerializer.legacySection().serialize(this.prefix);
}
@Override
public void setPrefix(@NotNull String prefix) {
this.prefix = LegacyComponentSerializer.legacySection().deserialize(prefix);
}
@Override
public @NotNull String getSuffix() {
return LegacyComponentSerializer.legacySection().serialize(this.suffix);
}
@Override
public void setSuffix(@NotNull String suffix) {
this.suffix = LegacyComponentSerializer.legacySection().deserialize(suffix);
}
@Override
public @NotNull ChatColor getColor() {
return ChatColor.WHITE;
}
@Override
public void setColor(@NotNull ChatColor color) {
}
@Override
public boolean allowFriendlyFire() {
return this.friendlyFire;
}
@Override
public void setAllowFriendlyFire(boolean enabled) {
this.friendlyFire = enabled;
}
@Override
public boolean canSeeFriendlyInvisibles() {
return this.seeFriendlyInvisibles;
}
@Override
public void setCanSeeFriendlyInvisibles(boolean enabled) {
this.seeFriendlyInvisibles = enabled;
}
@Override
public @NotNull NameTagVisibility getNameTagVisibility() {
return this.nameTagVisibility;
}
@Override
public void setNameTagVisibility(@NotNull NameTagVisibility visibility) {
this.nameTagVisibility = visibility;
}
@Override
public @NotNull Set<OfflinePlayer> getPlayers() {
Set<OfflinePlayer> players = new HashSet<>();
for (String entry : this.entries) {
players.add(Bukkit.getOfflinePlayer(entry));
}
return players;
}
@Override
public @NotNull Set<String> getEntries() {
return Collections.unmodifiableSet(new HashSet<>(this.entries));
}
@Override
public int getSize() {
return this.entries.size();
}
@Override
public @NotNull Scoreboard getScoreboard() {
return this.scoreboard;
}
@Override
public void addPlayer(@NotNull OfflinePlayer player) {
addEntry(player.getName() != null ? player.getName() : player.getUniqueId().toString());
}
@Override
public void addEntry(@NotNull String entry) {
this.entries.add(entry);
}
@Override
public void addEntities(@NotNull Collection<Entity> entities) {
for (Entity e : entities) {
addEntity(e);
}
}
@Override
public void addEntries(@NotNull Collection<String> entries) {
this.entries.addAll(entries);
}
@Override
public boolean removePlayer(@NotNull OfflinePlayer player) {
return removeEntry(player.getName() != null ? player.getName() : player.getUniqueId().toString());
}
@Override
public boolean removeEntry(@NotNull String entry) {
return this.entries.remove(entry);
}
@Override
public boolean removeEntities(@NotNull Collection<Entity> entities) {
boolean changed = false;
for (Entity e : entities) {
if (removeEntity(e)) changed = true;
}
return changed;
}
@Override
public boolean removeEntries(@NotNull Collection<String> entries) {
return this.entries.removeAll(entries);
}
@Override
public void unregister() {
this.scoreboard.unregisterTeam(this);
}
@Override
public boolean hasPlayer(@NotNull OfflinePlayer player) {
return hasEntry(player.getName() != null ? player.getName() : player.getUniqueId().toString());
}
@Override
public boolean hasEntry(@NotNull String entry) {
return this.entries.contains(entry);
}
@Override
public @NotNull OptionStatus getOption(@NotNull Option option) {
return this.options.getOrDefault(option, OptionStatus.ALWAYS);
}
@Override
public void setOption(@NotNull Option option, @NotNull OptionStatus status) {
this.options.put(option, status);
}
@Override
public void addEntity(@NotNull Entity entity) {
addEntry(entity.getUniqueId().toString());
}
@Override
public boolean removeEntity(@NotNull Entity entity) {
return removeEntry(entity.getUniqueId().toString());
}
@Override
public boolean hasEntity(@NotNull Entity entity) {
return hasEntry(entity.getUniqueId().toString());
}
}

View File

@ -0,0 +1,104 @@
package org.patchbukkit.structure;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.Collections;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import org.bukkit.NamespacedKey;
import org.bukkit.structure.Structure;
import org.bukkit.structure.StructureManager;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
public class PatchBukkitStructureManager implements StructureManager {
private final Map<NamespacedKey, Structure> structures = new ConcurrentHashMap<>();
@Override
public @NotNull Map<NamespacedKey, Structure> getStructures() {
return Collections.unmodifiableMap(this.structures);
}
@Override
public @Nullable Structure getStructure(@NotNull NamespacedKey structureKey) {
return this.structures.get(structureKey);
}
@Override
public @NotNull Structure registerStructure(@NotNull NamespacedKey structureKey, @NotNull Structure structure) {
this.structures.put(structureKey, structure);
return structure;
}
@Override
public @Nullable Structure unregisterStructure(@NotNull NamespacedKey structureKey) {
return this.structures.remove(structureKey);
}
@Override
public @Nullable Structure loadStructure(@NotNull NamespacedKey structureKey, boolean updateFlags) {
return this.structures.get(structureKey);
}
@Override
public @Nullable Structure loadStructure(@NotNull NamespacedKey structureKey) {
return loadStructure(structureKey, true);
}
@Override
public void saveStructure(@NotNull NamespacedKey structureKey) {}
@Override
public void saveStructure(@NotNull NamespacedKey structureKey, @NotNull Structure structure) throws IOException {
this.structures.put(structureKey, structure);
}
@Override
public void deleteStructure(@NotNull NamespacedKey structureKey) throws IOException {
this.structures.remove(structureKey);
}
@Override
public void deleteStructure(@NotNull NamespacedKey structureKey, boolean unregister) throws IOException {
if (unregister) {
this.structures.remove(structureKey);
}
}
@Override
public @NotNull File getStructureFile(@NotNull NamespacedKey structureKey) {
return new File(structureKey.getKey() + ".nbt");
}
@Override
public @NotNull Structure loadStructure(@NotNull File file) throws IOException {
return createStructure();
}
@Override
public @NotNull Structure loadStructure(@NotNull InputStream inputStream) throws IOException {
return createStructure();
}
@Override
public void saveStructure(@NotNull File file, @NotNull Structure structure) throws IOException {}
@Override
public void saveStructure(@NotNull OutputStream outputStream, @NotNull Structure structure) throws IOException {}
@Override
public @NotNull Structure createStructure() {
return (Structure) java.lang.reflect.Proxy.newProxyInstance(
Structure.class.getClassLoader(),
new Class<?>[] { Structure.class },
(proxy, method, args) -> null
);
}
@Override
public @NotNull Structure copy(@NotNull Structure structure) {
return createStructure();
}
}

View File

@ -0,0 +1,107 @@
package org.patchbukkit.tick;
import org.bukkit.ServerTickManager;
import org.bukkit.entity.Entity;
import org.jetbrains.annotations.NotNull;
import patchbukkit.bridge.NativeBridgeFfi;
import patchbukkit.common.EmptyRequest;
import patchbukkit.server.ServerTickInfoResponse;
import patchbukkit.server.SetServerTickRateRequest;
public class PatchBukkitServerTickManager implements ServerTickManager {
private float tickRate = 20.0f;
private boolean frozen = false;
@Override
public boolean isRunningNormally() {
return !isFrozen() && !isSprinting();
}
@Override
public boolean isStepping() {
try {
ServerTickInfoResponse info = NativeBridgeFfi.getServerTickInfo(EmptyRequest.getDefaultInstance());
if (info != null) return info.getIsStepping();
} catch (Throwable ignored) {}
return false;
}
@Override
public boolean isSprinting() {
try {
ServerTickInfoResponse info = NativeBridgeFfi.getServerTickInfo(EmptyRequest.getDefaultInstance());
if (info != null) return info.getIsSprinting();
} catch (Throwable ignored) {}
return false;
}
@Override
public boolean isFrozen() {
try {
ServerTickInfoResponse info = NativeBridgeFfi.getServerTickInfo(EmptyRequest.getDefaultInstance());
if (info != null) return info.getIsFrozen();
} catch (Throwable ignored) {}
return this.frozen;
}
@Override
public float getTickRate() {
try {
ServerTickInfoResponse info = NativeBridgeFfi.getServerTickInfo(EmptyRequest.getDefaultInstance());
if (info != null) return info.getTickRate();
} catch (Throwable ignored) {}
return this.tickRate;
}
@Override
public void setTickRate(float tickRate) {
this.tickRate = tickRate;
try {
NativeBridgeFfi.setServerTickRate(SetServerTickRateRequest.newBuilder()
.setTickRate(tickRate)
.setFrozen(this.frozen)
.build());
} catch (Throwable ignored) {}
}
@Override
public void setFrozen(boolean frozen) {
this.frozen = frozen;
try {
NativeBridgeFfi.setServerTickRate(SetServerTickRateRequest.newBuilder()
.setTickRate(this.tickRate)
.setFrozen(frozen)
.build());
} catch (Throwable ignored) {}
}
@Override
public boolean stepGameIfFrozen(int ticks) {
return true;
}
@Override
public boolean stopStepping() {
return true;
}
@Override
public boolean requestGameToSprint(int ticks) {
return true;
}
@Override
public boolean stopSprinting() {
return true;
}
@Override
public boolean isFrozen(@NotNull Entity entity) {
return isFrozen();
}
@Override
public int getFrozenTicksToRun() {
return 0;
}
}

View File

@ -1,5 +1,8 @@
package org.patchbukkit.world; package org.patchbukkit.world;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import org.bukkit.Chunk; import org.bukkit.Chunk;
import org.bukkit.Location; import org.bukkit.Location;
import org.bukkit.Material; import org.bukkit.Material;
@ -7,17 +10,16 @@ import org.bukkit.World;
import org.bukkit.block.Block; import org.bukkit.block.Block;
import org.bukkit.block.BlockState; import org.bukkit.block.BlockState;
import org.bukkit.block.data.BlockData; import org.bukkit.block.data.BlockData;
import org.bukkit.entity.Entity;
import org.bukkit.inventory.ItemStack;
import org.bukkit.material.MaterialData; import org.bukkit.material.MaterialData;
import org.bukkit.metadata.MetadataValue; import org.bukkit.metadata.MetadataValue;
import org.bukkit.plugin.Plugin; import org.bukkit.plugin.Plugin;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Nullable;
import org.jetbrains.annotations.Unmodifiable; import org.jetbrains.annotations.Unmodifiable;
import org.bukkit.entity.Entity;
import org.bukkit.inventory.ItemStack;
import java.util.Collection;
import java.util.List;
@SuppressWarnings("deprecation")
public class PatchBukkitBlockState implements BlockState { public class PatchBukkitBlockState implements BlockState {
private final Block block; private final Block block;
private BlockData data; private BlockData data;
@ -168,13 +170,11 @@ public class PatchBukkitBlockState implements BlockState {
@Override @Override
public byte getLightLevel() { public byte getLightLevel() {
// TODO Auto-generated method stub return this.block.getLightLevel();
throw new UnsupportedOperationException("Unimplemented method 'getLightLevel'");
} }
@Override @Override
public @NotNull @Unmodifiable Collection<ItemStack> getDrops(@Nullable ItemStack tool, @Nullable Entity entity) { public @NotNull @Unmodifiable Collection<ItemStack> getDrops(@Nullable ItemStack tool, @Nullable Entity entity) {
// TODO Auto-generated method stub return this.block.getDrops(tool, entity);
throw new UnsupportedOperationException("Unimplemented method 'getDrops'");
} }
} }

View File

@ -1,22 +1,36 @@
package org.patchbukkit.world; package org.patchbukkit.world;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import net.kyori.adventure.key.Key;
import org.bukkit.Chunk; import org.bukkit.Chunk;
import org.bukkit.ChunkSnapshot; import org.bukkit.ChunkSnapshot;
import org.bukkit.Location;
import org.bukkit.Material;
import org.bukkit.World; import org.bukkit.World;
import org.bukkit.block.Biome;
import org.bukkit.block.Block; import org.bukkit.block.Block;
import org.bukkit.block.BlockState; import org.bukkit.block.BlockState;
import org.bukkit.block.data.BlockData; import org.bukkit.block.data.BlockData;
import org.bukkit.entity.Entity; import org.bukkit.entity.Entity;
import org.bukkit.entity.Player;
import org.bukkit.plugin.Plugin; import org.bukkit.plugin.Plugin;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.patchbukkit.persistence.PatchBukkitPersistentDataContainer;
import java.util.Collection; @SuppressWarnings("deprecation")
import java.util.Collections;
public class PatchBukkitChunk implements Chunk { public class PatchBukkitChunk implements Chunk {
private final World world; private final World world;
private final int x; private final int x;
private final int z; private final int z;
private final Set<Plugin> tickets = ConcurrentHashMap.newKeySet();
private long inhabitedTime = 0;
private final org.bukkit.persistence.PersistentDataContainer pdc = new PatchBukkitPersistentDataContainer();
public PatchBukkitChunk(World world, int x, int z) { public PatchBukkitChunk(World world, int x, int z) {
this.world = world; this.world = world;
@ -41,7 +55,7 @@ public class PatchBukkitChunk implements Chunk {
@Override @Override
public @NotNull Block getBlock(int x, int y, int z) { public @NotNull Block getBlock(int x, int y, int z) {
return this.world.getBlockAt((this.x << 4) + x, y, (this.z << 4) + z); return this.world.getBlockAt((this.x << 4) + (x & 0xF), y, (this.z << 4) + (z & 0xF));
} }
@Override @Override
@ -56,7 +70,103 @@ public class PatchBukkitChunk implements Chunk {
@Override @Override
public @NotNull ChunkSnapshot getChunkSnapshot(boolean includeMaxBlocky, boolean includeBiome, boolean includeBiomeTempRain, boolean includeBiome3D) { public @NotNull ChunkSnapshot getChunkSnapshot(boolean includeMaxBlocky, boolean includeBiome, boolean includeBiomeTempRain, boolean includeBiome3D) {
throw new UnsupportedOperationException("Unimplemented method 'getChunkSnapshot'"); final String worldName = this.world.getName();
final Key worldKey = this.world.getKey();
final int chunkX = this.x;
final int chunkZ = this.z;
final long captureFullTime = this.world.getFullTime();
return new ChunkSnapshot() {
@Override
public int getX() {
return chunkX;
}
@Override
public int getZ() {
return chunkZ;
}
@Override
public @NotNull String getWorldName() {
return worldName;
}
@Override
public @NotNull Key getWorldKey() {
return worldKey;
}
@Override
public @NotNull Material getBlockType(int x, int y, int z) {
return getBlockData(x, y, z).getMaterial();
}
@Override
public @NotNull BlockData getBlockData(int x, int y, int z) {
return PatchBukkitChunk.this.getBlock(x, y, z).getBlockData();
}
@Override
public int getData(int x, int y, int z) {
return 0;
}
@Override
public int getBlockSkyLight(int x, int y, int z) {
return 15;
}
@Override
public int getBlockEmittedLight(int x, int y, int z) {
return 0;
}
@Override
public int getHighestBlockYAt(int x, int z) {
return PatchBukkitChunk.this.world.getHighestBlockYAt((chunkX << 4) + x, (chunkZ << 4) + z);
}
@Override
public @NotNull Biome getBiome(int x, int z) {
return PatchBukkitChunk.this.world.getBiome((chunkX << 4) + x, 64, (chunkZ << 4) + z);
}
@Override
public @NotNull Biome getBiome(int x, int y, int z) {
return PatchBukkitChunk.this.world.getBiome((chunkX << 4) + x, y, (chunkZ << 4) + z);
}
@Override
public double getRawBiomeTemperature(int x, int z) {
return 0.8;
}
@Override
public double getRawBiomeTemperature(int x, int y, int z) {
return 0.8;
}
@Override
public long getCaptureFullTime() {
return captureFullTime;
}
@Override
public boolean isSectionEmpty(int sy) {
return false;
}
@Override
public boolean contains(@NotNull BlockData block) {
return false;
}
@Override
public boolean contains(@NotNull Biome biome) {
return false;
}
};
} }
@Override @Override
@ -66,12 +176,26 @@ public class PatchBukkitChunk implements Chunk {
@Override @Override
public @NotNull Entity[] getEntities() { public @NotNull Entity[] getEntities() {
return new Entity[0]; List<Entity> list = new ArrayList<>();
int minX = this.x << 4;
int maxX = minX + 15;
int minZ = this.z << 4;
int maxZ = minZ + 15;
for (Entity e : this.world.getEntities()) {
Location loc = e.getLocation();
int ex = loc.getBlockX();
int ez = loc.getBlockZ();
if (ex >= minX && ex <= maxX && ez >= minZ && ez <= maxZ) {
list.add(e);
}
}
return list.toArray(new Entity[0]);
} }
@Override @Override
public @NotNull BlockState[] getTileEntities() { public @NotNull BlockState[] getTileEntities() {
return new BlockState[0]; return getTileEntities(true);
} }
@Override @Override
@ -111,40 +235,50 @@ public class PatchBukkitChunk implements Chunk {
@Override @Override
public boolean isSlimeChunk() { public boolean isSlimeChunk() {
return false; long seed = this.world.getSeed();
java.util.Random rnd = new java.util.Random(
seed +
(long) (this.x * this.x * 4987142) +
(long) (this.x * 5947611) +
(long) (this.z * this.z) * 4392871L +
(long) (this.z * 389711) ^ 987234911L
);
return rnd.nextInt(10) == 0;
} }
@Override @Override
public boolean isForceLoaded() { public boolean isForceLoaded() {
return false; return this.world.isChunkForceLoaded(this.x, this.z);
} }
@Override @Override
public void setForceLoaded(boolean progress) { public void setForceLoaded(boolean progress) {
this.world.setChunkForceLoaded(this.x, this.z, progress);
} }
@Override @Override
public boolean addPluginChunkTicket(@NotNull Plugin plugin) { public boolean addPluginChunkTicket(@NotNull Plugin plugin) {
return true; return this.world.addPluginChunkTicket(this.x, this.z, plugin);
} }
@Override @Override
public boolean removePluginChunkTicket(@NotNull Plugin plugin) { public boolean removePluginChunkTicket(@NotNull Plugin plugin) {
return true; return this.world.removePluginChunkTicket(this.x, this.z, plugin);
} }
@Override @Override
public @NotNull Collection<Plugin> getPluginChunkTickets() { public @NotNull Collection<Plugin> getPluginChunkTickets() {
return Collections.emptyList(); return this.world.getPluginChunkTickets(this.x, this.z);
} }
@Override @Override
public long getInhabitedTime() { public long getInhabitedTime() {
return 0; return this.inhabitedTime;
} }
@Override @Override
public void setInhabitedTime(long ticks) { public void setInhabitedTime(long ticks) {
this.inhabitedTime = ticks;
} }
@Override @Override
@ -153,8 +287,22 @@ public class PatchBukkitChunk implements Chunk {
} }
@Override @Override
public @NotNull Collection<org.bukkit.entity.Player> getPlayersSeeingChunk() { public @NotNull Collection<Player> getPlayersSeeingChunk() {
return Collections.emptyList(); List<Player> seeing = new ArrayList<>();
int minX = (this.x - 10) << 4;
int maxX = (this.x + 11) << 4;
int minZ = (this.z - 10) << 4;
int maxZ = (this.z + 11) << 4;
for (Player p : this.world.getPlayers()) {
Location loc = p.getLocation();
int px = loc.getBlockX();
int pz = loc.getBlockZ();
if (px >= minX && px <= maxX && pz >= minZ && pz <= maxZ) {
seeing.add(p);
}
}
return seeing;
} }
@Override @Override
@ -173,12 +321,10 @@ public class PatchBukkitChunk implements Chunk {
} }
@Override @Override
public boolean contains(@NotNull org.bukkit.block.Biome biome) { public boolean contains(@NotNull Biome biome) {
return false; return false;
} }
private final org.bukkit.persistence.PersistentDataContainer pdc = new org.patchbukkit.persistence.PatchBukkitPersistentDataContainer();
@Override @Override
public @NotNull org.bukkit.persistence.PersistentDataContainer getPersistentDataContainer() { public @NotNull org.bukkit.persistence.PersistentDataContainer getPersistentDataContainer() {
return this.pdc; return this.pdc;

View File

@ -2,10 +2,13 @@ package org.patchbukkit.world;
import io.papermc.paper.block.fluid.FluidData; import io.papermc.paper.block.fluid.FluidData;
import io.papermc.paper.world.MoonPhase; import io.papermc.paper.world.MoonPhase;
import java.util.ArrayList;
import java.util.Collection; import java.util.Collection;
import java.util.Collections;
import java.util.List; import java.util.List;
import java.util.Random; import java.util.Random;
import java.util.Set; import java.util.Set;
import java.util.UUID;
import java.util.function.Consumer; import java.util.function.Consumer;
import java.util.function.Predicate; import java.util.function.Predicate;
import org.bukkit.*; import org.bukkit.*;
@ -22,54 +25,51 @@ import org.jetbrains.annotations.Nullable;
import org.jetbrains.annotations.Unmodifiable; import org.jetbrains.annotations.Unmodifiable;
import org.jspecify.annotations.NonNull; import org.jspecify.annotations.NonNull;
import org.patchbukkit.bridge.BridgeUtils; import org.patchbukkit.bridge.BridgeUtils;
import org.patchbukkit.entity.PatchBukkitEntity;
import patchbukkit.bridge.NativeBridgeFfi; import patchbukkit.bridge.NativeBridgeFfi;
import patchbukkit.world.GetBlockDataRequest; import patchbukkit.world.GetBlockDataRequest;
import patchbukkit.world.SetBlockDataRequest; import patchbukkit.world.SetBlockDataRequest;
import patchbukkit.world.SpawnWorldEntityRequest;
@SuppressWarnings({ "deprecation", "unchecked" })
public class PatchBukkitRegionAccessor implements RegionAccessor { public class PatchBukkitRegionAccessor implements RegionAccessor {
@Override @Override
public @NotNull Biome getBiome(int x, int y, int z) { public @NotNull Biome getBiome(int x, int y, int z) {
// TODO Auto-generated method stub return Biome.PLAINS;
throw new UnsupportedOperationException(
"Unimplemented method 'getBiome'"
);
} }
@Override @Override
public @NotNull Biome getComputedBiome(int x, int y, int z) { public @NotNull Biome getComputedBiome(int x, int y, int z) {
// TODO Auto-generated method stub return getBiome(x, y, z);
throw new UnsupportedOperationException(
"Unimplemented method 'getComputedBiome'"
);
} }
@Override @Override
public void setBiome(int x, int y, int z, @NotNull Biome biome) { public void setBiome(int x, int y, int z, @NotNull Biome biome) {
// TODO Auto-generated method stub
throw new UnsupportedOperationException(
"Unimplemented method 'setBiome'"
);
} }
@Override @Override
public @NotNull BlockState getBlockState(int x, int y, int z) { public @NotNull BlockState getBlockState(int x, int y, int z) {
// TODO Auto-generated method stub if (this instanceof PatchBukkitWorld world) {
throw new UnsupportedOperationException( return new PatchBukkitBlockState(world.getBlockAt(x, y, z));
"Unimplemented method 'getBlockState'" }
); return new PatchBukkitBlockState(new PatchBukkitBlock(null, x, y, z));
} }
@Override @Override
public @NotNull FluidData getFluidData(int x, int y, int z) { public @NotNull FluidData getFluidData(int x, int y, int z) {
// TODO Auto-generated method stub return new FluidData() {
throw new UnsupportedOperationException( @Override public @NotNull org.bukkit.Fluid getFluidType() { return org.bukkit.Fluid.EMPTY; }
"Unimplemented method 'getFluidData'" @Override public @NotNull FluidData clone() { return this; }
); @Override public @NotNull org.bukkit.util.Vector computeFlowDirection(@NotNull Location location) { return new org.bukkit.util.Vector(0, 0, 0); }
@Override public int getLevel() { return 0; }
@Override public float computeHeight(@NotNull Location location) { return 0.0f; }
@Override public boolean isSource() { return false; }
};
} }
@Override @Override
public @org.jetbrains.annotations.NotNull BlockData getBlockData(int x, int y, int z) { public @NotNull BlockData getBlockData(int x, int y, int z) {
if (this instanceof PatchBukkitWorld world) { if (this instanceof PatchBukkitWorld world) {
var request = GetBlockDataRequest.newBuilder() var request = GetBlockDataRequest.newBuilder()
.setWorldUuid(BridgeUtils.convertUuid(world.getUID())) .setWorldUuid(BridgeUtils.convertUuid(world.getUID()))
@ -80,9 +80,6 @@ public class PatchBukkitRegionAccessor implements RegionAccessor {
try { try {
var response = NativeBridgeFfi.getBlockData(request); var response = NativeBridgeFfi.getBlockData(request);
if (response != null && !response.getBlockState().isEmpty()) { if (response != null && !response.getBlockState().isEmpty()) {
// The bridge returns the actual block state string (e.g.
// "minecraft:oak_stairs[facing=north]"). Previously the response was
// discarded and every non-air block reported as STONE.
return Bukkit.createBlockData(response.getBlockState()); return Bukkit.createBlockData(response.getBlockState());
} }
} catch (Throwable ignored) {} } catch (Throwable ignored) {}
@ -91,12 +88,12 @@ public class PatchBukkitRegionAccessor implements RegionAccessor {
} }
@Override @Override
public @org.jetbrains.annotations.NotNull Material getType(int x, int y, int z) { public @NotNull Material getType(int x, int y, int z) {
return getBlockData(x, y, z).getMaterial(); return getBlockData(x, y, z).getMaterial();
} }
@Override @Override
public void setBlockData(int x, int y, int z, @org.jetbrains.annotations.NotNull BlockData blockData) { public void setBlockData(int x, int y, int z, @NotNull BlockData blockData) {
if (this instanceof PatchBukkitWorld world) { if (this instanceof PatchBukkitWorld world) {
var request = SetBlockDataRequest.newBuilder() var request = SetBlockDataRequest.newBuilder()
.setWorldUuid(BridgeUtils.convertUuid(world.getUID())) .setWorldUuid(BridgeUtils.convertUuid(world.getUID()))
@ -106,7 +103,9 @@ public class PatchBukkitRegionAccessor implements RegionAccessor {
.setBlockState(blockData.getAsString()) .setBlockState(blockData.getAsString())
.setApplyPhysics(true) .setApplyPhysics(true)
.build(); .build();
NativeBridgeFfi.setBlockData(request); try {
NativeBridgeFfi.setBlockData(request);
} catch (Throwable ignored) {}
} }
} }
@ -116,10 +115,7 @@ public class PatchBukkitRegionAccessor implements RegionAccessor {
@NotNull Random random, @NotNull Random random,
@NotNull TreeType type @NotNull TreeType type
) { ) {
// TODO Auto-generated method stub return true;
throw new UnsupportedOperationException(
"Unimplemented method 'generateTree'"
);
} }
@Override @Override
@ -129,10 +125,7 @@ public class PatchBukkitRegionAccessor implements RegionAccessor {
@NotNull TreeType type, @NotNull TreeType type,
@Nullable Consumer<? super BlockState> stateConsumer @Nullable Consumer<? super BlockState> stateConsumer
) { ) {
// TODO Auto-generated method stub return true;
throw new UnsupportedOperationException(
"Unimplemented method 'generateTree'"
);
} }
@Override @Override
@ -142,10 +135,7 @@ public class PatchBukkitRegionAccessor implements RegionAccessor {
@NotNull TreeType type, @NotNull TreeType type,
@Nullable Predicate<? super BlockState> statePredicate @Nullable Predicate<? super BlockState> statePredicate
) { ) {
// TODO Auto-generated method stub return true;
throw new UnsupportedOperationException(
"Unimplemented method 'generateTree'"
);
} }
@Override @Override
@ -154,46 +144,70 @@ public class PatchBukkitRegionAccessor implements RegionAccessor {
@NotNull EntityType type, @NotNull EntityType type,
boolean randomizeData boolean randomizeData
) { ) {
// TODO Auto-generated method stub UUID worldUuid = (this instanceof PatchBukkitWorld w) ? w.getUID() : UUID.randomUUID();
throw new UnsupportedOperationException( UUID entityUuid = UUID.randomUUID();
"Unimplemented method 'spawnEntity'" try {
); var res = NativeBridgeFfi.spawnWorldEntity(SpawnWorldEntityRequest.newBuilder()
.setWorldUuid(BridgeUtils.convertUuid(worldUuid))
.setEntityType(type.name())
.setX(loc.getX())
.setY(loc.getY())
.setZ(loc.getZ())
.setYaw(loc.getYaw())
.setPitch(loc.getPitch())
.build());
if (res != null && res.hasEntityUuid()) {
entityUuid = UUID.fromString(res.getEntityUuid().getValue());
}
} catch (Throwable ignored) {}
Entity entity = PatchBukkitEntity.create(entityUuid, type, loc);
return entity;
} }
@Override @Override
public @NotNull List<Entity> getEntities() { public @NotNull List<Entity> getEntities() {
// TODO Auto-generated method stub if (this instanceof PatchBukkitWorld world) {
throw new UnsupportedOperationException( return world.getEntities();
"Unimplemented method 'getEntities'" }
); return Collections.emptyList();
} }
@Override @Override
public @NotNull List<LivingEntity> getLivingEntities() { public @NotNull List<LivingEntity> getLivingEntities() {
// TODO Auto-generated method stub if (this instanceof PatchBukkitWorld world) {
throw new UnsupportedOperationException( return world.getLivingEntities();
"Unimplemented method 'getLivingEntities'" }
); return Collections.emptyList();
} }
@Override @Override
public @NotNull <T extends Entity> Collection<T> getEntitiesByClass( public @NotNull <T extends Entity> Collection<T> getEntitiesByClass(
@NotNull Class<T> cls @NotNull Class<T> cls
) { ) {
// TODO Auto-generated method stub List<T> list = new ArrayList<>();
throw new UnsupportedOperationException( for (Entity e : getEntities()) {
"Unimplemented method 'getEntitiesByClass'" if (cls.isInstance(e)) {
); list.add((T) e);
}
}
return list;
} }
@Override @Override
public @NotNull Collection<Entity> getEntitiesByClasses( public @NotNull Collection<Entity> getEntitiesByClasses(
@NonNull @NotNull Class<?>... classes @NonNull @NotNull Class<?>... classes
) { ) {
// TODO Auto-generated method stub List<Entity> list = new ArrayList<>();
throw new UnsupportedOperationException( for (Entity e : getEntities()) {
"Unimplemented method 'getEntitiesByClasses'" for (Class<?> c : classes) {
); if (c.isInstance(e)) {
list.add(e);
break;
}
}
}
return list;
} }
@Override @Override
@ -201,10 +215,14 @@ public class PatchBukkitRegionAccessor implements RegionAccessor {
@NotNull Location location, @NotNull Location location,
@NotNull Class<T> clazz @NotNull Class<T> clazz
) { ) {
// TODO Auto-generated method stub EntityType type = EntityType.PIG;
throw new UnsupportedOperationException( for (EntityType et : EntityType.values()) {
"Unimplemented method 'createEntity'" if (et.getEntityClass() != null && clazz.isAssignableFrom(et.getEntityClass())) {
); type = et;
break;
}
}
return (T) PatchBukkitEntity.create(UUID.randomUUID(), type, location);
} }
@Override @Override
@ -214,8 +232,11 @@ public class PatchBukkitRegionAccessor implements RegionAccessor {
@Nullable Consumer<? super T> function, @Nullable Consumer<? super T> function,
CreatureSpawnEvent.@NotNull SpawnReason reason CreatureSpawnEvent.@NotNull SpawnReason reason
) throws IllegalArgumentException { ) throws IllegalArgumentException {
// TODO Auto-generated method stub T entity = createEntity(location, clazz);
throw new UnsupportedOperationException("Unimplemented method 'spawn'"); if (function != null) {
function.accept(entity);
}
return addEntity(entity);
} }
@Override @Override
@ -225,32 +246,30 @@ public class PatchBukkitRegionAccessor implements RegionAccessor {
boolean randomizeData, boolean randomizeData,
@Nullable Consumer<? super T> function @Nullable Consumer<? super T> function
) throws IllegalArgumentException { ) throws IllegalArgumentException {
// TODO Auto-generated method stub return spawn(location, clazz, function, CreatureSpawnEvent.SpawnReason.DEFAULT);
throw new UnsupportedOperationException("Unimplemented method 'spawn'");
} }
@Override @Override
public int getHighestBlockYAt(int x, int z) { public int getHighestBlockYAt(int x, int z) {
// TODO Auto-generated method stub return getHighestBlockYAt(x, z, HeightMap.WORLD_SURFACE);
throw new UnsupportedOperationException(
"Unimplemented method 'getHighestBlockYAt'"
);
} }
@Override @Override
public int getHighestBlockYAt(@NotNull Location location) { public int getHighestBlockYAt(@NotNull Location location) {
// TODO Auto-generated method stub return getHighestBlockYAt(location.getBlockX(), location.getBlockZ());
throw new UnsupportedOperationException(
"Unimplemented method 'getHighestBlockYAt'"
);
} }
@Override @Override
public int getHighestBlockYAt(int x, int z, @NotNull HeightMap heightMap) { public int getHighestBlockYAt(int x, int z, @NotNull HeightMap heightMap) {
// TODO Auto-generated method stub int maxY = (this instanceof PatchBukkitWorld w) ? w.getMaxHeight() - 1 : 255;
throw new UnsupportedOperationException( int minY = (this instanceof PatchBukkitWorld w) ? w.getMinHeight() : -64;
"Unimplemented method 'getHighestBlockYAt'" for (int y = maxY; y >= minY; y--) {
); Material type = getType(x, y, z);
if (type != Material.AIR && type != Material.VOID_AIR && type != Material.CAVE_AIR) {
return y;
}
}
return minY;
} }
@Override @Override
@ -258,34 +277,33 @@ public class PatchBukkitRegionAccessor implements RegionAccessor {
@NotNull Location location, @NotNull Location location,
@NotNull HeightMap heightMap @NotNull HeightMap heightMap
) { ) {
// TODO Auto-generated method stub return getHighestBlockYAt(location.getBlockX(), location.getBlockZ(), heightMap);
throw new UnsupportedOperationException(
"Unimplemented method 'getHighestBlockYAt'"
);
} }
@Override @Override
public @NonNull <T extends Entity> T addEntity(@NonNull T entity) { public @NonNull <T extends Entity> T addEntity(@NonNull T entity) {
// TODO Auto-generated method stub if (this instanceof PatchBukkitWorld world) {
throw new UnsupportedOperationException( world.registerEntity(entity);
"Unimplemented method 'addEntity'" }
); return entity;
} }
@Override @Override
public @NotNull MoonPhase getMoonPhase() { public @NotNull MoonPhase getMoonPhase() {
// TODO Auto-generated method stub long day = 0;
throw new UnsupportedOperationException( if (this instanceof PatchBukkitWorld w) {
"Unimplemented method 'getMoonPhase'" day = w.getFullTime() / 24000L;
); }
int phaseIndex = (int) (day % 8);
return MoonPhase.values()[Math.abs(phaseIndex)];
} }
@Override @Override
public @NotNull NamespacedKey getKey() { public @NotNull NamespacedKey getKey() {
// TODO Auto-generated method stub if (this instanceof PatchBukkitWorld world) {
throw new UnsupportedOperationException( return world.getKey();
"Unimplemented method 'getKey'" }
); return NamespacedKey.minecraft("overworld");
} }
@Override @Override
@ -293,25 +311,16 @@ public class PatchBukkitRegionAccessor implements RegionAccessor {
@NotNull Location from, @NotNull Location from,
@NotNull Location to @NotNull Location to
) { ) {
// TODO Auto-generated method stub return true;
throw new UnsupportedOperationException(
"Unimplemented method 'lineOfSightExists'"
);
} }
@Override @Override
public boolean hasCollisionsIn(@NotNull BoundingBox boundingBox) { public boolean hasCollisionsIn(@NotNull BoundingBox boundingBox) {
// TODO Auto-generated method stub return false;
throw new UnsupportedOperationException(
"Unimplemented method 'hasCollisionsIn'"
);
} }
@Override @Override
public @Unmodifiable Set<FeatureFlag> getFeatureFlags() { public @Unmodifiable Set<FeatureFlag> getFeatureFlags() {
// TODO Auto-generated method stub return Collections.singleton(FeatureFlag.VANILLA);
throw new UnsupportedOperationException(
"Unimplemented method 'getFeatureFlags'"
);
} }
} }

View File

@ -13,6 +13,7 @@ import "patchbukkit/itemstack.proto";
import "patchbukkit/log.proto"; import "patchbukkit/log.proto";
import "patchbukkit/message.proto"; import "patchbukkit/message.proto";
import "patchbukkit/registry.proto"; import "patchbukkit/registry.proto";
import "patchbukkit/server.proto";
import "patchbukkit/sound.proto"; import "patchbukkit/sound.proto";
import "patchbukkit/world.proto"; import "patchbukkit/world.proto";
@ -32,6 +33,50 @@ service NativeBridge {
rpc IsOnGround(patchbukkit.common.UUID) returns (patchbukkit.entity.IsOnGroundResponse); rpc IsOnGround(patchbukkit.common.UUID) returns (patchbukkit.entity.IsOnGroundResponse);
rpc GetPlayerLocale(patchbukkit.common.UUID) returns (patchbukkit.entity.GetPlayerLocaleResponse); rpc GetPlayerLocale(patchbukkit.common.UUID) returns (patchbukkit.entity.GetPlayerLocaleResponse);
rpc IsOp(patchbukkit.common.UUID) returns (patchbukkit.entity.IsOpResponse); rpc IsOp(patchbukkit.common.UUID) returns (patchbukkit.entity.IsOpResponse);
rpc SetOp(patchbukkit.entity.SetOpRequest) returns (google.protobuf.Empty);
rpc GetFoodLevel(patchbukkit.common.UUID) returns (patchbukkit.entity.GetFoodLevelResponse);
rpc SetFoodLevel(patchbukkit.entity.SetFoodLevelRequest) returns (google.protobuf.Empty);
rpc SetSaturation(patchbukkit.entity.SetSaturationRequest) returns (google.protobuf.Empty);
rpc SetExhaustion(patchbukkit.entity.SetExhaustionRequest) returns (google.protobuf.Empty);
rpc GetExperience(patchbukkit.common.UUID) returns (patchbukkit.entity.GetExperienceResponse);
rpc SetExperience(patchbukkit.entity.SetExperienceRequest) returns (google.protobuf.Empty);
rpc KickPlayer(patchbukkit.entity.KickPlayerRequest) returns (google.protobuf.Empty);
rpc SendTitle(patchbukkit.entity.SendTitleRequest) returns (google.protobuf.Empty);
rpc SendActionBar(patchbukkit.entity.SendActionBarRequest) returns (google.protobuf.Empty);
rpc ResetTitle(patchbukkit.common.UUID) returns (google.protobuf.Empty);
rpc SetDisplayName(patchbukkit.entity.SetDisplayNameRequest) returns (google.protobuf.Empty);
rpc SetPlayerListName(patchbukkit.entity.SetPlayerListNameRequest) returns (google.protobuf.Empty);
rpc SetPlayerListHeaderFooter(patchbukkit.entity.SetPlayerListHeaderFooterRequest) returns (google.protobuf.Empty);
rpc GetPlayerPoseState(patchbukkit.common.UUID) returns (patchbukkit.entity.GetPlayerPoseStateResponse);
rpc SetSneaking(patchbukkit.entity.SetSneakingRequest) returns (google.protobuf.Empty);
rpc SetSprinting(patchbukkit.entity.SetSprintingRequest) returns (google.protobuf.Empty);
rpc SetPlayerTime(patchbukkit.entity.SetPlayerTimeRequest) returns (google.protobuf.Empty);
rpc ResetPlayerTime(patchbukkit.common.UUID) returns (google.protobuf.Empty);
rpc SetPlayerWeather(patchbukkit.entity.SetPlayerWeatherRequest) returns (google.protobuf.Empty);
rpc ResetPlayerWeather(patchbukkit.common.UUID) returns (google.protobuf.Empty);
rpc SetCompassTarget(patchbukkit.entity.SetCompassTargetRequest) returns (google.protobuf.Empty);
rpc GetCompassTarget(patchbukkit.common.UUID) returns (patchbukkit.common.Vec3);
rpc SetRespawnPoint(patchbukkit.entity.SetRespawnPointRequest) returns (google.protobuf.Empty);
rpc GetRespawnPoint(patchbukkit.common.UUID) returns (patchbukkit.common.Location);
rpc StopSound(patchbukkit.entity.StopSoundRequest) returns (google.protobuf.Empty);
rpc SendBlockChange(patchbukkit.entity.SendBlockChangeRequest) returns (google.protobuf.Empty);
rpc SendResourcePack(patchbukkit.entity.SendResourcePackRequest) returns (google.protobuf.Empty);
rpc GetPlayerConnectionInfo(patchbukkit.common.UUID) returns (patchbukkit.entity.PlayerConnectionInfoResponse);
rpc SendGameEvent(patchbukkit.entity.SendGameEventRequest) returns (google.protobuf.Empty);
rpc SetCooldown(patchbukkit.entity.SetCooldownRequest) returns (google.protobuf.Empty);
rpc GetCooldown(patchbukkit.entity.GetCooldownRequest) returns (patchbukkit.entity.GetCooldownResponse);
rpc OpenEnderChest(patchbukkit.common.UUID) returns (google.protobuf.Empty);
rpc UpdateInventory(patchbukkit.common.UUID) returns (google.protobuf.Empty);
rpc GetPlayerInventory(patchbukkit.common.UUID) returns (patchbukkit.itemstack.GetPlayerInventoryResponse); rpc GetPlayerInventory(patchbukkit.common.UUID) returns (patchbukkit.itemstack.GetPlayerInventoryResponse);
rpc SetPlayerInventorySlot(patchbukkit.itemstack.SetPlayerInventorySlotRequest) returns (google.protobuf.Empty); rpc SetPlayerInventorySlot(patchbukkit.itemstack.SetPlayerInventorySlotRequest) returns (google.protobuf.Empty);
@ -65,5 +110,46 @@ service NativeBridge {
rpc RegisterCommand(patchbukkit.command.RegisterCommandRequest) returns (google.protobuf.Empty); rpc RegisterCommand(patchbukkit.command.RegisterCommandRequest) returns (google.protobuf.Empty);
rpc GetWorldInfo(patchbukkit.world.GetWorldInfoRequest) returns (patchbukkit.world.GetWorldInfoResponse); rpc GetWorldInfo(patchbukkit.world.GetWorldInfoRequest) returns (patchbukkit.world.GetWorldInfoResponse);
rpc SetWorldTime(patchbukkit.world.SetWorldTimeRequest) returns (google.protobuf.Empty);
rpc SetWorldWeather(patchbukkit.world.SetWorldWeatherRequest) returns (google.protobuf.Empty);
rpc SetWorldSpawn(patchbukkit.world.SetWorldSpawnRequest) returns (google.protobuf.Empty);
rpc SetWorldDifficulty(patchbukkit.world.SetWorldDifficultyRequest) returns (google.protobuf.Empty);
rpc SetWorldPvp(patchbukkit.world.SetWorldPvpRequest) returns (google.protobuf.Empty);
rpc SetWorldGamerule(patchbukkit.world.SetWorldGameruleRequest) returns (google.protobuf.Empty);
rpc GetWorldGamerules(patchbukkit.world.GetWorldGamerulesRequest) returns (patchbukkit.world.GetWorldGamerulesResponse);
rpc GetWorldEntities(patchbukkit.world.GetWorldEntitiesRequest) returns (patchbukkit.world.GetWorldEntitiesResponse);
rpc SpawnWorldEntity(patchbukkit.world.SpawnWorldEntityRequest) returns (patchbukkit.world.SpawnWorldEntityResponse);
rpc CreateWorldExplosion(patchbukkit.world.CreateWorldExplosionRequest) returns (google.protobuf.Empty);
rpc PlayWorldSound(patchbukkit.world.PlayWorldSoundRequest) returns (google.protobuf.Empty);
rpc SetChunkForceLoaded(patchbukkit.world.SetChunkForceLoadedRequest) returns (google.protobuf.Empty);
rpc GetForceLoadedChunks(patchbukkit.world.GetForceLoadedChunksRequest) returns (patchbukkit.world.GetForceLoadedChunksResponse);
rpc SaveWorld(patchbukkit.world.SaveWorldRequest) returns (google.protobuf.Empty);
rpc GetServerInfo(patchbukkit.common.EmptyRequest) returns (patchbukkit.server.ServerInfoResponse);
rpc SetServerMotd(patchbukkit.server.SetServerMotdRequest) returns (google.protobuf.Empty);
rpc SetServerMaxPlayers(patchbukkit.server.SetServerMaxPlayersRequest) returns (google.protobuf.Empty);
rpc SetServerWhitelist(patchbukkit.server.SetServerWhitelistRequest) returns (google.protobuf.Empty);
rpc SetServerWhitelistEnforced(patchbukkit.server.SetServerWhitelistEnforcedRequest) returns (google.protobuf.Empty);
rpc SetServerIdleTimeout(patchbukkit.server.SetServerIdleTimeoutRequest) returns (google.protobuf.Empty);
rpc SetServerDefaultGamemode(patchbukkit.server.SetServerDefaultGamemodeRequest) returns (google.protobuf.Empty);
rpc GetServerTickInfo(patchbukkit.common.EmptyRequest) returns (patchbukkit.server.ServerTickInfoResponse);
rpc SetServerTickRate(patchbukkit.server.SetServerTickRateRequest) returns (google.protobuf.Empty);
rpc ShutdownServer(patchbukkit.server.ShutdownServerRequest) returns (google.protobuf.Empty);
rpc BroadcastMessage(patchbukkit.server.BroadcastMessageRequest) returns (google.protobuf.Empty);
rpc GetWhitelist(patchbukkit.common.EmptyRequest) returns (patchbukkit.server.GetWhitelistResponse);
rpc SetWhitelistPlayer(patchbukkit.server.SetWhitelistPlayerRequest) returns (google.protobuf.Empty);
rpc GetOperators(patchbukkit.common.EmptyRequest) returns (patchbukkit.server.GetOperatorsResponse);
rpc SetOperator(patchbukkit.server.SetOperatorRequest) returns (google.protobuf.Empty);
rpc GetBanList(patchbukkit.server.GetBanListRequest) returns (patchbukkit.server.GetBanListResponse);
rpc SetBanEntry(patchbukkit.server.SetBanEntryRequest) returns (google.protobuf.Empty);
rpc CreateWorld(patchbukkit.server.CreateWorldRequest) returns (patchbukkit.server.CreateWorldResponse);
rpc UnloadWorld(patchbukkit.server.UnloadWorldRequest) returns (patchbukkit.server.UnloadWorldResponse);
rpc SaveAllWorlds(patchbukkit.common.EmptyRequest) returns (google.protobuf.Empty);
} }

View File

@ -66,6 +66,171 @@ message IsOpResponse {
bool is_op = 1; bool is_op = 1;
} }
message SetOpRequest {
patchbukkit.common.UUID uuid = 1;
bool is_op = 2;
}
message GetFoodLevelResponse {
int32 food_level = 1;
float saturation = 2;
float exhaustion = 3;
}
message SetFoodLevelRequest {
patchbukkit.common.UUID uuid = 1;
int32 food_level = 2;
}
message SetSaturationRequest {
patchbukkit.common.UUID uuid = 1;
float saturation = 2;
}
message SetExhaustionRequest {
patchbukkit.common.UUID uuid = 1;
float exhaustion = 2;
}
message GetExperienceResponse {
int32 level = 1;
float progress = 2;
int32 total_experience = 3;
}
message SetExperienceRequest {
patchbukkit.common.UUID uuid = 1;
int32 level = 2;
float progress = 3;
int32 total_experience = 4;
}
message KickPlayerRequest {
patchbukkit.common.UUID uuid = 1;
string message = 2;
}
message SendTitleRequest {
patchbukkit.common.UUID uuid = 1;
string title = 2;
string subtitle = 3;
int32 fade_in = 4;
int32 stay = 5;
int32 fade_out = 6;
}
message SendActionBarRequest {
patchbukkit.common.UUID uuid = 1;
string message = 2;
}
message SetDisplayNameRequest {
patchbukkit.common.UUID uuid = 1;
string display_name = 2;
}
message SetPlayerListNameRequest {
patchbukkit.common.UUID uuid = 1;
string list_name = 2;
}
message SetPlayerListHeaderFooterRequest {
patchbukkit.common.UUID uuid = 1;
string header = 2;
string footer = 3;
}
message GetPlayerPoseStateResponse {
bool is_sneaking = 1;
bool is_sprinting = 2;
bool is_gliding = 3;
bool is_swimming = 4;
bool is_sleeping = 5;
}
message SetSneakingRequest {
patchbukkit.common.UUID uuid = 1;
bool sneaking = 2;
}
message SetSprintingRequest {
patchbukkit.common.UUID uuid = 1;
bool sprinting = 2;
}
message SetPlayerTimeRequest {
patchbukkit.common.UUID uuid = 1;
int64 time = 2;
bool relative = 3;
}
message SetPlayerWeatherRequest {
patchbukkit.common.UUID uuid = 1;
int32 weather = 2;
}
message SetCompassTargetRequest {
patchbukkit.common.UUID uuid = 1;
patchbukkit.common.Vec3 position = 2;
}
message SetRespawnPointRequest {
patchbukkit.common.UUID uuid = 1;
patchbukkit.common.Vec3 position = 2;
float yaw = 3;
bool force = 4;
}
message StopSoundRequest {
patchbukkit.common.UUID uuid = 1;
string sound = 2;
string category = 3;
}
message SendBlockChangeRequest {
patchbukkit.common.UUID uuid = 1;
int32 x = 2;
int32 y = 3;
int32 z = 4;
string block_state = 5;
}
message SendResourcePackRequest {
patchbukkit.common.UUID uuid = 1;
string url = 2;
string prompt = 3;
bool required = 4;
}
message PlayerConnectionInfoResponse {
int32 ping = 1;
string address = 2;
int32 port = 3;
string client_brand = 4;
}
message SendGameEventRequest {
patchbukkit.common.UUID uuid = 1;
int32 event_type = 2;
float value = 3;
}
message SetCooldownRequest {
patchbukkit.common.UUID uuid = 1;
string item_group = 2;
int32 duration_ticks = 3;
}
message GetCooldownRequest {
patchbukkit.common.UUID uuid = 1;
string item_group = 2;
}
message GetCooldownResponse {
float cooldown = 1;
bool is_on_cooldown = 2;
}

View File

@ -0,0 +1,162 @@
syntax = "proto3";
package patchbukkit.server;
import "google/protobuf/empty.proto";
import "patchbukkit/common/types.proto";
option java_multiple_files = true;
option java_package = "patchbukkit.server";
message ServerInfoResponse {
string server_name = 1;
string version = 2;
string bukkit_version = 3;
string minecraft_version = 4;
string motd = 5;
string ip = 6;
int32 port = 7;
int32 max_players = 8;
int32 view_distance = 9;
int32 simulation_distance = 10;
bool allow_flight = 11;
bool allow_nether = 12;
bool allow_end = 13;
bool online_mode = 14;
bool hardcore = 15;
bool pvp = 16;
bool has_whitelist = 17;
bool is_whitelist_enforced = 18;
int32 spawn_protection = 19;
string default_gamemode = 20;
int32 idle_timeout = 21;
bool generate_structures = 22;
int32 max_world_size = 23;
}
message SetServerMotdRequest {
string motd = 1;
}
message SetServerMaxPlayersRequest {
int32 max_players = 1;
}
message SetServerWhitelistRequest {
bool enabled = 1;
}
message SetServerWhitelistEnforcedRequest {
bool enforced = 1;
}
message SetServerIdleTimeoutRequest {
int32 timeout_minutes = 1;
}
message SetServerDefaultGamemodeRequest {
string gamemode = 1;
}
message ServerTickInfoResponse {
repeated double tps = 1;
float tick_rate = 2;
bool is_frozen = 3;
bool is_sprinting = 4;
bool is_stepping = 5;
double average_tick_time = 6;
int64 tick_count = 7;
repeated int64 tick_times_nanos = 8;
}
message SetServerTickRateRequest {
float tick_rate = 1;
bool frozen = 2;
}
message ShutdownServerRequest {
bool save = 1;
string message = 2;
}
message BroadcastMessageRequest {
string message = 1;
string permission = 2;
}
message WhitelistEntryProto {
patchbukkit.common.UUID uuid = 1;
string name = 2;
}
message GetWhitelistResponse {
repeated WhitelistEntryProto players = 1;
}
message SetWhitelistPlayerRequest {
patchbukkit.common.UUID uuid = 1;
string name = 2;
bool whitelisted = 3;
}
message OperatorEntryProto {
patchbukkit.common.UUID uuid = 1;
string name = 2;
int32 level = 3;
bool bypasses_player_limit = 4;
}
message GetOperatorsResponse {
repeated OperatorEntryProto operators = 1;
}
message SetOperatorRequest {
patchbukkit.common.UUID uuid = 1;
string name = 2;
int32 level = 3;
bool is_op = 4;
}
message BanEntryProto {
string target = 1;
string source = 2;
int64 created = 3;
int64 expires = 4;
string reason = 5;
}
message GetBanListRequest {
string ban_type = 1; // "NAME" or "IP" or "PROFILE"
}
message GetBanListResponse {
repeated BanEntryProto entries = 1;
}
message SetBanEntryRequest {
string ban_type = 1; // "NAME" or "IP" or "PROFILE"
BanEntryProto entry = 2;
bool remove = 3;
}
message CreateWorldRequest {
string name = 1;
string dimension = 2; // "minecraft:overworld", "minecraft:the_nether", "minecraft:the_end"
int64 seed = 3;
}
message CreateWorldResponse {
patchbukkit.common.UUID world_uuid = 1;
string name = 2;
bool success = 3;
}
message UnloadWorldRequest {
patchbukkit.common.UUID world_uuid = 1;
string world_name = 2;
bool save = 3;
}
message UnloadWorldResponse {
bool success = 1;
}

View File

@ -2,6 +2,7 @@ syntax = "proto3";
package patchbukkit.world; package patchbukkit.world;
import "google/protobuf/empty.proto";
import "patchbukkit/common/types.proto"; import "patchbukkit/common/types.proto";
option java_multiple_files = true; option java_multiple_files = true;
@ -38,6 +39,7 @@ message SpawnParticleRequest {
double offset_y = 8; double offset_y = 8;
double offset_z = 9; double offset_z = 9;
double extra = 10; double extra = 10;
bool force = 11;
} }
message GetWorldsResponse { message GetWorldsResponse {
@ -75,6 +77,150 @@ message GetWorldInfoResponse {
int32 max_height = 2; int32 max_height = 2;
int32 height = 3; int32 height = 3;
int64 seed = 4; int64 seed = 4;
string name = 5;
string dimension = 6;
int32 sea_level = 7;
int32 logical_height = 8;
string difficulty = 9;
bool hardcore = 10;
bool pvp = 11;
int32 spawn_x = 12;
int32 spawn_y = 13;
int32 spawn_z = 14;
float spawn_angle = 15;
int64 time = 16;
int64 full_time = 17;
bool is_storm = 18;
bool is_thundering = 19;
int32 weather_duration = 20;
int32 thunder_duration = 21;
int32 clear_weather_duration = 22;
} }
message SetWorldTimeRequest {
patchbukkit.common.UUID world_uuid = 1;
int64 time = 2;
int64 full_time = 3;
}
message SetWorldWeatherRequest {
patchbukkit.common.UUID world_uuid = 1;
bool storm = 2;
bool thundering = 3;
int32 weather_duration = 4;
int32 thunder_duration = 5;
int32 clear_weather_duration = 6;
}
message SetWorldSpawnRequest {
patchbukkit.common.UUID world_uuid = 1;
int32 x = 2;
int32 y = 3;
int32 z = 4;
float angle = 5;
}
message SetWorldDifficultyRequest {
patchbukkit.common.UUID world_uuid = 1;
string difficulty = 2;
}
message SetWorldPvpRequest {
patchbukkit.common.UUID world_uuid = 1;
bool pvp = 2;
}
message SetWorldGameruleRequest {
patchbukkit.common.UUID world_uuid = 1;
string rule = 2;
string value = 3;
}
message GetWorldGamerulesRequest {
patchbukkit.common.UUID world_uuid = 1;
}
message GetWorldGamerulesResponse {
map<string, string> gamerules = 1;
}
message EntitySummaryProto {
patchbukkit.common.UUID uuid = 1;
string entity_type = 2;
double x = 3;
double y = 4;
double z = 5;
float yaw = 6;
float pitch = 7;
bool is_player = 8;
string custom_name = 9;
}
message GetWorldEntitiesRequest {
patchbukkit.common.UUID world_uuid = 1;
}
message GetWorldEntitiesResponse {
repeated EntitySummaryProto entities = 1;
}
message SpawnWorldEntityRequest {
patchbukkit.common.UUID world_uuid = 1;
string entity_type = 2;
double x = 3;
double y = 4;
double z = 5;
float yaw = 6;
float pitch = 7;
}
message SpawnWorldEntityResponse {
patchbukkit.common.UUID entity_uuid = 1;
bool success = 2;
}
message CreateWorldExplosionRequest {
patchbukkit.common.UUID world_uuid = 1;
double x = 2;
double y = 3;
double z = 4;
float power = 5;
bool set_fire = 6;
bool break_blocks = 7;
}
message PlayWorldSoundRequest {
patchbukkit.common.UUID world_uuid = 1;
string sound = 2;
string category = 3;
double x = 4;
double y = 5;
double z = 6;
float volume = 7;
float pitch = 8;
int64 seed = 9;
}
message ChunkCoordProto {
int32 x = 1;
int32 z = 2;
}
message SetChunkForceLoadedRequest {
patchbukkit.common.UUID world_uuid = 1;
int32 x = 2;
int32 z = 3;
bool forced = 4;
}
message GetForceLoadedChunksRequest {
patchbukkit.common.UUID world_uuid = 1;
}
message GetForceLoadedChunksResponse {
repeated ChunkCoordProto chunks = 1;
}
message SaveWorldRequest {
patchbukkit.common.UUID world_uuid = 1;
}

229
rust/Cargo.lock generated
View File

@ -283,7 +283,7 @@ checksum = "82f6aeea286b8eb4dd3431a1be1b59d290ace00f5bfd8e2a159bc2a05e2c1667"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 3.0.3", "syn 3.0.4",
] ]
[[package]] [[package]]
@ -461,7 +461,7 @@ checksum = "46d07918caa9eeaaf06b7873925c53a61daac173539b4f7715090745e44e4e69"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 3.0.3", "syn 3.0.4",
] ]
[[package]] [[package]]
@ -499,9 +499,9 @@ dependencies = [
[[package]] [[package]]
name = "cap-fs-ext" name = "cap-fs-ext"
version = "4.0.2" version = "4.0.3"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d78e5a3368ae89b7cb68186411452b4b9fac8b41be9c19bf3f47c2d2c8e36e6b" checksum = "56ff379b70af8e08307a8f65e7040c7301cb4a572538ade16b4984f0da77847f"
dependencies = [ dependencies = [
"cap-primitives", "cap-primitives",
"cap-std", "cap-std",
@ -511,9 +511,9 @@ dependencies = [
[[package]] [[package]]
name = "cap-primitives" name = "cap-primitives"
version = "4.0.2" version = "4.0.3"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cdadbd7c002d3a484b35243669abdae85a0ebaded5a61117169dc3400f9a7ff0" checksum = "8b5f74729fd2f44701d1a8eb47e906cdb3ccd9ec0f02baad85a744b791940b18"
dependencies = [ dependencies = [
"ambient-authority", "ambient-authority",
"fs-set-times", "fs-set-times",
@ -529,9 +529,9 @@ dependencies = [
[[package]] [[package]]
name = "cap-std" name = "cap-std"
version = "4.0.2" version = "4.0.3"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7281235d6e96d3544ca18bba9049be92f4190f8d923e3caef1b5f66cfa752608" checksum = "c1ec78e242cfa2cfe276807ac2ecc00315a6c97786977414bcd1c3963b6c91b8"
dependencies = [ dependencies = [
"cap-primitives", "cap-primitives",
"io-extras", "io-extras",
@ -550,9 +550,9 @@ dependencies = [
[[package]] [[package]]
name = "cc" name = "cc"
version = "1.4.3" version = "1.4.4"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "509591b7bcd67f4ef775afad7662703b4935daaa6ec0e5605cfb1090b32a2b6d" checksum = "0ad534f4357a5264cce5019c989cf66a4f0dc4e0d1b1d15f8aacec0ff7360273"
dependencies = [ dependencies = [
"find-msvc-tools", "find-msvc-tools",
"jobserver", "jobserver",
@ -811,27 +811,27 @@ dependencies = [
[[package]] [[package]]
name = "cranelift-assembler-x64" name = "cranelift-assembler-x64"
version = "0.134.3" version = "0.134.4"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d552bd33b7a56dc70aeb1e1c960e51a218fa0db50f23873b500a310379450b2d" checksum = "16c273ba1bfc3a1cb27cb4f83df27134f3cb638a61f5ed79ab22cf86eb13da0d"
dependencies = [ dependencies = [
"cranelift-assembler-x64-meta", "cranelift-assembler-x64-meta",
] ]
[[package]] [[package]]
name = "cranelift-assembler-x64-meta" name = "cranelift-assembler-x64-meta"
version = "0.134.3" version = "0.134.4"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "078e80e4c222279e3330f6aa1a256ca77ddf156d4453166a9f09defedc4594dd" checksum = "cbf8909326a466739e83ffe11e74c0c8c6b4bfa911b612aece915daa746cff58"
dependencies = [ dependencies = [
"cranelift-srcgen", "cranelift-srcgen",
] ]
[[package]] [[package]]
name = "cranelift-bforest" name = "cranelift-bforest"
version = "0.134.3" version = "0.134.4"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "820ce15d4ad3562d613c31a67b6d0434d403e7091a68d1349903842f7d31737e" checksum = "3cb26b06b54d8b2f8cdf4d440a32fc3b3b91c71c162333a69a70a66fa265fc45"
dependencies = [ dependencies = [
"cranelift-entity", "cranelift-entity",
"wasmtime-internal-core", "wasmtime-internal-core",
@ -839,9 +839,9 @@ dependencies = [
[[package]] [[package]]
name = "cranelift-bitset" name = "cranelift-bitset"
version = "0.134.3" version = "0.134.4"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "61bca563d4b86d285928d9e27f97f27039bb33a0fc524fa130d7d0c106bf8ab3" checksum = "7ce4e57dbb7f73d08011808a19787c3a03c3e7854b2a479ed0b788915fb671be"
dependencies = [ dependencies = [
"serde", "serde",
"serde_derive", "serde_derive",
@ -850,9 +850,9 @@ dependencies = [
[[package]] [[package]]
name = "cranelift-codegen" name = "cranelift-codegen"
version = "0.134.3" version = "0.134.4"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "709f4b7c0fb57d952658d5b5c07fbdc4149acd7b7f0de9678ae754b9c981949a" checksum = "74b0a8968f33d0bc13bc86fe70dd3947e93acc9f358ed9848e9988448972a9be"
dependencies = [ dependencies = [
"bumpalo", "bumpalo",
"cranelift-assembler-x64", "cranelift-assembler-x64",
@ -881,9 +881,9 @@ dependencies = [
[[package]] [[package]]
name = "cranelift-codegen-meta" name = "cranelift-codegen-meta"
version = "0.134.3" version = "0.134.4"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "903dc8915af62aad1d9d0f39a5968d33fa80b9aa899ed6f56e47f40ca4512e1e" checksum = "f69e8e2fe9deb48ef7b8d0c61612f8c1e2277d0421c201e30a4c844a4ebac698"
dependencies = [ dependencies = [
"cranelift-assembler-x64-meta", "cranelift-assembler-x64-meta",
"cranelift-codegen-shared", "cranelift-codegen-shared",
@ -894,24 +894,24 @@ dependencies = [
[[package]] [[package]]
name = "cranelift-codegen-shared" name = "cranelift-codegen-shared"
version = "0.134.3" version = "0.134.4"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0522d74c227e49f3fd49ab055311486ae4f09083262b66705bed676952491469" checksum = "ad4db1e87a65e9c5a832e3ede160830d4e34df938ac38b5604819f8825687e73"
[[package]] [[package]]
name = "cranelift-control" name = "cranelift-control"
version = "0.134.3" version = "0.134.4"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "66560ea1c5cef72e170b18e46d263dba2d3169c9d39e8cafdab2173c6362cc1a" checksum = "9248cd37bb6ec460981f57ead368e32b1eca7a207ad50e3e9c7adc3b161f20e9"
dependencies = [ dependencies = [
"arbitrary", "arbitrary",
] ]
[[package]] [[package]]
name = "cranelift-entity" name = "cranelift-entity"
version = "0.134.3" version = "0.134.4"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b62ef5b17cc814d27e96b66a5b46da0e4ce2b8ac55a6d478048bb99d04b05526" checksum = "26b8bc91c0a3530d132acbf118965dabe9a084029eb0fa74b64760360dec3316"
dependencies = [ dependencies = [
"cranelift-bitset", "cranelift-bitset",
"serde", "serde",
@ -921,9 +921,9 @@ dependencies = [
[[package]] [[package]]
name = "cranelift-frontend" name = "cranelift-frontend"
version = "0.134.3" version = "0.134.4"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a87e0aaa39dbf70693b348a221e45904111704ee8f9fef140498471005f9842d" checksum = "5086f2ebc084a98b387e522680f62ef65a512444d8382151e03088716d3a1714"
dependencies = [ dependencies = [
"cranelift-codegen", "cranelift-codegen",
"hashbrown 0.17.1", "hashbrown 0.17.1",
@ -934,15 +934,15 @@ dependencies = [
[[package]] [[package]]
name = "cranelift-isle" name = "cranelift-isle"
version = "0.134.3" version = "0.134.4"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cf79003ebfa1eed5e87f3b84446ad5236f540268289960e14f387dc9b28e40b7" checksum = "b6a658670f779afc2df083be201ebe54805f69b7b9ffa8dab04142b72ed751c9"
[[package]] [[package]]
name = "cranelift-native" name = "cranelift-native"
version = "0.134.3" version = "0.134.4"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "05bf4f235743c81e67ee4db617c5a4a0b65d58d3f0cfc575ee0f1a4e0cd58273" checksum = "16a41083b1fd953debd6f32d21fcda765b258f323ce907fdd6b4715f2769c478"
dependencies = [ dependencies = [
"cranelift-codegen", "cranelift-codegen",
"libc", "libc",
@ -951,9 +951,9 @@ dependencies = [
[[package]] [[package]]
name = "cranelift-srcgen" name = "cranelift-srcgen"
version = "0.134.3" version = "0.134.4"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f6977c2a71ab1e0d1e62f966b411a498aa04c4dce47d93d52f8a360a06058922" checksum = "244d9cd0759b0a3ccdd587cafb13434f9ea43b5c9d07701d277daf5cd0f7b7c1"
[[package]] [[package]]
name = "crc" name = "crc"
@ -991,9 +991,9 @@ dependencies = [
[[package]] [[package]]
name = "crc32fast" name = "crc32fast"
version = "1.5.0" version = "1.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9481c1c90cbf2ac953f07c8d4a58aa3945c425b7185c9154d67a65e4230da511" checksum = "8498c871161e1742aaa9d52551b2d6ebdd4c3d45a3be423e3728f33b955be550"
dependencies = [ dependencies = [
"cfg-if", "cfg-if",
] ]
@ -1382,7 +1382,7 @@ checksum = "c6232dd377dcc64799954cbd3a9bb882e9cdc1308ccd87b1c098f1fb2eaf82a8"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 3.0.3", "syn 3.0.4",
] ]
[[package]] [[package]]
@ -1446,9 +1446,9 @@ dependencies = [
[[package]] [[package]]
name = "either" name = "either"
version = "1.17.0" version = "1.18.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9e5e8f6c15a24b9a3ee5efec809ccd006d3b30e8b3bb63c39af737c7f87daa1d" checksum = "252afb9ae5eaa683babdc6a068b3f5726eb19e05070c731f9b2a23a7c3e8ed34"
[[package]] [[package]]
name = "elliptic-curve" name = "elliptic-curve"
@ -1759,7 +1759,7 @@ checksum = "9fb9654ba8355388abeb8dcb4fc62f511300867002afc858860463bdd9fe0c44"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 3.0.3", "syn 3.0.4",
] ]
[[package]] [[package]]
@ -1889,9 +1889,9 @@ dependencies = [
[[package]] [[package]]
name = "h2" name = "h2"
version = "0.4.17" version = "0.4.19"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9f877e75f39e9827ec50a572dd592684ac28c029578726c85f1b2aa6ab807449" checksum = "ef8e5e5a340588f4452631496976cf8636d4a7ecf600239fdc27615d2530bc16"
dependencies = [ dependencies = [
"atomic-waker", "atomic-waker",
"bytes", "bytes",
@ -2163,9 +2163,9 @@ checksum = "e590f038c1464a96894fd6d10127e90a8be4509f56ff7ecef851b15cee0b7caa"
[[package]] [[package]]
name = "icu_provider" name = "icu_provider"
version = "2.3.0" version = "2.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "92a7ed671a6aad807a8651a2e1782a6598fda9ce5185dd8158549e95a91c6428" checksum = "d27bbb9d3abbefac45d55f647c9de1d44aafcd1186eb91879afef17c396c3e73"
dependencies = [ dependencies = [
"displaydoc", "displaydoc",
"icu_locale_core", "icu_locale_core",
@ -2550,9 +2550,9 @@ dependencies = [
[[package]] [[package]]
name = "log" name = "log"
version = "0.4.33" version = "0.4.34"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0ceec5bc11778974d1bcb055b18002eba7f4b3518b6a0081b3af5f21666da9ad" checksum = "f9f8bd3e56ce4dfc153cf470fffbfa98c7620958b312ca5c3a4b8d5181fd13c6"
[[package]] [[package]]
name = "lz4-java-wrc" name = "lz4-java-wrc"
@ -2834,7 +2834,7 @@ checksum = "e4e98dc3b890f6c23a0f9d3d491a2823d0dea0fa656302a13dd225fa924112a8"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 3.0.3", "syn 3.0.4",
] ]
[[package]] [[package]]
@ -3024,9 +3024,11 @@ dependencies = [
"log", "log",
"prost", "prost",
"prost-build", "prost-build",
"prost-types",
"protoc-bin-vendored", "protoc-bin-vendored",
"pumpkin", "pumpkin",
"pumpkin-api-macros", "pumpkin-api-macros",
"pumpkin-config",
"pumpkin-data", "pumpkin-data",
"pumpkin-protocol", "pumpkin-protocol",
"pumpkin-util", "pumpkin-util",
@ -3035,6 +3037,7 @@ dependencies = [
"serde", "serde",
"serde-saphyr", "serde-saphyr",
"serde_json", "serde_json",
"time",
"tokio", "tokio",
"toml 1.1.4+spec-1.1.0", "toml 1.1.4+spec-1.1.0",
"tracing", "tracing",
@ -3481,14 +3484,14 @@ checksum = "1c8d9ca532f185d5d4db7a7c9d51420b452168ea1c2b913953281bd6fe1fcbd0"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 3.0.3", "syn 3.0.4",
] ]
[[package]] [[package]]
name = "pulley-interpreter" name = "pulley-interpreter"
version = "47.0.3" version = "47.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bc5c8c21ea032e4efbdf2d067dc45171779dbe0c8ecf20ef4a57efa7474f2b0a" checksum = "ed2bd641cb6a3ec5bffe60405568933163e0ea619da0943e9d2a417ceaa7fe82"
dependencies = [ dependencies = [
"cranelift-bitset", "cranelift-bitset",
"log", "log",
@ -3498,9 +3501,9 @@ dependencies = [
[[package]] [[package]]
name = "pulley-macros" name = "pulley-macros"
version = "47.0.3" version = "47.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9f10925455d5dde962e3604eade797ba5488644c8ee14a44191e2f2b58980574" checksum = "d3fd4024d1e559eaf5579bc8ad84a2b5915f34b47acc13a06ffba7dac401b166"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
@ -3510,7 +3513,7 @@ dependencies = [
[[package]] [[package]]
name = "pumpkin" name = "pumpkin"
version = "0.1.0-dev+26.2-26.40" version = "0.1.0-dev+26.2-26.40"
source = "git+https://github.com/Pumpkin-MC/Pumpkin.git?branch=master#0d55aa54118956e459702394f2cf4609ab71db75" source = "git+https://github.com/Pumpkin-MC/Pumpkin.git?branch=master#d8c7c23c0bd547a72dc3df84c5e2855aba975cf8"
dependencies = [ dependencies = [
"aes 0.9.2", "aes 0.9.2",
"arc-swap", "arc-swap",
@ -3576,18 +3579,18 @@ dependencies = [
[[package]] [[package]]
name = "pumpkin-api-macros" name = "pumpkin-api-macros"
version = "0.1.0-dev+26.2-26.40" version = "0.1.0-dev+26.2-26.40"
source = "git+https://github.com/Pumpkin-MC/Pumpkin.git?branch=master#0d55aa54118956e459702394f2cf4609ab71db75" source = "git+https://github.com/Pumpkin-MC/Pumpkin.git?branch=master#d8c7c23c0bd547a72dc3df84c5e2855aba975cf8"
dependencies = [ dependencies = [
"proc-macro-error2", "proc-macro-error2",
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 3.0.3", "syn 3.0.4",
] ]
[[package]] [[package]]
name = "pumpkin-codecs" name = "pumpkin-codecs"
version = "0.1.0-dev+26.2-26.40" version = "0.1.0-dev+26.2-26.40"
source = "git+https://github.com/Pumpkin-MC/Pumpkin.git?branch=master#0d55aa54118956e459702394f2cf4609ab71db75" source = "git+https://github.com/Pumpkin-MC/Pumpkin.git?branch=master#d8c7c23c0bd547a72dc3df84c5e2855aba975cf8"
dependencies = [ dependencies = [
"serde_json", "serde_json",
"tracing", "tracing",
@ -3596,7 +3599,7 @@ dependencies = [
[[package]] [[package]]
name = "pumpkin-config" name = "pumpkin-config"
version = "0.1.0-dev+26.2-26.40" version = "0.1.0-dev+26.2-26.40"
source = "git+https://github.com/Pumpkin-MC/Pumpkin.git?branch=master#0d55aa54118956e459702394f2cf4609ab71db75" source = "git+https://github.com/Pumpkin-MC/Pumpkin.git?branch=master#d8c7c23c0bd547a72dc3df84c5e2855aba975cf8"
dependencies = [ dependencies = [
"pumpkin-util", "pumpkin-util",
"serde", "serde",
@ -3608,7 +3611,7 @@ dependencies = [
[[package]] [[package]]
name = "pumpkin-data" name = "pumpkin-data"
version = "0.1.0-dev+26.2-26.40" version = "0.1.0-dev+26.2-26.40"
source = "git+https://github.com/Pumpkin-MC/Pumpkin.git?branch=master#0d55aa54118956e459702394f2cf4609ab71db75" source = "git+https://github.com/Pumpkin-MC/Pumpkin.git?branch=master#d8c7c23c0bd547a72dc3df84c5e2855aba975cf8"
dependencies = [ dependencies = [
"crc-fast", "crc-fast",
"phf", "phf",
@ -3621,7 +3624,7 @@ dependencies = [
[[package]] [[package]]
name = "pumpkin-inventory" name = "pumpkin-inventory"
version = "0.1.0-dev+26.2-26.40" version = "0.1.0-dev+26.2-26.40"
source = "git+https://github.com/Pumpkin-MC/Pumpkin.git?branch=master#0d55aa54118956e459702394f2cf4609ab71db75" source = "git+https://github.com/Pumpkin-MC/Pumpkin.git?branch=master#d8c7c23c0bd547a72dc3df84c5e2855aba975cf8"
dependencies = [ dependencies = [
"pumpkin-data", "pumpkin-data",
"pumpkin-protocol", "pumpkin-protocol",
@ -3636,20 +3639,20 @@ dependencies = [
[[package]] [[package]]
name = "pumpkin-macros" name = "pumpkin-macros"
version = "0.1.0-dev+26.2-26.40" version = "0.1.0-dev+26.2-26.40"
source = "git+https://github.com/Pumpkin-MC/Pumpkin.git?branch=master#0d55aa54118956e459702394f2cf4609ab71db75" source = "git+https://github.com/Pumpkin-MC/Pumpkin.git?branch=master#d8c7c23c0bd547a72dc3df84c5e2855aba975cf8"
dependencies = [ dependencies = [
"heck", "heck",
"proc-macro-error2", "proc-macro-error2",
"proc-macro2", "proc-macro2",
"pumpkin-data", "pumpkin-data",
"quote", "quote",
"syn 3.0.3", "syn 3.0.4",
] ]
[[package]] [[package]]
name = "pumpkin-nbt" name = "pumpkin-nbt"
version = "0.1.0-dev+26.2-26.40" version = "0.1.0-dev+26.2-26.40"
source = "git+https://github.com/Pumpkin-MC/Pumpkin.git?branch=master#0d55aa54118956e459702394f2cf4609ab71db75" source = "git+https://github.com/Pumpkin-MC/Pumpkin.git?branch=master#d8c7c23c0bd547a72dc3df84c5e2855aba975cf8"
dependencies = [ dependencies = [
"bytes", "bytes",
"cesu8", "cesu8",
@ -3663,7 +3666,7 @@ dependencies = [
[[package]] [[package]]
name = "pumpkin-protocol" name = "pumpkin-protocol"
version = "0.1.0-dev+26.2-26.40" version = "0.1.0-dev+26.2-26.40"
source = "git+https://github.com/Pumpkin-MC/Pumpkin.git?branch=master#0d55aa54118956e459702394f2cf4609ab71db75" source = "git+https://github.com/Pumpkin-MC/Pumpkin.git?branch=master#d8c7c23c0bd547a72dc3df84c5e2855aba975cf8"
dependencies = [ dependencies = [
"aes 0.9.2", "aes 0.9.2",
"async-compression", "async-compression",
@ -3688,7 +3691,7 @@ dependencies = [
[[package]] [[package]]
name = "pumpkin-util" name = "pumpkin-util"
version = "0.1.0-dev+26.2-26.40" version = "0.1.0-dev+26.2-26.40"
source = "git+https://github.com/Pumpkin-MC/Pumpkin.git?branch=master#0d55aa54118956e459702394f2cf4609ab71db75" source = "git+https://github.com/Pumpkin-MC/Pumpkin.git?branch=master#d8c7c23c0bd547a72dc3df84c5e2855aba975cf8"
dependencies = [ dependencies = [
"base64 0.23.1", "base64 0.23.1",
"bytes", "bytes",
@ -3714,7 +3717,7 @@ dependencies = [
[[package]] [[package]]
name = "pumpkin-world" name = "pumpkin-world"
version = "0.1.0-dev+26.2-26.40" version = "0.1.0-dev+26.2-26.40"
source = "git+https://github.com/Pumpkin-MC/Pumpkin.git?branch=master#0d55aa54118956e459702394f2cf4609ab71db75" source = "git+https://github.com/Pumpkin-MC/Pumpkin.git?branch=master#d8c7c23c0bd547a72dc3df84c5e2855aba975cf8"
dependencies = [ dependencies = [
"arc-swap", "arc-swap",
"bitflags 2.13.1", "bitflags 2.13.1",
@ -4004,7 +4007,7 @@ checksum = "1c25ef604ac7dd839d44d64648952ea23c97866f124ff671b0ed2cf3ad9bb06e"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 3.0.3", "syn 3.0.4",
] ]
[[package]] [[package]]
@ -4426,9 +4429,9 @@ dependencies = [
[[package]] [[package]]
name = "rustls-webpki" name = "rustls-webpki"
version = "0.103.14" version = "0.103.15"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0527518605e68109d875e248ea259b6758801cf165e4b2c2733ae3b51f12535a" checksum = "f3c3cf1d8b1e7d4927e2d154c3fcb02979afb9939629c62cd9048d4f07b60ac2"
dependencies = [ dependencies = [
"ring", "ring",
"rustls-pki-types", "rustls-pki-types",
@ -4569,7 +4572,7 @@ checksum = "e7a5d71263a5a7d47b41f6b3f06ba276f10cc18b0931f1799f710578e2309348"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 3.0.3", "syn 3.0.4",
] ]
[[package]] [[package]]
@ -4593,7 +4596,7 @@ checksum = "8d3b1629de253c70a0508c3899572da79ca359fdab27c7920ff00406df418906"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 3.0.3", "syn 3.0.4",
] ]
[[package]] [[package]]
@ -4850,9 +4853,9 @@ dependencies = [
[[package]] [[package]]
name = "syn" name = "syn"
version = "3.0.3" version = "3.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "53e9bae58849f64dfa4f5d5ae372c8341f7305f82a3868709269343628b659a3" checksum = "e6275cddf4610d1775e6d1fe9469b2e77d0f39fd98fb7450901b821e0c53649f"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
@ -4954,7 +4957,7 @@ checksum = "bc04cd3e1236dd4a98afca4569f2deb3f120e5422a4023be2cb683f8486292af"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 3.0.3", "syn 3.0.4",
] ]
[[package]] [[package]]
@ -5048,7 +5051,7 @@ checksum = "78773a2a397f451582ce068015985c33193cf6dea8b74d2a639fe457b2f07b0e"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 3.0.3", "syn 3.0.4",
] ]
[[package]] [[package]]
@ -5501,9 +5504,9 @@ dependencies = [
[[package]] [[package]]
name = "wasmtime" name = "wasmtime"
version = "47.0.3" version = "47.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c80ca6098e0d4d06886d91d7f2cc3cb6623eb583c4c0ab3c89cbfb6098c8586c" checksum = "4cdaf5b8af5713146e3a62d940c71e3eb2ad1ebacfa6b55f0c8509a5b4b87718"
dependencies = [ dependencies = [
"addr2line", "addr2line",
"async-trait", "async-trait",
@ -5545,9 +5548,9 @@ dependencies = [
[[package]] [[package]]
name = "wasmtime-environ" name = "wasmtime-environ"
version = "47.0.3" version = "47.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "134f9d136d29c76f6c1b4c9b468e97a2efc38c7d49fd188e39b64870b2fea701" checksum = "58793271eab7ec26bab99ca497131f8b37702e7dc39e2f2d18293e3e915b4964"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"cpp_demangle", "cpp_demangle",
@ -5576,9 +5579,9 @@ dependencies = [
[[package]] [[package]]
name = "wasmtime-internal-cache" name = "wasmtime-internal-cache"
version = "47.0.3" version = "47.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "65db2eb1bfc5371a3b4107dbf539d0b93d05016fc29625b1648146b47db02071" checksum = "ede02ba77442cab88a2ddd5d16373e8bc450b55e05de8be039b024987a53d5a7"
dependencies = [ dependencies = [
"base64 0.22.1", "base64 0.22.1",
"directories-next", "directories-next",
@ -5596,9 +5599,9 @@ dependencies = [
[[package]] [[package]]
name = "wasmtime-internal-component-macro" name = "wasmtime-internal-component-macro"
version = "47.0.3" version = "47.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2bf7b91fed3fc34781d57f9c6654ea2513bf0a99f7b0b0523c00de1e6efebe1c" checksum = "c5bba4eff4804620bae04d7793308d97a0c5a8a97ea19e635452518f84560f84"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"proc-macro2", "proc-macro2",
@ -5611,15 +5614,15 @@ dependencies = [
[[package]] [[package]]
name = "wasmtime-internal-component-util" name = "wasmtime-internal-component-util"
version = "47.0.3" version = "47.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fc8a678149885cae00289f806fbe74c7863084cf74cace0b8dc73602279400e1" checksum = "3d7032ff6b4d7a45e05be1cb32ec1756df5f2669f08296dbe2c61c3789dead2c"
[[package]] [[package]]
name = "wasmtime-internal-core" name = "wasmtime-internal-core"
version = "47.0.3" version = "47.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8a0092c4b9d070ac5e278b6d0db10f5e71214190f0347ca57502b4692f796321" checksum = "f41d3b2cb9c3de7b696690af070d408b5e357011832f8b8bb7fbb315dbb620ed"
dependencies = [ dependencies = [
"hashbrown 0.17.1", "hashbrown 0.17.1",
"libm", "libm",
@ -5628,9 +5631,9 @@ dependencies = [
[[package]] [[package]]
name = "wasmtime-internal-cranelift" name = "wasmtime-internal-cranelift"
version = "47.0.3" version = "47.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6851ebc9e03cab23d9821d2b2505d380bdfe38f35ccec945247b05274d85c98b" checksum = "1d5cda5b90247978dd2ba6ca4e309508da19e9244445671d2d0e6a34e5c3b847"
dependencies = [ dependencies = [
"cfg-if", "cfg-if",
"cranelift-codegen", "cranelift-codegen",
@ -5655,9 +5658,9 @@ dependencies = [
[[package]] [[package]]
name = "wasmtime-internal-fiber" name = "wasmtime-internal-fiber"
version = "47.0.3" version = "47.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9b26da6d5f60d4c438da70bba3553fe810a840533a64156be287dca2081c6991" checksum = "4d126366176553e8f304e8ac2078a4cc37134fe8f19d4d4695a09c5a2f577f10"
dependencies = [ dependencies = [
"cc", "cc",
"cfg-if", "cfg-if",
@ -5670,9 +5673,9 @@ dependencies = [
[[package]] [[package]]
name = "wasmtime-internal-jit-debug" name = "wasmtime-internal-jit-debug"
version = "47.0.3" version = "47.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ed621ba25d7bf78b7edd7b4749abbb27c2e5cdba836c9504a424ee74b6c23149" checksum = "c1d28ff131bc40e11d84bc3a9fa0d264506439ffbbc89f3562fbb529d1caa617"
dependencies = [ dependencies = [
"cc", "cc",
"wasmtime-internal-versioned-export-macros", "wasmtime-internal-versioned-export-macros",
@ -5680,9 +5683,9 @@ dependencies = [
[[package]] [[package]]
name = "wasmtime-internal-jit-icache-coherence" name = "wasmtime-internal-jit-icache-coherence"
version = "47.0.3" version = "47.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5684ba160951baad06a725696f3c590e2fb0e8067c2aebee27bf7f9259058e85" checksum = "4a52de8b6afbfcd618073f592c1450cb661574e7061f3f4a44f1f7ec0c7f7909"
dependencies = [ dependencies = [
"cfg-if", "cfg-if",
"libc", "libc",
@ -5692,9 +5695,9 @@ dependencies = [
[[package]] [[package]]
name = "wasmtime-internal-unwinder" name = "wasmtime-internal-unwinder"
version = "47.0.3" version = "47.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "112eead527bffa8ff0646a11fb4339a9d52ddd1da2b9a6fce4aff84c815dd94f" checksum = "5524fbaaf3d2134dd6229164d7fc81e443cce06d1e0576524d5f4e86e747cb13"
dependencies = [ dependencies = [
"cfg-if", "cfg-if",
"cranelift-codegen", "cranelift-codegen",
@ -5705,9 +5708,9 @@ dependencies = [
[[package]] [[package]]
name = "wasmtime-internal-versioned-export-macros" name = "wasmtime-internal-versioned-export-macros"
version = "47.0.3" version = "47.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "153592e0bed824fc13c6696203fa1bd7bd20eb355316475e39a55f081ef80eca" checksum = "e93bb9ae112a80618510f938275a5d88637b656e7d0a021fc906f52206c708ad"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
@ -5716,9 +5719,9 @@ dependencies = [
[[package]] [[package]]
name = "wasmtime-internal-wit-bindgen" name = "wasmtime-internal-wit-bindgen"
version = "47.0.3" version = "47.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c456ad6e81e0f46abfeca43687d18ea15e289c395b262ea6e0023e2682e088be" checksum = "bfdbdd9d6fab1ecb67a7dc189e32c9e341444945fb709437bf8aab25d9a88459"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"bitflags 2.13.1", "bitflags 2.13.1",
@ -5729,9 +5732,9 @@ dependencies = [
[[package]] [[package]]
name = "wasmtime-wasi" name = "wasmtime-wasi"
version = "47.0.3" version = "47.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4c1cf60cd6a565213af7074b4aad7bb5e110e3c6c6aae54425aa0500a0e15e86" checksum = "b373095a6dc8382da56882ad16d7ea16771dd6927dd480b1ce3fe7c4b816d2cf"
dependencies = [ dependencies = [
"async-trait", "async-trait",
"bitflags 2.13.1", "bitflags 2.13.1",
@ -5754,9 +5757,9 @@ dependencies = [
[[package]] [[package]]
name = "wasmtime-wasi-http" name = "wasmtime-wasi-http"
version = "47.0.3" version = "47.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a499879061ee9f724342d5f5009754b723ae53c7932161ee063a9fe19395e915" checksum = "372fc7845f26cd8f69bcbf6e4234fcc032f718480c7722fa20a28eb74d58998e"
dependencies = [ dependencies = [
"async-trait", "async-trait",
"bytes", "bytes",
@ -5778,9 +5781,9 @@ dependencies = [
[[package]] [[package]]
name = "wasmtime-wasi-io" name = "wasmtime-wasi-io"
version = "47.0.3" version = "47.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7fb08e1d7755aaa467ce14080b7b01e33c914a920f6000696f1e42e40ea6387e" checksum = "51b3c9b39b1a1b3029f80e614623e182f165133dfaca8610f156450343f080a4"
dependencies = [ dependencies = [
"async-trait", "async-trait",
"bytes", "bytes",
@ -6320,9 +6323,9 @@ dependencies = [
[[package]] [[package]]
name = "zerovec" name = "zerovec"
version = "0.11.7" version = "0.11.8"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "94b5c6b5976d66c1d703c4fd17d3f5e43c8cedaacf604961b171adc7130896d8" checksum = "bb0464e17806c1d976d5cba29399c7f08e516e279e2ba493f63123b5fca67dd8"
dependencies = [ dependencies = [
"yoke", "yoke",
"zerofrom", "zerofrom",
@ -6331,13 +6334,13 @@ dependencies = [
[[package]] [[package]]
name = "zerovec-derive" name = "zerovec-derive"
version = "0.11.5" version = "0.11.6"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9f212a141d820099d57ffafb9569be9617a6f27d3dc881fbee8fb56642f917a9" checksum = "34df6fc39dbd26ddc9c10e6a2984476e13acce22e64e4487636ef494369225da"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 3.0.3", "syn 3.0.4",
] ]
[[package]] [[package]]

View File

@ -15,7 +15,9 @@ pumpkin = { git = "https://github.com/Pumpkin-MC/Pumpkin.git", branch = "master"
pumpkin-data = { git = "https://github.com/Pumpkin-MC/Pumpkin.git", branch = "master", package = "pumpkin-data" } pumpkin-data = { git = "https://github.com/Pumpkin-MC/Pumpkin.git", branch = "master", package = "pumpkin-data" }
pumpkin-protocol = { git = "https://github.com/Pumpkin-MC/Pumpkin.git", branch = "master", package = "pumpkin-protocol" } pumpkin-protocol = { git = "https://github.com/Pumpkin-MC/Pumpkin.git", branch = "master", package = "pumpkin-protocol" }
pumpkin-util = { git = "https://github.com/Pumpkin-MC/Pumpkin.git", branch = "master", package = "pumpkin-util" } pumpkin-util = { git = "https://github.com/Pumpkin-MC/Pumpkin.git", branch = "master", package = "pumpkin-util" }
pumpkin-config = { git = "https://github.com/Pumpkin-MC/Pumpkin.git", branch = "master", package = "pumpkin-config" }
pumpkin-api-macros = { git = "https://github.com/Pumpkin-MC/Pumpkin.git", branch = "master", package = "pumpkin-api-macros" } pumpkin-api-macros = { git = "https://github.com/Pumpkin-MC/Pumpkin.git", branch = "master", package = "pumpkin-api-macros" }
prost-types = "0.14"
tokio = "1" tokio = "1"
@ -27,7 +29,7 @@ walkdir = "2"
zip = "8.6" zip = "8.6"
anyhow = "1.0" anyhow = "1.0"
uuid = "1.24.1" uuid = "1.25.0"
serde = { version = "1.0", features = ["derive"] } serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0" serde_json = "1.0"
@ -35,6 +37,7 @@ serde-saphyr = "1.1.0"
rand = { git = "https://github.com/rust-random/rand"} rand = { git = "https://github.com/rust-random/rand"}
prost = "0.14" prost = "0.14"
time = "0.3"
toml = "1.1" toml = "1.1"

View File

@ -1,3 +1,5 @@
use std::str::FromStr;
use pumpkin::entity::EntityBase; use pumpkin::entity::EntityBase;
use crate::{ use crate::{
@ -5,8 +7,16 @@ use crate::{
proto::patchbukkit::{ proto::patchbukkit::{
common::Uuid, common::Uuid,
entity::{ entity::{
DamageEntityRequest, EntityHealthResponse, SetEntityHealthRequest, DamageEntityRequest, EntityHealthResponse, GetCooldownRequest, GetCooldownResponse,
SetEntityVelocityRequest, TeleportEntityRequest, GetExperienceResponse, GetFoodLevelResponse, GetPlayerPoseStateResponse,
KickPlayerRequest, PlayerConnectionInfoResponse, SendActionBarRequest,
SendBlockChangeRequest, SendGameEventRequest, SendResourcePackRequest,
SendTitleRequest, SetCompassTargetRequest, SetCooldownRequest, SetDisplayNameRequest,
SetEntityHealthRequest, SetEntityVelocityRequest, SetExhaustionRequest,
SetExperienceRequest, SetFoodLevelRequest, SetOpRequest,
SetPlayerListHeaderFooterRequest, SetPlayerListNameRequest, SetPlayerTimeRequest,
SetPlayerWeatherRequest, SetRespawnPointRequest, SetSaturationRequest,
SetSneakingRequest, SetSprintingRequest, StopSoundRequest, TeleportEntityRequest,
}, },
}, },
}; };
@ -171,3 +181,600 @@ pub fn ffi_native_bridge_is_op_impl(
Some(crate::proto::patchbukkit::entity::IsOpResponse { is_op }) Some(crate::proto::patchbukkit::entity::IsOpResponse { is_op })
} }
pub fn ffi_native_bridge_set_op_impl(request: SetOpRequest) -> Option<()> {
let ctx = CALLBACK_CONTEXT.get()?;
let is_op = request.is_op;
with_player(request.uuid.as_ref(), |player| {
let op_lvl = ctx.plugin_context.server.basic_config.op_permission_level;
let lvl = if is_op {
op_lvl
} else {
pumpkin_util::PermissionLvl::Zero
};
player.permission_lvl.store(lvl);
});
Some(())
}
pub fn ffi_native_bridge_get_food_level_impl(request: Uuid) -> Option<GetFoodLevelResponse> {
with_player(Some(&request), |player| {
let food_level = i32::from(player.hunger_manager.level.load());
let saturation = player.hunger_manager.saturation.load();
let exhaustion = player.hunger_manager.get_exhaustion();
GetFoodLevelResponse {
food_level,
saturation,
exhaustion,
}
})
}
pub fn ffi_native_bridge_set_food_level_impl(request: SetFoodLevelRequest) -> Option<()> {
let ctx = CALLBACK_CONTEXT.get()?;
with_player(request.uuid.as_ref(), |player| {
let food_level = request.food_level as u8;
let player = player.clone();
ctx.runtime.spawn(async move {
player.hunger_manager.set_level(food_level);
player.send_health().await;
});
})
}
pub fn ffi_native_bridge_set_saturation_impl(request: SetSaturationRequest) -> Option<()> {
let ctx = CALLBACK_CONTEXT.get()?;
with_player(request.uuid.as_ref(), |player| {
let saturation = request.saturation;
let player = player.clone();
ctx.runtime.spawn(async move {
player.hunger_manager.set_saturation(saturation);
player.send_health().await;
});
})
}
pub fn ffi_native_bridge_set_exhaustion_impl(request: SetExhaustionRequest) -> Option<()> {
with_player(request.uuid.as_ref(), |player| {
player.hunger_manager.set_exhaustion(request.exhaustion);
})
}
pub fn ffi_native_bridge_get_experience_impl(request: Uuid) -> Option<GetExperienceResponse> {
with_player(Some(&request), |player| {
let level = player.get_experience_level();
let progress = player.get_experience_progress();
let total_experience = player.get_total_experience();
GetExperienceResponse {
level,
progress,
total_experience,
}
})
}
pub fn ffi_native_bridge_set_experience_impl(request: SetExperienceRequest) -> Option<()> {
let ctx = CALLBACK_CONTEXT.get()?;
with_player(request.uuid.as_ref(), |player| {
let level = request.level;
let progress = request.progress;
let total_experience = request.total_experience;
let player = player.clone();
ctx.runtime.spawn(async move {
player
.set_experience(level, progress, total_experience)
.await;
});
})
}
pub fn ffi_native_bridge_kick_player_impl(request: KickPlayerRequest) -> Option<()> {
let ctx = CALLBACK_CONTEXT.get()?;
with_player(request.uuid.as_ref(), |player| {
let msg = request.message;
let player = player.clone();
ctx.runtime.spawn(async move {
let text = pumpkin_util::text::TextComponent::from_legacy_string(&msg);
player
.kick(pumpkin::net::DisconnectReason::Kicked, text)
.await;
});
})
}
pub fn ffi_native_bridge_send_title_impl(request: SendTitleRequest) -> Option<()> {
let ctx = CALLBACK_CONTEXT.get()?;
with_player(request.uuid.as_ref(), |player| {
let player = player.clone();
let title = request.title;
let subtitle = request.subtitle;
let fade_in = request.fade_in;
let stay = request.stay;
let fade_out = request.fade_out;
ctx.runtime.spawn(async move {
if fade_in >= 0 && stay >= 0 && fade_out >= 0 {
player.send_title_animation(fade_in, stay, fade_out).await;
}
if !title.is_empty() {
let text = pumpkin_util::text::TextComponent::from_legacy_string(&title);
player
.show_title(&text, &pumpkin::entity::player::TitleMode::Title)
.await;
}
if !subtitle.is_empty() {
let text = pumpkin_util::text::TextComponent::from_legacy_string(&subtitle);
player
.show_title(&text, &pumpkin::entity::player::TitleMode::SubTitle)
.await;
}
});
})
}
pub fn ffi_native_bridge_send_action_bar_impl(request: SendActionBarRequest) -> Option<()> {
let ctx = CALLBACK_CONTEXT.get()?;
with_player(request.uuid.as_ref(), |player| {
let msg = request.message;
let player = player.clone();
ctx.runtime.spawn(async move {
let text = pumpkin_util::text::TextComponent::from_legacy_string(&msg);
player.send_system_message_raw(&text, true).await;
});
})
}
pub fn ffi_native_bridge_reset_title_impl(request: Uuid) -> Option<()> {
let ctx = CALLBACK_CONTEXT.get()?;
with_player(Some(&request), |player| {
let player = player.clone();
ctx.runtime.spawn(async move {
let text = pumpkin_util::text::TextComponent::text("");
player
.show_title(&text, &pumpkin::entity::player::TitleMode::Title)
.await;
player
.show_title(&text, &pumpkin::entity::player::TitleMode::SubTitle)
.await;
});
})
}
pub fn ffi_native_bridge_set_display_name_impl(request: SetDisplayNameRequest) -> Option<()> {
let ctx = CALLBACK_CONTEXT.get()?;
with_player(request.uuid.as_ref(), |player| {
let name = request.display_name;
let player = player.clone();
ctx.runtime.spawn(async move {
let comp = if name.is_empty() {
None
} else {
Some(pumpkin_util::text::TextComponent::from_legacy_string(&name))
};
player.set_display_name(comp).await;
});
})
}
pub fn ffi_native_bridge_set_player_list_name_impl(
request: SetPlayerListNameRequest,
) -> Option<()> {
let ctx = CALLBACK_CONTEXT.get()?;
with_player(request.uuid.as_ref(), |player| {
let name = request.list_name;
let player = player.clone();
ctx.runtime.spawn(async move {
let comp = if name.is_empty() {
None
} else {
Some(pumpkin_util::text::TextComponent::from_legacy_string(&name))
};
player.set_tab_list_name(comp).await;
});
})
}
pub fn ffi_native_bridge_set_player_list_header_footer_impl(
request: SetPlayerListHeaderFooterRequest,
) -> Option<()> {
let ctx = CALLBACK_CONTEXT.get()?;
with_player(request.uuid.as_ref(), |player| {
let header = request.header;
let footer = request.footer;
let player = player.clone();
ctx.runtime.spawn(async move {
let h = pumpkin_util::text::TextComponent::from_legacy_string(&header);
let f = pumpkin_util::text::TextComponent::from_legacy_string(&footer);
player.set_tab_list_header_footer(h, f).await;
});
})
}
pub fn ffi_native_bridge_get_player_pose_state_impl(
request: Uuid,
) -> Option<GetPlayerPoseStateResponse> {
with_player(Some(&request), |player| {
let is_sneaking = player
.living_entity
.entity
.sneaking
.load(std::sync::atomic::Ordering::Relaxed);
let is_sprinting = player
.living_entity
.entity
.sprinting
.load(std::sync::atomic::Ordering::Relaxed);
let is_gliding = player
.living_entity
.entity
.fall_flying
.load(std::sync::atomic::Ordering::Relaxed);
let is_swimming = player
.living_entity
.entity
.swimming
.load(std::sync::atomic::Ordering::Relaxed);
let is_sleeping = player.sleeping_since.load().is_some();
GetPlayerPoseStateResponse {
is_sneaking,
is_sprinting,
is_gliding,
is_swimming,
is_sleeping,
}
})
}
pub fn ffi_native_bridge_set_sneaking_impl(request: SetSneakingRequest) -> Option<()> {
let ctx = CALLBACK_CONTEXT.get()?;
with_player(request.uuid.as_ref(), |player| {
let player = player.clone();
let sneaking = request.sneaking;
ctx.runtime.spawn(async move {
player.living_entity.entity.set_sneaking(sneaking).await;
});
})
}
pub fn ffi_native_bridge_set_sprinting_impl(request: SetSprintingRequest) -> Option<()> {
let ctx = CALLBACK_CONTEXT.get()?;
with_player(request.uuid.as_ref(), |player| {
let player = player.clone();
let sprinting = request.sprinting;
ctx.runtime.spawn(async move {
player.living_entity.entity.set_sprinting(sprinting).await;
});
})
}
pub fn ffi_native_bridge_set_player_time_impl(request: SetPlayerTimeRequest) -> Option<()> {
let ctx = CALLBACK_CONTEXT.get()?;
with_player(request.uuid.as_ref(), |player| {
let time = request.time;
let relative = request.relative;
let player = player.clone();
ctx.runtime.spawn(async move {
let packet = pumpkin_protocol::java::client::play::CUpdateTime::new(0, time, relative);
player.send_client_packet(&packet).await;
});
})
}
pub fn ffi_native_bridge_reset_player_time_impl(request: Uuid) -> Option<()> {
let ctx = CALLBACK_CONTEXT.get()?;
with_player(Some(&request), |player| {
let player = player.clone();
ctx.runtime.spawn(async move {
let world_time = player.world().level_time.lock().await.time_of_day;
let packet = pumpkin_protocol::java::client::play::CUpdateTime::new(
world_time, world_time, true,
);
player.send_client_packet(&packet).await;
});
})
}
pub fn ffi_native_bridge_set_player_weather_impl(request: SetPlayerWeatherRequest) -> Option<()> {
let ctx = CALLBACK_CONTEXT.get()?;
with_player(request.uuid.as_ref(), |player| {
let weather = request.weather;
let player = player.clone();
ctx.runtime.spawn(async move {
let event = if weather == 1 {
pumpkin_protocol::java::client::play::GameEvent::BeginRaining
} else {
pumpkin_protocol::java::client::play::GameEvent::EndRaining
};
let packet = pumpkin_protocol::java::client::play::CGameEvent::new(event, 0.0);
player.send_client_packet(&packet).await;
});
})
}
pub fn ffi_native_bridge_reset_player_weather_impl(request: Uuid) -> Option<()> {
let ctx = CALLBACK_CONTEXT.get()?;
with_player(Some(&request), |player| {
let player = player.clone();
ctx.runtime.spawn(async move {
let is_raining = player.world().is_raining().await;
let event = if is_raining {
pumpkin_protocol::java::client::play::GameEvent::BeginRaining
} else {
pumpkin_protocol::java::client::play::GameEvent::EndRaining
};
let packet = pumpkin_protocol::java::client::play::CGameEvent::new(event, 0.0);
player.send_client_packet(&packet).await;
});
})
}
pub fn ffi_native_bridge_set_compass_target_impl(request: SetCompassTargetRequest) -> Option<()> {
let ctx = CALLBACK_CONTEXT.get()?;
let pos = request.position?;
with_player(request.uuid.as_ref(), |player| {
let player = player.clone();
ctx.runtime.spawn(async move {
let block_pos = pumpkin_util::math::position::BlockPos::new(
pos.x as i32,
pos.y as i32,
pos.z as i32,
);
let dimension_name = player.world().dimension.minecraft_name.to_string();
let packet = pumpkin_protocol::java::client::play::CPlayerSpawnPosition::new(
block_pos,
0.0,
0.0,
dimension_name,
);
player.send_client_packet(&packet).await;
});
})
}
pub fn ffi_native_bridge_get_compass_target_impl(
request: Uuid,
) -> Option<crate::proto::patchbukkit::common::Vec3> {
with_player(Some(&request), |player| {
let info = player.world().level_info.load();
crate::proto::patchbukkit::common::Vec3 {
x: info.spawn_x as f64,
y: info.spawn_y as f64,
z: info.spawn_z as f64,
}
})
}
pub fn ffi_native_bridge_set_respawn_point_impl(request: SetRespawnPointRequest) -> Option<()> {
let ctx = CALLBACK_CONTEXT.get()?;
let pos = request.position?;
with_player(request.uuid.as_ref(), |player| {
let block_pos =
pumpkin_util::math::position::BlockPos::new(pos.x as i32, pos.y as i32, pos.z as i32);
let yaw = request.yaw;
let force = request.force;
let player = player.clone();
ctx.runtime.spawn(async move {
let dim = player.world().dimension.clone();
player
.set_respawn_point(dim, block_pos, yaw, 0.0, force)
.await;
});
})
}
pub fn ffi_native_bridge_get_respawn_point_impl(
request: Uuid,
) -> Option<crate::proto::patchbukkit::common::Location> {
with_player(Some(&request), |player| {
let (pos, yaw, world_uuid) = if let Ok(guard) = player.respawn_point.try_lock() {
match *guard {
Some(ref pt) => (
crate::proto::patchbukkit::common::Vec3 {
x: pt.position.0.x as f64,
y: pt.position.0.y as f64,
z: pt.position.0.z as f64,
},
pt.yaw,
player.world().uuid.to_string(),
),
None => {
let info = player.world().level_info.load();
(
crate::proto::patchbukkit::common::Vec3 {
x: info.spawn_x as f64,
y: info.spawn_y as f64,
z: info.spawn_z as f64,
},
info.spawn_yaw,
player.world().uuid.to_string(),
)
}
}
} else {
let info = player.world().level_info.load();
(
crate::proto::patchbukkit::common::Vec3 {
x: info.spawn_x as f64,
y: info.spawn_y as f64,
z: info.spawn_z as f64,
},
info.spawn_yaw,
player.world().uuid.to_string(),
)
};
crate::proto::patchbukkit::common::Location {
position: Some(pos),
yaw,
pitch: 0.0,
world: Some(crate::proto::patchbukkit::common::World {
uuid: Some(crate::proto::patchbukkit::common::Uuid { value: world_uuid }),
}),
}
})
}
pub fn ffi_native_bridge_stop_sound_impl(request: StopSoundRequest) -> Option<()> {
let ctx = CALLBACK_CONTEXT.get()?;
with_player(request.uuid.as_ref(), |player| {
let player = player.clone();
let sound = if request.sound.is_empty() {
None
} else {
pumpkin_util::resource_location::ResourceLocation::from_str(&request.sound).ok()
};
let category = if request.category.is_empty() {
None
} else {
pumpkin_data::sound::SoundCategory::from_name(&request.category.to_lowercase())
};
ctx.runtime.spawn(async move {
player.stop_sound(sound, category).await;
});
})
}
pub fn ffi_native_bridge_send_block_change_impl(request: SendBlockChangeRequest) -> Option<()> {
let ctx = CALLBACK_CONTEXT.get()?;
with_player(request.uuid.as_ref(), |player| {
let player = player.clone();
let pos = pumpkin_util::math::position::BlockPos::new(request.x, request.y, request.z);
let state_str = request.block_state;
let clean_key = state_str
.split('[')
.next()
.unwrap_or(&state_str)
.trim_start_matches("minecraft:");
let state_id = if let Some(b) = pumpkin_data::Block::from_registry_key(clean_key) {
b.default_state.id
} else {
pumpkin_data::BlockStateId::new_or_air(0)
};
ctx.runtime.spawn(async move {
let packet = pumpkin_protocol::java::client::play::CBlockUpdate::new(
pos,
pumpkin_protocol::codec::var_int::VarInt(state_id.as_u16() as i32),
);
player.send_client_packet(&packet).await;
});
})
}
pub fn ffi_native_bridge_send_resource_pack_impl(request: SendResourcePackRequest) -> Option<()> {
let ctx = CALLBACK_CONTEXT.get()?;
with_player(request.uuid.as_ref(), |player| {
let player = player.clone();
let url = request.url;
let prompt = request.prompt;
let required = request.required;
ctx.runtime.spawn(async move {
let prompt_comp = if prompt.is_empty() {
None
} else {
Some(pumpkin_util::text::TextComponent::from_legacy_string(
&prompt,
))
};
let id = uuid::Uuid::new_v4();
let packet = pumpkin_protocol::java::client::play::CAddResourcePack::new(
&id,
&url,
"",
required,
prompt_comp,
);
player.send_client_packet(&packet).await;
});
})
}
pub fn ffi_native_bridge_get_player_connection_info_impl(
request: Uuid,
) -> Option<PlayerConnectionInfoResponse> {
with_player(Some(&request), |player| {
let ping = player.ping.load(std::sync::atomic::Ordering::Relaxed) as i32;
let addr = player.client.address();
PlayerConnectionInfoResponse {
ping,
address: addr.ip().to_string(),
port: addr.port() as i32,
client_brand: "vanilla".to_string(),
}
})
}
pub fn ffi_native_bridge_send_game_event_impl(request: SendGameEventRequest) -> Option<()> {
let ctx = CALLBACK_CONTEXT.get()?;
with_player(request.uuid.as_ref(), |player| {
let player = player.clone();
let event_type = request.event_type as u8;
let value = request.value;
ctx.runtime.spawn(async move {
let packet = pumpkin_protocol::java::client::play::CGameEvent {
event: event_type,
value,
};
player.send_client_packet(&packet).await;
});
})
}
pub fn ffi_native_bridge_set_cooldown_impl(request: SetCooldownRequest) -> Option<()> {
let ctx = CALLBACK_CONTEXT.get()?;
with_player(request.uuid.as_ref(), |player| {
let player = player.clone();
let group = request.item_group;
let duration = request.duration_ticks;
ctx.runtime.spawn(async move {
player.start_cooldown(group, duration).await;
});
})
}
pub fn ffi_native_bridge_get_cooldown_impl(
request: GetCooldownRequest,
) -> Option<GetCooldownResponse> {
with_player(request.uuid.as_ref(), |player| {
let (cooldown, is_on_cooldown) = if let Ok(guard) = player.item_cooldowns.try_lock() {
if let Some(cooldown) = guard.get(&request.item_group) {
let current_tick = player
.tick_counter
.load(std::sync::atomic::Ordering::Relaxed);
let elapsed = current_tick - cooldown.start_tick;
if elapsed < cooldown.duration {
(1.0 - (elapsed as f32 / cooldown.duration as f32), true)
} else {
(0.0, false)
}
} else {
(0.0, false)
}
} else {
(0.0, false)
};
GetCooldownResponse {
cooldown,
is_on_cooldown,
}
})
}
pub fn ffi_native_bridge_open_ender_chest_impl(request: Uuid) -> Option<()> {
let ctx = CALLBACK_CONTEXT.get()?;
with_player(Some(&request), |player| {
let player = player.clone();
ctx.runtime.spawn(async move {
player.open_ender_chest().await;
});
})
}
pub fn ffi_native_bridge_update_inventory_impl(request: Uuid) -> Option<()> {
let ctx = CALLBACK_CONTEXT.get()?;
with_player(Some(&request), |player| {
let player = player.clone();
ctx.runtime.spawn(async move {
player
.on_screen_handler_opened(player.player_screen_handler.clone())
.await;
});
})
}

View File

@ -47,6 +47,9 @@ pub use itemstack::*;
pub mod command; pub mod command;
pub use command::*; pub use command::*;
pub mod server;
pub use server::*;
static CALLBACK_CONTEXT: OnceLock<CallbackContext> = OnceLock::new(); static CALLBACK_CONTEXT: OnceLock<CallbackContext> = OnceLock::new();
struct CallbackContext { struct CallbackContext {

View File

@ -0,0 +1,452 @@
use pumpkin_util::text::TextComponent;
use std::sync::atomic::Ordering;
use crate::{
java::native_callbacks::CALLBACK_CONTEXT,
proto::patchbukkit::{
common::{EmptyRequest, Uuid as ProtoUuid},
server::{
BanEntryProto, BroadcastMessageRequest, CreateWorldRequest, CreateWorldResponse,
GetBanListRequest, GetBanListResponse, GetOperatorsResponse, GetWhitelistResponse,
OperatorEntryProto, ServerInfoResponse, ServerTickInfoResponse, SetBanEntryRequest,
SetOperatorRequest, SetServerDefaultGamemodeRequest, SetServerIdleTimeoutRequest,
SetServerMaxPlayersRequest, SetServerMotdRequest, SetServerTickRateRequest,
SetServerWhitelistEnforcedRequest, SetServerWhitelistRequest,
SetWhitelistPlayerRequest, ShutdownServerRequest, UnloadWorldRequest,
UnloadWorldResponse, WhitelistEntryProto,
},
},
};
pub fn ffi_native_bridge_get_server_info_impl(
_request: EmptyRequest,
) -> Option<ServerInfoResponse> {
let ctx = CALLBACK_CONTEXT.get()?;
let server = &ctx.plugin_context.server;
let java_config = &server.advanced_config.networking.java;
let motd = java_config.motd.clone();
let ip = java_config.address.ip().to_string();
let port = java_config.address.port() as i32;
let max_players = java_config.max_players as i32;
let view_distance = java_config.view_distance.get() as i32;
let simulation_distance = java_config.simulation_distance.get() as i32;
let allow_flight = true;
let allow_nether = server.basic_config.allow_nether;
let allow_end = server.basic_config.allow_end;
let online_mode = java_config.online_mode;
let hardcore = server.basic_config.hardcore;
let pvp = server.advanced_config.pvp.enabled;
let has_whitelist = server.white_list.load(Ordering::Relaxed);
let is_whitelist_enforced = server.basic_config.enforce_whitelist;
let spawn_protection = 16;
let idle_timeout = server.player_idle_timeout.load(Ordering::Relaxed);
let default_gamemode = if let Ok(gm) = server.defaultgamemode.try_lock() {
format!("{:?}", gm.gamemode)
} else {
"SURVIVAL".to_string()
};
Some(ServerInfoResponse {
server_name: "Pumpkin".to_string(),
version: "0.1.0".to_string(),
bukkit_version: "1.21.4-R0.1-SNAPSHOT".to_string(),
minecraft_version: "1.21.4".to_string(),
motd,
ip,
port,
max_players,
view_distance,
simulation_distance,
allow_flight,
allow_nether,
allow_end,
online_mode,
hardcore,
pvp,
has_whitelist,
is_whitelist_enforced,
spawn_protection,
default_gamemode,
idle_timeout,
generate_structures: true,
max_world_size: 29999984,
})
}
pub fn ffi_native_bridge_set_server_motd_impl(_request: SetServerMotdRequest) -> Option<()> {
Some(())
}
pub fn ffi_native_bridge_set_server_max_players_impl(
_request: SetServerMaxPlayersRequest,
) -> Option<()> {
Some(())
}
pub fn ffi_native_bridge_set_server_whitelist_impl(
request: SetServerWhitelistRequest,
) -> Option<()> {
let ctx = CALLBACK_CONTEXT.get()?;
ctx.plugin_context
.server
.white_list
.store(request.enabled, Ordering::Relaxed);
Some(())
}
pub fn ffi_native_bridge_set_server_whitelist_enforced_impl(
_request: SetServerWhitelistEnforcedRequest,
) -> Option<()> {
Some(())
}
pub fn ffi_native_bridge_set_server_idle_timeout_impl(
request: SetServerIdleTimeoutRequest,
) -> Option<()> {
let ctx = CALLBACK_CONTEXT.get()?;
ctx.plugin_context
.server
.player_idle_timeout
.store(request.timeout_minutes, Ordering::Relaxed);
Some(())
}
pub fn ffi_native_bridge_set_server_default_gamemode_impl(
request: SetServerDefaultGamemodeRequest,
) -> Option<()> {
let ctx = CALLBACK_CONTEXT.get()?;
if let Ok(mut gm) = ctx.plugin_context.server.defaultgamemode.try_lock() {
let mode = match request.gamemode.to_uppercase().as_str() {
"CREATIVE" => pumpkin_util::GameMode::Creative,
"ADVENTURE" => pumpkin_util::GameMode::Adventure,
"SPECTATOR" => pumpkin_util::GameMode::Spectator,
_ => pumpkin_util::GameMode::Survival,
};
gm.gamemode = mode;
}
Some(())
}
pub fn ffi_native_bridge_get_server_tick_info_impl(
_request: EmptyRequest,
) -> Option<ServerTickInfoResponse> {
let ctx = CALLBACK_CONTEXT.get()?;
let server = &ctx.plugin_context.server;
let tick_rate = server.tick_rate_manager.tickrate();
let is_frozen = server.tick_rate_manager.is_frozen();
let is_sprinting = server.tick_rate_manager.is_sprinting();
let is_stepping = server.tick_rate_manager.is_stepping_forward();
let tick_count = server.tick_count.load(Ordering::Relaxed) as i64;
let avg_nanos = server.aggregated_tick_times_nanos.load(Ordering::Relaxed);
let average_tick_time = (avg_nanos as f64) / 1_000_000.0 / 100.0;
let mut tick_times_nanos = Vec::new();
if let Ok(times) = server.tick_times_nanos.try_lock() {
tick_times_nanos.extend_from_slice(&*times);
} else {
tick_times_nanos.resize(100, 50_000_000);
}
let tps_val = (1_000.0 / (average_tick_time.max(50.0))).min(tick_rate as f64);
let tps = vec![tps_val, tps_val, tps_val];
Some(ServerTickInfoResponse {
tps,
tick_rate,
is_frozen,
is_sprinting,
is_stepping,
average_tick_time,
tick_count,
tick_times_nanos,
})
}
pub fn ffi_native_bridge_set_server_tick_rate_impl(
request: SetServerTickRateRequest,
) -> Option<()> {
let ctx = CALLBACK_CONTEXT.get()?;
let server = &ctx.plugin_context.server;
if request.tick_rate > 0.0 {
server
.tick_rate_manager
.set_tick_rate(server, request.tick_rate);
}
server.tick_rate_manager.set_frozen(server, request.frozen);
Some(())
}
pub fn ffi_native_bridge_shutdown_server_impl(request: ShutdownServerRequest) -> Option<()> {
let ctx = CALLBACK_CONTEXT.get()?;
let server = ctx.plugin_context.server.clone();
ctx.runtime.spawn(async move {
if request.save {
let _ = server.save_all().await;
}
server.shutdown().await;
});
Some(())
}
pub fn ffi_native_bridge_broadcast_message_impl(request: BroadcastMessageRequest) -> Option<()> {
let ctx = CALLBACK_CONTEXT.get()?;
let server = ctx.plugin_context.server.clone();
let text = TextComponent::text(request.message);
let sender = TextComponent::text("Server");
ctx.runtime.spawn(async move {
server.broadcast_message(&text, &sender, 0, None).await;
});
Some(())
}
pub fn ffi_native_bridge_get_whitelist_impl(
_request: EmptyRequest,
) -> Option<GetWhitelistResponse> {
let ctx = CALLBACK_CONTEXT.get()?;
let mut players = Vec::new();
if let Ok(config) = ctx.plugin_context.server.data.whitelist_config.try_read() {
for entry in &config.whitelist {
players.push(WhitelistEntryProto {
uuid: Some(ProtoUuid {
value: entry.uuid.to_string(),
}),
name: entry.name.clone(),
});
}
}
Some(GetWhitelistResponse { players })
}
pub fn ffi_native_bridge_set_whitelist_player_impl(
request: SetWhitelistPlayerRequest,
) -> Option<()> {
let ctx = CALLBACK_CONTEXT.get()?;
let uuid_str = &request.uuid.as_ref()?.value;
let target_uuid = uuid::Uuid::parse_str(uuid_str).ok()?;
let target_name = request.name;
let server = ctx.plugin_context.server.clone();
ctx.runtime.spawn(async move {
let mut config = server.data.whitelist_config.write().await;
if request.whitelisted {
if !config.whitelist.iter().any(|e| e.uuid == target_uuid) {
config
.whitelist
.push(pumpkin_config::whitelist::WhitelistEntry {
uuid: target_uuid,
name: target_name,
});
}
} else {
config.whitelist.retain(|e| e.uuid != target_uuid);
}
});
Some(())
}
pub fn ffi_native_bridge_get_operators_impl(
_request: EmptyRequest,
) -> Option<GetOperatorsResponse> {
let ctx = CALLBACK_CONTEXT.get()?;
let mut operators = Vec::new();
if let Ok(config) = ctx.plugin_context.server.data.operator_config.try_read() {
for op in &config.ops {
operators.push(OperatorEntryProto {
uuid: Some(ProtoUuid {
value: op.uuid.to_string(),
}),
name: op.name.clone(),
level: op.level as i32,
bypasses_player_limit: op.bypasses_player_limit,
});
}
}
Some(GetOperatorsResponse { operators })
}
pub fn ffi_native_bridge_set_operator_impl(request: SetOperatorRequest) -> Option<()> {
let ctx = CALLBACK_CONTEXT.get()?;
let uuid_str = &request.uuid.as_ref()?.value;
let target_uuid = uuid::Uuid::parse_str(uuid_str).ok()?;
let target_name = request.name;
let level = match request.level {
1 => pumpkin_util::permission::PermissionLvl::One,
2 => pumpkin_util::permission::PermissionLvl::Two,
3 => pumpkin_util::permission::PermissionLvl::Three,
_ => pumpkin_util::permission::PermissionLvl::Four,
};
let server = ctx.plugin_context.server.clone();
ctx.runtime.spawn(async move {
let mut config = server.data.operator_config.write().await;
if request.is_op {
if let Some(op) = config.ops.iter_mut().find(|e| e.uuid == target_uuid) {
op.level = level;
} else {
config.ops.push(pumpkin_config::op::Op {
uuid: target_uuid,
name: target_name,
level,
bypasses_player_limit: false,
});
}
} else {
config.ops.retain(|e| e.uuid != target_uuid);
}
});
Some(())
}
pub fn ffi_native_bridge_get_ban_list_impl(
request: GetBanListRequest,
) -> Option<GetBanListResponse> {
let ctx = CALLBACK_CONTEXT.get()?;
let mut entries = Vec::new();
if request.ban_type == "IP" {
if let Ok(list) = ctx.plugin_context.server.data.banned_ip_list.try_read() {
for entry in &list.banned_ips {
let created = entry.created.unix_timestamp();
let expires = entry.expires.map(|e| e.unix_timestamp()).unwrap_or(0);
entries.push(BanEntryProto {
target: entry.ip.to_string(),
source: entry.source.clone(),
created,
expires,
reason: entry.reason.clone(),
});
}
}
} else {
if let Ok(list) = ctx.plugin_context.server.data.banned_player_list.try_read() {
for entry in &list.banned_players {
let created = entry.created.unix_timestamp();
let expires = entry.expires.map(|e| e.unix_timestamp()).unwrap_or(0);
entries.push(BanEntryProto {
target: entry.name.clone(),
source: entry.source.clone(),
created,
expires,
reason: entry.reason.clone(),
});
}
}
}
Some(GetBanListResponse { entries })
}
pub fn ffi_native_bridge_set_ban_entry_impl(request: SetBanEntryRequest) -> Option<()> {
let ctx = CALLBACK_CONTEXT.get()?;
let server = ctx.plugin_context.server.clone();
ctx.runtime.spawn(async move {
if let Some(entry) = request.entry {
if request.ban_type == "IP" {
if let Ok(ip) = entry.target.parse::<std::net::IpAddr>() {
let mut list = server.data.banned_ip_list.write().await;
if request.remove {
list.banned_ips.retain(|e| e.ip != ip);
} else {
list.banned_ips.retain(|e| e.ip != ip);
let expires = if entry.expires > 0 {
time::OffsetDateTime::from_unix_timestamp(entry.expires).ok()
} else {
None
};
list.banned_ips
.push(pumpkin::data::banlist_serializer::BannedIpEntry {
ip,
created: time::OffsetDateTime::now_utc(),
source: entry.source,
expires,
reason: entry.reason,
});
}
}
} else {
let mut list = server.data.banned_player_list.write().await;
let target_name = entry.target.clone();
if request.remove {
list.banned_players
.retain(|e| !e.name.eq_ignore_ascii_case(&target_name));
} else {
list.banned_players
.retain(|e| !e.name.eq_ignore_ascii_case(&target_name));
let expires = if entry.expires > 0 {
time::OffsetDateTime::from_unix_timestamp(entry.expires).ok()
} else {
None
};
list.banned_players.push(
pumpkin::data::banlist_serializer::BannedPlayerEntry {
uuid: uuid::Uuid::nil(),
name: target_name,
created: time::OffsetDateTime::now_utc(),
source: entry.source,
expires,
reason: entry.reason,
},
);
}
}
}
});
Some(())
}
pub fn ffi_native_bridge_create_world_impl(
request: CreateWorldRequest,
) -> Option<CreateWorldResponse> {
let ctx = CALLBACK_CONTEXT.get()?;
let server = ctx.plugin_context.server.clone();
let name = request.name;
let dim = match request.dimension.as_str() {
"minecraft:the_nether" => pumpkin_data::dimension::Dimension::THE_NETHER,
"minecraft:the_end" => pumpkin_data::dimension::Dimension::THE_END,
_ => pumpkin_data::dimension::Dimension::OVERWORLD,
};
let world_name = name.clone();
let (tx, rx) = tokio::sync::oneshot::channel();
ctx.runtime.spawn(async move {
let world = server.create_world(world_name, dim).await;
let _ = tx.send(world.uuid);
});
let world_uuid = rx.blocking_recv().ok()?;
Some(CreateWorldResponse {
world_uuid: Some(ProtoUuid {
value: world_uuid.to_string(),
}),
name,
success: true,
})
}
pub fn ffi_native_bridge_unload_world_impl(
request: UnloadWorldRequest,
) -> Option<UnloadWorldResponse> {
let ctx = CALLBACK_CONTEXT.get()?;
let server = ctx.plugin_context.server.clone();
let name = request.world_name;
let (tx, rx) = tokio::sync::oneshot::channel();
ctx.runtime.spawn(async move {
let res = server.unload_world(&name).await;
let _ = tx.send(res.is_ok());
});
let success = rx.blocking_recv().unwrap_or(false);
Some(UnloadWorldResponse { success })
}
pub fn ffi_native_bridge_save_all_worlds_impl(_request: EmptyRequest) -> Option<()> {
let ctx = CALLBACK_CONTEXT.get()?;
let server = ctx.plugin_context.server.clone();
ctx.runtime.spawn(async move {
let _ = server.save_all().await;
});
Some(())
}

View File

@ -1,7 +1,22 @@
use pumpkin_util::math::vector3::Vector3;
use std::sync::Arc;
use crate::{ use crate::{
java::native_callbacks::CALLBACK_CONTEXT, java::native_callbacks::CALLBACK_CONTEXT,
proto::patchbukkit::world::{ proto::patchbukkit::{
GetBlockDataRequest, GetBlockDataResponse, SetBlockDataRequest, SpawnParticleRequest, common::{EmptyRequest, Uuid as ProtoUuid},
world::{
ChunkCoordProto, CreateWorldExplosionRequest, EntitySummaryProto, GetBlockDataRequest,
GetBlockDataResponse, GetForceLoadedChunksRequest, GetForceLoadedChunksResponse,
GetWorldBorderRequest, GetWorldEntitiesRequest, GetWorldEntitiesResponse,
GetWorldGamerulesRequest, GetWorldGamerulesResponse, GetWorldInfoRequest,
GetWorldInfoResponse, GetWorldsResponse, PlayWorldSoundRequest, SaveWorldRequest,
SetBlockDataRequest, SetChunkForceLoadedRequest, SetWorldBorderRequest,
SetWorldDifficultyRequest, SetWorldGameruleRequest, SetWorldPvpRequest,
SetWorldSpawnRequest, SetWorldTimeRequest, SetWorldWeatherRequest,
SpawnParticleRequest, SpawnWorldEntityRequest, SpawnWorldEntityResponse,
WorldBorderData,
},
}, },
}; };
@ -29,8 +44,6 @@ pub fn ffi_native_bridge_get_block_data_impl(
format!("minecraft:{key}") format!("minecraft:{key}")
}; };
// Include block-state properties (facing, half, waterlogged, ...) so the Java side can
// hand plugins a faithful BlockData string instead of the bare block name.
if let Some(props) = block.properties(state_id) { if let Some(props) = block.properties(state_id) {
let props = props.to_props(); let props = props.to_props();
if !props.is_empty() { if !props.is_empty() {
@ -71,8 +84,6 @@ pub fn ffi_native_bridge_set_block_data_impl(request: SetBlockDataRequest) -> Op
.trim_start_matches("minecraft:"); .trim_start_matches("minecraft:");
let state_id = if let Some(b) = pumpkin_data::Block::from_registry_key(clean_key) { let state_id = if let Some(b) = pumpkin_data::Block::from_registry_key(clean_key) {
// Apply the block-state properties from the "[k=v,...]" suffix instead of silently
// placing the default state (stairs used to lose their facing, doors their half, ...).
match block_state_str.split_once('[') { match block_state_str.split_once('[') {
Some((_, props_str)) => { Some((_, props_str)) => {
let props: Vec<(&str, &str)> = props_str let props: Vec<(&str, &str)> = props_str
@ -93,15 +104,6 @@ pub fn ffi_native_bridge_set_block_data_impl(request: SetBlockDataRequest) -> Op
pumpkin_data::BlockStateId::new_or_air(0) pumpkin_data::BlockStateId::new_or_air(0)
}; };
// Bukkit's contract is synchronous, ordered world mutation: Block#setType must be
// observable by a read on the next line, and two writes to the same position must land
// in call order. The previous fire-and-forget `runtime.spawn` guaranteed neither. This
// mirrors the blocking pattern the read callbacks in this module already use.
//
// Re-entrancy note: none of the events set_block_state can fire (BlockPhysicsEvent via
// NOTIFY_NEIGHBORS) are bridged to the JVM today. If one ever is, its blocking handler
// would post to the JvmWorker thread that is currently blocked inside this call — keep
// that in mind before wiring block-physics events.
let flags = if request.apply_physics { let flags = if request.apply_physics {
pumpkin::world::BlockFlags::NOTIFY_ALL pumpkin::world::BlockFlags::NOTIFY_ALL
} else { } else {
@ -120,24 +122,22 @@ pub fn ffi_native_bridge_spawn_particle_impl(_request: SpawnParticleRequest) ->
Some(()) Some(())
} }
pub fn ffi_native_bridge_get_worlds_impl( pub fn ffi_native_bridge_get_worlds_impl(_request: EmptyRequest) -> Option<GetWorldsResponse> {
_request: crate::proto::patchbukkit::common::EmptyRequest,
) -> Option<crate::proto::patchbukkit::world::GetWorldsResponse> {
let ctx = CALLBACK_CONTEXT.get()?; let ctx = CALLBACK_CONTEXT.get()?;
let worlds = ctx.plugin_context.server.worlds.load_full(); let worlds = ctx.plugin_context.server.worlds.load_full();
let world_uuids = worlds let world_uuids = worlds
.iter() .iter()
.map(|w| crate::proto::patchbukkit::common::Uuid { .map(|w| ProtoUuid {
value: w.uuid.to_string(), value: w.uuid.to_string(),
}) })
.collect(); .collect();
Some(crate::proto::patchbukkit::world::GetWorldsResponse { world_uuids }) Some(GetWorldsResponse { world_uuids })
} }
pub fn ffi_native_bridge_get_world_border_impl( pub fn ffi_native_bridge_get_world_border_impl(
request: crate::proto::patchbukkit::world::GetWorldBorderRequest, request: GetWorldBorderRequest,
) -> Option<crate::proto::patchbukkit::world::WorldBorderData> { ) -> Option<WorldBorderData> {
let ctx = CALLBACK_CONTEXT.get()?; let ctx = CALLBACK_CONTEXT.get()?;
let uuid_str = &request.world_uuid.as_ref()?.value; let uuid_str = &request.world_uuid.as_ref()?.value;
let world_uuid = uuid::Uuid::parse_str(uuid_str).ok()?; let world_uuid = uuid::Uuid::parse_str(uuid_str).ok()?;
@ -151,7 +151,7 @@ pub fn ffi_native_bridge_get_world_border_impl(
let wb = world.worldborder.try_lock().ok()?; let wb = world.worldborder.try_lock().ok()?;
Some(crate::proto::patchbukkit::world::WorldBorderData { Some(WorldBorderData {
center_x: wb.center_x, center_x: wb.center_x,
center_z: wb.center_z, center_z: wb.center_z,
size: wb.old_diameter, size: wb.old_diameter,
@ -165,9 +165,7 @@ pub fn ffi_native_bridge_get_world_border_impl(
}) })
} }
pub fn ffi_native_bridge_set_world_border_impl( pub fn ffi_native_bridge_set_world_border_impl(request: SetWorldBorderRequest) -> Option<()> {
request: crate::proto::patchbukkit::world::SetWorldBorderRequest,
) -> Option<()> {
let ctx = CALLBACK_CONTEXT.get()?; let ctx = CALLBACK_CONTEXT.get()?;
let uuid_str = &request.world_uuid.as_ref()?.value; let uuid_str = &request.world_uuid.as_ref()?.value;
let world_uuid = uuid::Uuid::parse_str(uuid_str).ok()?; let world_uuid = uuid::Uuid::parse_str(uuid_str).ok()?;
@ -198,8 +196,8 @@ pub fn ffi_native_bridge_set_world_border_impl(
} }
pub fn ffi_native_bridge_get_world_info_impl( pub fn ffi_native_bridge_get_world_info_impl(
request: crate::proto::patchbukkit::world::GetWorldInfoRequest, request: GetWorldInfoRequest,
) -> Option<crate::proto::patchbukkit::world::GetWorldInfoResponse> { ) -> Option<GetWorldInfoResponse> {
let ctx = CALLBACK_CONTEXT.get()?; let ctx = CALLBACK_CONTEXT.get()?;
let uuid_str = &request.world_uuid.as_ref()?.value; let uuid_str = &request.world_uuid.as_ref()?.value;
let world_uuid = uuid::Uuid::parse_str(uuid_str).ok()?; let world_uuid = uuid::Uuid::parse_str(uuid_str).ok()?;
@ -214,11 +212,411 @@ pub fn ffi_native_bridge_get_world_info_impl(
let min_height = world.dimension.min_y; let min_height = world.dimension.min_y;
let height = world.dimension.height; let height = world.dimension.height;
let max_height = min_height + height; let max_height = min_height + height;
let logical_height = world.dimension.logical_height;
let sea_level = world.sea_level;
let dimension = world.dimension.minecraft_name.to_string();
Some(crate::proto::patchbukkit::world::GetWorldInfoResponse { let level_data = world.level_info.load();
let name = level_data.level_name.clone();
let seed = 0i64;
let difficulty = format!("{:?}", level_data.difficulty);
let hardcore = false;
let spawn_x = level_data.spawn_x;
let spawn_y = level_data.spawn_y;
let spawn_z = level_data.spawn_z;
let spawn_angle = level_data.spawn_yaw;
let (time, full_time) = if let Ok(lt) = world.level_time.try_lock() {
(lt.time_of_day, lt.world_age)
} else {
(0, 0)
};
let (is_storm, is_thundering, weather_duration, thunder_duration, clear_weather_duration) =
if let Ok(w) = world.weather.try_lock() {
(
w.raining,
w.thundering,
w.rain_time,
w.thunder_time,
w.clear_weather_time,
)
} else {
(false, false, 0, 0, 0)
};
let pvp = ctx.plugin_context.server.advanced_config.pvp.enabled;
Some(GetWorldInfoResponse {
min_height, min_height,
max_height, max_height,
height, height,
seed: 0, seed,
name,
dimension,
sea_level,
logical_height,
difficulty,
hardcore,
pvp,
spawn_x,
spawn_y,
spawn_z,
spawn_angle,
time,
full_time,
is_storm,
is_thundering,
weather_duration,
thunder_duration,
clear_weather_duration,
}) })
} }
pub fn ffi_native_bridge_set_world_time_impl(request: SetWorldTimeRequest) -> 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())?;
if let Ok(mut lt) = world.level_time.try_lock() {
if request.time >= 0 {
lt.time_of_day = request.time;
}
if request.full_time >= 0 {
lt.world_age = request.full_time;
}
}
Some(())
}
pub fn ffi_native_bridge_set_world_weather_impl(request: SetWorldWeatherRequest) -> 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())?;
if let Ok(mut w) = world.weather.try_lock() {
w.raining = request.storm;
w.thundering = request.thundering;
if request.weather_duration > 0 {
w.rain_time = request.weather_duration;
}
if request.thunder_duration > 0 {
w.thunder_time = request.thunder_duration;
}
if request.clear_weather_duration > 0 {
w.clear_weather_time = request.clear_weather_duration;
}
}
Some(())
}
pub fn ffi_native_bridge_set_world_spawn_impl(request: SetWorldSpawnRequest) -> 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 mut level_data = (**world.level_info.load()).clone();
level_data.spawn_x = request.x;
level_data.spawn_y = request.y;
level_data.spawn_z = request.z;
level_data.spawn_yaw = request.angle;
world.level_info.store(Arc::new(level_data));
Some(())
}
pub fn ffi_native_bridge_set_world_difficulty_impl(
request: SetWorldDifficultyRequest,
) -> 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 diff = match request.difficulty.to_uppercase().as_str() {
"PEACEFUL" => pumpkin_util::Difficulty::Peaceful,
"EASY" => pumpkin_util::Difficulty::Easy,
"HARD" => pumpkin_util::Difficulty::Hard,
_ => pumpkin_util::Difficulty::Normal,
};
let mut level_data = (**world.level_info.load()).clone();
level_data.difficulty = diff;
world.level_info.store(Arc::new(level_data));
Some(())
}
pub fn ffi_native_bridge_set_world_pvp_impl(_request: SetWorldPvpRequest) -> Option<()> {
Some(())
}
pub fn ffi_native_bridge_set_world_gamerule_impl(_request: SetWorldGameruleRequest) -> Option<()> {
Some(())
}
pub fn ffi_native_bridge_get_world_gamerules_impl(
_request: GetWorldGamerulesRequest,
) -> Option<GetWorldGamerulesResponse> {
let mut gamerules = std::collections::HashMap::new();
gamerules.insert("doDaylightCycle".to_string(), "true".to_string());
gamerules.insert("doMobSpawning".to_string(), "true".to_string());
gamerules.insert("doFireTick".to_string(), "true".to_string());
gamerules.insert("keepInventory".to_string(), "false".to_string());
gamerules.insert("mobGriefing".to_string(), "true".to_string());
gamerules.insert("doWeatherCycle".to_string(), "true".to_string());
Some(GetWorldGamerulesResponse { gamerules })
}
pub fn ffi_native_bridge_get_world_entities_impl(
request: GetWorldEntitiesRequest,
) -> Option<GetWorldEntitiesResponse> {
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 mut entities = Vec::new();
for p in world.players.load().iter() {
let pos = p.living_entity.entity.pos.load();
entities.push(EntitySummaryProto {
uuid: Some(ProtoUuid {
value: p.gameprofile.id.to_string(),
}),
entity_type: "PLAYER".to_string(),
x: pos.x,
y: pos.y,
z: pos.z,
yaw: p.living_entity.entity.yaw.load(),
pitch: p.living_entity.entity.pitch.load(),
is_player: true,
custom_name: p.gameprofile.name.clone(),
});
}
for e in world.entities.load().iter() {
let base = e.get_entity();
let pos = base.pos.load();
entities.push(EntitySummaryProto {
uuid: Some(ProtoUuid {
value: base.entity_uuid.to_string(),
}),
entity_type: format!("{:?}", base.entity_type),
x: pos.x,
y: pos.y,
z: pos.z,
yaw: base.yaw.load(),
pitch: base.pitch.load(),
is_player: false,
custom_name: String::new(),
});
}
Some(GetWorldEntitiesResponse { entities })
}
pub fn ffi_native_bridge_spawn_world_entity_impl(
request: SpawnWorldEntityRequest,
) -> Option<SpawnWorldEntityResponse> {
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 new_uuid = uuid::Uuid::new_v4();
let pos = Vector3::new(request.x, request.y, request.z);
let w = world.clone();
ctx.runtime.spawn(async move {
let entity_type: &'static pumpkin_data::entity::EntityType =
match request.entity_type.to_uppercase().as_str() {
"LIGHTNING_BOLT" | "LIGHTNING" => &pumpkin_data::entity::EntityType::LIGHTNING_BOLT,
"ITEM" | "DROPPED_ITEM" => &pumpkin_data::entity::EntityType::ITEM,
"ZOMBIE" => &pumpkin_data::entity::EntityType::ZOMBIE,
"SKELETON" => &pumpkin_data::entity::EntityType::SKELETON,
"CREEPER" => &pumpkin_data::entity::EntityType::CREEPER,
"COW" => &pumpkin_data::entity::EntityType::COW,
"PIG" => &pumpkin_data::entity::EntityType::PIG,
"SHEEP" => &pumpkin_data::entity::EntityType::SHEEP,
_ => &pumpkin_data::entity::EntityType::PIG,
};
let entity = pumpkin::entity::r#type::from_type(entity_type, pos, &w, new_uuid);
w.spawn_entity(entity).await;
});
Some(SpawnWorldEntityResponse {
entity_uuid: Some(ProtoUuid {
value: new_uuid.to_string(),
}),
success: true,
})
}
pub fn ffi_native_bridge_create_world_explosion_impl(
request: CreateWorldExplosionRequest,
) -> 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 = Vector3::new(request.x, request.y, request.z);
let power = request.power;
let interaction = if request.break_blocks {
pumpkin::world::ExplosionInteraction::Block
} else {
pumpkin::world::ExplosionInteraction::None
};
let w = world.clone();
ctx.runtime.spawn(async move {
w.explode(pos, power, interaction).await;
});
Some(())
}
pub fn ffi_native_bridge_play_world_sound_impl(request: PlayWorldSoundRequest) -> 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 = Vector3::new(request.x, request.y, request.z);
let sound_name = request.sound;
if let Some(sound) = pumpkin_data::sound::Sound::from_name(&sound_name) {
world.play_sound_raw(
sound as u16,
pumpkin_data::sound::SoundCategory::Master,
&pos,
request.volume,
request.pitch,
);
}
Some(())
}
pub fn ffi_native_bridge_set_chunk_force_loaded_impl(
request: SetChunkForceLoadedRequest,
) -> 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 coord = pumpkin_util::math::vector2::Vector2::new(request.x, request.z);
if let Ok(mut forced) = world.forced_chunks.lock() {
if request.forced {
forced.insert(coord);
} else {
forced.remove(&coord);
}
}
Some(())
}
pub fn ffi_native_bridge_get_force_loaded_chunks_impl(
request: GetForceLoadedChunksRequest,
) -> Option<GetForceLoadedChunksResponse> {
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 mut chunks = Vec::new();
if let Ok(forced) = world.forced_chunks.lock() {
for coord in forced.iter() {
chunks.push(ChunkCoordProto {
x: coord.x,
z: coord.y,
});
}
}
Some(GetForceLoadedChunksResponse { chunks })
}
pub fn ffi_native_bridge_save_world_impl(request: SaveWorldRequest) -> 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 w = world.clone();
ctx.runtime.spawn(async move {
let _ = w.save().await;
});
Some(())
}

View File

@ -50,6 +50,10 @@ pub mod patchbukkit {
pub mod command { pub mod command {
include!(concat!(env!("OUT_DIR"), "/patchbukkit.command.rs")); include!(concat!(env!("OUT_DIR"), "/patchbukkit.command.rs"));
} }
pub mod server {
include!(concat!(env!("OUT_DIR"), "/patchbukkit.server.rs"));
}
} }
include!(concat!(env!("OUT_DIR"), "/ffi_init.rs")); include!(concat!(env!("OUT_DIR"), "/ffi_init.rs"));