From 76244747ed89a27106508a69d81902b788d65d69 Mon Sep 17 00:00:00 2001 From: Alexander Medvedev Date: Tue, 25 Aug 2026 14:24:45 +0200 Subject: [PATCH] more additions --- java/patchbukkit-test-plugin/build.gradle.kts | 2 +- java/patchbukkit/build.gradle.kts | 2 +- .../org/patchbukkit/PatchBukkitServer.java | 1337 +++---- .../patchbukkit/ban/PatchBukkitBanEntry.java | 111 + .../patchbukkit/ban/PatchBukkitBanList.java | 192 + .../patchbukkit/boss/PatchBukkitBossBar.java | 136 + .../boss/PatchBukkitKeyedBossBar.java | 29 + .../datapack/PatchBukkitDatapackManager.java | 83 + .../patchbukkit/entity/PatchBukkitEntity.java | 26 +- .../entity/PatchBukkitEntityFactory.java | 42 + .../entity/PatchBukkitHumanEntity.java | 1561 +++----- .../entity/PatchBukkitLivingEntity.java | 317 +- .../entity/PatchBukkitMobGoals.java | 75 + .../entity/PatchBukkitOfflinePlayer.java | 8 + .../patchbukkit/entity/PatchBukkitPlayer.java | 3537 +++++++++-------- .../inventory/PatchBukkitInventory.java | 358 ++ .../inventory/PatchBukkitInventoryView.java | 143 + .../inventory/PatchBukkitMerchant.java | 56 + .../patchbukkit/map/PatchBukkitMapView.java | 122 + .../potion/PatchBukkitPotionBrewer.java | 28 + .../profile/PatchBukkitPlayerProfile.java | 221 + .../scheduler/PatchBukkitAsyncScheduler.java | 77 + .../PatchBukkitGlobalRegionScheduler.java | 68 + .../scheduler/PatchBukkitRegionScheduler.java | 73 + .../scheduler/PatchBukkitScheduledTask.java | 60 + .../scoreboard/PatchBukkitObjective.java | 149 + .../scoreboard/PatchBukkitScore.java | 102 + .../scoreboard/PatchBukkitScoreboard.java | 203 + .../PatchBukkitScoreboardManager.java | 19 + .../scoreboard/PatchBukkitTeam.java | 277 ++ .../PatchBukkitStructureManager.java | 104 + .../tick/PatchBukkitServerTickManager.java | 107 + .../world/PatchBukkitBlockState.java | 16 +- .../patchbukkit/world/PatchBukkitChunk.java | 182 +- .../world/PatchBukkitRegionAccessor.java | 223 +- .../patchbukkit/world/PatchBukkitWorld.java | 3124 ++++++++------- proto/patchbukkit/bridge.proto | 86 + proto/patchbukkit/entity.proto | 165 + proto/patchbukkit/server.proto | 162 + proto/patchbukkit/world.proto | 146 + rust/Cargo.lock | 229 +- rust/Cargo.toml | 5 +- rust/src/java/native_callbacks/entity.rs | 611 ++- rust/src/java/native_callbacks/mod.rs | 3 + rust/src/java/native_callbacks/server.rs | 452 +++ rust/src/java/native_callbacks/world.rs | 458 ++- rust/src/proto/mod.rs | 4 + 47 files changed, 10249 insertions(+), 5242 deletions(-) create mode 100644 java/patchbukkit/src/main/java/org/patchbukkit/ban/PatchBukkitBanEntry.java create mode 100644 java/patchbukkit/src/main/java/org/patchbukkit/ban/PatchBukkitBanList.java create mode 100644 java/patchbukkit/src/main/java/org/patchbukkit/boss/PatchBukkitBossBar.java create mode 100644 java/patchbukkit/src/main/java/org/patchbukkit/boss/PatchBukkitKeyedBossBar.java create mode 100644 java/patchbukkit/src/main/java/org/patchbukkit/datapack/PatchBukkitDatapackManager.java create mode 100644 java/patchbukkit/src/main/java/org/patchbukkit/entity/PatchBukkitEntityFactory.java create mode 100644 java/patchbukkit/src/main/java/org/patchbukkit/entity/PatchBukkitMobGoals.java create mode 100644 java/patchbukkit/src/main/java/org/patchbukkit/inventory/PatchBukkitInventory.java create mode 100644 java/patchbukkit/src/main/java/org/patchbukkit/inventory/PatchBukkitInventoryView.java create mode 100644 java/patchbukkit/src/main/java/org/patchbukkit/inventory/PatchBukkitMerchant.java create mode 100644 java/patchbukkit/src/main/java/org/patchbukkit/map/PatchBukkitMapView.java create mode 100644 java/patchbukkit/src/main/java/org/patchbukkit/potion/PatchBukkitPotionBrewer.java create mode 100644 java/patchbukkit/src/main/java/org/patchbukkit/profile/PatchBukkitPlayerProfile.java create mode 100644 java/patchbukkit/src/main/java/org/patchbukkit/scheduler/PatchBukkitAsyncScheduler.java create mode 100644 java/patchbukkit/src/main/java/org/patchbukkit/scheduler/PatchBukkitGlobalRegionScheduler.java create mode 100644 java/patchbukkit/src/main/java/org/patchbukkit/scheduler/PatchBukkitRegionScheduler.java create mode 100644 java/patchbukkit/src/main/java/org/patchbukkit/scheduler/PatchBukkitScheduledTask.java create mode 100644 java/patchbukkit/src/main/java/org/patchbukkit/scoreboard/PatchBukkitObjective.java create mode 100644 java/patchbukkit/src/main/java/org/patchbukkit/scoreboard/PatchBukkitScore.java create mode 100644 java/patchbukkit/src/main/java/org/patchbukkit/scoreboard/PatchBukkitScoreboard.java create mode 100644 java/patchbukkit/src/main/java/org/patchbukkit/scoreboard/PatchBukkitScoreboardManager.java create mode 100644 java/patchbukkit/src/main/java/org/patchbukkit/scoreboard/PatchBukkitTeam.java create mode 100644 java/patchbukkit/src/main/java/org/patchbukkit/structure/PatchBukkitStructureManager.java create mode 100644 java/patchbukkit/src/main/java/org/patchbukkit/tick/PatchBukkitServerTickManager.java create mode 100644 proto/patchbukkit/server.proto create mode 100644 rust/src/java/native_callbacks/server.rs diff --git a/java/patchbukkit-test-plugin/build.gradle.kts b/java/patchbukkit-test-plugin/build.gradle.kts index 7568f00..9616d8a 100644 --- a/java/patchbukkit-test-plugin/build.gradle.kts +++ b/java/patchbukkit-test-plugin/build.gradle.kts @@ -11,7 +11,7 @@ repositories { dependencies { 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 { diff --git a/java/patchbukkit/build.gradle.kts b/java/patchbukkit/build.gradle.kts index 7f39025..5cad5fb 100644 --- a/java/patchbukkit/build.gradle.kts +++ b/java/patchbukkit/build.gradle.kts @@ -47,7 +47,7 @@ repositories { } 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("org.apache.maven:maven-resolver-provider:3.9.6") implementation("org.apache.maven.resolver:maven-resolver-impl:1.9.18") diff --git a/java/patchbukkit/src/main/java/org/patchbukkit/PatchBukkitServer.java b/java/patchbukkit/src/main/java/org/patchbukkit/PatchBukkitServer.java index 6514369..f471c91 100644 --- a/java/patchbukkit/src/main/java/org/patchbukkit/PatchBukkitServer.java +++ b/java/patchbukkit/src/main/java/org/patchbukkit/PatchBukkitServer.java @@ -1,16 +1,14 @@ package org.patchbukkit; import com.destroystokyo.paper.entity.ai.MobGoals; +import com.destroystokyo.paper.profile.PlayerProfile; import com.google.common.base.Preconditions; - import io.papermc.paper.ban.BanListType; -import io.papermc.paper.configuration.ServerConfiguration; import io.papermc.paper.datapack.DatapackManager; import io.papermc.paper.math.Position; import io.papermc.paper.threadedregions.scheduler.AsyncScheduler; import io.papermc.paper.threadedregions.scheduler.GlobalRegionScheduler; import io.papermc.paper.threadedregions.scheduler.RegionScheduler; - import java.awt.image.BufferedImage; import java.io.ByteArrayOutputStream; import java.io.File; @@ -18,24 +16,31 @@ import java.io.PrintStream; import java.io.PrintWriter; import java.io.StringWriter; import java.net.InetAddress; +import java.net.URI; import java.nio.charset.StandardCharsets; import java.nio.file.Path; import java.text.MessageFormat; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; +import java.util.Date; +import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Set; import java.util.UUID; -import org.patchbukkit.world.PatchBukkitWorld; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.CopyOnWriteArrayList; +import java.util.concurrent.atomic.AtomicInteger; import java.util.function.Consumer; import java.util.logging.Level; import java.util.logging.Logger; import net.kyori.adventure.audience.Audience; import net.kyori.adventure.key.Key; import net.kyori.adventure.text.Component; +import net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer; +import net.kyori.adventure.text.serializer.plain.PlainTextComponentSerializer; import org.bukkit.BanList; import org.bukkit.GameMode; import org.bukkit.Keyed; @@ -66,9 +71,9 @@ import org.bukkit.command.Command; import org.bukkit.command.CommandException; import org.bukkit.command.CommandMap; import org.bukkit.command.CommandSender; -import org.bukkit.command.SimpleCommandMap; import org.bukkit.command.ConsoleCommandSender; import org.bukkit.command.PluginCommand; +import org.bukkit.command.SimpleCommandMap; import org.bukkit.entity.Entity; import org.bukkit.entity.EntityFactory; import org.bukkit.entity.Player; @@ -92,7 +97,6 @@ import org.bukkit.plugin.PluginManager; import org.bukkit.plugin.ServicesManager; import org.bukkit.plugin.messaging.Messenger; import org.bukkit.potion.PotionBrewer; -import org.bukkit.profile.PlayerProfile; import org.bukkit.scheduler.BukkitScheduler; import org.bukkit.scoreboard.Criteria; import org.bukkit.scoreboard.ScoreboardManager; @@ -100,15 +104,58 @@ import org.bukkit.structure.StructureManager; import org.bukkit.util.CachedServerIcon; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +import org.patchbukkit.ban.PatchBukkitBanList; +import org.patchbukkit.PatchBukkitBlockData; +import org.patchbukkit.boss.PatchBukkitBossBar; +import org.patchbukkit.boss.PatchBukkitKeyedBossBar; import org.patchbukkit.command.PatchBukkitCommandMap; import org.patchbukkit.command.PatchBukkitConsoleCommandSender; +import org.patchbukkit.datapack.PatchBukkitDatapackManager; +import org.patchbukkit.entity.PatchBukkitEntityFactory; +import org.patchbukkit.entity.PatchBukkitMobGoals; +import org.patchbukkit.entity.PatchBukkitOfflinePlayer; +import org.patchbukkit.entity.PatchBukkitPlayer; import org.patchbukkit.events.PatchBukkitEventManager; +import org.patchbukkit.help.PatchBukkitHelpMap; +import org.patchbukkit.inventory.PatchBukkitInventory; +import org.patchbukkit.inventory.PatchBukkitItemFactory; +import org.patchbukkit.inventory.PatchBukkitMerchant; +import org.patchbukkit.map.PatchBukkitMapView; +import org.patchbukkit.messaging.PatchBukkitMessenger; +import org.patchbukkit.potion.PatchBukkitPotionBrewer; +import org.patchbukkit.profile.PatchBukkitPlayerProfile; +import org.patchbukkit.scheduler.PatchBukkitAsyncScheduler; +import org.patchbukkit.scheduler.PatchBukkitGlobalRegionScheduler; +import org.patchbukkit.scheduler.PatchBukkitRegionScheduler; import org.patchbukkit.scheduler.PatchBukkitScheduler; +import org.patchbukkit.scoreboard.PatchBukkitScoreboardManager; +import org.patchbukkit.structure.PatchBukkitStructureManager; +import org.patchbukkit.tick.PatchBukkitServerTickManager; import org.patchbukkit.versioning.Versioning; - +import org.patchbukkit.PatchBukkitLegacy; +import org.patchbukkit.world.PatchBukkitWorld; +import org.patchbukkit.world.PatchBukkitWorldBorder; import patchbukkit.bridge.NativeBridgeFfi; +import patchbukkit.common.EmptyRequest; import patchbukkit.log.LogLevel; import patchbukkit.log.SendLogRequest; +import patchbukkit.server.CreateWorldRequest; +import patchbukkit.server.GetOperatorsResponse; +import patchbukkit.server.GetWhitelistResponse; +import patchbukkit.server.OperatorEntryProto; +import patchbukkit.server.ServerInfoResponse; +import patchbukkit.server.ServerTickInfoResponse; +import patchbukkit.server.SetOperatorRequest; +import patchbukkit.server.SetServerDefaultGamemodeRequest; +import patchbukkit.server.SetServerIdleTimeoutRequest; +import patchbukkit.server.SetServerMaxPlayersRequest; +import patchbukkit.server.SetServerMotdRequest; +import patchbukkit.server.SetServerWhitelistEnforcedRequest; +import patchbukkit.server.SetServerWhitelistRequest; +import patchbukkit.server.SetWhitelistPlayerRequest; +import patchbukkit.server.ShutdownServerRequest; +import patchbukkit.server.UnloadWorldRequest; +import patchbukkit.server.WhitelistEntryProto; @SuppressWarnings({ "deprecation", "removal", "unchecked" }) public class PatchBukkitServer implements Server { @@ -121,6 +168,7 @@ public class PatchBukkitServer implements Server { name = io.papermc.paper.ServerBuildInfo.buildInfo().brandName(); } catch (Throwable ignored) {} this.serverName = name; + syncServerInfo(); } public static PatchBukkitServer getInstance() { @@ -159,16 +207,154 @@ public class PatchBukkitServer implements Server { public PatchBukkitPluginManager pluginManager = new PatchBukkitPluginManager(this); public ServicesManager servicesManager = new PatchBukkitServicesManager(); + private final Map onlinePlayers = new ConcurrentHashMap<>(); + private final Map onlinePlayersByName = new ConcurrentHashMap<>(); - private final Map onlinePlayers = new java.util.concurrent.ConcurrentHashMap<>(); - private final Map onlinePlayersByName = new java.util.concurrent.ConcurrentHashMap<>(); + private final Set operatorUuids = Collections.synchronizedSet(new HashSet<>()); + private final Set operatorNames = Collections.synchronizedSet(new HashSet<>()); - private final Set operatorUuids = java.util.Collections.synchronizedSet(new java.util.HashSet<>()); - private final Set operatorNames = java.util.Collections.synchronizedSet(new java.util.HashSet<>()); + private final ServerTickManager tickManager = new PatchBukkitServerTickManager(); + private final PatchBukkitBanList nameBanList = new PatchBukkitBanList<>("NAME"); + private final PatchBukkitBanList ipBanList = new PatchBukkitBanList<>("IP"); + private final PatchBukkitBanList profileBanList = new PatchBukkitBanList<>("PROFILE"); + private final ScoreboardManager scoreboardManager = new PatchBukkitScoreboardManager(); + private final DatapackManager datapackManager = new PatchBukkitDatapackManager(); + private final StructureManager structureManager = new PatchBukkitStructureManager(); + private final PotionBrewer potionBrewer = PatchBukkitPotionBrewer.INSTANCE; + private final MobGoals mobGoals = PatchBukkitMobGoals.INSTANCE; + private final EntityFactory entityFactory = PatchBukkitEntityFactory.INSTANCE; + private final RegionScheduler regionScheduler = new PatchBukkitRegionScheduler(); + private final AsyncScheduler asyncScheduler = new PatchBukkitAsyncScheduler(); + private final GlobalRegionScheduler globalRegionScheduler = new PatchBukkitGlobalRegionScheduler(); + + private final Map bossBars = new ConcurrentHashMap<>(); + private final Map maps = new ConcurrentHashMap<>(); + private final AtomicInteger mapCounter = new AtomicInteger(0); + private final List recipes = new CopyOnWriteArrayList<>(); + + private int spawnRadius = 16; + private int idleTimeout = 0; + private int pauseWhenEmptyTime = 60; + private GameMode defaultGameMode = GameMode.SURVIVAL; + private boolean whitelistEnforced = false; + private boolean whitelistEnabled = false; + private String motd = "A PatchBukkit Server"; + private int maxPlayers = 20; + private int port = 25565; + private String ip = "127.0.0.1"; + private int viewDistance = 10; + private int simulationDistance = 10; + private boolean allowFlight = true; + private boolean allowNether = true; + private boolean allowEnd = true; + private boolean onlineMode = true; + private boolean hardcore = false; + private boolean pvp = true; + private String shutdownMessage = "Server closed"; + private boolean isStopping = false; + + private final Spigot spigot = new Spigot() { + @Override + public void restart() { + PatchBukkitServer.this.restart(); + } + }; + + private final ServerLinks serverLinks = new ServerLinks() { + private final List links = new ArrayList<>(); + + @Override + public @Nullable ServerLink getLink(@NotNull Type type) { + return null; + } + + @Override + public @NotNull List getLinks() { + return Collections.unmodifiableList(this.links); + } + + @Override + public @NotNull ServerLink setLink(@NotNull Type type, @NotNull URI uri) { + return addLink(type.name(), uri); + } + + @Override + public @NotNull ServerLink addLink(@NotNull Type type, @NotNull URI uri) { + return addLink(type.name(), uri); + } + + @Override + public @NotNull ServerLink addLink(@NotNull Component component, @NotNull URI uri) { + return addLink(PlainTextComponentSerializer.plainText().serialize(component), uri); + } + + @Override + public @NotNull ServerLink addLink(@NotNull String string, @NotNull URI uri) { + ServerLink link = new ServerLink() { + @Override public @Nullable Type getType() { return null; } + @Override public @NotNull Component displayName() { return Component.text(string); } + @Override public @NotNull String getDisplayName() { return string; } + @Override public @NotNull URI getUrl() { return uri; } + }; + this.links.add(link); + return link; + } + + @Override + public boolean removeLink(@NotNull ServerLink serverLink) { + return this.links.remove(serverLink); + } + + @Override + public @NotNull ServerLinks copy() { + return this; + } + }; + + public void syncServerInfo() { + try { + ServerInfoResponse info = NativeBridgeFfi.getServerInfo(EmptyRequest.getDefaultInstance()); + if (info != null) { + this.motd = info.getMotd(); + this.ip = info.getIp(); + this.port = info.getPort(); + this.maxPlayers = info.getMaxPlayers(); + this.viewDistance = info.getViewDistance(); + this.simulationDistance = info.getSimulationDistance(); + this.allowFlight = info.getAllowFlight(); + this.allowNether = info.getAllowNether(); + this.allowEnd = info.getAllowEnd(); + this.onlineMode = info.getOnlineMode(); + this.hardcore = info.getHardcore(); + this.pvp = info.getPvp(); + this.whitelistEnabled = info.getHasWhitelist(); + this.whitelistEnforced = info.getIsWhitelistEnforced(); + this.idleTimeout = info.getIdleTimeout(); + try { + this.defaultGameMode = GameMode.valueOf(info.getDefaultGamemode().toUpperCase()); + } catch (Throwable ignored) {} + } + } catch (Throwable ignored) {} + } public boolean isOp(UUID uuid, String name) { if (uuid != null && operatorUuids.contains(uuid)) return true; if (name != null && operatorNames.contains(name.toLowerCase())) return true; + try { + GetOperatorsResponse ops = NativeBridgeFfi.getOperators(EmptyRequest.getDefaultInstance()); + if (ops != null) { + for (OperatorEntryProto op : ops.getOperatorsList()) { + if (uuid != null && op.hasUuid() && op.getUuid().getValue().equalsIgnoreCase(uuid.toString())) { + operatorUuids.add(uuid); + return true; + } + if (name != null && op.getName().equalsIgnoreCase(name)) { + operatorNames.add(name.toLowerCase()); + return true; + } + } + } + } catch (Throwable ignored) {} return false; } @@ -179,6 +365,18 @@ public class PatchBukkitServer implements Server { if (name != null) { if (value) operatorNames.add(name.toLowerCase()); else operatorNames.remove(name.toLowerCase()); } + try { + var req = SetOperatorRequest.newBuilder() + .setIsOp(value) + .setLevel(4); + if (uuid != null) { + req.setUuid(patchbukkit.common.UUID.newBuilder().setValue(uuid.toString()).build()); + } + if (name != null) { + req.setName(name); + } + NativeBridgeFfi.setOperator(req.build()); + } catch (Throwable ignored) {} } private static final PrintStream ORIGINAL_OUT = System.out; @@ -207,7 +405,7 @@ public class PatchBukkitServer implements Server { for (java.util.logging.Handler h : root.getHandlers()) { root.removeHandler(h); } - root.setLevel(Level.ALL); // accept everything, let your Rust side filter + root.setLevel(Level.ALL); root.addHandler(new java.util.logging.Handler() { @Override public void publish(java.util.logging.LogRecord record) { @@ -343,9 +541,6 @@ public class PatchBukkitServer implements Server { } } - /** - * Called from Rust when a player joins the Pumpkin server - */ public void registerPlayer(Player player) { this.onlinePlayers.put(player.getUniqueId(), player); this.onlinePlayersByName.put(player.getName().toLowerCase(), player); @@ -354,7 +549,7 @@ public class PatchBukkitServer implements Server { public static void registerPlayer(String uuidStr, String name, boolean isOp) { try { UUID uuid = UUID.fromString(uuidStr); - org.patchbukkit.entity.PatchBukkitPlayer player = new org.patchbukkit.entity.PatchBukkitPlayer(uuid, name); + PatchBukkitPlayer player = new PatchBukkitPlayer(uuid, name); if (isOp) { player.setOp(true); } @@ -370,9 +565,6 @@ public class PatchBukkitServer implements Server { return this.pluginManager.getEventManager(); } - /** - * Called from Rust when a player leaves - */ public void unregisterPlayer(UUID uuid) { Player p = this.onlinePlayers.remove(uuid); if (p != null) { @@ -384,8 +576,8 @@ public class PatchBukkitServer implements Server { this.pluginManager.registerPlugin(plugin); } - private final Messenger messenger = new org.patchbukkit.messaging.PatchBukkitMessenger(); - private final HelpMap helpMap = new org.patchbukkit.help.PatchBukkitHelpMap(); + private final Messenger messenger = new PatchBukkitMessenger(); + private final HelpMap helpMap = new PatchBukkitHelpMap(); private File pluginsFolder = new File("plugins"); private static void validateChannel(String channel) { @@ -416,11 +608,11 @@ public class PatchBukkitServer implements Server { @Override public @NotNull Set getListeningPluginChannels() { - Set channels = new java.util.HashSet<>(); + Set channels = new HashSet<>(); for (Player player : getOnlinePlayers()) { channels.addAll(player.getListeningPluginChannels()); } - return java.util.Collections.unmodifiableSet(channels); + return Collections.unmodifiableSet(channels); } @Override @@ -467,31 +659,37 @@ public class PatchBukkitServer implements Server { @Override public int getMaxPlayers() { - return 20; + return this.maxPlayers; } @Override public void setMaxPlayers(int maxPlayers) { + this.maxPlayers = maxPlayers; + try { + NativeBridgeFfi.setServerMaxPlayers(SetServerMaxPlayersRequest.newBuilder() + .setMaxPlayers(maxPlayers) + .build()); + } catch (Throwable ignored) {} } @Override public int getPort() { - return 25565; + return this.port; } @Override public int getViewDistance() { - return 10; + return this.viewDistance; } @Override public int getSimulationDistance() { - return 10; + return this.simulationDistance; } @Override public @NotNull String getIp() { - return "127.0.0.1"; + return this.ip; } @Override @@ -501,186 +699,140 @@ public class PatchBukkitServer implements Server { @Override public boolean getGenerateStructures() { - // TODO Auto-generated method stub - throw new UnsupportedOperationException( - "Unimplemented method 'getGenerateStructures'" - ); + return true; } @Override public int getMaxWorldSize() { - // TODO Auto-generated method stub - throw new UnsupportedOperationException( - "Unimplemented method 'getMaxWorldSize'" - ); + return 29999984; } @Override public boolean getAllowEnd() { - // TODO Auto-generated method stub - throw new UnsupportedOperationException( - "Unimplemented method 'getAllowEnd'" - ); + return this.allowEnd; } @Override public boolean getAllowNether() { - // TODO Auto-generated method stub - throw new UnsupportedOperationException( - "Unimplemented method 'getAllowNether'" - ); + return this.allowNether; } @Override public boolean isLoggingIPs() { - // TODO Auto-generated method stub - throw new UnsupportedOperationException( - "Unimplemented method 'isLoggingIPs'" - ); + return true; } @Override public @NotNull List getInitialEnabledPacks() { - // TODO Auto-generated method stub - throw new UnsupportedOperationException( - "Unimplemented method 'getInitialEnabledPacks'" - ); + return List.of("vanilla"); } @Override public @NotNull List getInitialDisabledPacks() { - // TODO Auto-generated method stub - throw new UnsupportedOperationException( - "Unimplemented method 'getInitialDisabledPacks'" - ); + return List.of(); } @Override public @NotNull ServerTickManager getServerTickManager() { - // TODO Auto-generated method stub - throw new UnsupportedOperationException( - "Unimplemented method 'getServerTickManager'" - ); + return this.tickManager; } @Override public @Nullable ResourcePack getServerResourcePack() { - // TODO Auto-generated method stub - throw new UnsupportedOperationException( - "Unimplemented method 'getServerResourcePack'" - ); + return null; } @Override public @NotNull String getResourcePack() { - // TODO Auto-generated method stub - throw new UnsupportedOperationException( - "Unimplemented method 'getResourcePack'" - ); + return ""; } @Override public @NotNull String getResourcePackHash() { - // TODO Auto-generated method stub - throw new UnsupportedOperationException( - "Unimplemented method 'getResourcePackHash'" - ); + return ""; } @Override public @NotNull String getResourcePackPrompt() { - // TODO Auto-generated method stub - throw new UnsupportedOperationException( - "Unimplemented method 'getResourcePackPrompt'" - ); + return ""; } @Override public boolean isResourcePackRequired() { - // TODO Auto-generated method stub - throw new UnsupportedOperationException( - "Unimplemented method 'isResourcePackRequired'" - ); + return false; } @Override public boolean hasWhitelist() { - // TODO Auto-generated method stub - throw new UnsupportedOperationException( - "Unimplemented method 'hasWhitelist'" - ); + return this.whitelistEnabled; } @Override public void setWhitelist(boolean value) { - // TODO Auto-generated method stub - throw new UnsupportedOperationException( - "Unimplemented method 'setWhitelist'" - ); + this.whitelistEnabled = value; + try { + NativeBridgeFfi.setServerWhitelist(SetServerWhitelistRequest.newBuilder() + .setEnabled(value) + .build()); + } catch (Throwable ignored) {} } @Override public boolean isWhitelistEnforced() { - // TODO Auto-generated method stub - throw new UnsupportedOperationException( - "Unimplemented method 'isWhitelistEnforced'" - ); + return this.whitelistEnforced; } @Override public void setWhitelistEnforced(boolean value) { - // TODO Auto-generated method stub - throw new UnsupportedOperationException( - "Unimplemented method 'setWhitelistEnforced'" - ); + this.whitelistEnforced = value; + try { + NativeBridgeFfi.setServerWhitelistEnforced(SetServerWhitelistEnforcedRequest.newBuilder() + .setEnforced(value) + .build()); + } catch (Throwable ignored) {} } @Override public @NotNull Set getWhitelistedPlayers() { - // TODO Auto-generated method stub - throw new UnsupportedOperationException( - "Unimplemented method 'getWhitelistedPlayers'" - ); + Set list = new HashSet<>(); + try { + GetWhitelistResponse res = NativeBridgeFfi.getWhitelist(EmptyRequest.getDefaultInstance()); + if (res != null) { + for (WhitelistEntryProto entry : res.getPlayersList()) { + if (entry.hasUuid()) { + list.add(getOfflinePlayer(UUID.fromString(entry.getUuid().getValue()))); + } else { + list.add(getOfflinePlayer(entry.getName())); + } + } + } + } catch (Throwable ignored) {} + return list; } @Override public void reloadWhitelist() { - // TODO Auto-generated method stub - throw new UnsupportedOperationException( - "Unimplemented method 'reloadWhitelist'" - ); + syncServerInfo(); } @Override public @NotNull String getUpdateFolder() { - // TODO Auto-generated method stub - throw new UnsupportedOperationException( - "Unimplemented method 'getUpdateFolder'" - ); + return "update"; } @Override public @NotNull File getUpdateFolderFile() { - // TODO Auto-generated method stub - throw new UnsupportedOperationException( - "Unimplemented method 'getUpdateFolderFile'" - ); + return new File("update"); } @Override public long getConnectionThrottle() { - // TODO Auto-generated method stub - throw new UnsupportedOperationException( - "Unimplemented method 'getConnectionThrottle'" - ); + return 0; } @Override public int getTicksPerSpawns(@NotNull SpawnCategory spawnCategory) { - // TODO Auto-generated method stub - throw new UnsupportedOperationException( - "Unimplemented method 'getTicksPerSpawns'" - ); + return 1; } @Override @@ -695,23 +847,26 @@ public class PatchBukkitServer implements Server { @Override public @NotNull List matchPlayer(@NotNull String name) { - // TODO Auto-generated method stub - throw new UnsupportedOperationException( - "Unimplemented method 'matchPlayer'" - ); + List matches = new ArrayList<>(); + String query = name.toLowerCase(); + for (Player p : getOnlinePlayers()) { + if (p.getName().toLowerCase().startsWith(query)) { + matches.add(p); + } + } + return matches; } - @Override + @Override public @Nullable Player getPlayer(@NotNull UUID id) { return onlinePlayers.get(id); } @Override public @Nullable UUID getPlayerUniqueId(@NotNull String playerName) { - // TODO Auto-generated method stub - throw new UnsupportedOperationException( - "Unimplemented method 'getPlayerUniqueId'" - ); + Player player = getPlayer(playerName); + if (player != null) return player.getUniqueId(); + return getOfflinePlayer(playerName).getUniqueId(); } @Override @@ -731,7 +886,7 @@ public class PatchBukkitServer implements Server { @Override public @NotNull List getWorlds() { - var response = NativeBridgeFfi.getWorlds(patchbukkit.common.EmptyRequest.newBuilder().build()); + var response = NativeBridgeFfi.getWorlds(EmptyRequest.getDefaultInstance()); if (response == null) return List.of(); List list = new ArrayList<>(); for (patchbukkit.common.UUID u : response.getWorldUuidsList()) { @@ -742,34 +897,50 @@ public class PatchBukkitServer implements Server { @Override public boolean isTickingWorlds() { - // TODO Auto-generated method stub - throw new UnsupportedOperationException( - "Unimplemented method 'isTickingWorlds'" - ); + return true; } @Override public @Nullable World createWorld(@NotNull WorldCreator creator) { - // TODO Auto-generated method stub - throw new UnsupportedOperationException( - "Unimplemented method 'createWorld'" - ); + Preconditions.checkArgument(creator != null, "WorldCreator cannot be null"); + try { + var dim = switch (creator.environment()) { + case NETHER -> "minecraft:the_nether"; + case THE_END -> "minecraft:the_end"; + default -> "minecraft:overworld"; + }; + var res = NativeBridgeFfi.createWorld(CreateWorldRequest.newBuilder() + .setName(creator.name()) + .setDimension(dim) + .setSeed(creator.seed()) + .build()); + if (res != null && res.hasWorldUuid()) { + return PatchBukkitWorld.getOrCreate(res.getWorldUuid().getValue()); + } + } catch (Throwable ignored) {} + return getWorld(creator.name()); } @Override public boolean unloadWorld(@NotNull String name, boolean save) { - // TODO Auto-generated method stub - throw new UnsupportedOperationException( - "Unimplemented method 'unloadWorld'" - ); + World world = getWorld(name); + if (world != null) { + return unloadWorld(world, save); + } + return false; } @Override public boolean unloadWorld(@NotNull World world, boolean save) { - // TODO Auto-generated method stub - throw new UnsupportedOperationException( - "Unimplemented method 'unloadWorld'" - ); + try { + var res = NativeBridgeFfi.unloadWorld(UnloadWorldRequest.newBuilder() + .setWorldUuid(patchbukkit.common.UUID.newBuilder().setValue(world.getUID().toString()).build()) + .setWorldName(world.getName()) + .setSave(save) + .build()); + return res != null && res.getSuccess(); + } catch (Throwable ignored) {} + return false; } public @NotNull World getRespawnWorld() { @@ -777,16 +948,10 @@ public class PatchBukkitServer implements Server { if (!worlds.isEmpty()) { return worlds.get(0); } - throw new UnsupportedOperationException( - "Unimplemented method 'getRespawnWorld'" - ); + return PatchBukkitWorld.getOrCreate(UUID.randomUUID().toString()); } public void setRespawnWorld(@NotNull World world) { - // TODO Auto-generated method stub - throw new UnsupportedOperationException( - "Unimplemented method 'setRespawnWorld'" - ); } @Override @@ -821,23 +986,20 @@ public class PatchBukkitServer implements Server { @Override public @NotNull WorldBorder createWorldBorder() { - return new org.patchbukkit.world.PatchBukkitWorldBorder(null); + return new PatchBukkitWorldBorder(null); } @Override public @Nullable MapView getMap(int id) { - // TODO Auto-generated method stub - throw new UnsupportedOperationException( - "Unimplemented method 'getMap'" - ); + return this.maps.get(id); } @Override public @NotNull MapView createMap(@NotNull World world) { - // TODO Auto-generated method stub - throw new UnsupportedOperationException( - "Unimplemented method 'createMap'" - ); + int id = mapCounter.incrementAndGet(); + MapView map = new PatchBukkitMapView(id, world); + this.maps.put(id, map); + return map; } @Override @@ -848,10 +1010,7 @@ public class PatchBukkitServer implements Server { int radius, boolean findUnexplored ) { - // TODO Auto-generated method stub - throw new UnsupportedOperationException( - "Unimplemented method 'createExplorerMap'" - ); + return new ItemStack(Material.FILLED_MAP); } @Override @@ -863,18 +1022,12 @@ public class PatchBukkitServer implements Server { int radius, boolean findUnexplored ) { - // TODO Auto-generated method stub - throw new UnsupportedOperationException( - "Unimplemented method 'createExplorerMap'" - ); + return new ItemStack(Material.FILLED_MAP); } @Override public void reload() { - // TODO Auto-generated method stub - throw new UnsupportedOperationException( - "Unimplemented method 'reload'" - ); + reloadData(); } public int getReloadCount() { @@ -883,45 +1036,33 @@ public class PatchBukkitServer implements Server { @Override public void reloadData() { - // TODO Auto-generated method stub - throw new UnsupportedOperationException( - "Unimplemented method 'reloadData'" - ); + syncServerInfo(); } @Override public void updateResources() { - // TODO Auto-generated method stub - throw new UnsupportedOperationException( - "Unimplemented method 'updateResources'" - ); } @Override public void updateRecipes() { - // TODO Auto-generated method stub - throw new UnsupportedOperationException( - "Unimplemented method 'updateRecipes'" - ); } @Override public @NotNull Logger getLogger() { - return this.logger; + return logger; } @Override public @Nullable PluginCommand getPluginCommand(@NotNull String name) { Command cmd = this.commandMap.getCommand(name); - return (cmd instanceof PluginCommand) ? (PluginCommand) cmd : null; + return (cmd instanceof PluginCommand) ? (PluginCommand) cmd : null; } @Override public void savePlayers() { - // TODO Auto-generated method stub - throw new UnsupportedOperationException( - "Unimplemented method 'savePlayers'" - ); + try { + NativeBridgeFfi.saveAllWorlds(EmptyRequest.getDefaultInstance()); + } catch (Throwable ignored) {} } @Override @@ -937,26 +1078,32 @@ public class PatchBukkitServer implements Server { @Override public boolean addRecipe(@Nullable Recipe recipe, boolean resendRecipes) { - // TODO Auto-generated method stub - throw new UnsupportedOperationException( - "Unimplemented method 'addRecipe'" - ); + if (recipe != null) { + this.recipes.add(recipe); + return true; + } + return false; } @Override public @NotNull List getRecipesFor(@NotNull ItemStack result) { - // TODO Auto-generated method stub - throw new UnsupportedOperationException( - "Unimplemented method 'getRecipesFor'" - ); + List matches = new ArrayList<>(); + for (Recipe r : this.recipes) { + if (r.getResult().isSimilar(result)) { + matches.add(r); + } + } + return matches; } @Override public @Nullable Recipe getRecipe(@NotNull NamespacedKey recipeKey) { - // TODO Auto-generated method stub - throw new UnsupportedOperationException( - "Unimplemented method 'getRecipe'" - ); + for (Recipe r : this.recipes) { + if (r instanceof Keyed keyed && keyed.getKey().equals(recipeKey)) { + return r; + } + } + return null; } @Override @@ -964,10 +1111,7 @@ public class PatchBukkitServer implements Server { @NotNull ItemStack@NotNull [] craftingMatrix, @NotNull World world ) { - // TODO Auto-generated method stub - throw new UnsupportedOperationException( - "Unimplemented method 'getCraftingRecipe'" - ); + return null; } @Override @@ -976,10 +1120,7 @@ public class PatchBukkitServer implements Server { @NotNull World world, @NotNull Player player ) { - // TODO Auto-generated method stub - throw new UnsupportedOperationException( - "Unimplemented method 'craftItemResult'" - ); + return craftItemResult(craftingMatrix, world); } @Override @@ -987,34 +1128,26 @@ public class PatchBukkitServer implements Server { @NotNull ItemStack@NotNull [] craftingMatrix, @NotNull World world ) { - // TODO Auto-generated method stub - throw new UnsupportedOperationException( - "Unimplemented method 'craftItemResult'" - ); + return new ItemCraftResult() { + @Override public @NotNull ItemStack getResult() { return ItemStack.empty(); } + @Override public @NotNull ItemStack[] getResultingMatrix() { return craftingMatrix; } + @Override public @NotNull List getOverflowItems() { return Collections.emptyList(); } + }; } @Override public @NotNull Iterator recipeIterator() { - // TODO Auto-generated method stub - throw new UnsupportedOperationException( - "Unimplemented method 'recipeIterator'" - ); + return this.recipes.iterator(); } @Override public void clearRecipes() { - // TODO Auto-generated method stub - throw new UnsupportedOperationException( - "Unimplemented method 'clearRecipes'" - ); + this.recipes.clear(); } @Override public void resetRecipes() { - // TODO Auto-generated method stub - throw new UnsupportedOperationException( - "Unimplemented method 'resetRecipes'" - ); + this.recipes.clear(); } @Override @@ -1022,255 +1155,228 @@ public class PatchBukkitServer implements Server { @NotNull NamespacedKey key, boolean resendRecipes ) { - // TODO Auto-generated method stub - throw new UnsupportedOperationException( - "Unimplemented method 'removeRecipe'" - ); + return this.recipes.removeIf(r -> r instanceof Keyed keyed && keyed.getKey().equals(key)); } @Override public @NotNull Map getCommandAliases() { - // TODO Auto-generated method stub - throw new UnsupportedOperationException( - "Unimplemented method 'getCommandAliases'" - ); + return Collections.emptyMap(); } @Override public int getSpawnRadius() { - // TODO Auto-generated method stub - throw new UnsupportedOperationException( - "Unimplemented method 'getSpawnRadius'" - ); + return this.spawnRadius; } @Override public void setSpawnRadius(int value) { - // TODO Auto-generated method stub - throw new UnsupportedOperationException( - "Unimplemented method 'setSpawnRadius'" - ); - } - - @Override - public boolean isEnforcingSecureProfiles() { - // TODO Auto-generated method stub - throw new UnsupportedOperationException( - "Unimplemented method 'isEnforcingSecureProfiles'" - ); - } - - @Override - public boolean isAcceptingTransfers() { - // TODO Auto-generated method stub - throw new UnsupportedOperationException( - "Unimplemented method 'isAcceptingTransfers'" - ); + this.spawnRadius = value; } @Override public boolean getHideOnlinePlayers() { - // TODO Auto-generated method stub - throw new UnsupportedOperationException( - "Unimplemented method 'getHideOnlinePlayers'" - ); + return false; } @Override public boolean getOnlineMode() { - // TODO Auto-generated method stub - throw new UnsupportedOperationException( - "Unimplemented method 'getOnlineMode'" - ); - } - - public @NotNull ServerConfiguration getServerConfig() { - // TODO Auto-generated method stub - throw new UnsupportedOperationException( - "Unimplemented method 'getServerConfig'" - ); + return this.onlineMode; } @Override public boolean getAllowFlight() { - // TODO Auto-generated method stub - throw new UnsupportedOperationException( - "Unimplemented method 'getAllowFlight'" - ); + return this.allowFlight; } @Override public boolean isHardcore() { - // TODO Auto-generated method stub - throw new UnsupportedOperationException( - "Unimplemented method 'isHardcore'" - ); + return this.hardcore; + } + + @Override + public boolean isAcceptingTransfers() { + return true; } @Override public void shutdown() { - // TODO Auto-generated method stub - throw new UnsupportedOperationException( - "Unimplemented method 'shutdown'" - ); + this.isStopping = true; + try { + NativeBridgeFfi.shutdownServer(ShutdownServerRequest.newBuilder() + .setSave(true) + .setMessage(this.shutdownMessage) + .build()); + } catch (Throwable ignored) {} } @Override - public int broadcast( - @NotNull Component message, - @NotNull String permission - ) { - // TODO Auto-generated method stub - throw new UnsupportedOperationException( - "Unimplemented method 'broadcast'" - ); + public int broadcast(@NotNull Component message, @NotNull String permission) { + int count = 0; + for (Player player : getOnlinePlayers()) { + if (permission.isEmpty() || player.hasPermission(permission)) { + player.sendMessage(message); + count++; + } + } + ConsoleCommandSender console = getConsoleSender(); + if (permission.isEmpty() || console.hasPermission(permission)) { + console.sendMessage(message); + count++; + } + return count; + } + + @Override + public int broadcast(@NotNull String message, @NotNull String permission) { + return broadcast(LegacyComponentSerializer.legacySection().deserialize(message), permission); + } + + @Override + public int broadcastMessage(@NotNull String message) { + return broadcast(message, Server.BROADCAST_CHANNEL_USERS); } @Override public @NotNull OfflinePlayer getOfflinePlayer(@NotNull String name) { - Player online = getPlayer(name); - if (online != null) return online; - return new org.patchbukkit.entity.PatchBukkitOfflinePlayer(null, name); - } - - @Override - public @Nullable OfflinePlayer getOfflinePlayerIfCached(@NotNull String name) { - Player online = getPlayer(name); - if (online != null) return online; - return new org.patchbukkit.entity.PatchBukkitOfflinePlayer(null, name); + Player player = getPlayerExact(name); + if (player != null) return player; + return new PatchBukkitOfflinePlayer(name); } @Override public @NotNull OfflinePlayer getOfflinePlayer(@NotNull UUID id) { - Player online = getPlayer(id); - if (online != null) return online; - return new org.patchbukkit.entity.PatchBukkitOfflinePlayer(id, null); + Player player = getPlayer(id); + if (player != null) return player; + return new PatchBukkitOfflinePlayer(id); } @Override - public @NotNull PlayerProfile createPlayerProfile( - @Nullable UUID uniqueId, + public @Nullable OfflinePlayer getOfflinePlayerIfCached(@NotNull String name) { + return getOfflinePlayer(name); + } + + @Override + public boolean isEnforcingSecureProfiles() { + return false; + } + + @Override + public org.bukkit.profile.@NotNull PlayerProfile createPlayerProfile(@NotNull UUID id) { + return new PatchBukkitPlayerProfile(id, null); + } + + @Override + public org.bukkit.profile.@NotNull PlayerProfile createPlayerProfile(@NotNull String name) { + return new PatchBukkitPlayerProfile(null, name); + } + + @Override + public org.bukkit.profile.@NotNull PlayerProfile createPlayerProfile( + @Nullable UUID id, @Nullable String name ) { - // TODO Auto-generated method stub - throw new UnsupportedOperationException( - "Unimplemented method 'createPlayerProfile'" - ); - } - - @Override - public @NotNull PlayerProfile createPlayerProfile(@NotNull UUID uniqueId) { - // TODO Auto-generated method stub - throw new UnsupportedOperationException( - "Unimplemented method 'createPlayerProfile'" - ); - } - - @Override - public @NotNull PlayerProfile createPlayerProfile(@NotNull String name) { - // TODO Auto-generated method stub - throw new UnsupportedOperationException( - "Unimplemented method 'createPlayerProfile'" - ); + return new PatchBukkitPlayerProfile(id, name); } @Override public @NotNull Set getIPBans() { - // TODO Auto-generated method stub - throw new UnsupportedOperationException( - "Unimplemented method 'getIPBans'" - ); + Set set = new HashSet<>(); + for (org.bukkit.BanEntry entry : this.ipBanList.getBanEntries()) { + set.add(entry.getTarget()); + } + return set; } @Override public void banIP(@NotNull String address) { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'banIP'"); + this.ipBanList.addBan(address, "Banned by operator", (Date) null, "Server"); } @Override public void unbanIP(@NotNull String address) { - // TODO Auto-generated method stub - throw new UnsupportedOperationException( - "Unimplemented method 'unbanIP'" - ); + this.ipBanList.pardon(address); } @Override public void banIP(@NotNull InetAddress address) { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'banIP'"); + this.ipBanList.addBan(address, "Banned by operator", (Date) null, "Server"); } @Override public void unbanIP(@NotNull InetAddress address) { - // TODO Auto-generated method stub - throw new UnsupportedOperationException( - "Unimplemented method 'unbanIP'" - ); + this.ipBanList.pardon(address); } @Override public @NotNull Set getBannedPlayers() { - // TODO Auto-generated method stub - throw new UnsupportedOperationException( - "Unimplemented method 'getBannedPlayers'" - ); + Set list = new HashSet<>(); + for (org.bukkit.BanEntry entry : this.nameBanList.getBanEntries()) { + list.add(getOfflinePlayer(entry.getTarget())); + } + return list; } @Override public > @NotNull T getBanList( org.bukkit.BanList.@NotNull Type type ) { - // TODO Auto-generated method stub - throw new UnsupportedOperationException( - "Unimplemented method 'getBanList'" - ); + return switch (type) { + case IP -> (T) this.ipBanList; + case PROFILE -> (T) this.profileBanList; + default -> (T) this.nameBanList; + }; } @Override public , E> @NotNull B getBanList( @NotNull BanListType type ) { - // TODO Auto-generated method stub - throw new UnsupportedOperationException( - "Unimplemented method 'getBanList'" - ); + if (type == BanListType.IP) return (B) this.ipBanList; + if (type == BanListType.PROFILE) return (B) this.profileBanList; + return (B) this.nameBanList; } @Override public @NotNull Set getOperators() { - Set ops = new java.util.HashSet<>(); + Set ops = new HashSet<>(); for (UUID uuid : operatorUuids) { ops.add(getOfflinePlayer(uuid)); } for (String name : operatorNames) { ops.add(getOfflinePlayer(name)); } + try { + GetOperatorsResponse res = NativeBridgeFfi.getOperators(EmptyRequest.getDefaultInstance()); + if (res != null) { + for (OperatorEntryProto op : res.getOperatorsList()) { + if (op.hasUuid()) { + ops.add(getOfflinePlayer(UUID.fromString(op.getUuid().getValue()))); + } else { + ops.add(getOfflinePlayer(op.getName())); + } + } + } + } catch (Throwable ignored) {} return ops; } @Override public @NotNull GameMode getDefaultGameMode() { - // TODO Auto-generated method stub - throw new UnsupportedOperationException( - "Unimplemented method 'getDefaultGameMode'" - ); + return this.defaultGameMode; } @Override public void setDefaultGameMode(@NotNull GameMode mode) { - // TODO Auto-generated method stub - throw new UnsupportedOperationException( - "Unimplemented method 'setDefaultGameMode'" - ); + this.defaultGameMode = mode; + try { + NativeBridgeFfi.setServerDefaultGamemode(SetServerDefaultGamemodeRequest.newBuilder() + .setGamemode(mode.name()) + .build()); + } catch (Throwable ignored) {} } public boolean forcesDefaultGameMode() { - // TODO Auto-generated method stub - throw new UnsupportedOperationException( - "Unimplemented method 'forcesDefaultGameMode'" - ); + return false; } @Override @@ -1282,26 +1388,30 @@ public class PatchBukkitServer implements Server { public @NotNull CommandSender createCommandSender( @NotNull Consumer feedback ) { - // TODO Auto-generated method stub - throw new UnsupportedOperationException( - "Unimplemented method 'createCommandSender'" - ); + return new PatchBukkitConsoleCommandSender() { + @Override + public void sendMessage(@NotNull Component message) { + feedback.accept(message); + } + @Override + public void sendMessage(@NotNull String message) { + feedback.accept(LegacyComponentSerializer.legacySection().deserialize(message)); + } + }; } @Override public @NotNull File getWorldContainer() { - // TODO Auto-generated method stub - throw new UnsupportedOperationException( - "Unimplemented method 'getWorldContainer'" - ); + return new File("."); } @Override public @NotNull OfflinePlayer@NotNull [] getOfflinePlayers() { - // TODO Auto-generated method stub - throw new UnsupportedOperationException( - "Unimplemented method 'getOfflinePlayers'" - ); + Set players = new HashSet<>(getOnlinePlayers()); + players.addAll(getOperators()); + players.addAll(getWhitelistedPlayers()); + players.addAll(getBannedPlayers()); + return players.toArray(new OfflinePlayer[0]); } @Override @@ -1319,10 +1429,7 @@ public class PatchBukkitServer implements Server { @Nullable InventoryHolder owner, @NotNull InventoryType type ) { - // TODO Auto-generated method stub - throw new UnsupportedOperationException( - "Unimplemented method 'createInventory'" - ); + return new PatchBukkitInventory(owner, type); } @Override @@ -1331,10 +1438,7 @@ public class PatchBukkitServer implements Server { @NotNull InventoryType type, @NotNull Component title ) { - // TODO Auto-generated method stub - throw new UnsupportedOperationException( - "Unimplemented method 'createInventory'" - ); + return new PatchBukkitInventory(owner, type, title); } @Override @@ -1343,10 +1447,7 @@ public class PatchBukkitServer implements Server { @NotNull InventoryType type, @NotNull String title ) { - // TODO Auto-generated method stub - throw new UnsupportedOperationException( - "Unimplemented method 'createInventory'" - ); + return new PatchBukkitInventory(owner, type, title); } @Override @@ -1354,10 +1455,7 @@ public class PatchBukkitServer implements Server { @Nullable InventoryHolder owner, int size ) throws IllegalArgumentException { - // TODO Auto-generated method stub - throw new UnsupportedOperationException( - "Unimplemented method 'createInventory'" - ); + return new PatchBukkitInventory(owner, size); } @Override @@ -1366,10 +1464,7 @@ public class PatchBukkitServer implements Server { int size, @NotNull Component title ) throws IllegalArgumentException { - // TODO Auto-generated method stub - throw new UnsupportedOperationException( - "Unimplemented method 'createInventory'" - ); + return new PatchBukkitInventory(owner, size, title); } @Override @@ -1378,50 +1473,41 @@ public class PatchBukkitServer implements Server { int size, @NotNull String title ) throws IllegalArgumentException { - // TODO Auto-generated method stub - throw new UnsupportedOperationException( - "Unimplemented method 'createInventory'" - ); + return new PatchBukkitInventory(owner, size, title); } @Override public @NotNull Merchant createMerchant(@Nullable Component title) { - // TODO Auto-generated method stub - throw new UnsupportedOperationException( - "Unimplemented method 'createMerchant'" - ); + return new PatchBukkitMerchant(title); } @Override public @NotNull Merchant createMerchant(@Nullable String title) { - // TODO Auto-generated method stub - throw new UnsupportedOperationException( - "Unimplemented method 'createMerchant'" - ); + return new PatchBukkitMerchant(title != null ? Component.text(title) : null); } @Override public int getMaxChainedNeighborUpdates() { - // TODO Auto-generated method stub - throw new UnsupportedOperationException( - "Unimplemented method 'getMaxChainedNeighborUpdates'" - ); + return 1000000; } @Override public @NotNull Merchant createMerchant() { - // TODO Auto-generated method stub - throw new UnsupportedOperationException( - "Unimplemented method 'createMerchant'" - ); + return new PatchBukkitMerchant(null); } @Override public int getSpawnLimit(@NotNull SpawnCategory spawnCategory) { - // TODO Auto-generated method stub - throw new UnsupportedOperationException( - "Unimplemented method 'getSpawnLimit'" - ); + return switch (spawnCategory) { + case MONSTER -> 70; + case ANIMAL -> 10; + case WATER_ANIMAL -> 5; + case WATER_AMBIENT -> 20; + case WATER_UNDERGROUND_CREATURE -> 5; + case AMBIENT -> 15; + case AXOLOTL -> 5; + default -> 10; + }; } @Override @@ -1431,54 +1517,43 @@ public class PatchBukkitServer implements Server { @Override public @NotNull Component motd() { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'motd'"); + return Component.text(this.motd); } @Override public void motd(@NotNull Component motd) { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'motd'"); + this.motd = PlainTextComponentSerializer.plainText().serialize(motd); + try { + NativeBridgeFfi.setServerMotd(SetServerMotdRequest.newBuilder().setMotd(this.motd).build()); + } catch (Throwable ignored) {} } @Override public @Nullable Component shutdownMessage() { - // TODO Auto-generated method stub - throw new UnsupportedOperationException( - "Unimplemented method 'shutdownMessage'" - ); + return Component.text(this.shutdownMessage); } @Override public @NotNull String getMotd() { - // TODO Auto-generated method stub - throw new UnsupportedOperationException( - "Unimplemented method 'getMotd'" - ); + return this.motd; } @Override public void setMotd(@NotNull String motd) { - // TODO Auto-generated method stub - throw new UnsupportedOperationException( - "Unimplemented method 'setMotd'" - ); + this.motd = motd; + try { + NativeBridgeFfi.setServerMotd(SetServerMotdRequest.newBuilder().setMotd(motd).build()); + } catch (Throwable ignored) {} } @Override public @NotNull ServerLinks getServerLinks() { - // TODO Auto-generated method stub - throw new UnsupportedOperationException( - "Unimplemented method 'getServerLinks'" - ); + return this.serverLinks; } @Override public @Nullable String getShutdownMessage() { - // TODO Auto-generated method stub - throw new UnsupportedOperationException( - "Unimplemented method 'getShutdownMessage'" - ); + return this.shutdownMessage; } @Override @@ -1488,97 +1563,77 @@ public class PatchBukkitServer implements Server { @Override public @NotNull ItemFactory getItemFactory() { - return org.patchbukkit.inventory.PatchBukkitItemFactory.INSTANCE; + return PatchBukkitItemFactory.INSTANCE; } @Override public @NotNull EntityFactory getEntityFactory() { - // TODO Auto-generated method stub - throw new UnsupportedOperationException( - "Unimplemented method 'getEntityFactory'" - ); + return this.entityFactory; } @Override public @NotNull ScoreboardManager getScoreboardManager() { - // TODO Auto-generated method stub - throw new UnsupportedOperationException( - "Unimplemented method 'getScoreboardManager'" - ); + return this.scoreboardManager; } @Override public @NotNull Criteria getScoreboardCriteria(@NotNull String name) { - // TODO Auto-generated method stub - throw new UnsupportedOperationException( - "Unimplemented method 'getScoreboardCriteria'" - ); + return Criteria.create(name); } @Override public @Nullable CachedServerIcon getServerIcon() { - // TODO Auto-generated method stub - throw new UnsupportedOperationException( - "Unimplemented method 'getServerIcon'" - ); + return null; } @Override public @NotNull CachedServerIcon loadServerIcon(@NotNull File file) throws IllegalArgumentException, Exception { - // TODO Auto-generated method stub - throw new UnsupportedOperationException( - "Unimplemented method 'loadServerIcon'" - ); + return () -> ""; } @Override public @NotNull CachedServerIcon loadServerIcon( @NotNull BufferedImage image ) throws IllegalArgumentException, Exception { - // TODO Auto-generated method stub - throw new UnsupportedOperationException( - "Unimplemented method 'loadServerIcon'" - ); + return () -> ""; } @Override public void setIdleTimeout(int threshold) { - // TODO Auto-generated method stub - throw new UnsupportedOperationException( - "Unimplemented method 'setIdleTimeout'" - ); + this.idleTimeout = threshold; + try { + NativeBridgeFfi.setServerIdleTimeout(SetServerIdleTimeoutRequest.newBuilder().setTimeoutMinutes(threshold).build()); + } catch (Throwable ignored) {} } @Override public int getIdleTimeout() { - // TODO Auto-generated method stub - throw new UnsupportedOperationException( - "Unimplemented method 'getIdleTimeout'" - ); + return this.idleTimeout; } @Override public int getPauseWhenEmptyTime() { - // TODO Auto-generated method stub - throw new UnsupportedOperationException( - "Unimplemented method 'getPauseWhenEmptyTime'" - ); + return this.pauseWhenEmptyTime; } @Override public void setPauseWhenEmptyTime(int seconds) { - // TODO Auto-generated method stub - throw new UnsupportedOperationException( - "Unimplemented method 'setPauseWhenEmptyTime'" - ); + this.pauseWhenEmptyTime = seconds; } @Override public @NotNull ChunkData createChunkData(@NotNull World world) { - // TODO Auto-generated method stub - throw new UnsupportedOperationException( - "Unimplemented method 'createChunkData'" + return (ChunkData) java.lang.reflect.Proxy.newProxyInstance( + ChunkData.class.getClassLoader(), + new Class[] { ChunkData.class }, + (proxy, method, args) -> { + if ("getMinHeight".equals(method.getName())) return world.getMinHeight(); + if ("getMaxHeight".equals(method.getName())) return world.getMaxHeight(); + if ("getBlockData".equals(method.getName())) return Material.AIR.createBlockData(); + if ("getType".equals(method.getName())) return Material.AIR; + return null; + } ); } @@ -1589,10 +1644,7 @@ public class PatchBukkitServer implements Server { @NotNull BarStyle style, @NotNull BarFlag... flags ) { - // TODO Auto-generated method stub - throw new UnsupportedOperationException( - "Unimplemented method 'createBossBar'" - ); + return new PatchBukkitBossBar(title, color, style, flags); } @Override @@ -1603,60 +1655,74 @@ public class PatchBukkitServer implements Server { @NotNull BarStyle style, @NotNull BarFlag... flags ) { - // TODO Auto-generated method stub - throw new UnsupportedOperationException( - "Unimplemented method 'createBossBar'" - ); + KeyedBossBar bar = new PatchBukkitKeyedBossBar(key, title, color, style, flags); + this.bossBars.put(key, bar); + return bar; } @Override public @NotNull Iterator getBossBars() { - // TODO Auto-generated method stub - throw new UnsupportedOperationException( - "Unimplemented method 'getBossBars'" - ); + return this.bossBars.values().iterator(); } @Override public @Nullable KeyedBossBar getBossBar(@NotNull NamespacedKey key) { - // TODO Auto-generated method stub - throw new UnsupportedOperationException( - "Unimplemented method 'getBossBar'" - ); + return this.bossBars.get(key); } @Override public boolean removeBossBar(@NotNull NamespacedKey key) { - // TODO Auto-generated method stub - throw new UnsupportedOperationException( - "Unimplemented method 'removeBossBar'" - ); + return this.bossBars.remove(key) != null; } @Override public @Nullable Entity getEntity(@NotNull UUID uuid) { - // TODO Auto-generated method stub - throw new UnsupportedOperationException( - "Unimplemented method 'getEntity'" - ); + for (World world : getWorlds()) { + Entity e = world.getEntity(uuid); + if (e != null) return e; + } + return null; } @Override public double@NotNull [] getTPS() { + try { + ServerTickInfoResponse info = NativeBridgeFfi.getServerTickInfo(EmptyRequest.getDefaultInstance()); + if (info != null && info.getTpsCount() > 0) { + double[] arr = new double[info.getTpsCount()]; + for (int i = 0; i < info.getTpsCount(); i++) { + arr[i] = info.getTps(i); + } + return arr; + } + } catch (Throwable ignored) {} return new double[] { 20.0, 20.0, 20.0 }; } @Override public long@NotNull [] getTickTimes() { + try { + ServerTickInfoResponse info = NativeBridgeFfi.getServerTickInfo(EmptyRequest.getDefaultInstance()); + if (info != null && info.getTickTimesNanosCount() > 0) { + long[] arr = new long[info.getTickTimesNanosCount()]; + for (int i = 0; i < info.getTickTimesNanosCount(); i++) { + arr[i] = info.getTickTimesNanos(i); + } + return arr; + } + } catch (Throwable ignored) {} return new long[100]; } @Override public double getAverageTickTime() { - // TODO Auto-generated method stub - throw new UnsupportedOperationException( - "Unimplemented method 'getAverageTickTime'" - ); + try { + ServerTickInfoResponse info = NativeBridgeFfi.getServerTickInfo(EmptyRequest.getDefaultInstance()); + if (info != null) { + return info.getAverageTickTime(); + } + } catch (Throwable ignored) {} + return 50.0; } @Override @@ -1666,18 +1732,12 @@ public class PatchBukkitServer implements Server { @Override public @Nullable Advancement getAdvancement(@NotNull NamespacedKey key) { - // TODO Auto-generated method stub - throw new UnsupportedOperationException( - "Unimplemented method 'getAdvancement'" - ); + return null; } @Override public @NotNull Iterator advancementIterator() { - // TODO Auto-generated method stub - throw new UnsupportedOperationException( - "Unimplemented method 'advancementIterator'" - ); + return Collections.emptyIterator(); } @Override @@ -1764,7 +1824,6 @@ public class PatchBukkitServer implements Server { } @Override - @SuppressWarnings("unchecked") public @Nullable Tag getTag( @NotNull String registry, @NotNull NamespacedKey tag, @@ -1774,7 +1833,7 @@ public class PatchBukkitServer implements Server { return null; } try { - for (java.lang.reflect.Field field : org.bukkit.Tag.class.getFields()) { + for (java.lang.reflect.Field field : Tag.class.getFields()) { if (Tag.class.isAssignableFrom(field.getType())) { Tag val = (Tag) field.get(null); if (val != null && val.getKey().equals(tag)) { @@ -1782,20 +1841,18 @@ public class PatchBukkitServer implements Server { } } } - } catch (Throwable ignored) { - } + } catch (Throwable ignored) {} return new org.patchbukkit.tag.PatchBukkitTag<>(tag); } @Override - @SuppressWarnings("unchecked") public @NotNull Iterable> getTags( @NotNull String registry, @NotNull Class clazz ) { List> result = new ArrayList<>(); try { - for (java.lang.reflect.Field field : org.bukkit.Tag.class.getFields()) { + for (java.lang.reflect.Field field : Tag.class.getFields()) { if (Tag.class.isAssignableFrom(field.getType())) { Tag val = (Tag) field.get(null); if (val != null) { @@ -1803,17 +1860,13 @@ public class PatchBukkitServer implements Server { } } } - } catch (Throwable ignored) { - } + } catch (Throwable ignored) {} return result; } @Override public @Nullable LootTable getLootTable(@NotNull NamespacedKey key) { - // TODO Auto-generated method stub - throw new UnsupportedOperationException( - "Unimplemented method 'getLootTable'" - ); + return null; } @Override @@ -1821,18 +1874,23 @@ public class PatchBukkitServer implements Server { @NotNull CommandSender sender, @NotNull String selector ) throws IllegalArgumentException { - // TODO Auto-generated method stub - throw new UnsupportedOperationException( - "Unimplemented method 'selectEntities'" - ); + List list = new ArrayList<>(); + if ("@a".equalsIgnoreCase(selector) || "@e".equalsIgnoreCase(selector)) { + list.addAll(getOnlinePlayers()); + } else if ("@s".equalsIgnoreCase(selector) || "@p".equalsIgnoreCase(selector)) { + if (sender instanceof Entity e) { + list.add(e); + } + } else { + Player p = getPlayer(selector); + if (p != null) list.add(p); + } + return list; } @Override public @NotNull StructureManager getStructureManager() { - // TODO Auto-generated method stub - throw new UnsupportedOperationException( - "Unimplemented method 'getStructureManager'" - ); + return this.structureManager; } @Override @@ -1849,34 +1907,21 @@ public class PatchBukkitServer implements Server { @Override public @NotNull Spigot spigot() { - // TODO Auto-generated method stub - throw new UnsupportedOperationException( - "Unimplemented method 'spigot'" - ); + return this.spigot; } @Override public void restart() { - // TODO Auto-generated method stub - throw new UnsupportedOperationException( - "Unimplemented method 'restart'" - ); + shutdown(); } @Override public void reloadPermissions() { - // TODO Auto-generated method stub - throw new UnsupportedOperationException( - "Unimplemented method 'reloadPermissions'" - ); } @Override public boolean reloadCommandAliases() { - // TODO Auto-generated method stub - throw new UnsupportedOperationException( - "Unimplemented method 'reloadCommandAliases'" - ); + return true; } @Override @@ -1886,118 +1931,78 @@ public class PatchBukkitServer implements Server { @Override public @NotNull String getPermissionMessage() { - // TODO Auto-generated method stub - throw new UnsupportedOperationException( - "Unimplemented method 'getPermissionMessage'" - ); + return "I'm sorry, but you do not have permission to perform this command. Please contact the server administrators if you believe that this is in error."; } @Override public @NotNull Component permissionMessage() { - // TODO Auto-generated method stub - throw new UnsupportedOperationException( - "Unimplemented method 'permissionMessage'" - ); + return Component.text(getPermissionMessage()); } @Override - public com.destroystokyo.paper.profile.@NotNull PlayerProfile createProfile( - @NotNull UUID uuid - ) { - // TODO Auto-generated method stub - throw new UnsupportedOperationException( - "Unimplemented method 'createProfile'" - ); + public PlayerProfile createProfile(@NotNull UUID uuid) { + return new PatchBukkitPlayerProfile(uuid, null); } @Override - public com.destroystokyo.paper.profile.@NotNull PlayerProfile createProfile( - @NotNull String name - ) { - // TODO Auto-generated method stub - throw new UnsupportedOperationException( - "Unimplemented method 'createProfile'" - ); + public PlayerProfile createProfile(@NotNull String name) { + return new PatchBukkitPlayerProfile(null, name); } @Override - public com.destroystokyo.paper.profile.@NotNull PlayerProfile createProfile( - @Nullable UUID uuid, - @Nullable String name - ) { - // TODO Auto-generated method stub - throw new UnsupportedOperationException( - "Unimplemented method 'createProfile'" - ); + public PlayerProfile createProfile(@Nullable UUID uuid, @Nullable String name) { + return new PatchBukkitPlayerProfile(uuid, name); } @Override - public com.destroystokyo.paper.profile.@NotNull PlayerProfile createProfileExact( - @Nullable UUID uuid, - @Nullable String name - ) { - // TODO Auto-generated method stub - throw new UnsupportedOperationException( - "Unimplemented method 'createProfileExact'" - ); + public PlayerProfile createProfileExact(@Nullable UUID uuid, @Nullable String name) { + return new PatchBukkitPlayerProfile(uuid, name); } @Override public int getCurrentTick() { + try { + ServerTickInfoResponse info = NativeBridgeFfi.getServerTickInfo(EmptyRequest.getDefaultInstance()); + if (info != null && info.getTickCount() > 0) { + return (int) info.getTickCount(); + } + } catch (Throwable ignored) {} return (int) (System.currentTimeMillis() / 50); } @Override public boolean isStopping() { - return false; + return this.isStopping; } @Override public @NotNull MobGoals getMobGoals() { - // TODO Auto-generated method stub - throw new UnsupportedOperationException( - "Unimplemented method 'getMobGoals'" - ); + return this.mobGoals; } @Override public @NotNull DatapackManager getDatapackManager() { - // TODO Auto-generated method stub - throw new UnsupportedOperationException( - "Unimplemented method 'getDatapackManager'" - ); + return this.datapackManager; } @Override public @NotNull PotionBrewer getPotionBrewer() { - // TODO Auto-generated method stub - throw new UnsupportedOperationException( - "Unimplemented method 'getPotionBrewer'" - ); + return this.potionBrewer; } @Override public @NotNull RegionScheduler getRegionScheduler() { - // TODO Auto-generated method stub - throw new UnsupportedOperationException( - "Unimplemented method 'getRegionScheduler'" - ); + return this.regionScheduler; } @Override public @NotNull AsyncScheduler getAsyncScheduler() { - // TODO Auto-generated method stub - throw new UnsupportedOperationException( - "Unimplemented method 'getAsyncScheduler'" - ); + return this.asyncScheduler; } @Override public @NotNull GlobalRegionScheduler getGlobalRegionScheduler() { - // TODO Auto-generated method stub - throw new UnsupportedOperationException( - "Unimplemented method 'getGlobalRegionScheduler'" - ); + return this.globalRegionScheduler; } @Override @@ -2005,10 +2010,7 @@ public class PatchBukkitServer implements Server { @NotNull World world, @NotNull Position position ) { - // TODO Auto-generated method stub - throw new UnsupportedOperationException( - "Unimplemented method 'isOwnedByCurrentRegion'" - ); + return true; } @Override @@ -2017,18 +2019,12 @@ public class PatchBukkitServer implements Server { @NotNull Position position, int squareRadiusChunks ) { - // TODO Auto-generated method stub - throw new UnsupportedOperationException( - "Unimplemented method 'isOwnedByCurrentRegion'" - ); + return true; } @Override public boolean isOwnedByCurrentRegion(@NotNull Location location) { - // TODO Auto-generated method stub - throw new UnsupportedOperationException( - "Unimplemented method 'isOwnedByCurrentRegion'" - ); + return true; } @Override @@ -2036,10 +2032,7 @@ public class PatchBukkitServer implements Server { @NotNull Location location, int squareRadiusChunks ) { - // TODO Auto-generated method stub - throw new UnsupportedOperationException( - "Unimplemented method 'isOwnedByCurrentRegion'" - ); + return true; } @Override @@ -2048,10 +2041,7 @@ public class PatchBukkitServer implements Server { int chunkX, int chunkZ ) { - // TODO Auto-generated method stub - throw new UnsupportedOperationException( - "Unimplemented method 'isOwnedByCurrentRegion'" - ); + return true; } @Override @@ -2061,10 +2051,7 @@ public class PatchBukkitServer implements Server { int chunkZ, int squareRadiusChunks ) { - // TODO Auto-generated method stub - throw new UnsupportedOperationException( - "Unimplemented method 'isOwnedByCurrentRegion'" - ); + return true; } @Override @@ -2075,47 +2062,30 @@ public class PatchBukkitServer implements Server { int maxChunkX, int maxChunkZ ) { - // TODO Auto-generated method stub - throw new UnsupportedOperationException( - "Unimplemented method 'isOwnedByCurrentRegion'" - ); + return true; } @Override public boolean isOwnedByCurrentRegion(@NotNull Entity entity) { - // TODO Auto-generated method stub - throw new UnsupportedOperationException( - "Unimplemented method 'isOwnedByCurrentRegion'" - ); + return true; } @Override public boolean isGlobalTickThread() { - // TODO Auto-generated method stub - throw new UnsupportedOperationException( - "Unimplemented method 'isGlobalTickThread'" - ); + return isPrimaryThread(); } @Override public boolean isPaused() { - // TODO Auto-generated method stub - throw new UnsupportedOperationException( - "Unimplemented method 'isPaused'" - ); + return false; } @Override public void allowPausing(@NotNull Plugin plugin, boolean value) { - // TODO Auto-generated method stub - throw new UnsupportedOperationException( - "Unimplemented method 'allowPausing'" - ); } public @NotNull Path getLevelDirectory() { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'getLevelDirectory'"); + return Path.of("."); } @Override @@ -2137,4 +2107,19 @@ public class PatchBukkitServer implements Server { public int getWaterAnimalSpawnLimit() { return 5; } + + @Override + public @NotNull io.papermc.paper.configuration.ServerConfiguration getServerConfig() { + return new io.papermc.paper.configuration.ServerConfiguration() { + @Override + public boolean isProxyOnlineMode() { + return false; + } + + @Override + public boolean isProxyEnabled() { + return false; + } + }; + } } diff --git a/java/patchbukkit/src/main/java/org/patchbukkit/ban/PatchBukkitBanEntry.java b/java/patchbukkit/src/main/java/org/patchbukkit/ban/PatchBukkitBanEntry.java new file mode 100644 index 0000000..b72e9a5 --- /dev/null +++ b/java/patchbukkit/src/main/java/org/patchbukkit/ban/PatchBukkitBanEntry.java @@ -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 implements BanEntry { + private final String target; + private final T banTarget; + private Date created; + private String source; + private Date expiration; + private String reason; + private final PatchBukkitBanList list; + + public PatchBukkitBanEntry( + @NotNull String target, + @Nullable T banTarget, + @Nullable Date created, + @Nullable String source, + @Nullable Date expiration, + @Nullable String reason, + @Nullable PatchBukkitBanList 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); + } +} diff --git a/java/patchbukkit/src/main/java/org/patchbukkit/ban/PatchBukkitBanList.java b/java/patchbukkit/src/main/java/org/patchbukkit/ban/PatchBukkitBanList.java new file mode 100644 index 0000000..c7632e5 --- /dev/null +++ b/java/patchbukkit/src/main/java/org/patchbukkit/ban/PatchBukkitBanList.java @@ -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 implements BanList { + private final String banType; + private final Map> 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 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 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 > @Nullable E getBanEntry(@NotNull String target) { + return (E) entries.get(target.toLowerCase()); + } + + @Override + public @Nullable BanEntry getBanEntry(@NotNull T target) { + return entries.get(targetToString(target).toLowerCase()); + } + + @Override + public > @Nullable E addBan( + @NotNull String target, + @Nullable String reason, + @Nullable Date expires, + @Nullable String source + ) { + PatchBukkitBanEntry entry = new PatchBukkitBanEntry<>( + target, + parseTarget(target), + new Date(), + source, + expires, + reason, + this + ); + saveEntry(entry); + return (E) entry; + } + + @Override + public @Nullable BanEntry addBan( + @NotNull T target, + @Nullable String reason, + @Nullable Date expires, + @Nullable String source + ) { + return addBan(targetToString(target), reason, expires, source); + } + + @Override + public @Nullable BanEntry 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 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 getBanEntries() { + return new HashSet<>(entries.values()); + } + + @Override + public > @NotNull Set getEntries() { + return (Set) new HashSet<>(entries.values()); + } + + @Override + public boolean isBanned(@NotNull T target) { + return isBanned(targetToString(target)); + } + + @Override + public boolean isBanned(@NotNull String target) { + BanEntry 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); + } +} diff --git a/java/patchbukkit/src/main/java/org/patchbukkit/boss/PatchBukkitBossBar.java b/java/patchbukkit/src/main/java/org/patchbukkit/boss/PatchBukkitBossBar.java new file mode 100644 index 0000000..5648f5e --- /dev/null +++ b/java/patchbukkit/src/main/java/org/patchbukkit/boss/PatchBukkitBossBar.java @@ -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 flags = EnumSet.noneOf(BarFlag.class); + private double progress = 1.0; + private boolean visible = true; + private final List 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 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); + } +} diff --git a/java/patchbukkit/src/main/java/org/patchbukkit/boss/PatchBukkitKeyedBossBar.java b/java/patchbukkit/src/main/java/org/patchbukkit/boss/PatchBukkitKeyedBossBar.java new file mode 100644 index 0000000..0baa4b3 --- /dev/null +++ b/java/patchbukkit/src/main/java/org/patchbukkit/boss/PatchBukkitKeyedBossBar.java @@ -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; + } +} diff --git a/java/patchbukkit/src/main/java/org/patchbukkit/datapack/PatchBukkitDatapackManager.java b/java/patchbukkit/src/main/java/org/patchbukkit/datapack/PatchBukkitDatapackManager.java new file mode 100644 index 0000000..ca9c679 --- /dev/null +++ b/java/patchbukkit/src/main/java/org/patchbukkit/datapack/PatchBukkitDatapackManager.java @@ -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 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 getPacks() { + return Collections.singletonList(VANILLA); + } + + @Override + public @NotNull Collection getEnabledPacks() { + return Collections.singletonList(VANILLA); + } +} diff --git a/java/patchbukkit/src/main/java/org/patchbukkit/entity/PatchBukkitEntity.java b/java/patchbukkit/src/main/java/org/patchbukkit/entity/PatchBukkitEntity.java index 57d490b..33e2c04 100644 --- a/java/patchbukkit/src/main/java/org/patchbukkit/entity/PatchBukkitEntity.java +++ b/java/patchbukkit/src/main/java/org/patchbukkit/entity/PatchBukkitEntity.java @@ -83,6 +83,16 @@ public class PatchBukkitEntity implements Entity { 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( UUID uuid, String name @@ -263,10 +273,15 @@ public class PatchBukkitEntity implements Entity { @Override public @NotNull Location getLocation() { - var location = NativeBridgeFfi.getLocation(BridgeUtils.convertUuid(this.uuid)); - 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()); + try { + var location = NativeBridgeFfi.getLocation(BridgeUtils.convertUuid(this.uuid)); + if (location != null && location.hasWorld() && location.hasPosition()) { + 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 @@ -646,8 +661,7 @@ public class PatchBukkitEntity implements Entity { @Override public @NotNull EntityType getType() { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'getType'"); + return this.entityType != null ? this.entityType : EntityType.UNKNOWN; } @Override diff --git a/java/patchbukkit/src/main/java/org/patchbukkit/entity/PatchBukkitEntityFactory.java b/java/patchbukkit/src/main/java/org/patchbukkit/entity/PatchBukkitEntityFactory.java new file mode 100644 index 0000000..315c01d --- /dev/null +++ b/java/patchbukkit/src/main/java/org/patchbukkit/entity/PatchBukkitEntityFactory.java @@ -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; + } + }; + } +} diff --git a/java/patchbukkit/src/main/java/org/patchbukkit/entity/PatchBukkitHumanEntity.java b/java/patchbukkit/src/main/java/org/patchbukkit/entity/PatchBukkitHumanEntity.java index 6914e56..708ede0 100644 --- a/java/patchbukkit/src/main/java/org/patchbukkit/entity/PatchBukkitHumanEntity.java +++ b/java/patchbukkit/src/main/java/org/patchbukkit/entity/PatchBukkitHumanEntity.java @@ -1,103 +1,124 @@ package org.patchbukkit.entity; import java.util.Collection; +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; import java.util.List; +import java.util.Map; import java.util.Set; import java.util.UUID; import java.util.function.Consumer; -import org.bukkit.Color; -import org.bukkit.FluidCollisionMode; import org.bukkit.GameMode; import org.bukkit.Location; import org.bukkit.Material; import org.bukkit.NamespacedKey; import org.bukkit.Sound; -import org.bukkit.attribute.Attribute; -import org.bukkit.attribute.AttributeInstance; -import org.bukkit.block.Block; -import org.bukkit.block.BlockFace; import org.bukkit.block.Sign; import org.bukkit.block.sign.Side; -import org.bukkit.damage.DamageSource; import org.bukkit.entity.Entity; -import org.bukkit.entity.EntityCategory; import org.bukkit.entity.Firework; import org.bukkit.entity.FishHook; import org.bukkit.entity.HumanEntity; import org.bukkit.entity.Item; import org.bukkit.entity.Player; -import org.bukkit.entity.Projectile; -import org.bukkit.entity.memory.MemoryKey; -import org.bukkit.event.entity.EntityRegainHealthEvent.RegainReason; import org.bukkit.event.inventory.InventoryCloseEvent.Reason; import org.bukkit.inventory.EntityEquipment; import org.bukkit.inventory.EquipmentSlot; import org.bukkit.inventory.Inventory; import org.bukkit.inventory.InventoryView; import org.bukkit.inventory.InventoryView.Property; -import org.bukkit.permissions.PermissibleBase; -import org.bukkit.permissions.Permission; import org.bukkit.inventory.ItemStack; import org.bukkit.inventory.MainHand; import org.bukkit.inventory.Merchant; import org.bukkit.inventory.PlayerInventory; -import org.bukkit.potion.PotionEffect; -import org.bukkit.potion.PotionEffectType; -import org.bukkit.util.RayTraceResult; -import org.bukkit.util.Vector; -import org.checkerframework.checker.index.qual.NonNegative; +import org.bukkit.permissions.PermissibleBase; +import org.bukkit.permissions.Permission; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Range; import org.patchbukkit.PatchBukkitServer; import org.patchbukkit.bridge.BridgeUtils; -import patchbukkit.bridge.NativeBridgeFfi; +import org.patchbukkit.inventory.PatchBukkitInventory; -import com.destroystokyo.paper.block.TargetBlockInfo; -import com.destroystokyo.paper.block.TargetBlockInfo.FluidMode; -import com.destroystokyo.paper.entity.TargetEntityInfo; - -import io.papermc.paper.world.damagesource.CombatTracker; import net.kyori.adventure.key.Key; -import net.kyori.adventure.util.TriState; +import patchbukkit.bridge.NativeBridgeFfi; +import patchbukkit.entity.GetCooldownRequest; +import patchbukkit.entity.SetCooldownRequest; +import patchbukkit.entity.SetExhaustionRequest; +import patchbukkit.entity.SetFoodLevelRequest; +import patchbukkit.entity.SetGamemodeRequest; +import patchbukkit.entity.SetOpRequest; +import patchbukkit.entity.SetSaturationRequest; +import patchbukkit.entity.SetSneakingRequest; +import patchbukkit.entity.SetSprintingRequest; @SuppressWarnings({ "deprecation", "removal" }) public class PatchBukkitHumanEntity extends PatchBukkitLivingEntity implements HumanEntity { + private boolean op; protected final PermissibleBase perm = new PermissibleBase(this); protected final PlayerInventory inventory = new org.patchbukkit.inventory.PatchBukkitPlayerInventory(this); protected final EntityEquipment equipment = new org.patchbukkit.inventory.PatchBukkitEntityEquipment(this); + protected final Inventory enderChest = new PatchBukkitInventory(this, 27, "Ender Chest"); private ItemStack cursorItem = ItemStack.empty(); + private InventoryView openInventoryView; + private final Set discoveredRecipes = new HashSet<>(); + private final Map cooldowns = new HashMap<>(); + private Location lastDeathLocation; + private Location bedLocation; + private Entity shoulderEntityLeft; + private Entity shoulderEntityRight; + private int saturatedRegenRate = 10; + private int unsaturatedRegenRate = 80; + private int starvationRate = 80; + private int sleepTicks = 0; + private int enchantmentSeed = 0; - - public PatchBukkitHumanEntity(UUID uuid, - String name) { + public PatchBukkitHumanEntity(UUID uuid, String name) { super(uuid, name); } @Override public boolean undiscoverRecipe(@NotNull NamespacedKey key) { - return false; + return this.discoveredRecipes.remove(key); } @Override public int undiscoverRecipes(@NotNull Collection keys) { - return 0; + int count = 0; + for (NamespacedKey k : keys) { + if (this.discoveredRecipes.remove(k)) count++; + } + return count; } @Override public boolean discoverRecipe(@NotNull NamespacedKey key) { - return false; + return this.discoveredRecipes.add(key); } @Override public int discoverRecipes(@NotNull Collection keys) { - return 0; + int count = 0; + for (NamespacedKey k : keys) { + if (this.discoveredRecipes.add(k)) count++; + } + return count; } + @Override + public boolean hasDiscoveredRecipe(NamespacedKey recipe) { + return this.discoveredRecipes.contains(recipe); + } + + @Override + public Set getDiscoveredRecipes() { + return Collections.unmodifiableSet(this.discoveredRecipes); + } @Override public boolean isOp() { @@ -129,6 +150,13 @@ public class PatchBukkitHumanEntity public void setOp(boolean value) { this.op = value; PatchBukkitServer.getInstance().setOperator(getUniqueId(), getName(), value); + try { + var req = SetOpRequest.newBuilder() + .setUuid(BridgeUtils.convertUuid(getUniqueId())) + .setIsOp(value) + .build(); + NativeBridgeFfi.setOp(req); + } catch (Throwable ignored) {} this.perm.recalculatePermissions(); } @@ -152,701 +180,6 @@ public class PatchBukkitHumanEntity return this.perm.hasPermission(perm); } - - - @Override - public @NotNull List getLineOfSight(@Nullable Set transparent, int maxDistance) { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'getLineOfSight'"); - } - - @Override - public @NotNull Block getTargetBlock(@Nullable Set transparent, int maxDistance) { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'getTargetBlock'"); - } - - @Override - public @Nullable Block getTargetBlock(int maxDistance, @NotNull FluidMode fluidMode) { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'getTargetBlock'"); - } - - @Override - public @Nullable BlockFace getTargetBlockFace(int maxDistance, @NotNull FluidMode fluidMode) { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'getTargetBlockFace'"); - } - - @Override - public @Nullable BlockFace getTargetBlockFace(int maxDistance, @NotNull FluidCollisionMode fluidMode) { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'getTargetBlockFace'"); - } - - @Override - public @Nullable TargetBlockInfo getTargetBlockInfo(int maxDistance, @NotNull FluidMode fluidMode) { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'getTargetBlockInfo'"); - } - - @Override - public @Nullable Entity getTargetEntity(int maxDistance, boolean ignoreBlocks) { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'getTargetEntity'"); - } - - @Override - public @Nullable TargetEntityInfo getTargetEntityInfo(int maxDistance, boolean ignoreBlocks) { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'getTargetEntityInfo'"); - } - - @Override - public @Nullable RayTraceResult rayTraceEntities(int maxDistance, boolean ignoreBlocks) { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'rayTraceEntities'"); - } - - @Override - public @NotNull List getLastTwoTargetBlocks(@Nullable Set transparent, int maxDistance) { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'getLastTwoTargetBlocks'"); - } - - @Override - public @Nullable Block getTargetBlockExact(int maxDistance, @NotNull FluidCollisionMode fluidCollisionMode) { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'getTargetBlockExact'"); - } - - @Override - public @Nullable RayTraceResult rayTraceBlocks(double maxDistance, @NotNull FluidCollisionMode fluidCollisionMode) { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'rayTraceBlocks'"); - } - - @Override - public int getRemainingAir() { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'getRemainingAir'"); - } - - @Override - public void setRemainingAir(int ticks) { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'setRemainingAir'"); - } - - @Override - public int getMaximumAir() { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'getMaximumAir'"); - } - - @Override - public void setMaximumAir(int ticks) { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'setMaximumAir'"); - } - - @Override - public @Nullable ItemStack getItemInUse() { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'getItemInUse'"); - } - - @Override - public int getItemInUseTicks() { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'getItemInUseTicks'"); - } - - @Override - public void setItemInUseTicks(int ticks) { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'setItemInUseTicks'"); - } - - @Override - public @NonNegative int getArrowCooldown() { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'getArrowCooldown'"); - } - - @Override - public void setArrowCooldown(@NonNegative int ticks) { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'setArrowCooldown'"); - } - - @Override - public @NonNegative int getArrowsInBody() { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'getArrowsInBody'"); - } - - @Override - public void setArrowsInBody(@NonNegative int count, boolean fireEvent) { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'setArrowsInBody'"); - } - - @Override - public @NonNegative int getBeeStingerCooldown() { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'getBeeStingerCooldown'"); - } - - @Override - public void setBeeStingerCooldown(@NonNegative int ticks) { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'setBeeStingerCooldown'"); - } - - @Override - public @NonNegative int getBeeStingersInBody() { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'getBeeStingersInBody'"); - } - - @Override - public void setBeeStingersInBody(@NonNegative int count) { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'setBeeStingersInBody'"); - } - - @Override - public int getMaximumNoDamageTicks() { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'getMaximumNoDamageTicks'"); - } - - @Override - public void setMaximumNoDamageTicks(int ticks) { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'setMaximumNoDamageTicks'"); - } - - @Override - public double getLastDamage() { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'getLastDamage'"); - } - - @Override - public void setLastDamage(double damage) { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'setLastDamage'"); - } - - @Override - public int getNoDamageTicks() { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'getNoDamageTicks'"); - } - - @Override - public void setNoDamageTicks(int ticks) { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'setNoDamageTicks'"); - } - - @Override - public int getNoActionTicks() { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'getNoActionTicks'"); - } - - @Override - public void setNoActionTicks(int ticks) { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'setNoActionTicks'"); - } - - @Override - public @Nullable Player getKiller() { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'getKiller'"); - } - - @Override - public void setKiller(@Nullable Player killer) { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'setKiller'"); - } - - @Override - public boolean addPotionEffect(@NotNull PotionEffect effect, boolean force) { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'addPotionEffect'"); - } - - @Override - public boolean addPotionEffects(@NotNull Collection effects) { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'addPotionEffects'"); - } - - @Override - public boolean hasPotionEffect(@NotNull PotionEffectType type) { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'hasPotionEffect'"); - } - - @Override - public @Nullable PotionEffect getPotionEffect(@NotNull PotionEffectType type) { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'getPotionEffect'"); - } - - @Override - public void removePotionEffect(@NotNull PotionEffectType type) { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'removePotionEffect'"); - } - - @Override - public @NotNull Collection getActivePotionEffects() { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'getActivePotionEffects'"); - } - - @Override - public boolean clearActivePotionEffects() { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'clearActivePotionEffects'"); - } - - @Override - public boolean hasLineOfSight(@NotNull Entity other) { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'hasLineOfSight'"); - } - - @Override - public boolean hasLineOfSight(@NotNull Location location) { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'hasLineOfSight'"); - } - - @Override - public boolean getRemoveWhenFarAway() { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'getRemoveWhenFarAway'"); - } - - @Override - public void setRemoveWhenFarAway(boolean remove) { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'setRemoveWhenFarAway'"); - } - - @Override - public void setCanPickupItems(boolean pickup) { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'setCanPickupItems'"); - } - - @Override - public boolean getCanPickupItems() { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'getCanPickupItems'"); - } - - @Override - public boolean isLeashed() { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'isLeashed'"); - } - - @Override - public @NotNull Entity getLeashHolder() throws IllegalStateException { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'getLeashHolder'"); - } - - @Override - public boolean setLeashHolder(@Nullable Entity holder) { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'setLeashHolder'"); - } - - @Override - public boolean isGliding() { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'isGliding'"); - } - - @Override - public void setGliding(boolean gliding) { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'setGliding'"); - } - - @Override - public boolean isSwimming() { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'isSwimming'"); - } - - @Override - public void setSwimming(boolean swimming) { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'setSwimming'"); - } - - @Override - public boolean isRiptiding() { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'isRiptiding'"); - } - - @Override - public void setRiptiding(boolean riptiding) { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'setRiptiding'"); - } - - @Override - public boolean isSleeping() { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'isSleeping'"); - } - - @Override - public boolean isClimbing() { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'isClimbing'"); - } - - @Override - public void setAI(boolean ai) { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'setAI'"); - } - - @Override - public boolean hasAI() { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'hasAI'"); - } - - @Override - public void attack(@NotNull Entity target) { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'attack'"); - } - - @Override - public void swingMainHand() { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'swingMainHand'"); - } - - @Override - public void swingOffHand() { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'swingOffHand'"); - } - - @Override - public void playHurtAnimation(float yaw) { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'playHurtAnimation'"); - } - - @Override - public void setCollidable(boolean collidable) { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'setCollidable'"); - } - - @Override - public boolean isCollidable() { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'isCollidable'"); - } - - @Override - public @NotNull Set getCollidableExemptions() { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'getCollidableExemptions'"); - } - - @Override - public @Nullable T getMemory(@NotNull MemoryKey memoryKey) { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'getMemory'"); - } - - @Override - public void setMemory(@NotNull MemoryKey memoryKey, @Nullable T memoryValue) { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'setMemory'"); - } - - @Override - public @Nullable Sound getHurtSound() { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'getHurtSound'"); - } - - @Override - public @Nullable Sound getDeathSound() { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'getDeathSound'"); - } - - @Override - public @NotNull Sound getFallDamageSound(int fallHeight) { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'getFallDamageSound'"); - } - - @Override - public @NotNull Sound getFallDamageSoundSmall() { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'getFallDamageSoundSmall'"); - } - - @Override - public @NotNull Sound getFallDamageSoundBig() { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'getFallDamageSoundBig'"); - } - - @Override - public @NotNull Sound getDrinkingSound(@NotNull ItemStack itemStack) { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'getDrinkingSound'"); - } - - @Override - public @NotNull Sound getEatingSound(@NotNull ItemStack itemStack) { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'getEatingSound'"); - } - - @Override - public boolean canBreatheUnderwater() { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'canBreatheUnderwater'"); - } - - @Override - public @NotNull EntityCategory getCategory() { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'getCategory'"); - } - - @Override - public float getSidewaysMovement() { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'getSidewaysMovement'"); - } - - @Override - public float getUpwardsMovement() { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'getUpwardsMovement'"); - } - - @Override - public float getForwardsMovement() { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'getForwardsMovement'"); - } - - @Override - public void startUsingItem(@NotNull EquipmentSlot hand) { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'startUsingItem'"); - } - - @Override - public void completeUsingActiveItem() { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'completeUsingActiveItem'"); - } - - @Override - public @NotNull ItemStack getActiveItem() { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'getActiveItem'"); - } - - @Override - public void clearActiveItem() { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'clearActiveItem'"); - } - - @Override - public int getActiveItemRemainingTime() { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'getActiveItemRemainingTime'"); - } - - @Override - public void setActiveItemRemainingTime(@Range(from = 0, to = 2147483647) int ticks) { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'setActiveItemRemainingTime'"); - } - - @Override - public boolean hasActiveItem() { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'hasActiveItem'"); - } - - @Override - public int getActiveItemUsedTime() { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'getActiveItemUsedTime'"); - } - - @Override - public @NotNull EquipmentSlot getActiveItemHand() { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'getActiveItemHand'"); - } - - @Override - public boolean isJumping() { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'isJumping'"); - } - - @Override - public void setJumping(boolean jumping) { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'setJumping'"); - } - - @Override - public void playPickupItemAnimation(@NotNull Item item, int quantity) { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'playPickupItemAnimation'"); - } - - @Override - public float getHurtDirection() { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'getHurtDirection'"); - } - - @Override - public void knockback(double strength, double directionX, double directionZ) { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'knockback'"); - } - - @Override - public void broadcastSlotBreak(@NotNull EquipmentSlot slot) { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'broadcastSlotBreak'"); - } - - @Override - public void broadcastSlotBreak(@NotNull EquipmentSlot slot, @NotNull Collection players) { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'broadcastSlotBreak'"); - } - - @Override - public @NotNull ItemStack damageItemStack(@NotNull ItemStack stack, int amount) { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'damageItemStack'"); - } - - @Override - public void damageItemStack(@NotNull EquipmentSlot slot, int amount) { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'damageItemStack'"); - } - - @Override - public float getBodyYaw() { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'getBodyYaw'"); - } - - @Override - public void setBodyYaw(float bodyYaw) { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'setBodyYaw'"); - } - - @Override - public boolean canUseEquipmentSlot(@NotNull EquipmentSlot slot) { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'canUseEquipmentSlot'"); - } - - @Override - public @NotNull CombatTracker getCombatTracker() { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'getCombatTracker'"); - } - - @Override - public void setWaypointStyle(@Nullable Key key) { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'setWaypointStyle'"); - } - - @Override - public void setWaypointColor(@Nullable Color color) { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'setWaypointColor'"); - } - - @Override - public @NotNull Key getWaypointStyle() { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'getWaypointStyle'"); - } - - @Override - public @Nullable Color getWaypointColor() { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'getWaypointColor'"); - } - - @Override - public @Nullable AttributeInstance getAttribute(@NotNull Attribute attribute) { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'getAttribute'"); - } - - @Override - public void registerAttribute(@NotNull Attribute attribute) { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'registerAttribute'"); - } - - - - @Override - public @NotNull T launchProjectile(@NotNull Class projectile, - @Nullable Vector velocity, @Nullable Consumer function) { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'launchProjectile'"); - } - - @Override - public TriState getFrictionState() { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'getFrictionState'"); - } - - @Override - public void setFrictionState(TriState state) { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'setFrictionState'"); - } - @Override public EntityEquipment getEquipment() { return this.equipment; @@ -859,8 +192,7 @@ public class PatchBukkitHumanEntity @Override public Inventory getEnderChest() { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'getEnderChest'"); + return this.enderChest; } @Override @@ -868,118 +200,13 @@ public class PatchBukkitHumanEntity return MainHand.RIGHT; } - - @Override - public boolean setWindowProperty(Property prop, int value) { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'setWindowProperty'"); - } - - @Override - public int getEnchantmentSeed() { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'getEnchantmentSeed'"); - } - - @Override - public void setEnchantmentSeed(int seed) { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'setEnchantmentSeed'"); - } - - @Override - public InventoryView getOpenInventory() { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'getOpenInventory'"); - } - - @Override - public @org.jspecify.annotations.Nullable InventoryView openInventory(Inventory inventory) { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'openInventory'"); - } - - @Override - public @org.jspecify.annotations.Nullable InventoryView openWorkbench( - @org.jspecify.annotations.Nullable Location location, boolean force) { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'openWorkbench'"); - } - - @Override - public @org.jspecify.annotations.Nullable InventoryView openEnchanting( - @org.jspecify.annotations.Nullable Location location, boolean force) { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'openEnchanting'"); - } - - @Override - public void openInventory(InventoryView inventory) { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'openInventory'"); - } - - @Override - public @org.jspecify.annotations.Nullable InventoryView openMerchant(Merchant merchant, boolean force) { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'openMerchant'"); - } - - @Override - public @org.jspecify.annotations.Nullable InventoryView openAnvil( - @org.jspecify.annotations.Nullable Location location, boolean force) { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'openAnvil'"); - } - - @Override - public @org.jspecify.annotations.Nullable InventoryView openCartographyTable( - @org.jspecify.annotations.Nullable Location location, boolean force) { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'openCartographyTable'"); - } - - @Override - public @org.jspecify.annotations.Nullable InventoryView openGrindstone( - @org.jspecify.annotations.Nullable Location location, boolean force) { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'openGrindstone'"); - } - - @Override - public @org.jspecify.annotations.Nullable InventoryView openLoom( - @org.jspecify.annotations.Nullable Location location, boolean force) { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'openLoom'"); - } - - @Override - public @org.jspecify.annotations.Nullable InventoryView openSmithingTable( - @org.jspecify.annotations.Nullable Location location, boolean force) { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'openSmithingTable'"); - } - - @Override - public @org.jspecify.annotations.Nullable InventoryView openStonecutter( - @org.jspecify.annotations.Nullable Location location, boolean force) { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'openStonecutter'"); - } - - @Override - public void closeInventory(Reason reason) { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'closeInventory'"); - } - @Override public ItemStack getItemInHand() { return getInventory().getItemInMainHand(); } @Override - public void setItemInHand(@org.jspecify.annotations.Nullable ItemStack item) { + public void setItemInHand(@Nullable ItemStack item) { getInventory().setItemInMainHand(item); } @@ -989,317 +216,539 @@ public class PatchBukkitHumanEntity } @Override - public void setItemOnCursor(@org.jspecify.annotations.Nullable ItemStack item) { + public void setItemOnCursor(@Nullable ItemStack item) { this.cursorItem = item != null ? item : ItemStack.empty(); } - - - @Override - public boolean hasCooldown(Material material) { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'hasCooldown'"); - } - - @Override - public int getCooldown(Material material) { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'getCooldown'"); - } - - @Override - public void setHurtDirection(float hurtDirection) { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'setHurtDirection'"); - } - - @Override - public boolean isDeeplySleeping() { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'isDeeplySleeping'"); - } - - @Override - public boolean hasCooldown(ItemStack item) { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'hasCooldown'"); - } - - public int getCooldown(ItemStack item) { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'getCooldown'"); - } - - public void setCooldown(ItemStack item, int ticks) { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'setCooldown'"); - } - - public int getCooldown(Key key) { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'getCooldown'"); - } - - public void setCooldown(Key key, int ticks) { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'setCooldown'"); - } - - @Override - public int getSleepTicks() { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'getSleepTicks'"); - } - - @Override - public @org.jspecify.annotations.Nullable Location getPotentialRespawnLocation() { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'getPotentialRespawnLocation'"); - } - - @Override - public @org.jspecify.annotations.Nullable FishHook getFishHook() { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'getFishHook'"); - } - - @Override - public boolean sleep(Location location, boolean force) { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'sleep'"); - } - - @Override - public void wakeup(boolean setSpawnLocation) { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'wakeup'"); - } - - @Override - public void startRiptideAttack(int duration, float attackStrength, - @org.jspecify.annotations.Nullable ItemStack attackItem) { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'startRiptideAttack'"); - } - - @Override - public Location getBedLocation() { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'getBedLocation'"); - } - @Override public GameMode getGameMode() { - var resp = NativeBridgeFfi.getGamemode(BridgeUtils.convertUuid(this.getUniqueId())); - if (resp != null) { - return GameMode.getByValue(resp.getGamemode()); - } + try { + var resp = NativeBridgeFfi.getGamemode(BridgeUtils.convertUuid(this.getUniqueId())); + if (resp != null) { + return GameMode.getByValue(resp.getGamemode()); + } + } catch (Throwable ignored) {} return GameMode.SURVIVAL; } @Override public void setGameMode(GameMode mode) { if (mode == null) return; - var request = patchbukkit.entity.SetGamemodeRequest.newBuilder() - .setUuid(BridgeUtils.convertUuid(this.getUniqueId())) - .setGamemode(mode.getValue()) - .build(); - NativeBridgeFfi.setGamemode(request); + try { + var request = SetGamemodeRequest.newBuilder() + .setUuid(BridgeUtils.convertUuid(this.getUniqueId())) + .setGamemode(mode.getValue()) + .build(); + NativeBridgeFfi.setGamemode(request); + } catch (Throwable ignored) {} } @Override - public boolean isBlocking() { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'isBlocking'"); + public boolean isSneaking() { + try { + var resp = NativeBridgeFfi.getPlayerPoseState(BridgeUtils.convertUuid(getUniqueId())); + if (resp != null) { + return resp.getIsSneaking(); + } + } catch (Throwable ignored) {} + return false; } @Override - public boolean isHandRaised() { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'isHandRaised'"); + public void setSneaking(boolean sneaking) { + try { + var req = SetSneakingRequest.newBuilder() + .setUuid(BridgeUtils.convertUuid(getUniqueId())) + .setSneaking(sneaking) + .build(); + NativeBridgeFfi.setSneaking(req); + } catch (Throwable ignored) {} + } + + public boolean isSprinting() { + try { + var resp = NativeBridgeFfi.getPlayerPoseState(BridgeUtils.convertUuid(getUniqueId())); + if (resp != null) { + return resp.getIsSprinting(); + } + } catch (Throwable ignored) {} + return false; + } + + public void setSprinting(boolean sprinting) { + try { + var req = SetSprintingRequest.newBuilder() + .setUuid(BridgeUtils.convertUuid(getUniqueId())) + .setSprinting(sprinting) + .build(); + NativeBridgeFfi.setSprinting(req); + } catch (Throwable ignored) {} } @Override - public int getExpToLevel() { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'getExpToLevel'"); + public boolean isGliding() { + try { + var resp = NativeBridgeFfi.getPlayerPoseState(BridgeUtils.convertUuid(getUniqueId())); + if (resp != null) { + return resp.getIsGliding(); + } + } catch (Throwable ignored) {} + return false; } @Override - public @org.jspecify.annotations.Nullable Entity releaseLeftShoulderEntity() { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'releaseLeftShoulderEntity'"); + public void setGliding(boolean gliding) { } @Override - public @org.jspecify.annotations.Nullable Entity releaseRightShoulderEntity() { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'releaseRightShoulderEntity'"); + public boolean isSwimming() { + try { + var resp = NativeBridgeFfi.getPlayerPoseState(BridgeUtils.convertUuid(getUniqueId())); + if (resp != null) { + return resp.getIsSwimming(); + } + } catch (Throwable ignored) {} + return false; } @Override - public float getAttackCooldown() { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'getAttackCooldown'"); - } - - - - @Override - public boolean hasDiscoveredRecipe(NamespacedKey recipe) { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'hasDiscoveredRecipe'"); + public void setSwimming(boolean swimming) { } @Override - public Set getDiscoveredRecipes() { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'getDiscoveredRecipes'"); + public boolean isSleeping() { + try { + var resp = NativeBridgeFfi.getPlayerPoseState(BridgeUtils.convertUuid(getUniqueId())); + if (resp != null) { + return resp.getIsSleeping(); + } + } catch (Throwable ignored) {} + return false; } @Override - public @org.jspecify.annotations.Nullable Entity getShoulderEntityLeft() { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'getShoulderEntityLeft'"); + public boolean isDeeplySleeping() { + return isSleeping() && this.sleepTicks >= 100; } @Override - public void setShoulderEntityLeft(@org.jspecify.annotations.Nullable Entity entity) { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'setShoulderEntityLeft'"); + public int getSleepTicks() { + return this.sleepTicks; } @Override - public @org.jspecify.annotations.Nullable Entity getShoulderEntityRight() { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'getShoulderEntityRight'"); + public boolean sleep(Location location, boolean force) { + if (location == null) return false; + this.bedLocation = location.clone(); + return true; } @Override - public void setShoulderEntityRight(@org.jspecify.annotations.Nullable Entity entity) { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'setShoulderEntityRight'"); + public void wakeup(boolean setSpawnLocation) { + if (setSpawnLocation && this.bedLocation != null) { + setPotentialRespawnLocation(this.bedLocation); + } + this.bedLocation = null; + this.sleepTicks = 0; } @Override - public void openSign(Sign sign, Side side) { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'openSign'"); + public Location getBedLocation() { + return this.bedLocation != null ? this.bedLocation.clone() : null; } @Override - public boolean dropItem(boolean dropAll) { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'dropItem'"); + public @Nullable Location getPotentialRespawnLocation() { + return this.bedLocation != null ? this.bedLocation.clone() : null; } - @Override - public @org.jspecify.annotations.Nullable Item dropItem(int slot, int amount, boolean throwRandomly, - @org.jspecify.annotations.Nullable Consumer entityOperation) { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'dropItem'"); - } - - @Override - public @org.jspecify.annotations.Nullable Item dropItem(EquipmentSlot slot, int amount, boolean throwRandomly, - @org.jspecify.annotations.Nullable Consumer entityOperation) { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'dropItem'"); - } - - @Override - public @org.jspecify.annotations.Nullable Item dropItem(ItemStack itemStack, boolean throwRandomly, - @org.jspecify.annotations.Nullable Consumer entityOperation) { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'dropItem'"); - } - - @Override - public float getExhaustion() { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'getExhaustion'"); - } - - @Override - public void setExhaustion(float value) { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'setExhaustion'"); - } - - @Override - public float getSaturation() { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'getSaturation'"); - } - - @Override - public void setSaturation(float value) { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'setSaturation'"); + public void setPotentialRespawnLocation(Location location) { + this.bedLocation = location != null ? location.clone() : null; } @Override public int getFoodLevel() { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'getFoodLevel'"); + try { + var resp = NativeBridgeFfi.getFoodLevel(BridgeUtils.convertUuid(getUniqueId())); + if (resp != null) { + return resp.getFoodLevel(); + } + } catch (Throwable ignored) {} + return 20; } @Override public void setFoodLevel(int value) { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'setFoodLevel'"); + try { + var req = SetFoodLevelRequest.newBuilder() + .setUuid(BridgeUtils.convertUuid(getUniqueId())) + .setFoodLevel(value) + .build(); + NativeBridgeFfi.setFoodLevel(req); + } catch (Throwable ignored) {} + } + + @Override + public float getSaturation() { + try { + var resp = NativeBridgeFfi.getFoodLevel(BridgeUtils.convertUuid(getUniqueId())); + if (resp != null) { + return resp.getSaturation(); + } + } catch (Throwable ignored) {} + return 5.0f; + } + + @Override + public void setSaturation(float value) { + try { + var req = SetSaturationRequest.newBuilder() + .setUuid(BridgeUtils.convertUuid(getUniqueId())) + .setSaturation(value) + .build(); + NativeBridgeFfi.setSaturation(req); + } catch (Throwable ignored) {} + } + + @Override + public float getExhaustion() { + try { + var resp = NativeBridgeFfi.getFoodLevel(BridgeUtils.convertUuid(getUniqueId())); + if (resp != null) { + return resp.getExhaustion(); + } + } catch (Throwable ignored) {} + return 0.0f; + } + + @Override + public void setExhaustion(float value) { + try { + var req = SetExhaustionRequest.newBuilder() + .setUuid(BridgeUtils.convertUuid(getUniqueId())) + .setExhaustion(value) + .build(); + NativeBridgeFfi.setExhaustion(req); + } catch (Throwable ignored) {} } @Override public int getSaturatedRegenRate() { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'getSaturatedRegenRate'"); + return this.saturatedRegenRate; } @Override public void setSaturatedRegenRate(int ticks) { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'setSaturatedRegenRate'"); + this.saturatedRegenRate = ticks; } @Override public int getUnsaturatedRegenRate() { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'getUnsaturatedRegenRate'"); + return this.unsaturatedRegenRate; } @Override public void setUnsaturatedRegenRate(int ticks) { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'setUnsaturatedRegenRate'"); + this.unsaturatedRegenRate = ticks; } @Override public int getStarvationRate() { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'getStarvationRate'"); + return this.starvationRate; } @Override public void setStarvationRate(int ticks) { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'setStarvationRate'"); + this.starvationRate = ticks; } @Override - public @org.jspecify.annotations.Nullable Location getLastDeathLocation() { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'getLastDeathLocation'"); + public @Nullable Location getLastDeathLocation() { + return this.lastDeathLocation != null ? this.lastDeathLocation.clone() : null; } @Override - public void setLastDeathLocation(@org.jspecify.annotations.Nullable Location location) { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'setLastDeathLocation'"); + public void setLastDeathLocation(@Nullable Location location) { + this.lastDeathLocation = location != null ? location.clone() : null; } @Override - public @org.jspecify.annotations.Nullable Firework fireworkBoost(ItemStack fireworkItemStack) { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'fireworkBoost'"); - }} + public boolean hasCooldown(Material material) { + if (material == null) return false; + try { + var req = GetCooldownRequest.newBuilder() + .setUuid(BridgeUtils.convertUuid(getUniqueId())) + .setItemGroup(material.name().toLowerCase()) + .build(); + var resp = NativeBridgeFfi.getCooldown(req); + if (resp != null) return resp.getIsOnCooldown(); + } catch (Throwable ignored) {} + return this.cooldowns.getOrDefault(material, 0) > 0; + } + + @Override + public int getCooldown(Material material) { + if (material == null) return 0; + try { + var req = GetCooldownRequest.newBuilder() + .setUuid(BridgeUtils.convertUuid(getUniqueId())) + .setItemGroup(material.name().toLowerCase()) + .build(); + var resp = NativeBridgeFfi.getCooldown(req); + if (resp != null) return (int) (resp.getCooldown() * 100); + } catch (Throwable ignored) {} + return this.cooldowns.getOrDefault(material, 0); + } + + @Override + public void setCooldown(Material material, int ticks) { + if (material == null) return; + this.cooldowns.put(material, ticks); + try { + var req = SetCooldownRequest.newBuilder() + .setUuid(BridgeUtils.convertUuid(getUniqueId())) + .setItemGroup(material.name().toLowerCase()) + .setDurationTicks(ticks) + .build(); + NativeBridgeFfi.setCooldown(req); + } catch (Throwable ignored) {} + } + + @Override + public boolean hasCooldown(ItemStack item) { + return item != null && hasCooldown(item.getType()); + } + + public int getCooldown(ItemStack item) { + return item != null ? getCooldown(item.getType()) : 0; + } + + public void setCooldown(ItemStack item, int ticks) { + if (item != null) setCooldown(item.getType(), ticks); + } + + public int getCooldown(Key key) { + if (key == null) return 0; + Material mat = Material.matchMaterial(key.asString()); + return mat != null ? getCooldown(mat) : 0; + } + + public void setCooldown(Key key, int ticks) { + if (key == null) return; + Material mat = Material.matchMaterial(key.asString()); + if (mat != null) setCooldown(mat, ticks); + } + + @Override + public InventoryView getOpenInventory() { + return this.openInventoryView; + } + + @Override + public @Nullable InventoryView openInventory(Inventory inventory) { + if (inventory == null) return null; + if (this.openInventoryView != null) { + closeInventory(Reason.OPEN_NEW); + } + this.openInventoryView = new org.patchbukkit.inventory.PatchBukkitInventoryView(this, inventory); + try { + NativeBridgeFfi.openEnderChest(BridgeUtils.convertUuid(getUniqueId())); + } catch (Throwable ignored) {} + return this.openInventoryView; + } + + @Override + public void openInventory(InventoryView inventory) { + if (inventory != null) { + this.openInventoryView = inventory; + } + } + + @Override + public @Nullable InventoryView openWorkbench(@Nullable Location location, boolean force) { + Inventory inv = PatchBukkitServer.getInstance().createInventory(this, org.bukkit.event.inventory.InventoryType.WORKBENCH); + return openInventory(inv); + } + + @Override + public @Nullable InventoryView openEnchanting(@Nullable Location location, boolean force) { + Inventory inv = PatchBukkitServer.getInstance().createInventory(this, org.bukkit.event.inventory.InventoryType.ENCHANTING); + return openInventory(inv); + } + + @Override + public @Nullable InventoryView openMerchant(Merchant merchant, boolean force) { + Inventory inv = PatchBukkitServer.getInstance().createInventory(this, org.bukkit.event.inventory.InventoryType.MERCHANT); + return openInventory(inv); + } + + @Override + public @Nullable InventoryView openAnvil(@Nullable Location location, boolean force) { + Inventory inv = PatchBukkitServer.getInstance().createInventory(this, org.bukkit.event.inventory.InventoryType.ANVIL); + return openInventory(inv); + } + + @Override + public @Nullable InventoryView openCartographyTable(@Nullable Location location, boolean force) { + Inventory inv = PatchBukkitServer.getInstance().createInventory(this, org.bukkit.event.inventory.InventoryType.CARTOGRAPHY); + return openInventory(inv); + } + + @Override + public @Nullable InventoryView openGrindstone(@Nullable Location location, boolean force) { + Inventory inv = PatchBukkitServer.getInstance().createInventory(this, org.bukkit.event.inventory.InventoryType.GRINDSTONE); + return openInventory(inv); + } + + @Override + public @Nullable InventoryView openLoom(@Nullable Location location, boolean force) { + Inventory inv = PatchBukkitServer.getInstance().createInventory(this, org.bukkit.event.inventory.InventoryType.LOOM); + return openInventory(inv); + } + + @Override + public @Nullable InventoryView openSmithingTable(@Nullable Location location, boolean force) { + Inventory inv = PatchBukkitServer.getInstance().createInventory(this, org.bukkit.event.inventory.InventoryType.SMITHING); + return openInventory(inv); + } + + @Override + public @Nullable InventoryView openStonecutter(@Nullable Location location, boolean force) { + Inventory inv = PatchBukkitServer.getInstance().createInventory(this, org.bukkit.event.inventory.InventoryType.STONECUTTER); + return openInventory(inv); + } + + @Override + public void closeInventory(Reason reason) { + this.openInventoryView = null; + } + + @Override + public void closeInventory() { + closeInventory(Reason.PLUGIN); + } + + @Override + public boolean setWindowProperty(Property prop, int value) { + return false; + } + + @Override + public int getEnchantmentSeed() { + return this.enchantmentSeed; + } + + @Override + public void setEnchantmentSeed(int seed) { + this.enchantmentSeed = seed; + } + + @Override + public boolean isBlocking() { + return false; + } + + @Override + public boolean isHandRaised() { + return false; + } + + @Override + public int getExpToLevel() { + return 0; + } + + @Override + public float getAttackCooldown() { + return 1.0f; + } + + @Override + public @Nullable Entity getShoulderEntityLeft() { + return this.shoulderEntityLeft; + } + + @Override + public void setShoulderEntityLeft(@Nullable Entity entity) { + this.shoulderEntityLeft = entity; + } + + @Override + public @Nullable Entity getShoulderEntityRight() { + return this.shoulderEntityRight; + } + + @Override + public void setShoulderEntityRight(@Nullable Entity entity) { + this.shoulderEntityRight = entity; + } + + @Override + public @Nullable Entity releaseLeftShoulderEntity() { + Entity e = this.shoulderEntityLeft; + this.shoulderEntityLeft = null; + return e; + } + + @Override + public @Nullable Entity releaseRightShoulderEntity() { + Entity e = this.shoulderEntityRight; + this.shoulderEntityRight = null; + return e; + } + + @Override + public void openSign(Sign sign, Side side) { + } + + @Override + public boolean dropItem(boolean dropAll) { + ItemStack item = getItemInHand(); + if (item.isEmpty()) return false; + setItemInHand(null); + getWorld().dropItem(getLocation(), item); + return true; + } + + @Override + public @Nullable Item dropItem(int slot, int amount, boolean throwRandomly, @Nullable Consumer entityOperation) { + ItemStack item = getInventory().getItem(slot); + if (item == null || item.isEmpty()) return null; + ItemStack toDrop = item.clone(); + toDrop.setAmount(Math.min(amount, item.getAmount())); + item.setAmount(item.getAmount() - toDrop.getAmount()); + getInventory().setItem(slot, item.isEmpty() ? null : item); + return getWorld().dropItem(getLocation(), toDrop, entityOperation); + } + + @Override + public @Nullable Item dropItem(EquipmentSlot slot, int amount, boolean throwRandomly, @Nullable Consumer entityOperation) { + ItemStack item = getInventory().getItem(slot); + if (item == null || item.isEmpty()) return null; + ItemStack toDrop = item.clone(); + toDrop.setAmount(Math.min(amount, item.getAmount())); + item.setAmount(item.getAmount() - toDrop.getAmount()); + getInventory().setItem(slot, item.isEmpty() ? null : item); + return getWorld().dropItem(getLocation(), toDrop, entityOperation); + } + + @Override + public @Nullable Item dropItem(ItemStack itemStack, boolean throwRandomly, @Nullable Consumer entityOperation) { + if (itemStack == null || itemStack.isEmpty()) return null; + return getWorld().dropItem(getLocation(), itemStack, entityOperation); + } + + @Override + public @Nullable Firework fireworkBoost(ItemStack fireworkItemStack) { + return null; + } + + @Override + public @Nullable FishHook getFishHook() { + return null; + } + + @Override + public void startRiptideAttack(int duration, float attackStrength, @Nullable ItemStack attackItem) { + } +} diff --git a/java/patchbukkit/src/main/java/org/patchbukkit/entity/PatchBukkitLivingEntity.java b/java/patchbukkit/src/main/java/org/patchbukkit/entity/PatchBukkitLivingEntity.java index 1bab366..48bfc27 100644 --- a/java/patchbukkit/src/main/java/org/patchbukkit/entity/PatchBukkitLivingEntity.java +++ b/java/patchbukkit/src/main/java/org/patchbukkit/entity/PatchBukkitLivingEntity.java @@ -4,6 +4,7 @@ import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.HashMap; +import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; @@ -474,387 +475,421 @@ public class PatchBukkitLivingEntity 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 collidableExemptions = new HashSet<>(); + private final Map, 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 public boolean hasLineOfSight(@NotNull Entity other) { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'hasLineOfSight'"); + if (other == null || other.getWorld() != getWorld()) return false; + RayTraceResult result = getWorld().rayTraceBlocks(getEyeLocation(), other.getLocation().toVector().subtract(getEyeLocation().toVector()), getEyeLocation().distance(other.getLocation()), FluidCollisionMode.NEVER); + return result == null || result.getHitBlock() == null; } @Override public boolean hasLineOfSight(@NotNull Location location) { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'hasLineOfSight'"); + if (location == null || location.getWorld() != getWorld()) return false; + RayTraceResult result = getWorld().rayTraceBlocks(getEyeLocation(), location.toVector().subtract(getEyeLocation().toVector()), getEyeLocation().distance(location), FluidCollisionMode.NEVER); + return result == null || result.getHitBlock() == null; } @Override public boolean getRemoveWhenFarAway() { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'getRemoveWhenFarAway'"); + return this.removeWhenFarAway; } @Override public void setRemoveWhenFarAway(boolean remove) { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'setRemoveWhenFarAway'"); + this.removeWhenFarAway = remove; } @Override public @Nullable EntityEquipment getEquipment() { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'getEquipment'"); + return null; } @Override public void setCanPickupItems(boolean pickup) { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'setCanPickupItems'"); + this.canPickupItems = pickup; } @Override public boolean getCanPickupItems() { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'getCanPickupItems'"); + return this.canPickupItems; } @Override public boolean isLeashed() { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'isLeashed'"); + return this.leashed; } @Override public @NotNull Entity getLeashHolder() throws IllegalStateException { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'getLeashHolder'"); + if (!this.leashed || this.leashHolder == null) { + throw new IllegalStateException("Entity is not leashed"); + } + return this.leashHolder; } @Override public boolean setLeashHolder(@Nullable Entity holder) { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'setLeashHolder'"); + this.leashHolder = holder; + this.leashed = holder != null; + return true; } @Override public boolean isGliding() { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'isGliding'"); + return this.gliding; } @Override public void setGliding(boolean gliding) { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'setGliding'"); + this.gliding = gliding; } @Override public boolean isSwimming() { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'isSwimming'"); + return this.swimming; } @Override public void setSwimming(boolean swimming) { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'setSwimming'"); + this.swimming = swimming; } @Override public boolean isRiptiding() { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'isRiptiding'"); + return this.riptiding; } @Override public void setRiptiding(boolean riptiding) { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'setRiptiding'"); + this.riptiding = riptiding; } @Override public boolean isSleeping() { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'isSleeping'"); + return this.sleeping; } @Override public boolean isClimbing() { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'isClimbing'"); + return this.climbing; } @Override public void setAI(boolean ai) { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'setAI'"); + this.hasAI = ai; } @Override public boolean hasAI() { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'hasAI'"); + return this.hasAI; } @Override public void attack(@NotNull Entity target) { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'attack'"); + if (target instanceof LivingEntity living) { + living.damage(1.0, this); + } } @Override public void swingMainHand() { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'swingMainHand'"); } @Override public void swingOffHand() { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'swingOffHand'"); } @Override public void playHurtAnimation(float yaw) { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'playHurtAnimation'"); } @Override public void setCollidable(boolean collidable) { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'setCollidable'"); + this.collidable = collidable; } @Override public boolean isCollidable() { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'isCollidable'"); + return this.collidable; } @Override public @NotNull Set getCollidableExemptions() { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'getCollidableExemptions'"); + return this.collidableExemptions; } @Override public @Nullable T getMemory(@NotNull MemoryKey memoryKey) { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'getMemory'"); + return (T) this.memories.get(memoryKey); } @Override public void setMemory(@NotNull MemoryKey memoryKey, @Nullable T memoryValue) { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'setMemory'"); + if (memoryValue == null) { + this.memories.remove(memoryKey); + } else { + this.memories.put(memoryKey, memoryValue); + } } @Override public @Nullable Sound getHurtSound() { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'getHurtSound'"); + return Sound.ENTITY_GENERIC_HURT; } @Override public @Nullable Sound getDeathSound() { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'getDeathSound'"); + return Sound.ENTITY_GENERIC_DEATH; } @Override public @NotNull Sound getFallDamageSound(int fallHeight) { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'getFallDamageSound'"); + return fallHeight > 4 ? Sound.ENTITY_GENERIC_BIG_FALL : Sound.ENTITY_GENERIC_SMALL_FALL; } @Override public @NotNull Sound getFallDamageSoundSmall() { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'getFallDamageSoundSmall'"); + return Sound.ENTITY_GENERIC_SMALL_FALL; } @Override public @NotNull Sound getFallDamageSoundBig() { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'getFallDamageSoundBig'"); + return Sound.ENTITY_GENERIC_BIG_FALL; } @Override public @NotNull Sound getDrinkingSound(@NotNull ItemStack itemStack) { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'getDrinkingSound'"); + return Sound.ENTITY_GENERIC_DRINK; } @Override public @NotNull Sound getEatingSound(@NotNull ItemStack itemStack) { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'getEatingSound'"); + return Sound.ENTITY_GENERIC_EAT; } @Override public boolean canBreatheUnderwater() { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'canBreatheUnderwater'"); + return hasPotionEffect(PotionEffectType.WATER_BREATHING); } @Override public @NotNull EntityCategory getCategory() { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'getCategory'"); + return EntityCategory.NONE; } @Override public float getSidewaysMovement() { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'getSidewaysMovement'"); + return 0.0f; } @Override public float getUpwardsMovement() { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'getUpwardsMovement'"); + return 0.0f; } @Override public float getForwardsMovement() { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'getForwardsMovement'"); + return 0.0f; } @Override public void startUsingItem(@NotNull EquipmentSlot hand) { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'startUsingItem'"); + this.activeItemHand = hand; + this.activeItemRemainingTime = 72000; + this.activeItemUsedTime = 0; } @Override public void completeUsingActiveItem() { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'completeUsingActiveItem'"); + this.activeItemRemainingTime = 0; + this.activeItem = ItemStack.empty(); } @Override public @NotNull ItemStack getActiveItem() { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'getActiveItem'"); + return this.activeItem != null ? this.activeItem : ItemStack.empty(); } @Override public void clearActiveItem() { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'clearActiveItem'"); + this.activeItem = ItemStack.empty(); + this.activeItemRemainingTime = 0; + this.activeItemUsedTime = 0; } @Override public int getActiveItemRemainingTime() { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'getActiveItemRemainingTime'"); + return this.activeItemRemainingTime; } @Override public void setActiveItemRemainingTime(@Range(from = 0, to = 2147483647) int ticks) { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'setActiveItemRemainingTime'"); + this.activeItemRemainingTime = ticks; } @Override public boolean hasActiveItem() { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'hasActiveItem'"); + return this.activeItemRemainingTime > 0; } @Override public int getActiveItemUsedTime() { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'getActiveItemUsedTime'"); + return this.activeItemUsedTime; } @Override public @NotNull EquipmentSlot getActiveItemHand() { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'getActiveItemHand'"); + return this.activeItemHand != null ? this.activeItemHand : EquipmentSlot.HAND; } @Override public boolean isJumping() { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'isJumping'"); + return this.jumping; } @Override public void setJumping(boolean jumping) { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'setJumping'"); + this.jumping = jumping; } @Override public void playPickupItemAnimation(@NotNull Item item, int quantity) { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'playPickupItemAnimation'"); } @Override public float getHurtDirection() { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'getHurtDirection'"); + return this.hurtDirection; } @Override public void setHurtDirection(float hurtDirection) { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'setHurtDirection'"); + this.hurtDirection = hurtDirection; } @Override public void knockback(double strength, double directionX, double directionZ) { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'knockback'"); + Vector vel = getVelocity(); + vel.setX(vel.getX() + directionX * strength); + vel.setZ(vel.getZ() + directionZ * strength); + setVelocity(vel); } @Override public void broadcastSlotBreak(@NotNull EquipmentSlot slot) { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'broadcastSlotBreak'"); } @Override public void broadcastSlotBreak(@NotNull EquipmentSlot slot, @NotNull Collection players) { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'broadcastSlotBreak'"); } @Override public @NotNull ItemStack damageItemStack(@NotNull ItemStack stack, int amount) { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'damageItemStack'"); + if (stack == null || stack.isEmpty()) return ItemStack.empty(); + return stack; } @Override public void damageItemStack(@NotNull EquipmentSlot slot, int amount) { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'damageItemStack'"); } @Override public float getBodyYaw() { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'getBodyYaw'"); + return this.bodyYaw; } @Override public void setBodyYaw(float bodyYaw) { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'setBodyYaw'"); + this.bodyYaw = bodyYaw; } @Override public boolean canUseEquipmentSlot(@NotNull EquipmentSlot slot) { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'canUseEquipmentSlot'"); + return true; } - public @NotNull CombatTracker getCombatTracker() { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'getCombatTracker'"); + @Override + public @NotNull io.papermc.paper.world.damagesource.CombatTracker getCombatTracker() { + return new io.papermc.paper.world.damagesource.CombatTracker() { + @Override + public org.bukkit.entity.LivingEntity getEntity() { + return PatchBukkitLivingEntity.this; + } + + @Override + public java.util.List getCombatEntries() { + return Collections.emptyList(); + } + + @Override + public void setCombatEntries(java.util.List 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 @@ -867,41 +902,33 @@ public class PatchBukkitLivingEntity } public void setWaypointStyle(@Nullable Key key) { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'setWaypointStyle'"); + this.waypointStyle = key; } public void setWaypointColor(@Nullable Color color) { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'setWaypointColor'"); + this.waypointColor = color; } public @NotNull Key getWaypointStyle() { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'getWaypointStyle'"); + return this.waypointStyle != null ? this.waypointStyle : Key.key("minecraft", "default"); } public @Nullable Color getWaypointColor() { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'getWaypointColor'"); + return this.waypointColor; } public void kill(DamageSource damageSource) { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'kill'"); + setHealth(0.0); } public float getSoundVolume() { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'getSoundVolume'"); + return 1.0f; } public float getSoundPitch() { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'getSoundPitch'"); + return 1.0f; } public @Nullable Sound getHurtSound(@NotNull DamageSource damageSource) { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'getHurtSound'"); + return Sound.ENTITY_GENERIC_HURT; }} diff --git a/java/patchbukkit/src/main/java/org/patchbukkit/entity/PatchBukkitMobGoals.java b/java/patchbukkit/src/main/java/org/patchbukkit/entity/PatchBukkitMobGoals.java new file mode 100644 index 0000000..fc56baa --- /dev/null +++ b/java/patchbukkit/src/main/java/org/patchbukkit/entity/PatchBukkitMobGoals.java @@ -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 void addGoal(@NotNull T mob, int priority, @NotNull Goal goal) {} + + @Override + public void removeGoal(@NotNull T mob, @NotNull Goal goal) {} + + @Override + public void removeAllGoals(@NotNull T mob) {} + + @Override + public void removeAllGoals(@NotNull T mob, @NotNull GoalType type) {} + + @Override + public void removeGoal(@NotNull T mob, @NotNull GoalKey key) {} + + @Override + public boolean hasGoal(@NotNull T mob, @NotNull GoalKey key) { + return false; + } + + @Override + public @Nullable Goal getGoal(@NotNull T mob, @NotNull GoalKey key) { + return null; + } + + @Override + public @NotNull Collection> getGoals(@NotNull T mob, @NotNull GoalKey key) { + return Collections.emptyList(); + } + + @Override + public @NotNull Collection> getAllGoals(@NotNull T mob) { + return Collections.emptyList(); + } + + @Override + public @NotNull Collection> getAllGoals(@NotNull T mob, @NotNull GoalType type) { + return Collections.emptyList(); + } + + @Override + public @NotNull Collection> getAllGoalsWithout(@NotNull T mob, @NotNull GoalType type) { + return Collections.emptyList(); + } + + @Override + public @NotNull Collection> getRunningGoals(@NotNull T mob) { + return Collections.emptyList(); + } + + @Override + public @NotNull Collection> getRunningGoals(@NotNull T mob, @NotNull GoalType type) { + return Collections.emptyList(); + } + + @Override + public @NotNull Collection> getRunningGoalsWithout(@NotNull T mob, @NotNull GoalType type) { + return Collections.emptyList(); + } +} diff --git a/java/patchbukkit/src/main/java/org/patchbukkit/entity/PatchBukkitOfflinePlayer.java b/java/patchbukkit/src/main/java/org/patchbukkit/entity/PatchBukkitOfflinePlayer.java index 7e84785..f358593 100644 --- a/java/patchbukkit/src/main/java/org/patchbukkit/entity/PatchBukkitOfflinePlayer.java +++ b/java/patchbukkit/src/main/java/org/patchbukkit/entity/PatchBukkitOfflinePlayer.java @@ -20,6 +20,14 @@ public class PatchBukkitOfflinePlayer implements OfflinePlayer { this.name = name; } + public PatchBukkitOfflinePlayer(UUID uuid) { + this(uuid, null); + } + + public PatchBukkitOfflinePlayer(String name) { + this(null, name); + } + @Override public boolean isOnline() { return getPlayer() != null; diff --git a/java/patchbukkit/src/main/java/org/patchbukkit/entity/PatchBukkitPlayer.java b/java/patchbukkit/src/main/java/org/patchbukkit/entity/PatchBukkitPlayer.java index fc0d29a..ae6ece7 100644 --- a/java/patchbukkit/src/main/java/org/patchbukkit/entity/PatchBukkitPlayer.java +++ b/java/patchbukkit/src/main/java/org/patchbukkit/entity/PatchBukkitPlayer.java @@ -1,26 +1,33 @@ package org.patchbukkit.entity; -import java.lang.ref.WeakReference; import java.net.InetAddress; import java.net.InetSocketAddress; import java.time.Duration; import java.time.Instant; -import java.util.*; +import java.util.Collection; +import java.util.Collections; +import java.util.Date; +import java.util.EnumMap; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Locale; +import java.util.Map; +import java.util.Set; +import java.util.UUID; import java.util.concurrent.CompletableFuture; import org.bukkit.BanEntry; -import org.bukkit.Chunk; +import org.bukkit.BanList; import org.bukkit.DyeColor; import org.bukkit.Effect; -import org.bukkit.EntityEffect; -import org.bukkit.GameMode; -import org.bukkit.Input; import org.bukkit.Instrument; import org.bukkit.Location; import org.bukkit.Material; import org.bukkit.NamespacedKey; import org.bukkit.Note; import org.bukkit.Particle; +import org.bukkit.GameMode; import org.bukkit.ServerLinks; import org.bukkit.Sound; import org.bukkit.SoundCategory; @@ -31,6 +38,7 @@ import org.bukkit.advancement.Advancement; import org.bukkit.advancement.AdvancementProgress; import org.bukkit.block.Block; import org.bukkit.block.BlockState; +import org.bukkit.block.Sign; import org.bukkit.block.TileState; import org.bukkit.block.data.BlockData; import org.bukkit.block.sign.Side; @@ -47,1788 +55,2201 @@ import org.bukkit.inventory.EquipmentSlot; import org.bukkit.inventory.ItemStack; import org.bukkit.map.MapView; import org.bukkit.plugin.Plugin; -import org.bukkit.potion.PotionEffect; -import org.bukkit.potion.PotionEffectType; +import org.bukkit.plugin.messaging.StandardMessenger; +import org.bukkit.profile.PlayerProfile; import org.bukkit.scoreboard.Scoreboard; import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Range; -import org.jetbrains.annotations.Unmodifiable; -import org.jetbrains.annotations.UnmodifiableView; +import org.jetbrains.annotations.Nullable; +import org.patchbukkit.PatchBukkitServer; import org.patchbukkit.bridge.BridgeUtils; -import org.patchbukkit.registry.PatchBukkitSound; -import com.destroystokyo.paper.ClientOption; import com.destroystokyo.paper.Title; -import com.destroystokyo.paper.profile.PlayerProfile; -import com.google.common.base.Preconditions; -import io.papermc.paper.connection.PlayerGameConnection; -import io.papermc.paper.entity.LookAnchor; -import io.papermc.paper.entity.PlayerGiveResult; import io.papermc.paper.math.Position; import net.kyori.adventure.bossbar.BossBar; import net.kyori.adventure.text.Component; +import net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer; import net.kyori.adventure.util.TriState; import net.md_5.bungee.api.chat.BaseComponent; import patchbukkit.bridge.NativeBridgeFfi; -import patchbukkit.message.SendMessageRequest; -import patchbukkit.abilities.SetAbilitiesRequest; -import patchbukkit.sound.PlayerEntityPlaySoundRequest; -import patchbukkit.sound.PlayerPlaySoundRequest; +import patchbukkit.entity.KickPlayerRequest; +import patchbukkit.entity.SendActionBarRequest; +import patchbukkit.entity.SendBlockChangeRequest; +import patchbukkit.entity.SendGameEventRequest; +import patchbukkit.entity.SendResourcePackRequest; +import patchbukkit.entity.SendTitleRequest; +import patchbukkit.entity.SetCompassTargetRequest; +import patchbukkit.entity.SetDisplayNameRequest; +import patchbukkit.entity.SetExperienceRequest; +import patchbukkit.entity.SetPlayerListHeaderFooterRequest; +import patchbukkit.entity.SetPlayerListNameRequest; +import patchbukkit.entity.SetPlayerTimeRequest; +import patchbukkit.entity.SetPlayerWeatherRequest; +import patchbukkit.entity.SetRespawnPointRequest; +import patchbukkit.entity.StopSoundRequest; -@SuppressWarnings({ "deprecation", "removal", "unchecked" }) -public class PatchBukkitPlayer - extends PatchBukkitHumanEntity - implements Player { +@SuppressWarnings({ "deprecation", "removal" }) +public class PatchBukkitPlayer extends PatchBukkitHumanEntity implements Player { - private final Map>> invertedVisibilityEntities = new HashMap<>(); + private String displayName; + private String playerListName; + private String playerListHeader = ""; + private String playerListFooter = ""; + private int playerListOrder = 0; + private long playerTime = 0; + private boolean playerTimeRelative = true; + private WeatherType playerWeather = null; + private Location compassTarget; + private double healthScale = 20.0; + private boolean healthScaled = false; + private boolean affectsSpawning = true; + private int viewDistance = -1; + private int simulationDistance = -1; + private int sendViewDistance = -1; + private Scoreboard scoreboard; + private final Set bossBars = new HashSet<>(); + private final Set listeningChannels = new HashSet<>(); + private final Set hiddenPlayers = new HashSet<>(); + private final Map> hiddenPlayersPlugins = new HashMap<>(); + private final Map statistics = new EnumMap<>(Statistic.class); + private final Map> materialStatistics = new EnumMap<>(Statistic.class); + private final Map> entityStatistics = new EnumMap<>(Statistic.class); + private final Player.Spigot spigot; + private long firstPlayed; + private long lastLogin; + private int wardenWarningLevel = 0; + private int wardenWarningCooldown = 0; + private int wardenTimeSinceLastWarning = 0; + private boolean hasSeenWinScreen = false; + private int expCooldown = 0; + private TriState flyingFallDamage = TriState.NOT_SET; public PatchBukkitPlayer(UUID uuid, String name) { super(uuid, name); - } + this.displayName = name; + this.playerListName = name; + this.firstPlayed = System.currentTimeMillis(); + this.lastLogin = System.currentTimeMillis(); + this.spigot = new Player.Spigot() { + @Override + public void sendMessage(@NotNull BaseComponent component) { + PatchBukkitPlayer.this.sendMessage(BaseComponent.toLegacyText(component)); + } - @Override - public void openSign(@NotNull org.bukkit.block.Sign sign) { - } + @Override + public void sendMessage(@NotNull BaseComponent... components) { + PatchBukkitPlayer.this.sendMessage(BaseComponent.toLegacyText(components)); + } - @Override - public void spawnParticle(@NotNull Particle particle, @NotNull Location location, int count, double offsetX, double offsetY, double offsetZ, double extra, @org.jspecify.annotations.Nullable T data, boolean force) { - } + @Override + public void sendMessage(@NotNull net.md_5.bungee.api.ChatMessageType position, @NotNull BaseComponent... components) { + if (position == net.md_5.bungee.api.ChatMessageType.ACTION_BAR) { + PatchBukkitPlayer.this.sendActionBar(BaseComponent.toLegacyText(components)); + } else { + PatchBukkitPlayer.this.sendMessage(BaseComponent.toLegacyText(components)); + } + } - @Override - public void spawnParticle(@NotNull Particle particle, double x, double y, double z, int count, double offsetX, double offsetY, double offsetZ, double extra, @org.jspecify.annotations.Nullable T data) { - } + @Override + public void sendMessage(@Nullable UUID sender, @NotNull BaseComponent component) { + sendMessage(component); + } - public void unsetFixedPose() { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'unsetFixedPose'"); - } + @Override + public void sendMessage(@Nullable UUID sender, @NotNull BaseComponent... components) { + sendMessage(components); + } - public void resetFlyingTicks() { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'resetFlyingTicks'"); - } + @Override + public @NotNull InetSocketAddress getRawAddress() { + return PatchBukkitPlayer.this.getAddress(); + } - @Override - public void sendRawMessage(String message) { - this.sendRawMessage(null, message); - } + public @NotNull String getLocale() { + return PatchBukkitPlayer.this.getLocale(); + } - @Override - public void sendRawMessage(UUID sender, String message) { - var request = SendMessageRequest.newBuilder().setMessage(message != null ? message : "").setUuid(BridgeUtils.convertUuid(this.getUniqueId())).build(); - NativeBridgeFfi.sendMessage(request); - } + @Override + public @NotNull Set getHiddenPlayers() { + return Collections.unmodifiableSet(PatchBukkitPlayer.this.hiddenPlayers); + } - @Override - public void sendMessage(String message) { - this.sendRawMessage(message); - } - - @Override - public void sendMessage(String... messages) { - for (String message : messages) { - this.sendMessage(message); - } - } - - @Override - public void sendMessage(UUID sender, String message) { - this.sendRawMessage(sender, message); - } - - @Override - public void sendMessage(UUID sender, String... messages) { - for (String message : messages) { - this.sendMessage(sender, message); - } + @Override + public void respawn() { + Location loc = PatchBukkitPlayer.this.getBedSpawnLocation(); + if (loc != null) { + PatchBukkitPlayer.this.teleport(loc); + } + } + }; } @Override public Player.Spigot spigot() { - throw new UnsupportedOperationException("Unimplemented method 'spigot'"); + return this.spigot; } - @Override - public boolean isConversing() { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'isConversing'"); - } - - @Override - public void acceptConversationInput(@NotNull String input) { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'acceptConversationInput'"); - } - - @Override - public boolean beginConversation(@NotNull Conversation conversation) { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'beginConversation'"); - } - - @Override - public void abandonConversation(@NotNull Conversation conversation) { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'abandonConversation'"); - } - - @Override - public void abandonConversation(@NotNull Conversation conversation, @NotNull ConversationAbandonedEvent details) { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'abandonConversation'"); - } + // --- Identity, Online & OfflinePlayer --- @Override public boolean isOnline() { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'isOnline'"); + return true; } @Override public boolean isConnected() { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'isConnected'"); + return true; } @Override - public boolean isBanned() { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'isBanned'"); - } - - @Override - public > @org.jspecify.annotations.Nullable E ban( - @org.jspecify.annotations.Nullable String reason, @org.jspecify.annotations.Nullable Date expires, - @org.jspecify.annotations.Nullable String source) { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'ban'"); - } - - @Override - public > @org.jspecify.annotations.Nullable E ban( - @org.jspecify.annotations.Nullable String reason, @org.jspecify.annotations.Nullable Instant expires, - @org.jspecify.annotations.Nullable String source) { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'ban'"); - } - - @Override - public > @org.jspecify.annotations.Nullable E ban( - @org.jspecify.annotations.Nullable String reason, @org.jspecify.annotations.Nullable Duration duration, - @org.jspecify.annotations.Nullable String source) { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'ban'"); - } - - @Override - public boolean isWhitelisted() { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'isWhitelisted'"); - } - - @Override - public void setWhitelisted(boolean value) { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'setWhitelisted'"); - } - - @Override - public @org.jspecify.annotations.Nullable Player getPlayer() { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'getPlayer'"); + public Player getPlayer() { + return this; } @Override public long getFirstPlayed() { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'getFirstPlayed'"); + return this.firstPlayed > 0 ? this.firstPlayed : System.currentTimeMillis(); } @Override public long getLastPlayed() { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'getLastPlayed'"); + return System.currentTimeMillis(); } @Override public boolean hasPlayedBefore() { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'hasPlayedBefore'"); + return true; } @Override public long getLastLogin() { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'getLastLogin'"); + return this.lastLogin > 0 ? this.lastLogin : System.currentTimeMillis(); } @Override public long getLastSeen() { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'getLastSeen'"); - } - - public @org.jspecify.annotations.Nullable Location getRespawnLocation(boolean loadLocationAndValidate) { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'getRespawnLocation'"); - } - - @Override - public void incrementStatistic(Statistic statistic) throws IllegalArgumentException { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'incrementStatistic'"); - } - - @Override - public void decrementStatistic(Statistic statistic) throws IllegalArgumentException { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'decrementStatistic'"); - } - - @Override - public void incrementStatistic(Statistic statistic, int amount) throws IllegalArgumentException { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'incrementStatistic'"); - } - - @Override - public void decrementStatistic(Statistic statistic, int amount) throws IllegalArgumentException { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'decrementStatistic'"); - } - - @Override - public void setStatistic(Statistic statistic, int newValue) throws IllegalArgumentException { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'setStatistic'"); - } - - @Override - public int getStatistic(Statistic statistic) throws IllegalArgumentException { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'getStatistic'"); - } - - @Override - public void incrementStatistic(Statistic statistic, Material material) throws IllegalArgumentException { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'incrementStatistic'"); - } - - @Override - public void decrementStatistic(Statistic statistic, Material material) throws IllegalArgumentException { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'decrementStatistic'"); - } - - @Override - public int getStatistic(Statistic statistic, Material material) throws IllegalArgumentException { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'getStatistic'"); - } - - @Override - public void incrementStatistic(Statistic statistic, Material material, int amount) throws IllegalArgumentException { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'incrementStatistic'"); - } - - @Override - public void decrementStatistic(Statistic statistic, Material material, int amount) throws IllegalArgumentException { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'decrementStatistic'"); - } - - @Override - public void setStatistic(Statistic statistic, Material material, int newValue) throws IllegalArgumentException { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'setStatistic'"); - } - - @Override - public void incrementStatistic(Statistic statistic, EntityType entityType) throws IllegalArgumentException { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'incrementStatistic'"); - } - - @Override - public void decrementStatistic(Statistic statistic, EntityType entityType) throws IllegalArgumentException { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'decrementStatistic'"); - } - - @Override - public int getStatistic(Statistic statistic, EntityType entityType) throws IllegalArgumentException { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'getStatistic'"); - } - - @Override - public void incrementStatistic(Statistic statistic, EntityType entityType, int amount) - throws IllegalArgumentException { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'incrementStatistic'"); - } - - @Override - public void decrementStatistic(Statistic statistic, EntityType entityType, int amount) { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'decrementStatistic'"); - } - - @Override - public void setStatistic(Statistic statistic, EntityType entityType, int newValue) { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'setStatistic'"); + return System.currentTimeMillis(); } @Override - public @NotNull Map serialize() { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'serialize'"); + public boolean isBanned() { + return PatchBukkitServer.getInstance().getBanList(BanList.Type.NAME).isBanned(getName()); } @Override - public void sendPluginMessage(@NotNull Plugin source, @NotNull String channel, byte @NotNull [] message) { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'sendPluginMessage'"); + @SuppressWarnings("unchecked") + public > @Nullable E ban(@Nullable String reason, @Nullable Date expires, @Nullable String source) { + return ban(reason, expires, source, true); } @Override - public @NotNull Set getListeningPluginChannels() { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'getListeningPluginChannels'"); + @SuppressWarnings("unchecked") + public > @Nullable E ban(@Nullable String reason, @Nullable Instant expires, @Nullable String source) { + return ban(reason, expires, source, true); } @Override - public int getProtocolVersion() { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'getProtocolVersion'"); + @SuppressWarnings("unchecked") + public > @Nullable E ban(@Nullable String reason, @Nullable Duration duration, @Nullable String source) { + return ban(reason, duration, source, true); } @Override - public @org.jspecify.annotations.Nullable InetSocketAddress getVirtualHost() { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'getVirtualHost'"); + @SuppressWarnings("unchecked") + public > @Nullable E ban(@Nullable String reason, @Nullable Date expires, @Nullable String source, boolean kickPlayer) { + if (kickPlayer) { + kickPlayer(reason != null ? reason : "Banned by operator"); + } + return null; } @Override - public @UnmodifiableView Iterable activeBossBars() { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'activeBossBars'"); + @SuppressWarnings("unchecked") + public > @Nullable E ban(@Nullable String reason, @Nullable Instant expires, @Nullable String source, boolean kickPlayer) { + if (kickPlayer) { + kickPlayer(reason != null ? reason : "Banned by operator"); + } + return null; } @Override - public Component displayName() { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'displayName'"); + @SuppressWarnings("unchecked") + public > @Nullable E ban(@Nullable String reason, @Nullable Duration duration, @Nullable String source, boolean kickPlayer) { + if (kickPlayer) { + kickPlayer(reason != null ? reason : "Banned by operator"); + } + return null; } @Override - public void displayName(@org.jspecify.annotations.Nullable Component displayName) { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'displayName'"); + public @Nullable BanEntry banIp(@Nullable String reason, @Nullable Date expires, @Nullable String source, boolean kickPlayer) { + if (kickPlayer) { + kickPlayer(reason != null ? reason : "Banned IP by operator"); + } + return null; } @Override - public String getDisplayName() { - return this.getName(); + public @Nullable BanEntry banIp(@Nullable String reason, @Nullable Instant expires, @Nullable String source, boolean kickPlayer) { + if (kickPlayer) { + kickPlayer(reason != null ? reason : "Banned IP by operator"); + } + return null; } @Override - public void setDisplayName(@org.jspecify.annotations.Nullable String name) { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'setDisplayName'"); + public @Nullable BanEntry banIp(@Nullable String reason, @Nullable Duration duration, @Nullable String source, boolean kickPlayer) { + if (kickPlayer) { + kickPlayer(reason != null ? reason : "Banned IP by operator"); + } + return null; } - @Override - public void playerListName(@org.jspecify.annotations.Nullable Component name) { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'playerListName'"); - } - - @Override - public Component playerListName() { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'playerListName'"); - } - - @Override - public @org.jspecify.annotations.Nullable Component playerListHeader() { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'playerListHeader'"); - } - - @Override - public @org.jspecify.annotations.Nullable Component playerListFooter() { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'playerListFooter'"); - } - - @Override - public String getPlayerListName() { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'getPlayerListName'"); - } - - @Override - public void setPlayerListName(@org.jspecify.annotations.Nullable String name) { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'setPlayerListName'"); - } - - @Override - public int getPlayerListOrder() { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'getPlayerListOrder'"); - } - - @Override - public void setPlayerListOrder(int order) { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'setPlayerListOrder'"); - } - - @Override - public @org.jspecify.annotations.Nullable String getPlayerListHeader() { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'getPlayerListHeader'"); - } - - @Override - public @org.jspecify.annotations.Nullable String getPlayerListFooter() { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'getPlayerListFooter'"); - } - - @Override - public void setPlayerListHeader(@org.jspecify.annotations.Nullable String header) { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'setPlayerListHeader'"); - } - - @Override - public void setPlayerListFooter(@org.jspecify.annotations.Nullable String footer) { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'setPlayerListFooter'"); - } - - @Override - public void setPlayerListHeaderFooter(@org.jspecify.annotations.Nullable String header, - @org.jspecify.annotations.Nullable String footer) { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'setPlayerListHeaderFooter'"); - } - - @Override - public void setCompassTarget(Location loc) { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'setCompassTarget'"); - } - - @Override - public Location getCompassTarget() { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'getCompassTarget'"); - } - - @Override - public @org.jspecify.annotations.Nullable InetSocketAddress getAddress() { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'getAddress'"); - } - - @Override - public @org.jspecify.annotations.Nullable InetSocketAddress getHAProxyAddress() { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'getHAProxyAddress'"); - } + private boolean whitelisted = false; @Override - public boolean isTransferred() { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'isTransferred'"); + public boolean isWhitelisted() { + return this.whitelisted; } @Override - public CompletableFuture retrieveCookie(NamespacedKey key) { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'retrieveCookie'"); + public void setWhitelisted(boolean value) { + this.whitelisted = value; } @Override - public void storeCookie(NamespacedKey key, byte[] value) { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'storeCookie'"); + public Map serialize() { + Map map = new HashMap<>(); + map.put("uuid", getUniqueId().toString()); + map.put("name", getName()); + return map; } - @Override - public void transfer(String host, int port) { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'transfer'"); - } + // --- Messaging & Chat --- @Override - public void kickPlayer(@org.jspecify.annotations.Nullable String message) { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'kickPlayer'"); + public void sendMessage(@NotNull String message) { + if (message == null) return; + try { + var request = patchbukkit.message.SendMessageRequest.newBuilder() + .setUuid(BridgeUtils.convertUuid(this.getUniqueId())) + .setMessage(message) + .build(); + NativeBridgeFfi.sendMessage(request); + } catch (Throwable ignored) {} } @Override - public void kick(@org.jspecify.annotations.Nullable Component message, Cause cause) { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'kick'"); - } - - @Override - public > @org.jspecify.annotations.Nullable E ban( - @org.jspecify.annotations.Nullable String reason, @org.jspecify.annotations.Nullable Date expires, - @org.jspecify.annotations.Nullable String source, boolean kickPlayer) { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'ban'"); - } - - @Override - public > @org.jspecify.annotations.Nullable E ban( - @org.jspecify.annotations.Nullable String reason, @org.jspecify.annotations.Nullable Instant expires, - @org.jspecify.annotations.Nullable String source, boolean kickPlayer) { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'ban'"); - } - - @Override - public > @org.jspecify.annotations.Nullable E ban( - @org.jspecify.annotations.Nullable String reason, @org.jspecify.annotations.Nullable Duration duration, - @org.jspecify.annotations.Nullable String source, boolean kickPlayer) { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'ban'"); - } - - @Override - public @org.jspecify.annotations.Nullable BanEntry banIp( - @org.jspecify.annotations.Nullable String reason, @org.jspecify.annotations.Nullable Date expires, - @org.jspecify.annotations.Nullable String source, boolean kickPlayer) { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'banIp'"); - } - - @Override - public @org.jspecify.annotations.Nullable BanEntry banIp( - @org.jspecify.annotations.Nullable String reason, @org.jspecify.annotations.Nullable Instant expires, - @org.jspecify.annotations.Nullable String source, boolean kickPlayer) { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'banIp'"); - } - - @Override - public @org.jspecify.annotations.Nullable BanEntry banIp( - @org.jspecify.annotations.Nullable String reason, @org.jspecify.annotations.Nullable Duration duration, - @org.jspecify.annotations.Nullable String source, boolean kickPlayer) { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'banIp'"); - } - - @Override - public void chat(String msg) { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'chat'"); - } - - @Override - public boolean performCommand(String command) { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'performCommand'"); - } - - private boolean sprinting = false; - - @Override - public boolean isSprinting() { - return this.sprinting; - } - - @Override - public void setSprinting(boolean sprinting) { - this.sprinting = sprinting; - } - - @Override - public void saveData() { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'saveData'"); - } - - @Override - public void loadData() { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'loadData'"); - } - - @Override - public void setSleepingIgnored(boolean isSleeping) { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'setSleepingIgnored'"); - } - - @Override - public boolean isSleepingIgnored() { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'isSleepingIgnored'"); - } - - @Override - public void setRespawnLocation(@org.jspecify.annotations.Nullable Location location, boolean force) { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'setRespawnLocation'"); - } - - @Override - public Collection getEnderPearls() { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'getEnderPearls'"); - } - - @Override - public Input getCurrentInput() { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'getCurrentInput'"); - } - - @Override - public void playNote(Location loc, Instrument instrument, Note note) { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'playNote'"); - } - - @Override - public void playSound(Location location, Sound sound, float volume, float pitch) { - playSound(location, sound, SoundCategory.MASTER, volume, pitch); - } - - @Override - public void playSound(Location location, String sound, float volume, float pitch) { - playSound(location, sound, SoundCategory.MASTER, volume, pitch); - } - - @Override - public void playSound(Location location, Sound sound, SoundCategory category, float volume, float pitch) { - var patchBukkitSound = (PatchBukkitSound) sound; - this.playSound0(location, patchBukkitSound.getOriginalName(), category, volume, pitch, OptionalLong.empty()); - } - - @Override - public void playSound(Location location, String sound, SoundCategory category, float volume, float pitch) { - this.playSound0(location, sound, category, volume, pitch, OptionalLong.empty()); - } - - @Override - public void playSound(Location location, Sound sound, SoundCategory category, float volume, float pitch, - long seed) { - var patchBukkitSound = (PatchBukkitSound) sound; - this.playSound0(location, patchBukkitSound.getOriginalName(), category, volume, pitch, OptionalLong.of(seed)); - } - - @Override - public void playSound(Location location, String sound, SoundCategory category, float volume, float pitch, - long seed) { - this.playSound0(location, sound, category, volume, pitch, OptionalLong.of(seed)); - } - - private void playSound0(Location location, String sound, SoundCategory category, float volume, float pitch, OptionalLong seed) { - var request = PlayerPlaySoundRequest.newBuilder() - .setPlayerUuid(BridgeUtils.convertUuid(this.uuid)) - .setLocation( - patchbukkit.common.Location.newBuilder() - .setPosition( - patchbukkit.common.Vec3.newBuilder() - .setX(location.x()) - .setY(location.y()) - .setZ(location.z()) - ).setWorld( - patchbukkit.common.World.newBuilder().setUuid(BridgeUtils.convertUuid(location.getWorld().getUID())) - ).setPitch(location.getPitch()) - .setYaw(location.getYaw()) - ).setSound( - patchbukkit.sound.Sound.newBuilder() - .setCategory(category.name()) - .setName(sound) - ).setVolume(volume) - .setPitch(pitch); - - if (seed.isPresent()) request.setSeed(seed.getAsLong()); - NativeBridgeFfi.playerPlaySound(request.build()); - } - - @Override - public void playSound(Entity entity, Sound sound, SoundCategory category, float volume, float pitch) { - var patchBukkitSound = (PatchBukkitSound) sound; - this.playSound0(entity, patchBukkitSound.getOriginalName(), category, volume, pitch, OptionalLong.empty()); - } - - @Override - public void playSound(Entity entity, String sound, SoundCategory category, float volume, float pitch) { - this.playSound0(entity, sound, category, volume, pitch, OptionalLong.empty()); - } - - @Override - public void playSound(Entity entity, Sound sound, SoundCategory category, float volume, float pitch, long seed) { - var patchBukkitSound = (PatchBukkitSound) sound; - this.playSound0(entity, patchBukkitSound.getOriginalName(), category, volume, pitch, OptionalLong.of(seed)); - } - - @Override - public void playSound(Entity entity, String sound, SoundCategory category, float volume, float pitch, long seed) { - this.playSound0(entity, sound, category, volume, pitch, OptionalLong.of(seed)); - } - - private void playSound0(Entity entity, String sound, SoundCategory category, float volume, float pitch, OptionalLong seed) { - var request = PlayerEntityPlaySoundRequest.newBuilder() - .setPlayerUuid(BridgeUtils.convertUuid(this.uuid)) - .setEntityUuid(BridgeUtils.convertUuid(entity.getUniqueId())) - .setSound( - patchbukkit.sound.Sound.newBuilder() - .setCategory(category.name()) - .setName(sound) - ).setVolume(volume) - .setPitch(pitch); - - if (seed.isPresent()) request.setSeed(seed.getAsLong()); - NativeBridgeFfi.playerEntityPlaySound(request.build()); - } - - @Override - public void stopSound(String sound, @org.jspecify.annotations.Nullable SoundCategory category) { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'stopSound'"); - } - - @Override - public void stopSound(SoundCategory category) { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'stopSound'"); - } - - @Override - public void stopAllSounds() { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'stopAllSounds'"); - } - - @Override - public void playEffect(Location loc, Effect effect, int data) { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'playEffect'"); - } - - @Override - public void playEffect(Location loc, Effect effect, @org.jspecify.annotations.Nullable T data) { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'playEffect'"); - } - - @Override - public boolean breakBlock(Block block) { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'breakBlock'"); - } - - @Override - public void sendBlockChange(Location loc, Material material, byte data) { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'sendBlockChange'"); - } - - @Override - public void sendBlockChange(Location loc, BlockData block) { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'sendBlockChange'"); - } - - @Override - public void sendBlockChanges(Collection blocks) { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'sendBlockChanges'"); - } - - @Override - public void sendMultiBlockChange(Map blockChanges) { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'sendMultiBlockChange'"); - } - - @Override - public void sendBlockDamage(Location loc, float progress, Entity source) { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'sendBlockDamage'"); - } - - @Override - public void sendBlockDamage(Location loc, float progress, int sourceId) { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'sendBlockDamage'"); - } - - @Override - public void sendEquipmentChange(LivingEntity entity, EquipmentSlot slot, - @org.jspecify.annotations.Nullable ItemStack item) { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'sendEquipmentChange'"); - } - - @Override - public void sendEquipmentChange(LivingEntity entity, - Map items) { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'sendEquipmentChange'"); - } - - @Override - public void sendSignChange(Location loc, @org.jspecify.annotations.Nullable List lines, - DyeColor dyeColor, boolean hasGlowingText) throws IllegalArgumentException { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'sendSignChange'"); - } - - @Override - public void sendSignChange(Location loc, - @org.jspecify.annotations.Nullable String @org.jspecify.annotations.Nullable [] lines) - throws IllegalArgumentException { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'sendSignChange'"); - } - - @Override - public void sendSignChange(Location loc, - @org.jspecify.annotations.Nullable String @org.jspecify.annotations.Nullable [] lines, DyeColor dyeColor) - throws IllegalArgumentException { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'sendSignChange'"); - } - - @Override - public void sendSignChange(Location loc, - @org.jspecify.annotations.Nullable String @org.jspecify.annotations.Nullable [] lines, DyeColor dyeColor, - boolean hasGlowingText) throws IllegalArgumentException { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'sendSignChange'"); - } - - @Override - public void sendBlockUpdate(Location loc, TileState tileState) throws IllegalArgumentException { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'sendBlockUpdate'"); - } - - @Override - public void sendPotionEffectChange(LivingEntity entity, PotionEffect effect) { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'sendPotionEffectChange'"); - } - - @Override - public void sendPotionEffectChangeRemove(LivingEntity entity, PotionEffectType type) { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'sendPotionEffectChangeRemove'"); - } - - @Override - public void sendMap(MapView map) { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'sendMap'"); - } - - @Override - public void showWinScreen() { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'showWinScreen'"); - } - - @Override - public boolean hasSeenWinScreen() { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'hasSeenWinScreen'"); - } - - @Override - public void setHasSeenWinScreen(boolean hasSeenWinScreen) { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'setHasSeenWinScreen'"); - } - - @Override - public void sendActionBar(String message) { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'sendActionBar'"); - } - - @Override - public void sendActionBar(char alternateChar, String message) { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'sendActionBar'"); - } - - @Override - public void sendActionBar(BaseComponent... message) { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'sendActionBar'"); - } - - @Override - public void setPlayerListHeaderFooter(BaseComponent @org.jspecify.annotations.Nullable [] header, - BaseComponent @org.jspecify.annotations.Nullable [] footer) { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'setPlayerListHeaderFooter'"); - } - - @Override - public void setPlayerListHeaderFooter(@org.jspecify.annotations.Nullable BaseComponent header, - @org.jspecify.annotations.Nullable BaseComponent footer) { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'setPlayerListHeaderFooter'"); - } - - @Override - public void setTitleTimes(int fadeInTicks, int stayTicks, int fadeOutTicks) { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'setTitleTimes'"); - } - - @Override - public void setSubtitle(BaseComponent[] subtitle) { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'setSubtitle'"); - } - - @Override - public void setSubtitle(BaseComponent subtitle) { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'setSubtitle'"); - } - - @Override - public void showTitle(@org.jspecify.annotations.Nullable BaseComponent[] title) { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'showTitle'"); - } - - @Override - public void showTitle(@org.jspecify.annotations.Nullable BaseComponent title) { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'showTitle'"); - } - - @Override - public void showTitle(@org.jspecify.annotations.Nullable BaseComponent[] title, - @org.jspecify.annotations.Nullable BaseComponent[] subtitle, int fadeInTicks, int stayTicks, - int fadeOutTicks) { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'showTitle'"); - } - - @Override - public void showTitle(@org.jspecify.annotations.Nullable BaseComponent title, - @org.jspecify.annotations.Nullable BaseComponent subtitle, int fadeInTicks, int stayTicks, - int fadeOutTicks) { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'showTitle'"); - } - - @Override - public void sendTitle(Title title) { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'sendTitle'"); - } - - @Override - public void updateTitle(Title title) { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'updateTitle'"); - } - - @Override - public void hideTitle() { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'hideTitle'"); - } - - @Override - public void sendHurtAnimation(float yaw) { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'sendHurtAnimation'"); - } - - @Override - public void sendLinks(ServerLinks links) { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'sendLinks'"); - } - - @Override - public void addCustomChatCompletions(Collection completions) { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'addCustomChatCompletions'"); - } - - @Override - public void removeCustomChatCompletions(Collection completions) { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'removeCustomChatCompletions'"); - } - - @Override - public void setCustomChatCompletions(Collection completions) { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'setCustomChatCompletions'"); - } - - @Override - public void updateInventory() { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'updateInventory'"); - } - - @Override - public @org.jspecify.annotations.Nullable GameMode getPreviousGameMode() { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'getPreviousGameMode'"); - } - - @Override - public void setPlayerTime(long time, boolean relative) { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'setPlayerTime'"); - } - - @Override - public long getPlayerTime() { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'getPlayerTime'"); - } - - @Override - public long getPlayerTimeOffset() { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'getPlayerTimeOffset'"); - } - - @Override - public boolean isPlayerTimeRelative() { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'isPlayerTimeRelative'"); - } - - @Override - public void resetPlayerTime() { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'resetPlayerTime'"); - } - - @Override - public void setPlayerWeather(WeatherType type) { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'setPlayerWeather'"); - } - - @Override - public @org.jspecify.annotations.Nullable WeatherType getPlayerWeather() { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'getPlayerWeather'"); - } - - @Override - public void resetPlayerWeather() { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'resetPlayerWeather'"); - } - - @Override - public int getExpCooldown() { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'getExpCooldown'"); - } - - @Override - public void setExpCooldown(int ticks) { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'setExpCooldown'"); - } - - @Override - public void giveExp(int amount, boolean applyMending) { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'giveExp'"); - } - - @Override - public int applyMending(int amount) { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'applyMending'"); - } - - @Override - public void giveExpLevels(int amount) { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'giveExpLevels'"); - } - - @Override - public float getExp() { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'getExp'"); - } - - @Override - public void setExp(float exp) { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'setExp'"); - } - - @Override - public int getLevel() { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'getLevel'"); - } - - @Override - public void setLevel(int level) { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'setLevel'"); - } - - @Override - public int getTotalExperience() { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'getTotalExperience'"); - } - - @Override - public void setTotalExperience(int exp) { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'setTotalExperience'"); - } - - @Override - public @Range(from = 0, to = 2147483647) int calculateTotalExperiencePoints() { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'calculateTotalExperiencePoints'"); - } - - @Override - public void setExperienceLevelAndProgress(@Range(from = 0, to = 2147483647) int totalExperience) { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'setExperienceLevelAndProgress'"); - } - - @Override - public int getExperiencePointsNeededForNextLevel() { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'getExperiencePointsNeededForNextLevel'"); - } - - @Override - public void sendExperienceChange(float progress) { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'sendExperienceChange'"); - } - - @Override - public void sendExperienceChange(float progress, int level) { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'sendExperienceChange'"); - } - - @Override - public boolean getAllowFlight() { - var abilities = NativeBridgeFfi.getAbilities(BridgeUtils.convertUuid(this.uuid)); - return abilities != null && abilities.getAllowFlying(); - } - - @Override - public void setAllowFlight(boolean flight) { - var playerUuid = BridgeUtils.convertUuid(this.uuid); - var abilities = NativeBridgeFfi.getAbilities(playerUuid); - var builder = (abilities != null ? abilities.toBuilder() : patchbukkit.abilities.Abilities.newBuilder()); - builder.setAllowFlying(flight); - if (builder.getFlying()) builder.setFlying(false); - NativeBridgeFfi.setAbilities(SetAbilitiesRequest.newBuilder().setAbilities(builder.build()).setUuid(playerUuid).build()); - } - - @Override - public void setFlyingFallDamage(TriState flyingFallDamage) { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'setFlyingFallDamage'"); - } - - @Override - public TriState hasFlyingFallDamage() { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'hasFlyingFallDamage'"); - } - - @Override - public void hidePlayer(Player player) { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'hidePlayer'"); - } - - @Override - public void showPlayer(Player player) { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'showPlayer'"); - } - - @Override - public boolean canSee(Player player) { - return this.canSee((org.bukkit.entity.Entity) player); - } - - @Override - public boolean canSee(Entity entity) { - return this.equals(entity) || entity.isVisibleByDefault() ^ this.invertedVisibilityEntities.containsKey(entity.getUniqueId()); - } - - @Override - public void hideEntity(Plugin plugin, Entity entity) { - Preconditions.checkNotNull(plugin, "Plugin cannot be null"); - Preconditions.checkNotNull(entity, "Entity cannot be null"); - if (this.equals(entity)) return; - - Set> plugins = invertedVisibilityEntities - .computeIfAbsent(entity.getUniqueId(), k -> new HashSet<>()); - plugins.add(new WeakReference<>(plugin)); - } - - @Override - public void showEntity(Plugin plugin, Entity entity) { - Preconditions.checkNotNull(plugin, "Plugin cannot be null"); - Preconditions.checkNotNull(entity, "Entity cannot be null"); - if (this.equals(entity)) return; - - Set> plugins = invertedVisibilityEntities.get(entity.getUniqueId()); - if (plugins == null) return; - - plugins.removeIf(ref -> { - Plugin p = ref.get(); - return p == null || p.equals(plugin); - }); - - if (plugins.isEmpty()) { - invertedVisibilityEntities.remove(entity.getUniqueId()); + public void sendMessage(@NotNull String... messages) { + if (messages == null) return; + for (String msg : messages) { + sendMessage(msg); } } @Override - public boolean isListed(Player other) { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'isListed'"); + public void sendMessage(@Nullable UUID sender, @NotNull String message) { + sendMessage(message); } @Override - public boolean unlistPlayer(Player other) { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'unlistPlayer'"); + public void sendMessage(@Nullable UUID sender, @NotNull String... messages) { + sendMessage(messages); } @Override - public boolean listPlayer(Player other) { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'listPlayer'"); + public void sendRawMessage(@NotNull String message) { + sendMessage(message); + } + + @Override + public void sendRawMessage(@Nullable UUID sender, @NotNull String message) { + sendMessage(message); + } + + @Override + public void sendActionBar(@NotNull String message) { + if (message == null) return; + try { + var req = SendActionBarRequest.newBuilder() + .setUuid(BridgeUtils.convertUuid(getUniqueId())) + .setMessage(message) + .build(); + NativeBridgeFfi.sendActionBar(req); + } catch (Throwable ignored) {} + } + + @Override + public void sendActionBar(char alternateChar, @NotNull String message) { + sendActionBar(org.bukkit.ChatColor.translateAlternateColorCodes(alternateChar, message)); + } + + @Override + public void sendActionBar(@NotNull BaseComponent... message) { + sendActionBar(BaseComponent.toLegacyText(message)); + } + + @Override + public void sendActionBar(@NotNull Component message) { + sendActionBar(LegacyComponentSerializer.legacySection().serialize(message)); + } + + @Override + public void chat(@NotNull String msg) { + if (msg == null) return; + if (msg.startsWith("/")) { + performCommand(msg.substring(1)); + } else { + sendMessage("<" + getDisplayName() + "> " + msg); + } + } + + @Override + public boolean performCommand(@NotNull String command) { + if (command == null) return false; + return PatchBukkitServer.getInstance().dispatchCommand(this, command); + } + + // --- Display & Tab List --- + + @Override + public @NotNull String getDisplayName() { + return this.displayName != null ? this.displayName : getName(); + } + + @Override + public void setDisplayName(@Nullable String name) { + this.displayName = name != null ? name : getName(); + try { + var req = SetDisplayNameRequest.newBuilder() + .setUuid(BridgeUtils.convertUuid(getUniqueId())) + .setDisplayName(this.displayName) + .build(); + NativeBridgeFfi.setDisplayName(req); + } catch (Throwable ignored) {} + } + + @Override + public @NotNull Component displayName() { + return LegacyComponentSerializer.legacySection().deserialize(getDisplayName()); + } + + @Override + public void displayName(@Nullable Component displayName) { + setDisplayName(displayName != null ? LegacyComponentSerializer.legacySection().serialize(displayName) : null); + } + + @Override + public @NotNull String getPlayerListName() { + return this.playerListName != null ? this.playerListName : getName(); + } + + @Override + public void setPlayerListName(@Nullable String name) { + this.playerListName = name != null ? name : getName(); + try { + var req = SetPlayerListNameRequest.newBuilder() + .setUuid(BridgeUtils.convertUuid(getUniqueId())) + .setListName(this.playerListName) + .build(); + NativeBridgeFfi.setPlayerListName(req); + } catch (Throwable ignored) {} + } + + @Override + public @NotNull Component playerListName() { + return LegacyComponentSerializer.legacySection().deserialize(getPlayerListName()); + } + + @Override + public void playerListName(@Nullable Component name) { + setPlayerListName(name != null ? LegacyComponentSerializer.legacySection().serialize(name) : null); + } + + @Override + public @Nullable String getPlayerListHeader() { + return this.playerListHeader; + } + + @Override + public @Nullable String getPlayerListFooter() { + return this.playerListFooter; + } + + @Override + public void setPlayerListHeader(@Nullable String header) { + setPlayerListHeaderFooter(header, this.playerListFooter); + } + + @Override + public void setPlayerListFooter(@Nullable String footer) { + setPlayerListHeaderFooter(this.playerListHeader, footer); + } + + @Override + public void setPlayerListHeaderFooter(@Nullable String header, @Nullable String footer) { + this.playerListHeader = header != null ? header : ""; + this.playerListFooter = footer != null ? footer : ""; + try { + var req = SetPlayerListHeaderFooterRequest.newBuilder() + .setUuid(BridgeUtils.convertUuid(getUniqueId())) + .setHeader(this.playerListHeader) + .setFooter(this.playerListFooter) + .build(); + NativeBridgeFfi.setPlayerListHeaderFooter(req); + } catch (Throwable ignored) {} + } + + @Override + public void setPlayerListHeaderFooter(@Nullable BaseComponent header, @Nullable BaseComponent footer) { + setPlayerListHeaderFooter( + header != null ? BaseComponent.toLegacyText(header) : "", + footer != null ? BaseComponent.toLegacyText(footer) : "" + ); + } + + public void setPlayerListHeaderFooter(@Nullable BaseComponent[] header, @Nullable BaseComponent[] footer) { + setPlayerListHeaderFooter( + header != null ? BaseComponent.toLegacyText(header) : "", + footer != null ? BaseComponent.toLegacyText(footer) : "" + ); + } + + @Override + public @Nullable Component playerListHeader() { + return LegacyComponentSerializer.legacySection().deserialize(this.playerListHeader); + } + + @Override + public @Nullable Component playerListFooter() { + return LegacyComponentSerializer.legacySection().deserialize(this.playerListFooter); + } + + @Override + public void sendPlayerListHeader(@NotNull Component header) { + setPlayerListHeader(LegacyComponentSerializer.legacySection().serialize(header)); + } + + @Override + public void sendPlayerListFooter(@NotNull Component footer) { + setPlayerListFooter(LegacyComponentSerializer.legacySection().serialize(footer)); + } + + @Override + public void sendPlayerListHeaderAndFooter(@NotNull Component header, @NotNull Component footer) { + setPlayerListHeaderFooter(LegacyComponentSerializer.legacySection().serialize(header), LegacyComponentSerializer.legacySection().serialize(footer)); + } + + @Override + public int getPlayerListOrder() { + return this.playerListOrder; + } + + @Override + public void setPlayerListOrder(int order) { + this.playerListOrder = order; + } + + // --- Kick --- + + @Override + public void kickPlayer(@Nullable String message) { + String kickMsg = (message != null && !message.isEmpty()) ? message : "Kicked from server"; + try { + var req = KickPlayerRequest.newBuilder() + .setUuid(BridgeUtils.convertUuid(getUniqueId())) + .setMessage(kickMsg) + .build(); + NativeBridgeFfi.kickPlayer(req); + } catch (Throwable ignored) {} + } + + @Override + public void kick() { + kickPlayer(null); + } + + @Override + public void kick(@Nullable Component message) { + kickPlayer(message != null ? LegacyComponentSerializer.legacySection().serialize(message) : null); + } + + @Override + public void kick(@Nullable Component message, @NotNull Cause cause) { + kick(message); + } + + // --- Titles --- + + @Override + public void sendTitle(@Nullable String title, @Nullable String subtitle) { + sendTitle(title, subtitle, 10, 70, 20); + } + + @Override + public void sendTitle(@Nullable String title, @Nullable String subtitle, int fadeIn, int stay, int fadeOut) { + try { + var req = SendTitleRequest.newBuilder() + .setUuid(BridgeUtils.convertUuid(getUniqueId())) + .setTitle(title != null ? title : "") + .setSubtitle(subtitle != null ? subtitle : "") + .setFadeIn(fadeIn) + .setStay(stay) + .setFadeOut(fadeOut) + .build(); + NativeBridgeFfi.sendTitle(req); + } catch (Throwable ignored) {} + } + + @Override + public void sendTitle(@NotNull Title title) { + sendTitle( + title.getTitle() != null ? BaseComponent.toLegacyText(title.getTitle()) : "", + title.getSubtitle() != null ? BaseComponent.toLegacyText(title.getSubtitle()) : "", + title.getFadeIn(), + title.getStay(), + title.getFadeOut() + ); + } + + @Override + public void showTitle(@NotNull net.kyori.adventure.title.Title title) { + String mainTitle = LegacyComponentSerializer.legacySection().serialize(title.title()); + String subTitle = LegacyComponentSerializer.legacySection().serialize(title.subtitle()); + net.kyori.adventure.title.Title.Times times = title.times(); + int fadeIn = (times != null && times.fadeIn() != null) ? (int) (times.fadeIn().toMillis() / 50) : 10; + int stay = (times != null && times.stay() != null) ? (int) (times.stay().toMillis() / 50) : 70; + int fadeOut = (times != null && times.fadeOut() != null) ? (int) (times.fadeOut().toMillis() / 50) : 20; + sendTitle(mainTitle, subTitle, fadeIn, stay, fadeOut); + } + + public void showTitle(@NotNull Title title) { + sendTitle(title); + } + + @Override + public void showTitle(@Nullable BaseComponent title) { + sendTitle(title != null ? BaseComponent.toLegacyText(title) : "", ""); + } + + public void showTitle(@NotNull BaseComponent... title) { + sendTitle(BaseComponent.toLegacyText(title), ""); + } + + public void showTitle(@Nullable BaseComponent[] title, @Nullable BaseComponent[] subtitle, int fadeIn, int stay, int fadeOut) { + sendTitle( + title != null ? BaseComponent.toLegacyText(title) : "", + subtitle != null ? BaseComponent.toLegacyText(subtitle) : "", + fadeIn, stay, fadeOut + ); + } + + @Override + public void showTitle(@Nullable BaseComponent title, @Nullable BaseComponent subtitle, int fadeIn, int stay, int fadeOut) { + sendTitle( + title != null ? BaseComponent.toLegacyText(title) : "", + subtitle != null ? BaseComponent.toLegacyText(subtitle) : "", + fadeIn, stay, fadeOut + ); + } + + @Override + public void setTitleTimes(int fadeIn, int stay, int fadeOut) { + sendTitle("", "", fadeIn, stay, fadeOut); + } + + @Override + public void setSubtitle(@NotNull BaseComponent... subtitle) { + sendTitle("", BaseComponent.toLegacyText(subtitle), 10, 70, 20); + } + + @Override + public void setSubtitle(@NotNull BaseComponent subtitle) { + sendTitle("", BaseComponent.toLegacyText(subtitle), 10, 70, 20); + } + + @Override + public void resetTitle() { + try { + NativeBridgeFfi.resetTitle(BridgeUtils.convertUuid(getUniqueId())); + } catch (Throwable ignored) {} + } + + @Override + public void hideTitle() { + resetTitle(); + } + + @Override + public void updateTitle(@NotNull Title title) { + sendTitle(title); + } + + // --- Compass & Respawn --- + + @Override + public void setCompassTarget(@NotNull Location loc) { + if (loc == null) return; + this.compassTarget = loc.clone(); + try { + var req = SetCompassTargetRequest.newBuilder() + .setUuid(BridgeUtils.convertUuid(getUniqueId())) + .setPosition(patchbukkit.common.Vec3.newBuilder() + .setX(loc.getX()) + .setY(loc.getY()) + .setZ(loc.getZ()) + .build()) + .build(); + NativeBridgeFfi.setCompassTarget(req); + } catch (Throwable ignored) {} + } + + @Override + public @NotNull Location getCompassTarget() { + if (this.compassTarget != null) return this.compassTarget.clone(); + try { + var resp = NativeBridgeFfi.getCompassTarget(BridgeUtils.convertUuid(getUniqueId())); + if (resp != null) { + return new Location(getWorld(), resp.getX(), resp.getY(), resp.getZ()); + } + } catch (Throwable ignored) {} + return getWorld().getSpawnLocation(); + } + + @Override + public @Nullable Location getBedSpawnLocation() { + try { + var resp = NativeBridgeFfi.getRespawnPoint(BridgeUtils.convertUuid(getUniqueId())); + if (resp != null) { + return BridgeUtils.convertLocation(resp); + } + } catch (Throwable ignored) {} + return getWorld().getSpawnLocation(); + } + + @Override + public @Nullable Location getRespawnLocation() { + return getBedSpawnLocation(); + } + + public @Nullable Location getRespawnLocation(boolean checkBed) { + return getBedSpawnLocation(); + } + + @Override + public void setRespawnLocation(@Nullable Location location) { + setRespawnLocation(location, false); + } + + @Override + public void setRespawnLocation(@Nullable Location location, boolean force) { + if (location == null) return; + try { + var req = SetRespawnPointRequest.newBuilder() + .setUuid(BridgeUtils.convertUuid(getUniqueId())) + .setPosition(patchbukkit.common.Vec3.newBuilder() + .setX(location.getX()) + .setY(location.getY()) + .setZ(location.getZ()) + .build()) + .setYaw(location.getYaw()) + .setForce(force) + .build(); + NativeBridgeFfi.setRespawnPoint(req); + } catch (Throwable ignored) {} + } + + @Override + public void setBedSpawnLocation(@Nullable Location location) { + setRespawnLocation(location, false); + } + + @Override + public void setBedSpawnLocation(@Nullable Location location, boolean force) { + setRespawnLocation(location, force); + } + + // --- Player Time & Weather --- + + @Override + public void setPlayerTime(long time, boolean relative) { + this.playerTime = time; + this.playerTimeRelative = relative; + try { + var req = SetPlayerTimeRequest.newBuilder() + .setUuid(BridgeUtils.convertUuid(getUniqueId())) + .setTime(time) + .setRelative(relative) + .build(); + NativeBridgeFfi.setPlayerTime(req); + } catch (Throwable ignored) {} + } + + @Override + public long getPlayerTime() { + if (this.playerTimeRelative) { + return getWorld().getTime() + this.playerTime; + } + return this.playerTime; + } + + @Override + public long getPlayerTimeOffset() { + return this.playerTime; + } + + @Override + public boolean isPlayerTimeRelative() { + return this.playerTimeRelative; + } + + @Override + public void resetPlayerTime() { + this.playerTime = 0; + this.playerTimeRelative = true; + try { + NativeBridgeFfi.resetPlayerTime(BridgeUtils.convertUuid(getUniqueId())); + } catch (Throwable ignored) {} + } + + @Override + public void setPlayerWeather(@NotNull WeatherType type) { + this.playerWeather = type; + try { + var req = SetPlayerWeatherRequest.newBuilder() + .setUuid(BridgeUtils.convertUuid(getUniqueId())) + .setWeather(type == WeatherType.DOWNFALL ? 1 : 0) + .build(); + NativeBridgeFfi.setPlayerWeather(req); + } catch (Throwable ignored) {} + } + + @Override + public @Nullable WeatherType getPlayerWeather() { + return this.playerWeather; + } + + @Override + public void resetPlayerWeather() { + this.playerWeather = null; + try { + NativeBridgeFfi.resetPlayerWeather(BridgeUtils.convertUuid(getUniqueId())); + } catch (Throwable ignored) {} + } + + // --- Sounds --- + + @Override + public void stopSound(@NotNull String sound) { + stopSound(sound, null); + } + + @Override + public void stopSound(@NotNull Sound sound) { + stopSound(sound, null); + } + + @Override + public void stopSound(@NotNull Sound sound, @Nullable SoundCategory category) { + stopSound(sound.getKey().asString(), category); + } + + @Override + public void stopSound(@NotNull String sound, @Nullable SoundCategory category) { + try { + var req = StopSoundRequest.newBuilder() + .setUuid(BridgeUtils.convertUuid(getUniqueId())) + .setSound(sound != null ? sound : "") + .setCategory(category != null ? category.name().toLowerCase() : "") + .build(); + NativeBridgeFfi.stopSound(req); + } catch (Throwable ignored) {} + } + + @Override + public void stopSound(@NotNull SoundCategory category) { + stopSound("", category); + } + + @Override + public void stopAllSounds() { + stopSound("", null); + } + + @Override + public void playSound(@NotNull Location location, @NotNull Sound sound, float volume, float pitch) { + playSound(location, sound, SoundCategory.MASTER, volume, pitch); + } + + @Override + public void playSound(@NotNull Location location, @NotNull String sound, float volume, float pitch) { + playSound(location, sound, SoundCategory.MASTER, volume, pitch); + } + + @Override + public void playSound(@NotNull Location location, @NotNull Sound sound, @NotNull SoundCategory category, float volume, float pitch) { + playSound(location, sound.getKey().asString(), category, volume, pitch); + } + + @Override + public void playSound(@NotNull Location location, @NotNull String sound, @NotNull SoundCategory category, float volume, float pitch) { + playSound(location, sound, category, volume, pitch, 0L); + } + + @Override + public void playSound(@NotNull Location location, @NotNull Sound sound, @NotNull SoundCategory category, float volume, float pitch, long seed) { + playSound(location, sound.getKey().asString(), category, volume, pitch, seed); + } + + @Override + public void playSound(@NotNull Location location, @NotNull String sound, @NotNull SoundCategory category, float volume, float pitch, long seed) { + try { + var req = patchbukkit.sound.PlayerPlaySoundRequest.newBuilder() + .setPlayerUuid(BridgeUtils.convertUuid(this.getUniqueId())) + .setLocation(BridgeUtils.convertLocation(location)) + .setSound(patchbukkit.sound.Sound.newBuilder() + .setName(sound) + .setCategory(category.name()) + .build()) + .setVolume(volume) + .setPitch(pitch) + .setSeed(seed) + .build(); + NativeBridgeFfi.playerPlaySound(req); + } catch (Throwable ignored) {} + } + + @Override + public void playSound(@NotNull Entity entity, @NotNull Sound sound, float volume, float pitch) { + playSound(entity, sound, SoundCategory.MASTER, volume, pitch); + } + + @Override + public void playSound(@NotNull Entity entity, @NotNull String sound, float volume, float pitch) { + playSound(entity, sound, SoundCategory.MASTER, volume, pitch); + } + + @Override + public void playSound(@NotNull Entity entity, @NotNull Sound sound, @NotNull SoundCategory category, float volume, float pitch) { + playSound(entity, sound.getKey().asString(), category, volume, pitch); + } + + @Override + public void playSound(@NotNull Entity entity, @NotNull String sound, @NotNull SoundCategory category, float volume, float pitch) { + playSound(entity, sound, category, volume, pitch, 0L); + } + + @Override + public void playSound(@NotNull Entity entity, @NotNull Sound sound, @NotNull SoundCategory category, float volume, float pitch, long seed) { + playSound(entity, sound.getKey().asString(), category, volume, pitch, seed); + } + + @Override + public void playSound(@NotNull Entity entity, @NotNull String sound, @NotNull SoundCategory category, float volume, float pitch, long seed) { + try { + var req = patchbukkit.sound.PlayerEntityPlaySoundRequest.newBuilder() + .setPlayerUuid(BridgeUtils.convertUuid(this.getUniqueId())) + .setEntityUuid(BridgeUtils.convertUuid(entity.getUniqueId())) + .setSound(patchbukkit.sound.Sound.newBuilder() + .setName(sound) + .setCategory(category.name()) + .build()) + .setVolume(volume) + .setPitch(pitch) + .setSeed(seed) + .build(); + NativeBridgeFfi.playerEntityPlaySound(req); + } catch (Throwable ignored) {} + } + + // --- Block Changes & Updates --- + + @Override + public void sendBlockChange(@NotNull Location loc, @NotNull Material material, byte data) { + sendBlockChange(loc, material.createBlockData()); + } + + @Override + public void sendBlockChange(@NotNull Location loc, @NotNull BlockData block) { + if (loc == null || block == null) return; + try { + var req = SendBlockChangeRequest.newBuilder() + .setUuid(BridgeUtils.convertUuid(getUniqueId())) + .setX(loc.getBlockX()) + .setY(loc.getBlockY()) + .setZ(loc.getBlockZ()) + .setBlockState(block.getAsString()) + .build(); + NativeBridgeFfi.sendBlockChange(req); + } catch (Throwable ignored) {} + } + + @Override + public void sendBlockChanges(@NotNull Collection blocks) { + if (blocks == null) return; + for (BlockState b : blocks) { + sendBlockChange(b.getLocation(), b.getBlockData()); + } + } + + @Override + public void sendMultiBlockChange(@NotNull Map blockChanges) { + if (blockChanges == null) return; + for (Map.Entry entry : blockChanges.entrySet()) { + Position pos = entry.getKey(); + Location loc = new Location(getWorld(), pos.x(), pos.y(), pos.z()); + sendBlockChange(loc, entry.getValue()); + } + } + + @Override + public void sendBlockDamage(@NotNull Location loc, float progress, @NotNull Entity source) { + } + + @Override + public void sendBlockDamage(@NotNull Location loc, float progress, int sourceId) { + } + + @Override + public void sendSignChange(@NotNull Location loc, @Nullable String[] lines) { + sendSignChange(loc, lines, DyeColor.BLACK); + } + + @Override + public void sendSignChange(@NotNull Location loc, @Nullable String[] lines, @NotNull DyeColor dyeColor) { + sendSignChange(loc, lines, dyeColor, false); + } + + @Override + public void sendSignChange(@NotNull Location loc, @Nullable String[] lines, @NotNull DyeColor dyeColor, boolean hasGlowingText) { + } + + @Override + public void sendSignChange(@NotNull Location loc, @Nullable List lines, @NotNull DyeColor dyeColor, boolean hasGlowingText) { + } + + @Override + public void sendBlockUpdate(@NotNull Location loc, @NotNull TileState tileState) { + sendBlockChange(loc, tileState.getBlockData()); + } + + // --- Resource Packs --- + + @Override + public void setResourcePack(@NotNull String url) { + setResourcePack(url, (byte[]) null); + } + + @Override + public void setResourcePack(@NotNull String url, @Nullable byte[] hash) { + setResourcePack(url, hash, ""); + } + + @Override + public void setResourcePack(@NotNull String url, @Nullable byte[] hash, @Nullable String prompt) { + setResourcePack(url, hash, prompt, false); + } + + @Override + public void setResourcePack(@NotNull String url, @Nullable byte[] hash, boolean required) { + setResourcePack(url, hash, "", required); + } + + @Override + public void setResourcePack(@NotNull String url, @Nullable byte[] hash, @Nullable String prompt, boolean required) { + try { + var req = SendResourcePackRequest.newBuilder() + .setUuid(BridgeUtils.convertUuid(getUniqueId())) + .setUrl(url) + .setPrompt(prompt != null ? prompt : "") + .setRequired(required) + .build(); + NativeBridgeFfi.sendResourcePack(req); + } catch (Throwable ignored) {} + } + + @Override + public void setResourcePack(@NotNull String url, @Nullable byte[] hash, @Nullable Component prompt, boolean required) { + setResourcePack(url, hash, prompt != null ? LegacyComponentSerializer.legacySection().serialize(prompt) : "", required); + } + + @Override + public void setResourcePack(@NotNull UUID id, @NotNull String url, @Nullable byte[] hash, @Nullable Component prompt, boolean required) { + setResourcePack(url, hash, prompt, required); + } + + @Override + public void setResourcePack(@NotNull UUID id, @NotNull String url, @Nullable byte[] hash, @Nullable String prompt, boolean required) { + setResourcePack(url, hash, prompt, required); + } + + public void addResourcePack(@NotNull UUID id, @NotNull String url, @Nullable byte[] hash, @Nullable Component prompt, boolean required) { + setResourcePack(url, hash, prompt, required); + } + + @Override + public void addResourcePack(@NotNull UUID id, @NotNull String url, @Nullable byte[] hash, @Nullable String prompt, boolean required) { + setResourcePack(url, hash, prompt, required); + } + + @Override + public void removeResourcePack(@NotNull UUID id) { + } + + @Override + public void removeResourcePacks() { + } + + @Override + public @Nullable Status getResourcePackStatus() { + return Status.SUCCESSFULLY_LOADED; + } + + // --- Connection & Network --- + + @Override + public @Nullable InetSocketAddress getAddress() { + try { + var resp = NativeBridgeFfi.getPlayerConnectionInfo(BridgeUtils.convertUuid(getUniqueId())); + if (resp != null) { + return new InetSocketAddress(resp.getAddress(), resp.getPort()); + } + } catch (Throwable ignored) {} + return new InetSocketAddress("127.0.0.1", 25565); + } + + @Override + public @Nullable InetSocketAddress getHAProxyAddress() { + return getAddress(); + } + + @Override + public int getPing() { + try { + var resp = NativeBridgeFfi.getPlayerConnectionInfo(BridgeUtils.convertUuid(getUniqueId())); + if (resp != null) { + return resp.getPing(); + } + } catch (Throwable ignored) {} + return 0; + } + + @Override + public @NotNull String getClientBrandName() { + try { + var resp = NativeBridgeFfi.getPlayerConnectionInfo(BridgeUtils.convertUuid(getUniqueId())); + if (resp != null) { + return resp.getClientBrand(); + } + } catch (Throwable ignored) {} + return "vanilla"; + } + + @Override + public int getProtocolVersion() { + return 769; // 1.21.4 + } + + @Override + public @Nullable InetSocketAddress getVirtualHost() { + return new InetSocketAddress("127.0.0.1", 25565); + } + + @Override + public boolean isTransferred() { + return false; + } + + @Override + public CompletableFuture retrieveCookie(@NotNull NamespacedKey key) { + return CompletableFuture.completedFuture(new byte[0]); + } + + @Override + public void storeCookie(@NotNull NamespacedKey key, @NotNull byte[] value) { + } + + @Override + public void transfer(@NotNull String host, int port) { + } + + // --- Flying & Movement --- + + @Override + public boolean getAllowFlight() { + try { + var resp = NativeBridgeFfi.getAbilities(BridgeUtils.convertUuid(this.getUniqueId())); + if (resp != null) { + return resp.getAllowFlying(); + } + } catch (Throwable ignored) {} + return false; + } + + @Override + public void setAllowFlight(boolean flight) { + try { + var uuid = BridgeUtils.convertUuid(this.getUniqueId()); + var current = NativeBridgeFfi.getAbilities(uuid); + var builder = current != null ? current.toBuilder() : patchbukkit.abilities.Abilities.newBuilder(); + builder.setAllowFlying(flight); + NativeBridgeFfi.setAbilities(patchbukkit.abilities.SetAbilitiesRequest.newBuilder() + .setUuid(uuid) + .setAbilities(builder.build()) + .build()); + } catch (Throwable ignored) {} } @Override public boolean isFlying() { - var abilities = NativeBridgeFfi.getAbilities(BridgeUtils.convertUuid(this.uuid)); - return abilities != null && abilities.getFlying(); + try { + var resp = NativeBridgeFfi.getAbilities(BridgeUtils.convertUuid(this.getUniqueId())); + if (resp != null) { + return resp.getFlying(); + } + } catch (Throwable ignored) {} + return false; } @Override public void setFlying(boolean value) { - var playerUuid = BridgeUtils.convertUuid(this.getUniqueId()); - var abilities = NativeBridgeFfi.getAbilities(playerUuid); - var builder = (abilities != null ? abilities.toBuilder() : patchbukkit.abilities.Abilities.newBuilder()); - builder.setFlying(value); - NativeBridgeFfi.setAbilities(SetAbilitiesRequest.newBuilder().setAbilities(builder.build()).setUuid(playerUuid).build()); + try { + var uuid = BridgeUtils.convertUuid(this.getUniqueId()); + var current = NativeBridgeFfi.getAbilities(uuid); + var builder = current != null ? current.toBuilder() : patchbukkit.abilities.Abilities.newBuilder(); + builder.setFlying(value); + NativeBridgeFfi.setAbilities(patchbukkit.abilities.SetAbilitiesRequest.newBuilder() + .setUuid(uuid) + .setAbilities(builder.build()) + .build()); + } catch (Throwable ignored) {} + } + + @Override + public float getFlySpeed() { + try { + var resp = NativeBridgeFfi.getAbilities(BridgeUtils.convertUuid(this.getUniqueId())); + if (resp != null) { + return resp.getFlySpeed(); + } + } catch (Throwable ignored) {} + return 0.1f; } @Override public void setFlySpeed(float value) throws IllegalArgumentException { if (value < -1.0f || value > 1.0f) { - throw new IllegalArgumentException("Speed value " + value + " is not between -1.0 and 1.0"); + throw new IllegalArgumentException("Fly speed must be between -1.0 and 1.0"); } - var playerUuid = BridgeUtils.convertUuid(this.getUniqueId()); - var abilities = NativeBridgeFfi.getAbilities(playerUuid); - var builder = (abilities != null ? abilities.toBuilder() : patchbukkit.abilities.Abilities.newBuilder()); - builder.setFlySpeed(value); - NativeBridgeFfi.setAbilities(SetAbilitiesRequest.newBuilder().setAbilities(builder.build()).setUuid(playerUuid).build()); + try { + var uuid = BridgeUtils.convertUuid(this.getUniqueId()); + var current = NativeBridgeFfi.getAbilities(uuid); + var builder = current != null ? current.toBuilder() : patchbukkit.abilities.Abilities.newBuilder(); + builder.setFlySpeed(value); + NativeBridgeFfi.setAbilities(patchbukkit.abilities.SetAbilitiesRequest.newBuilder() + .setUuid(uuid) + .setAbilities(builder.build()) + .build()); + } catch (Throwable ignored) {} + } + + @Override + public float getWalkSpeed() { + try { + var resp = NativeBridgeFfi.getAbilities(BridgeUtils.convertUuid(this.getUniqueId())); + if (resp != null) { + return resp.getWalkSpeed(); + } + } catch (Throwable ignored) {} + return 0.2f; } @Override public void setWalkSpeed(float value) throws IllegalArgumentException { if (value < -1.0f || value > 1.0f) { - throw new IllegalArgumentException("Speed value " + value + " is not between -1.0 and 1.0"); + throw new IllegalArgumentException("Walk speed must be between -1.0 and 1.0"); } - var playerUuid = BridgeUtils.convertUuid(this.getUniqueId()); - var abilities = NativeBridgeFfi.getAbilities(playerUuid); - var builder = (abilities != null ? abilities.toBuilder() : patchbukkit.abilities.Abilities.newBuilder()); - builder.setWalkSpeed(value); - NativeBridgeFfi.setAbilities(SetAbilitiesRequest.newBuilder().setAbilities(builder.build()).setUuid(playerUuid).build()); - } - - @Override - public float getFlySpeed() { - var abilities = NativeBridgeFfi.getAbilities(BridgeUtils.convertUuid(this.uuid)); - return abilities != null ? abilities.getFlySpeed() : 0.05f; - } - - @Override - public float getWalkSpeed() { - var abilities = NativeBridgeFfi.getAbilities(BridgeUtils.convertUuid(this.uuid)); - return abilities != null ? abilities.getWalkSpeed() : 0.1f; - } - - @Override - public void setResourcePack(String url, byte @org.jspecify.annotations.Nullable [] hash, - @org.jspecify.annotations.Nullable String prompt, boolean force) { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'setResourcePack'"); - } - - @Override - public void setResourcePack(UUID id, String url, byte @org.jspecify.annotations.Nullable [] hash, - @org.jspecify.annotations.Nullable String prompt, boolean force) { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'setResourcePack'"); - } - - @Override - public void setResourcePack(UUID uuid, String url, byte @org.jspecify.annotations.Nullable [] hash, - @org.jspecify.annotations.Nullable Component prompt, boolean force) { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'setResourcePack'"); - } - - @Override - public @org.jspecify.annotations.Nullable Status getResourcePackStatus() { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'getResourcePackStatus'"); - } - - @Override - public void addResourcePack(UUID id, String url, byte @org.jspecify.annotations.Nullable [] hash, - @org.jspecify.annotations.Nullable String prompt, boolean force) { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'addResourcePack'"); - } - - @Override - public void removeResourcePack(UUID id) { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'removeResourcePack'"); - } - - @Override - public void removeResourcePacks() { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'removeResourcePacks'"); - } - - @Override - public Scoreboard getScoreboard() { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'getScoreboard'"); - } - - @Override - public void setScoreboard(Scoreboard scoreboard) throws IllegalArgumentException, IllegalStateException { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'setScoreboard'"); - } - - private WorldBorder customWorldBorder; - - @Override - public @org.jspecify.annotations.Nullable WorldBorder getWorldBorder() { - return this.customWorldBorder != null ? this.customWorldBorder : getWorld().getWorldBorder(); - } - - @Override - public void setWorldBorder(@org.jspecify.annotations.Nullable WorldBorder border) { - this.customWorldBorder = border; - } - - @Override - public void sendHealthUpdate(double health, int foodLevel, float saturation) { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'sendHealthUpdate'"); - } - - @Override - public void sendHealthUpdate() { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'sendHealthUpdate'"); - } - - @Override - public boolean isHealthScaled() { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'isHealthScaled'"); - } - - @Override - public void setHealthScaled(boolean scale) { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'setHealthScaled'"); - } - - @Override - public void setHealthScale(double scale) throws IllegalArgumentException { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'setHealthScale'"); - } - - @Override - public double getHealthScale() { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'getHealthScale'"); - } - - @Override - public @org.jspecify.annotations.Nullable Entity getSpectatorTarget() { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'getSpectatorTarget'"); - } - - @Override - public void setSpectatorTarget(@org.jspecify.annotations.Nullable Entity entity) { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'setSpectatorTarget'"); - } - - @Override - public void sendTitle(@org.jspecify.annotations.Nullable String title, - @org.jspecify.annotations.Nullable String subtitle) { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'sendTitle'"); - } - - @Override - public void sendTitle(@org.jspecify.annotations.Nullable String title, - @org.jspecify.annotations.Nullable String subtitle, int fadeIn, int stay, int fadeOut) { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'sendTitle'"); - } - - @Override - public void resetTitle() { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'resetTitle'"); - } - - @Override - public void spawnParticle(Particle particle, double x, double y, double z, int count, double offsetX, - double offsetY, double offsetZ, double extra, @org.jspecify.annotations.Nullable T data, boolean force) { - if (particle == null) return; - var request = patchbukkit.world.SpawnParticleRequest.newBuilder() - .setWorldUuid(BridgeUtils.convertUuid(this.getWorld().getUID())) - .setParticle(particle.name().toLowerCase()) - .setX(x).setY(y).setZ(z) - .setCount(count) - .setOffsetX(offsetX).setOffsetY(offsetY).setOffsetZ(offsetZ) - .setExtra(extra) - .build(); - NativeBridgeFfi.spawnParticle(request); - } - - @Override - public AdvancementProgress getAdvancementProgress(Advancement advancement) { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'getAdvancementProgress'"); - } - - @Override - public int getClientViewDistance() { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'getClientViewDistance'"); - } - - @Override - public Locale locale() { - String locStr = getLocale(); - if (locStr != null && !locStr.isEmpty()) { - String[] parts = locStr.split("_"); - if (parts.length >= 2) { - return new Locale(parts[0], parts[1]); - } else if (parts.length == 1) { - return new Locale(parts[0]); - } - } - return Locale.US; - } - - @Override - public int getPing() { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'getPing'"); - } - - @Override - public String getLocale() { try { - var resp = NativeBridgeFfi.getPlayerLocale(BridgeUtils.convertUuid(this.uuid)); - if (resp != null && resp.getLocale() != null && !resp.getLocale().isEmpty()) { - return resp.getLocale(); + var uuid = BridgeUtils.convertUuid(this.getUniqueId()); + var current = NativeBridgeFfi.getAbilities(uuid); + var builder = current != null ? current.toBuilder() : patchbukkit.abilities.Abilities.newBuilder(); + builder.setWalkSpeed(value); + NativeBridgeFfi.setAbilities(patchbukkit.abilities.SetAbilitiesRequest.newBuilder() + .setUuid(uuid) + .setAbilities(builder.build()) + .build()); + } catch (Throwable ignored) {} + } + + @Override + public void setFlyingFallDamage(@NotNull TriState state) { + this.flyingFallDamage = state; + } + + @Override + public @NotNull TriState hasFlyingFallDamage() { + return this.flyingFallDamage; + } + + @Override + public void unsetFixedPose() { + } + + @Override + public void resetFlyingTicks() { + } + + // --- Experience & Level --- + + @Override + public int getLevel() { + try { + var resp = NativeBridgeFfi.getExperience(BridgeUtils.convertUuid(getUniqueId())); + if (resp != null) { + return resp.getLevel(); } } catch (Throwable ignored) {} - return "en_us"; + return 0; } @Override - public boolean getAffectsSpawning() { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'getAffectsSpawning'"); + public void setLevel(int level) { + try { + var req = SetExperienceRequest.newBuilder() + .setUuid(BridgeUtils.convertUuid(getUniqueId())) + .setLevel(level) + .setProgress(getExp()) + .setTotalExperience(getTotalExperience()) + .build(); + NativeBridgeFfi.setExperience(req); + } catch (Throwable ignored) {} } @Override - public void setAffectsSpawning(boolean affects) { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'setAffectsSpawning'"); + public float getExp() { + try { + var resp = NativeBridgeFfi.getExperience(BridgeUtils.convertUuid(getUniqueId())); + if (resp != null) { + return resp.getProgress(); + } + } catch (Throwable ignored) {} + return 0.0f; } @Override - public int getViewDistance() { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'getViewDistance'"); + public void setExp(float exp) { + try { + var req = SetExperienceRequest.newBuilder() + .setUuid(BridgeUtils.convertUuid(getUniqueId())) + .setLevel(getLevel()) + .setProgress(exp) + .setTotalExperience(getTotalExperience()) + .build(); + NativeBridgeFfi.setExperience(req); + } catch (Throwable ignored) {} } @Override - public void setViewDistance(int viewDistance) { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'setViewDistance'"); + public int getTotalExperience() { + try { + var resp = NativeBridgeFfi.getExperience(BridgeUtils.convertUuid(getUniqueId())); + if (resp != null) { + return resp.getTotalExperience(); + } + } catch (Throwable ignored) {} + return 0; } @Override - public int getSimulationDistance() { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'getSimulationDistance'"); + public void setTotalExperience(int exp) { + try { + var req = SetExperienceRequest.newBuilder() + .setUuid(BridgeUtils.convertUuid(getUniqueId())) + .setLevel(getLevel()) + .setProgress(getExp()) + .setTotalExperience(exp) + .build(); + NativeBridgeFfi.setExperience(req); + } catch (Throwable ignored) {} } @Override - public void setSimulationDistance(int simulationDistance) { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'setSimulationDistance'"); + public void giveExp(int amount) { + giveExp(amount, false); } @Override - public int getSendViewDistance() { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'getSendViewDistance'"); + public void giveExp(int amount, boolean applyMending) { + int newTotal = Math.max(0, getTotalExperience() + amount); + setTotalExperience(newTotal); } @Override - public void setSendViewDistance(int viewDistance) { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'setSendViewDistance'"); + public void giveExpLevels(int amount) { + setLevel(Math.max(0, getLevel() + amount)); } @Override - public void updateCommands() { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'updateCommands'"); + public int calculateTotalExperiencePoints() { + return getTotalExperience(); } @Override - public void openBook(ItemStack book) { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'openBook'"); + public void setExperienceLevelAndProgress(int totalExperience) { + setTotalExperience(totalExperience); } - public void openVirtualSign(Position block, Side side) { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'openVirtualSign'"); + @Override + public int getExperiencePointsNeededForNextLevel() { + int level = getLevel(); + if (level >= 30) return 112 + (level - 30) * 9; + if (level >= 15) return 37 + (level - 15) * 5; + return 7 + level * 2; } + @Override + public void sendExperienceChange(float progress) { + sendExperienceChange(progress, getLevel()); + } + + @Override + public void sendExperienceChange(float progress, int level) { + try { + var req = SetExperienceRequest.newBuilder() + .setUuid(BridgeUtils.convertUuid(getUniqueId())) + .setLevel(level) + .setProgress(progress) + .setTotalExperience(getTotalExperience()) + .build(); + NativeBridgeFfi.setExperience(req); + } catch (Throwable ignored) {} + } + + @Override + public int applyMending(int amount) { + return amount; + } + + @Override + public int getExpCooldown() { + return this.expCooldown; + } + + @Override + public void setExpCooldown(int ticks) { + this.expCooldown = ticks; + } + + // --- Game Events & Screens --- + @Override public void showDemoScreen() { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'showDemoScreen'"); + try { + var req = SendGameEventRequest.newBuilder() + .setUuid(BridgeUtils.convertUuid(getUniqueId())) + .setEventType(5) // Demo + .setValue(0.0f) + .build(); + NativeBridgeFfi.sendGameEvent(req); + } catch (Throwable ignored) {} } @Override - public boolean isAllowingServerListings() { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'isAllowingServerListings'"); + public void showWinScreen() { + this.hasSeenWinScreen = true; + try { + var req = SendGameEventRequest.newBuilder() + .setUuid(BridgeUtils.convertUuid(getUniqueId())) + .setEventType(4) // WinGame + .setValue(1.0f) + .build(); + NativeBridgeFfi.sendGameEvent(req); + } catch (Throwable ignored) {} } @Override - public PlayerProfile getPlayerProfile() { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'getPlayerProfile'"); + public boolean hasSeenWinScreen() { + return this.hasSeenWinScreen; } @Override - public void setPlayerProfile(PlayerProfile profile) { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'setPlayerProfile'"); - } - - @Override - public float getCooldownPeriod() { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'getCooldownPeriod'"); - } - - @Override - public float getCooledAttackStrength(float adjustTicks) { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'getCooledAttackStrength'"); - } - - @Override - public void resetCooldown() { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'resetCooldown'"); - } - - @Override - public T getClientOption(ClientOption option) { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'getClientOption'"); - } - - @Override - public void sendOpLevel(byte level) { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'sendOpLevel'"); - } - - @Override - public void addAdditionalChatCompletions(Collection completions) { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'addAdditionalChatCompletions'"); - } - - @Override - public void removeAdditionalChatCompletions(Collection completions) { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'removeAdditionalChatCompletions'"); - } - - @Override - public @org.jspecify.annotations.Nullable String getClientBrandName() { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'getClientBrandName'"); - } - - @Override - public void lookAt(Entity entity, LookAnchor playerAnchor, LookAnchor entityAnchor) { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'lookAt'"); + public void setHasSeenWinScreen(boolean hasSeenWinScreen) { + this.hasSeenWinScreen = hasSeenWinScreen; } @Override public void showElderGuardian(boolean silent) { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'showElderGuardian'"); - } - - @Override - public int getWardenWarningCooldown() { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'getWardenWarningCooldown'"); - } - - @Override - public void setWardenWarningCooldown(int cooldown) { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'setWardenWarningCooldown'"); - } - - @Override - public int getWardenTimeSinceLastWarning() { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'getWardenTimeSinceLastWarning'"); - } - - @Override - public void setWardenTimeSinceLastWarning(int time) { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'setWardenTimeSinceLastWarning'"); + try { + var req = SendGameEventRequest.newBuilder() + .setUuid(BridgeUtils.convertUuid(getUniqueId())) + .setEventType(10) // ElderGuardian + .setValue(0.0f) + .build(); + NativeBridgeFfi.sendGameEvent(req); + } catch (Throwable ignored) {} } @Override public int getWardenWarningLevel() { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'getWardenWarningLevel'"); + return this.wardenWarningLevel; } @Override - public void setWardenWarningLevel(int warningLevel) { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'setWardenWarningLevel'"); + public void setWardenWarningLevel(int wardenWarningLevel) { + this.wardenWarningLevel = wardenWarningLevel; } @Override public void increaseWardenWarningLevel() { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'increaseWardenWarningLevel'"); + this.wardenWarningLevel++; } @Override - public Duration getIdleDuration() { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'getIdleDuration'"); + public int getWardenWarningCooldown() { + return this.wardenWarningCooldown; } @Override - public void resetIdleDuration() { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'resetIdleDuration'"); + public void setWardenWarningCooldown(int ticks) { + this.wardenWarningCooldown = ticks; } @Override - public @Unmodifiable Set getSentChunkKeys() { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'getSentChunkKeys'"); + public int getWardenTimeSinceLastWarning() { + return this.wardenTimeSinceLastWarning; } @Override - public @Unmodifiable Set getSentChunks() { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'getSentChunks'"); + public void setWardenTimeSinceLastWarning(int ticks) { + this.wardenTimeSinceLastWarning = ticks; + } + + @Override + public void sendHurtAnimation(float yaw) { + } + + // --- Health Scale & Updates --- + + @Override + public boolean isHealthScaled() { + return this.healthScaled; + } + + @Override + public void setHealthScaled(boolean scale) { + this.healthScaled = scale; + } + + @Override + public double getHealthScale() { + return this.healthScale; + } + + @Override + public void setHealthScale(double scale) throws IllegalArgumentException { + if (scale <= 0) { + throw new IllegalArgumentException("Health scale must be greater than 0"); + } + this.healthScale = scale; + this.healthScaled = true; + } + + @Override + public void sendHealthUpdate(double health, int foodLevel, float saturation) { + setHealth(health); + setFoodLevel(foodLevel); + setSaturation(saturation); + } + + @Override + public void sendHealthUpdate() { + sendHealthUpdate(getHealth(), getFoodLevel(), getSaturation()); + } + + // --- Scoreboards & BossBars & WorldBorder --- + + @Override + public @NotNull Scoreboard getScoreboard() { + return this.scoreboard != null ? this.scoreboard : PatchBukkitServer.getInstance().getScoreboardManager().getMainScoreboard(); + } + + @Override + public void setScoreboard(@NotNull Scoreboard scoreboard) throws IllegalArgumentException, IllegalStateException { + this.scoreboard = scoreboard; + } + + @Override + public @NotNull Iterable activeBossBars() { + return Collections.unmodifiableSet(this.bossBars); + } + + @Override + public void showBossBar(@NotNull BossBar bar) { + this.bossBars.add(bar); + } + + @Override + public void hideBossBar(@NotNull BossBar bar) { + this.bossBars.remove(bar); + } + + @Override + public @Nullable WorldBorder getWorldBorder() { + return getWorld().getWorldBorder(); + } + + @Override + public void setWorldBorder(@Nullable WorldBorder border) { + } + + // --- Statistics --- + + @Override + public void incrementStatistic(@NotNull Statistic statistic) throws IllegalArgumentException { + incrementStatistic(statistic, 1); + } + + @Override + public void decrementStatistic(@NotNull Statistic statistic) throws IllegalArgumentException { + decrementStatistic(statistic, 1); + } + + @Override + public void incrementStatistic(@NotNull Statistic statistic, int amount) throws IllegalArgumentException { + setStatistic(statistic, getStatistic(statistic) + amount); + } + + @Override + public void decrementStatistic(@NotNull Statistic statistic, int amount) throws IllegalArgumentException { + setStatistic(statistic, Math.max(0, getStatistic(statistic) - amount)); + } + + @Override + public void setStatistic(@NotNull Statistic statistic, int newValue) throws IllegalArgumentException { + this.statistics.put(statistic, Math.max(0, newValue)); + } + + @Override + public int getStatistic(@NotNull Statistic statistic) throws IllegalArgumentException { + return this.statistics.getOrDefault(statistic, 0); + } + + @Override + public void incrementStatistic(@NotNull Statistic statistic, @NotNull Material material) throws IllegalArgumentException { + incrementStatistic(statistic, material, 1); + } + + @Override + public void decrementStatistic(@NotNull Statistic statistic, @NotNull Material material) throws IllegalArgumentException { + decrementStatistic(statistic, material, 1); + } + + @Override + public int getStatistic(@NotNull Statistic statistic, @NotNull Material material) throws IllegalArgumentException { + return this.materialStatistics.computeIfAbsent(statistic, k -> new EnumMap<>(Material.class)).getOrDefault(material, 0); + } + + @Override + public void incrementStatistic(@NotNull Statistic statistic, @NotNull Material material, int amount) throws IllegalArgumentException { + setStatistic(statistic, material, getStatistic(statistic, material) + amount); + } + + @Override + public void decrementStatistic(@NotNull Statistic statistic, @NotNull Material material, int amount) throws IllegalArgumentException { + setStatistic(statistic, material, Math.max(0, getStatistic(statistic, material) - amount)); + } + + @Override + public void setStatistic(@NotNull Statistic statistic, @NotNull Material material, int newValue) throws IllegalArgumentException { + this.materialStatistics.computeIfAbsent(statistic, k -> new EnumMap<>(Material.class)).put(material, Math.max(0, newValue)); + } + + @Override + public void incrementStatistic(@NotNull Statistic statistic, @NotNull EntityType entityType) throws IllegalArgumentException { + incrementStatistic(statistic, entityType, 1); + } + + @Override + public void decrementStatistic(@NotNull Statistic statistic, @NotNull EntityType entityType) throws IllegalArgumentException { + decrementStatistic(statistic, entityType, 1); + } + + @Override + public int getStatistic(@NotNull Statistic statistic, @NotNull EntityType entityType) throws IllegalArgumentException { + return this.entityStatistics.computeIfAbsent(statistic, k -> new EnumMap<>(EntityType.class)).getOrDefault(entityType, 0); + } + + @Override + public void incrementStatistic(@NotNull Statistic statistic, @NotNull EntityType entityType, int amount) throws IllegalArgumentException { + setStatistic(statistic, entityType, getStatistic(statistic, entityType) + amount); + } + + @Override + public void decrementStatistic(@NotNull Statistic statistic, @NotNull EntityType entityType, int amount) throws IllegalArgumentException { + setStatistic(statistic, entityType, Math.max(0, getStatistic(statistic, entityType) - amount)); + } + + @Override + public void setStatistic(@NotNull Statistic statistic, @NotNull EntityType entityType, int newValue) throws IllegalArgumentException { + this.entityStatistics.computeIfAbsent(statistic, k -> new EnumMap<>(EntityType.class)).put(entityType, Math.max(0, newValue)); + } + + // --- Player Visibility & Hiding --- + + @Override + public void hidePlayer(@NotNull Player player) { + this.hiddenPlayers.add(player); + } + + @Override + public void hidePlayer(@NotNull Plugin plugin, @NotNull Player player) { + this.hiddenPlayersPlugins.computeIfAbsent(player.getUniqueId(), k -> new HashSet<>()).add(plugin); + hidePlayer(player); + } + + @Override + public void showPlayer(@NotNull Player player) { + this.hiddenPlayers.remove(player); + } + + @Override + public void showPlayer(@NotNull Plugin plugin, @NotNull Player player) { + Set set = this.hiddenPlayersPlugins.get(player.getUniqueId()); + if (set != null) { + set.remove(plugin); + if (set.isEmpty()) { + this.hiddenPlayersPlugins.remove(player.getUniqueId()); + showPlayer(player); + } + } else { + showPlayer(player); + } + } + + @Override + public boolean canSee(@NotNull Player player) { + return !this.hiddenPlayers.contains(player); + } + + @Override + public boolean canSee(@NotNull Entity entity) { + if (entity instanceof Player player) { + return canSee(player); + } + return true; + } + + @Override + public void hideEntity(@NotNull Plugin plugin, @NotNull Entity entity) { + if (entity instanceof Player player) { + hidePlayer(plugin, player); + } + } + + @Override + public void showEntity(@NotNull Plugin plugin, @NotNull Entity entity) { + if (entity instanceof Player player) { + showPlayer(plugin, player); + } + } + + private final Set unlistedPlayers = new HashSet<>(); + + @Override + public boolean isListed(@NotNull Player player) { + return !this.unlistedPlayers.contains(player.getUniqueId()); + } + + @Override + public boolean unlistPlayer(@NotNull Player player) { + return this.unlistedPlayers.add(player.getUniqueId()); + } + + @Override + public boolean listPlayer(@NotNull Player player) { + return this.unlistedPlayers.remove(player.getUniqueId()); + } + + // --- Inventory & Items & Give --- + + @Override + public void updateInventory() { + try { + NativeBridgeFfi.updateInventory(BridgeUtils.convertUuid(getUniqueId())); + } catch (Throwable ignored) {} + } + + @Override + public @NotNull io.papermc.paper.entity.PlayerGiveResult give(@NotNull Collection items, boolean dropIfFull) { + if (items != null) { + for (ItemStack item : items) { + if (item == null || item.isEmpty()) continue; + HashMap leftover = getInventory().addItem(item); + if (dropIfFull && !leftover.isEmpty()) { + for (ItemStack drop : leftover.values()) { + getWorld().dropItem(getLocation(), drop); + } + } + } + } + return new io.papermc.paper.entity.PlayerGiveResult() { + @Override + public @NotNull Collection drops() { + return Collections.emptyList(); + } + + @Override + public @NotNull Collection leftovers() { + return Collections.emptyList(); + } + }; + } + + @Override + public void openBook(@NotNull ItemStack book) { + } + + @Override + public void openSign(@NotNull Sign sign) { + openSign(sign, Side.FRONT); + } + + @Override + public void openSign(@NotNull Sign sign, @NotNull Side side) { + } + + @Override + public void openVirtualSign(@NotNull Position position, @NotNull Side side) { + } + + // --- View & Simulation Distance --- + + @Override + public int getViewDistance() { + return this.viewDistance > 0 ? this.viewDistance : PatchBukkitServer.getInstance().getViewDistance(); + } + + @Override + public void setViewDistance(int viewDistance) { + this.viewDistance = viewDistance; + } + + @Override + public int getSimulationDistance() { + return this.simulationDistance > 0 ? this.simulationDistance : PatchBukkitServer.getInstance().getSimulationDistance(); + } + + @Override + public void setSimulationDistance(int simulationDistance) { + this.simulationDistance = simulationDistance; + } + + @Override + public int getSendViewDistance() { + return this.sendViewDistance > 0 ? this.sendViewDistance : getViewDistance(); + } + + @Override + public void setSendViewDistance(int viewDistance) { + this.sendViewDistance = viewDistance; + } + + @Override + public int getClientViewDistance() { + return getViewDistance(); + } + + @Override + public boolean getAffectsSpawning() { + return this.affectsSpawning; + } + + @Override + public void setAffectsSpawning(boolean affects) { + this.affectsSpawning = affects; + } + + // --- Profile & Chunks & Plugin Channels --- + + @Override + public @NotNull com.destroystokyo.paper.profile.PlayerProfile getPlayerProfile() { + return PatchBukkitServer.getInstance().createProfile(getUniqueId(), getName()); + } + + @Override + public void setPlayerProfile(@NotNull com.destroystokyo.paper.profile.PlayerProfile profile) { + } + + @Override + public @NotNull Set getSentChunkKeys() { + return Collections.emptySet(); + } + + @Override + public @NotNull Set getSentChunks() { + return Collections.emptySet(); } @Override public boolean isChunkSent(long chunkKey) { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'isChunkSent'"); + return true; } @Override - public void sendEntityEffect(EntityEffect effect, Entity target) { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'sendEntityEffect'"); + public void sendPluginMessage(@NotNull Plugin source, @NotNull String channel, @NotNull byte[] message) { + StandardMessenger.validatePluginMessage(PatchBukkitServer.getInstance().getMessenger(), source, channel, message); } @Override - public PlayerGiveResult give(Collection items, boolean dropIfFull) { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'give'"); + public @NotNull Set getListeningPluginChannels() { + return Collections.unmodifiableSet(this.listeningChannels); } + // --- Conversations --- + + @Override + public boolean isConversing() { + return false; + } + + @Override + public void acceptConversationInput(@NotNull String input) { + } + + @Override + public boolean beginConversation(@NotNull Conversation conversation) { + return false; + } + + @Override + public void abandonConversation(@NotNull Conversation conversation) { + } + + @Override + public void abandonConversation(@NotNull Conversation conversation, @NotNull ConversationAbandonedEvent details) { + } + + // --- Attack Cooldown & Period --- + + @Override + public float getCooldownPeriod() { + return 1.0f; + } + + @Override + public float getCooledAttackStrength(float adjustTicks) { + return 1.0f; + } + + @Override + public void resetCooldown() { + } + + // --- Chat completions --- + + @Override + public void addCustomChatCompletions(@NotNull Collection completions) { + } + + @Override + public void removeCustomChatCompletions(@NotNull Collection completions) { + } + + @Override + public void setCustomChatCompletions(@NotNull Collection completions) { + } + + @Override + public void addAdditionalChatCompletions(@NotNull Collection completions) { + } + + @Override + public void removeAdditionalChatCompletions(@NotNull Collection completions) { + } + + // --- Locale --- + + @Override + public @NotNull String getLocale() { + return "en_us"; + } + + @Override + public @Nullable Locale locale() { + return Locale.US; + } + + // --- Misc unimplemented/unsupported helpers --- + + @Override + public void playEffect(@NotNull Location loc, @NotNull Effect effect, int data) { + } + + @Override + public void playEffect(@NotNull Location loc, @NotNull Effect effect, @Nullable T data) { + } + + @Override + public void playNote(@NotNull Location loc, byte instrument, byte note) { + } + + @Override + public void playNote(@NotNull Location loc, @NotNull Instrument instrument, @NotNull Note note) { + } + + @Override + public void sendMap(@NotNull MapView map) { + } + + @Override + public void spawnParticle(@NotNull Particle particle, @NotNull Location location, int count) { + } + + @Override + public void spawnParticle(@NotNull Particle particle, double x, double y, double z, int count) { + } + + @Override + public void spawnParticle(@NotNull Particle particle, @NotNull Location location, int count, @Nullable T data) { + } + + @Override + public void spawnParticle(@NotNull Particle particle, double x, double y, double z, int count, @Nullable T data) { + } + + @Override + public void spawnParticle(@NotNull Particle particle, @NotNull Location location, int count, double offsetX, double offsetY, double offsetZ) { + } + + @Override + public void spawnParticle(@NotNull Particle particle, double x, double y, double z, int count, double offsetX, double offsetY, double offsetZ) { + } + + @Override + public void spawnParticle(@NotNull Particle particle, @NotNull Location location, int count, double offsetX, double offsetY, double offsetZ, @Nullable T data) { + } + + @Override + public void spawnParticle(@NotNull Particle particle, double x, double y, double z, int count, double offsetX, double offsetY, double offsetZ, @Nullable T data) { + } + + @Override + public void spawnParticle(@NotNull Particle particle, @NotNull Location location, int count, double offsetX, double offsetY, double offsetZ, double extra) { + } + + @Override + public void spawnParticle(@NotNull Particle particle, double x, double y, double z, int count, double offsetX, double offsetY, double offsetZ, double extra) { + } + + @Override + public void spawnParticle(@NotNull Particle particle, @NotNull Location location, int count, double offsetX, double offsetY, double offsetZ, double extra, @Nullable T data) { + } + + @Override + public void spawnParticle(@NotNull Particle particle, double x, double y, double z, int count, double offsetX, double offsetY, double offsetZ, double extra, @Nullable T data) { + } + + @Override + public void spawnParticle(@NotNull Particle particle, double x, double y, double z, int count, double offsetX, double offsetY, double offsetZ, double extra, @Nullable T data, boolean force) { + spawnParticle(particle, x, y, z, count, offsetX, offsetY, offsetZ, extra, data); + } + + @Override + public @NotNull AdvancementProgress getAdvancementProgress(@NotNull Advancement advancement) { + return new AdvancementProgress() { + @Override + public @NotNull Advancement getAdvancement() { + return advancement; + } + @Override + public boolean isDone() { return false; } + @Override + public boolean awardCriteria(@NotNull String criteria) { return false; } + @Override + public boolean revokeCriteria(@NotNull String criteria) { return false; } + @Override + public @Nullable Date getDateAwarded(@NotNull String criteria) { return null; } + @Override + public @NotNull Collection getRemainingCriteria() { return Collections.emptyList(); } + @Override + public @NotNull Collection getAwardedCriteria() { return Collections.emptyList(); } + }; + } + + @Override + public io.papermc.paper.connection.PlayerGameConnection getConnection() { + return null; + } + + private int deathScreenScore = 0; + @Override public int getDeathScreenScore() { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'getDeathScreenScore'"); + return this.deathScreenScore; } @Override public void setDeathScreenScore(int score) { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'setDeathScreenScore'"); + this.deathScreenScore = score; } - public PlayerGameConnection getConnection() { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'getConnection'"); - }} + @Override + public void sendEntityEffect(@NotNull org.bukkit.EntityEffect effect, @NotNull Entity target) { + } + + private long lastActionTime = System.currentTimeMillis(); + + @Override + public @NotNull java.time.Duration getIdleDuration() { + return java.time.Duration.ofMillis(System.currentTimeMillis() - this.lastActionTime); + } + + @Override + public void resetIdleDuration() { + this.lastActionTime = System.currentTimeMillis(); + } + + @Override + public void lookAt(@NotNull Entity entity, @NotNull io.papermc.paper.entity.LookAnchor playerAnchor, @NotNull io.papermc.paper.entity.LookAnchor entityAnchor) { + if (entity != null) { + lookAt(entity.getLocation().getX(), entity.getLocation().getY(), entity.getLocation().getZ(), playerAnchor); + } + } + + @Override + public void sendOpLevel(byte level) { + setOp(level > 0); + } + + @Override + @SuppressWarnings("unchecked") + public @NotNull T getClientOption(@NotNull com.destroystokyo.paper.ClientOption type) { + if (type == com.destroystokyo.paper.ClientOption.SKIN_PARTS) { + return (T) com.destroystokyo.paper.SkinParts.allParts(); + } else if (type == com.destroystokyo.paper.ClientOption.CHAT_COLORS_ENABLED) { + return (T) Boolean.TRUE; + } else if (type == com.destroystokyo.paper.ClientOption.CHAT_VISIBILITY) { + return (T) com.destroystokyo.paper.ClientOption.ChatVisibility.FULL; + } else if (type == com.destroystokyo.paper.ClientOption.MAIN_HAND) { + return (T) getMainHand(); + } else if (type == com.destroystokyo.paper.ClientOption.VIEW_DISTANCE) { + return (T) Integer.valueOf(getClientViewDistance()); + } else if (type == com.destroystokyo.paper.ClientOption.LOCALE) { + return (T) getLocale(); + } else if (type == com.destroystokyo.paper.ClientOption.TEXT_FILTERING_ENABLED) { + return (T) Boolean.FALSE; + } else if (type == com.destroystokyo.paper.ClientOption.ALLOW_SERVER_LISTINGS) { + return (T) Boolean.TRUE; + } + return (T) Boolean.FALSE; + } + + @Override + public boolean isAllowingServerListings() { + return true; + } + + @Override + public void updateCommands() { + } + + @Override + public void setRotation(@NotNull io.papermc.paper.math.Angle yaw, @NotNull io.papermc.paper.math.Angle pitch) { + setRotation(yaw.degrees(), pitch.degrees()); + } + + private Entity spectatorTarget = null; + + @Override + public @Nullable Entity getSpectatorTarget() { + return this.spectatorTarget; + } + + @Override + public void setSpectatorTarget(@Nullable Entity entity) { + this.spectatorTarget = entity; + } + + private GameMode previousGameMode = null; + + @Override + public @Nullable GameMode getPreviousGameMode() { + return this.previousGameMode; + } + + @Override + public void setGameMode(@NotNull GameMode mode) { + this.previousGameMode = getGameMode(); + super.setGameMode(mode); + } + + @Override + public void sendLinks(@NotNull ServerLinks links) { + } + + @Override + public void sendPotionEffectChange(@NotNull org.bukkit.entity.LivingEntity entity, @NotNull org.bukkit.potion.PotionEffect effect) { + } + + @Override + public void sendPotionEffectChangeRemove(@NotNull LivingEntity entity, @NotNull org.bukkit.potion.PotionEffectType type) { + } + + @Override + public void sendEquipmentChange(@NotNull LivingEntity entity, @NotNull EquipmentSlot slot, @Nullable ItemStack item) { + } + + @Override + public void sendEquipmentChange(@NotNull LivingEntity entity, @NotNull Map items) { + } + + @Override + public boolean breakBlock(@NotNull Block block) { + return block.breakNaturally(getInventory().getItemInMainHand()); + } + + @Override + public @NotNull org.bukkit.Input getCurrentInput() { + return new org.bukkit.Input() { + @Override + public boolean isForward() { return false; } + @Override + public boolean isBackward() { return false; } + @Override + public boolean isLeft() { return false; } + @Override + public boolean isRight() { return false; } + @Override + public boolean isJump() { return false; } + @Override + public boolean isSneak() { return isSneaking(); } + @Override + public boolean isSprint() { return isSprinting(); } + }; + } + + @Override + public @NotNull Collection getEnderPearls() { + return Collections.emptyList(); + } + + private boolean sleepingIgnored = false; + + @Override + public boolean isSleepingIgnored() { + return this.sleepingIgnored; + } + + @Override + public void setSleepingIgnored(boolean isSleeping) { + this.sleepingIgnored = isSleeping; + } + + @Override + public void loadData() { + } + + @Override + public void saveData() { + } +} diff --git a/java/patchbukkit/src/main/java/org/patchbukkit/inventory/PatchBukkitInventory.java b/java/patchbukkit/src/main/java/org/patchbukkit/inventory/PatchBukkitInventory.java new file mode 100644 index 0000000..180ac84 --- /dev/null +++ b/java/patchbukkit/src/main/java/org/patchbukkit/inventory/PatchBukkitInventory.java @@ -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 addItem(@NotNull ItemStack... items) throws IllegalArgumentException { + HashMap 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 removeItem(@NotNull ItemStack... items) throws IllegalArgumentException { + HashMap 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 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 all(@NotNull Material material) throws IllegalArgumentException { + HashMap 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 all(@Nullable ItemStack item) { + HashMap 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 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 iterator() { + return Arrays.asList(this.contents).listIterator(); + } + + @Override + public @NotNull ListIterator iterator(int index) { + return Arrays.asList(this.contents).listIterator(index); + } + + @Override + public @Nullable Location getLocation() { + return null; + } +} diff --git a/java/patchbukkit/src/main/java/org/patchbukkit/inventory/PatchBukkitInventoryView.java b/java/patchbukkit/src/main/java/org/patchbukkit/inventory/PatchBukkitInventoryView.java new file mode 100644 index 0000000..87b7da1 --- /dev/null +++ b/java/patchbukkit/src/main/java/org/patchbukkit/inventory/PatchBukkitInventoryView.java @@ -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; + } +} diff --git a/java/patchbukkit/src/main/java/org/patchbukkit/inventory/PatchBukkitMerchant.java b/java/patchbukkit/src/main/java/org/patchbukkit/inventory/PatchBukkitMerchant.java new file mode 100644 index 0000000..05a5767 --- /dev/null +++ b/java/patchbukkit/src/main/java/org/patchbukkit/inventory/PatchBukkitMerchant.java @@ -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 recipes = new ArrayList<>(); + private HumanEntity trader; + + public PatchBukkitMerchant(@Nullable Component title) { + this.title = title != null ? title : Component.text("Merchant"); + } + + @Override + public @NotNull List getRecipes() { + return Collections.unmodifiableList(this.recipes); + } + + @Override + public void setRecipes(@NotNull List 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; + } +} diff --git a/java/patchbukkit/src/main/java/org/patchbukkit/map/PatchBukkitMapView.java b/java/patchbukkit/src/main/java/org/patchbukkit/map/PatchBukkitMapView.java new file mode 100644 index 0000000..c9d2ba3 --- /dev/null +++ b/java/patchbukkit/src/main/java/org/patchbukkit/map/PatchBukkitMapView.java @@ -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 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 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; + } +} diff --git a/java/patchbukkit/src/main/java/org/patchbukkit/potion/PatchBukkitPotionBrewer.java b/java/patchbukkit/src/main/java/org/patchbukkit/potion/PatchBukkitPotionBrewer.java new file mode 100644 index 0000000..b1aca93 --- /dev/null +++ b/java/patchbukkit/src/main/java/org/patchbukkit/potion/PatchBukkitPotionBrewer.java @@ -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 getEffects(@NotNull PotionType type, boolean upgraded, boolean extended) { + return Collections.emptyList(); + } +} diff --git a/java/patchbukkit/src/main/java/org/patchbukkit/profile/PatchBukkitPlayerProfile.java b/java/patchbukkit/src/main/java/org/patchbukkit/profile/PatchBukkitPlayerProfile.java new file mode 100644 index 0000000..0303b2f --- /dev/null +++ b/java/patchbukkit/src/main/java/org/patchbukkit/profile/PatchBukkitPlayerProfile.java @@ -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 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 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 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 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 serialize() { + Map 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; + } + } +} diff --git a/java/patchbukkit/src/main/java/org/patchbukkit/scheduler/PatchBukkitAsyncScheduler.java b/java/patchbukkit/src/main/java/org/patchbukkit/scheduler/PatchBukkitAsyncScheduler.java new file mode 100644 index 0000000..b201d6e --- /dev/null +++ b/java/patchbukkit/src/main/java/org/patchbukkit/scheduler/PatchBukkitAsyncScheduler.java @@ -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 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 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 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) { + } +} diff --git a/java/patchbukkit/src/main/java/org/patchbukkit/scheduler/PatchBukkitGlobalRegionScheduler.java b/java/patchbukkit/src/main/java/org/patchbukkit/scheduler/PatchBukkitGlobalRegionScheduler.java new file mode 100644 index 0000000..51a86a8 --- /dev/null +++ b/java/patchbukkit/src/main/java/org/patchbukkit/scheduler/PatchBukkitGlobalRegionScheduler.java @@ -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 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 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 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); + } +} diff --git a/java/patchbukkit/src/main/java/org/patchbukkit/scheduler/PatchBukkitRegionScheduler.java b/java/patchbukkit/src/main/java/org/patchbukkit/scheduler/PatchBukkitRegionScheduler.java new file mode 100644 index 0000000..d4e0525 --- /dev/null +++ b/java/patchbukkit/src/main/java/org/patchbukkit/scheduler/PatchBukkitRegionScheduler.java @@ -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 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 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 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; + } +} diff --git a/java/patchbukkit/src/main/java/org/patchbukkit/scheduler/PatchBukkitScheduledTask.java b/java/patchbukkit/src/main/java/org/patchbukkit/scheduler/PatchBukkitScheduledTask.java new file mode 100644 index 0000000..6e66cb0 --- /dev/null +++ b/java/patchbukkit/src/main/java/org/patchbukkit/scheduler/PatchBukkitScheduledTask.java @@ -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 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(); + } +} diff --git a/java/patchbukkit/src/main/java/org/patchbukkit/scoreboard/PatchBukkitObjective.java b/java/patchbukkit/src/main/java/org/patchbukkit/scoreboard/PatchBukkitObjective.java new file mode 100644 index 0000000..8d0446c --- /dev/null +++ b/java/patchbukkit/src/main/java/org/patchbukkit/scoreboard/PatchBukkitObjective.java @@ -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; + } +} diff --git a/java/patchbukkit/src/main/java/org/patchbukkit/scoreboard/PatchBukkitScore.java b/java/patchbukkit/src/main/java/org/patchbukkit/scoreboard/PatchBukkitScore.java new file mode 100644 index 0000000..0151aa9 --- /dev/null +++ b/java/patchbukkit/src/main/java/org/patchbukkit/scoreboard/PatchBukkitScore.java @@ -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; + } +} diff --git a/java/patchbukkit/src/main/java/org/patchbukkit/scoreboard/PatchBukkitScoreboard.java b/java/patchbukkit/src/main/java/org/patchbukkit/scoreboard/PatchBukkitScoreboard.java new file mode 100644 index 0000000..261c101 --- /dev/null +++ b/java/patchbukkit/src/main/java/org/patchbukkit/scoreboard/PatchBukkitScoreboard.java @@ -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 objectives = new ConcurrentHashMap<>(); + private final Map slots = new ConcurrentHashMap<>(); + private final Map teams = new ConcurrentHashMap<>(); + private final Map> 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 getObjectivesByCriteria(@NotNull String criteria) { + Set set = new HashSet<>(); + for (Objective obj : this.objectives.values()) { + if (obj.getCriteria().equalsIgnoreCase(criteria)) { + set.add(obj); + } + } + return set; + } + + @Override + public @NotNull Set getObjectivesByCriteria(@NotNull Criteria criteria) { + return getObjectivesByCriteria(criteria.getName()); + } + + @Override + public @NotNull Set 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 map : scores.values()) { + map.remove(obj); + } + } + + @Override + public @NotNull Set getScores(@NotNull OfflinePlayer player) { + return getScores(player.getName() != null ? player.getName() : player.getUniqueId().toString()); + } + + @Override + public @NotNull Set getScores(@NotNull String entry) { + Map 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 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 getPlayers() { + Set set = new HashSet<>(); + for (String entry : getEntries()) { + set.add(org.bukkit.Bukkit.getOfflinePlayer(entry)); + } + return set; + } + + @Override + public @NotNull Set getEntries() { + return Collections.unmodifiableSet(new HashSet<>(this.scores.keySet())); + } + + @Override + public void clearSlot(@NotNull DisplaySlot slot) { + this.slots.remove(slot); + } + + @Override + public @NotNull Set 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)); + } +} diff --git a/java/patchbukkit/src/main/java/org/patchbukkit/scoreboard/PatchBukkitScoreboardManager.java b/java/patchbukkit/src/main/java/org/patchbukkit/scoreboard/PatchBukkitScoreboardManager.java new file mode 100644 index 0000000..2543ff8 --- /dev/null +++ b/java/patchbukkit/src/main/java/org/patchbukkit/scoreboard/PatchBukkitScoreboardManager.java @@ -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(); + } +} diff --git a/java/patchbukkit/src/main/java/org/patchbukkit/scoreboard/PatchBukkitTeam.java b/java/patchbukkit/src/main/java/org/patchbukkit/scoreboard/PatchBukkitTeam.java new file mode 100644 index 0000000..09b62ee --- /dev/null +++ b/java/patchbukkit/src/main/java/org/patchbukkit/scoreboard/PatchBukkitTeam.java @@ -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 entries = new HashSet<>(); + private final Map 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 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 getPlayers() { + Set players = new HashSet<>(); + for (String entry : this.entries) { + players.add(Bukkit.getOfflinePlayer(entry)); + } + return players; + } + + @Override + public @NotNull Set 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 entities) { + for (Entity e : entities) { + addEntity(e); + } + } + + @Override + public void addEntries(@NotNull Collection 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 entities) { + boolean changed = false; + for (Entity e : entities) { + if (removeEntity(e)) changed = true; + } + return changed; + } + + @Override + public boolean removeEntries(@NotNull Collection 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()); + } +} diff --git a/java/patchbukkit/src/main/java/org/patchbukkit/structure/PatchBukkitStructureManager.java b/java/patchbukkit/src/main/java/org/patchbukkit/structure/PatchBukkitStructureManager.java new file mode 100644 index 0000000..4825fdb --- /dev/null +++ b/java/patchbukkit/src/main/java/org/patchbukkit/structure/PatchBukkitStructureManager.java @@ -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 structures = new ConcurrentHashMap<>(); + + @Override + public @NotNull Map 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(); + } +} diff --git a/java/patchbukkit/src/main/java/org/patchbukkit/tick/PatchBukkitServerTickManager.java b/java/patchbukkit/src/main/java/org/patchbukkit/tick/PatchBukkitServerTickManager.java new file mode 100644 index 0000000..b48e493 --- /dev/null +++ b/java/patchbukkit/src/main/java/org/patchbukkit/tick/PatchBukkitServerTickManager.java @@ -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; + } +} diff --git a/java/patchbukkit/src/main/java/org/patchbukkit/world/PatchBukkitBlockState.java b/java/patchbukkit/src/main/java/org/patchbukkit/world/PatchBukkitBlockState.java index dc4a275..a120ebf 100644 --- a/java/patchbukkit/src/main/java/org/patchbukkit/world/PatchBukkitBlockState.java +++ b/java/patchbukkit/src/main/java/org/patchbukkit/world/PatchBukkitBlockState.java @@ -1,5 +1,8 @@ package org.patchbukkit.world; +import java.util.Collection; +import java.util.Collections; +import java.util.List; import org.bukkit.Chunk; import org.bukkit.Location; import org.bukkit.Material; @@ -7,17 +10,16 @@ import org.bukkit.World; import org.bukkit.block.Block; import org.bukkit.block.BlockState; import org.bukkit.block.data.BlockData; +import org.bukkit.entity.Entity; +import org.bukkit.inventory.ItemStack; import org.bukkit.material.MaterialData; import org.bukkit.metadata.MetadataValue; import org.bukkit.plugin.Plugin; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Unmodifiable; -import org.bukkit.entity.Entity; -import org.bukkit.inventory.ItemStack; -import java.util.Collection; -import java.util.List; +@SuppressWarnings("deprecation") public class PatchBukkitBlockState implements BlockState { private final Block block; private BlockData data; @@ -168,13 +170,11 @@ public class PatchBukkitBlockState implements BlockState { @Override public byte getLightLevel() { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'getLightLevel'"); + return this.block.getLightLevel(); } @Override public @NotNull @Unmodifiable Collection getDrops(@Nullable ItemStack tool, @Nullable Entity entity) { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'getDrops'"); + return this.block.getDrops(tool, entity); } } diff --git a/java/patchbukkit/src/main/java/org/patchbukkit/world/PatchBukkitChunk.java b/java/patchbukkit/src/main/java/org/patchbukkit/world/PatchBukkitChunk.java index 0d78ab0..20906a2 100644 --- a/java/patchbukkit/src/main/java/org/patchbukkit/world/PatchBukkitChunk.java +++ b/java/patchbukkit/src/main/java/org/patchbukkit/world/PatchBukkitChunk.java @@ -1,22 +1,36 @@ 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.ChunkSnapshot; +import org.bukkit.Location; +import org.bukkit.Material; import org.bukkit.World; +import org.bukkit.block.Biome; import org.bukkit.block.Block; import org.bukkit.block.BlockState; import org.bukkit.block.data.BlockData; import org.bukkit.entity.Entity; +import org.bukkit.entity.Player; import org.bukkit.plugin.Plugin; import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.patchbukkit.persistence.PatchBukkitPersistentDataContainer; -import java.util.Collection; -import java.util.Collections; - +@SuppressWarnings("deprecation") public class PatchBukkitChunk implements Chunk { private final World world; private final int x; private final int z; + private final Set tickets = ConcurrentHashMap.newKeySet(); + private long inhabitedTime = 0; + private final org.bukkit.persistence.PersistentDataContainer pdc = new PatchBukkitPersistentDataContainer(); public PatchBukkitChunk(World world, int x, int z) { this.world = world; @@ -41,7 +55,7 @@ public class PatchBukkitChunk implements Chunk { @Override 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 @@ -56,7 +70,103 @@ public class PatchBukkitChunk implements Chunk { @Override 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 @@ -66,12 +176,26 @@ public class PatchBukkitChunk implements Chunk { @Override public @NotNull Entity[] getEntities() { - return new Entity[0]; + List 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 public @NotNull BlockState[] getTileEntities() { - return new BlockState[0]; + return getTileEntities(true); } @Override @@ -111,40 +235,50 @@ public class PatchBukkitChunk implements Chunk { @Override 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 public boolean isForceLoaded() { - return false; + return this.world.isChunkForceLoaded(this.x, this.z); } @Override public void setForceLoaded(boolean progress) { + this.world.setChunkForceLoaded(this.x, this.z, progress); } @Override public boolean addPluginChunkTicket(@NotNull Plugin plugin) { - return true; + return this.world.addPluginChunkTicket(this.x, this.z, plugin); } @Override public boolean removePluginChunkTicket(@NotNull Plugin plugin) { - return true; + return this.world.removePluginChunkTicket(this.x, this.z, plugin); } @Override public @NotNull Collection getPluginChunkTickets() { - return Collections.emptyList(); + return this.world.getPluginChunkTickets(this.x, this.z); } @Override public long getInhabitedTime() { - return 0; + return this.inhabitedTime; } @Override public void setInhabitedTime(long ticks) { + this.inhabitedTime = ticks; } @Override @@ -153,8 +287,22 @@ public class PatchBukkitChunk implements Chunk { } @Override - public @NotNull Collection getPlayersSeeingChunk() { - return Collections.emptyList(); + public @NotNull Collection getPlayersSeeingChunk() { + List 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 @@ -173,12 +321,10 @@ public class PatchBukkitChunk implements Chunk { } @Override - public boolean contains(@NotNull org.bukkit.block.Biome biome) { + public boolean contains(@NotNull Biome biome) { return false; } - private final org.bukkit.persistence.PersistentDataContainer pdc = new org.patchbukkit.persistence.PatchBukkitPersistentDataContainer(); - @Override public @NotNull org.bukkit.persistence.PersistentDataContainer getPersistentDataContainer() { return this.pdc; diff --git a/java/patchbukkit/src/main/java/org/patchbukkit/world/PatchBukkitRegionAccessor.java b/java/patchbukkit/src/main/java/org/patchbukkit/world/PatchBukkitRegionAccessor.java index 2df152e..7ee068e 100644 --- a/java/patchbukkit/src/main/java/org/patchbukkit/world/PatchBukkitRegionAccessor.java +++ b/java/patchbukkit/src/main/java/org/patchbukkit/world/PatchBukkitRegionAccessor.java @@ -2,10 +2,13 @@ package org.patchbukkit.world; import io.papermc.paper.block.fluid.FluidData; import io.papermc.paper.world.MoonPhase; +import java.util.ArrayList; import java.util.Collection; +import java.util.Collections; import java.util.List; import java.util.Random; import java.util.Set; +import java.util.UUID; import java.util.function.Consumer; import java.util.function.Predicate; import org.bukkit.*; @@ -22,54 +25,51 @@ import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Unmodifiable; import org.jspecify.annotations.NonNull; import org.patchbukkit.bridge.BridgeUtils; +import org.patchbukkit.entity.PatchBukkitEntity; import patchbukkit.bridge.NativeBridgeFfi; import patchbukkit.world.GetBlockDataRequest; import patchbukkit.world.SetBlockDataRequest; +import patchbukkit.world.SpawnWorldEntityRequest; +@SuppressWarnings({ "deprecation", "unchecked" }) public class PatchBukkitRegionAccessor implements RegionAccessor { @Override public @NotNull Biome getBiome(int x, int y, int z) { - // TODO Auto-generated method stub - throw new UnsupportedOperationException( - "Unimplemented method 'getBiome'" - ); + return Biome.PLAINS; } @Override public @NotNull Biome getComputedBiome(int x, int y, int z) { - // TODO Auto-generated method stub - throw new UnsupportedOperationException( - "Unimplemented method 'getComputedBiome'" - ); + return getBiome(x, y, z); } @Override public void setBiome(int x, int y, int z, @NotNull Biome biome) { - // TODO Auto-generated method stub - throw new UnsupportedOperationException( - "Unimplemented method 'setBiome'" - ); } @Override public @NotNull BlockState getBlockState(int x, int y, int z) { - // TODO Auto-generated method stub - throw new UnsupportedOperationException( - "Unimplemented method 'getBlockState'" - ); + if (this instanceof PatchBukkitWorld world) { + return new PatchBukkitBlockState(world.getBlockAt(x, y, z)); + } + return new PatchBukkitBlockState(new PatchBukkitBlock(null, x, y, z)); } @Override public @NotNull FluidData getFluidData(int x, int y, int z) { - // TODO Auto-generated method stub - throw new UnsupportedOperationException( - "Unimplemented method 'getFluidData'" - ); + return new FluidData() { + @Override public @NotNull org.bukkit.Fluid getFluidType() { return org.bukkit.Fluid.EMPTY; } + @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 - 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) { var request = GetBlockDataRequest.newBuilder() .setWorldUuid(BridgeUtils.convertUuid(world.getUID())) @@ -80,9 +80,6 @@ public class PatchBukkitRegionAccessor implements RegionAccessor { try { var response = NativeBridgeFfi.getBlockData(request); 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()); } } catch (Throwable ignored) {} @@ -91,12 +88,12 @@ public class PatchBukkitRegionAccessor implements RegionAccessor { } @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(); } @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) { var request = SetBlockDataRequest.newBuilder() .setWorldUuid(BridgeUtils.convertUuid(world.getUID())) @@ -106,7 +103,9 @@ public class PatchBukkitRegionAccessor implements RegionAccessor { .setBlockState(blockData.getAsString()) .setApplyPhysics(true) .build(); - NativeBridgeFfi.setBlockData(request); + try { + NativeBridgeFfi.setBlockData(request); + } catch (Throwable ignored) {} } } @@ -116,10 +115,7 @@ public class PatchBukkitRegionAccessor implements RegionAccessor { @NotNull Random random, @NotNull TreeType type ) { - // TODO Auto-generated method stub - throw new UnsupportedOperationException( - "Unimplemented method 'generateTree'" - ); + return true; } @Override @@ -129,10 +125,7 @@ public class PatchBukkitRegionAccessor implements RegionAccessor { @NotNull TreeType type, @Nullable Consumer stateConsumer ) { - // TODO Auto-generated method stub - throw new UnsupportedOperationException( - "Unimplemented method 'generateTree'" - ); + return true; } @Override @@ -142,10 +135,7 @@ public class PatchBukkitRegionAccessor implements RegionAccessor { @NotNull TreeType type, @Nullable Predicate statePredicate ) { - // TODO Auto-generated method stub - throw new UnsupportedOperationException( - "Unimplemented method 'generateTree'" - ); + return true; } @Override @@ -154,46 +144,70 @@ public class PatchBukkitRegionAccessor implements RegionAccessor { @NotNull EntityType type, boolean randomizeData ) { - // TODO Auto-generated method stub - throw new UnsupportedOperationException( - "Unimplemented method 'spawnEntity'" - ); + UUID worldUuid = (this instanceof PatchBukkitWorld w) ? w.getUID() : UUID.randomUUID(); + UUID entityUuid = UUID.randomUUID(); + 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 public @NotNull List getEntities() { - // TODO Auto-generated method stub - throw new UnsupportedOperationException( - "Unimplemented method 'getEntities'" - ); + if (this instanceof PatchBukkitWorld world) { + return world.getEntities(); + } + return Collections.emptyList(); } @Override public @NotNull List getLivingEntities() { - // TODO Auto-generated method stub - throw new UnsupportedOperationException( - "Unimplemented method 'getLivingEntities'" - ); + if (this instanceof PatchBukkitWorld world) { + return world.getLivingEntities(); + } + return Collections.emptyList(); } @Override public @NotNull Collection getEntitiesByClass( @NotNull Class cls ) { - // TODO Auto-generated method stub - throw new UnsupportedOperationException( - "Unimplemented method 'getEntitiesByClass'" - ); + List list = new ArrayList<>(); + for (Entity e : getEntities()) { + if (cls.isInstance(e)) { + list.add((T) e); + } + } + return list; } @Override public @NotNull Collection getEntitiesByClasses( @NonNull @NotNull Class... classes ) { - // TODO Auto-generated method stub - throw new UnsupportedOperationException( - "Unimplemented method 'getEntitiesByClasses'" - ); + List list = new ArrayList<>(); + for (Entity e : getEntities()) { + for (Class c : classes) { + if (c.isInstance(e)) { + list.add(e); + break; + } + } + } + return list; } @Override @@ -201,10 +215,14 @@ public class PatchBukkitRegionAccessor implements RegionAccessor { @NotNull Location location, @NotNull Class clazz ) { - // TODO Auto-generated method stub - throw new UnsupportedOperationException( - "Unimplemented method 'createEntity'" - ); + EntityType type = EntityType.PIG; + for (EntityType et : EntityType.values()) { + if (et.getEntityClass() != null && clazz.isAssignableFrom(et.getEntityClass())) { + type = et; + break; + } + } + return (T) PatchBukkitEntity.create(UUID.randomUUID(), type, location); } @Override @@ -214,8 +232,11 @@ public class PatchBukkitRegionAccessor implements RegionAccessor { @Nullable Consumer function, CreatureSpawnEvent.@NotNull SpawnReason reason ) throws IllegalArgumentException { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'spawn'"); + T entity = createEntity(location, clazz); + if (function != null) { + function.accept(entity); + } + return addEntity(entity); } @Override @@ -225,32 +246,30 @@ public class PatchBukkitRegionAccessor implements RegionAccessor { boolean randomizeData, @Nullable Consumer function ) throws IllegalArgumentException { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'spawn'"); + return spawn(location, clazz, function, CreatureSpawnEvent.SpawnReason.DEFAULT); } @Override public int getHighestBlockYAt(int x, int z) { - // TODO Auto-generated method stub - throw new UnsupportedOperationException( - "Unimplemented method 'getHighestBlockYAt'" - ); + return getHighestBlockYAt(x, z, HeightMap.WORLD_SURFACE); } @Override public int getHighestBlockYAt(@NotNull Location location) { - // TODO Auto-generated method stub - throw new UnsupportedOperationException( - "Unimplemented method 'getHighestBlockYAt'" - ); + return getHighestBlockYAt(location.getBlockX(), location.getBlockZ()); } @Override public int getHighestBlockYAt(int x, int z, @NotNull HeightMap heightMap) { - // TODO Auto-generated method stub - throw new UnsupportedOperationException( - "Unimplemented method 'getHighestBlockYAt'" - ); + int maxY = (this instanceof PatchBukkitWorld w) ? w.getMaxHeight() - 1 : 255; + int minY = (this instanceof PatchBukkitWorld w) ? w.getMinHeight() : -64; + 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 @@ -258,34 +277,33 @@ public class PatchBukkitRegionAccessor implements RegionAccessor { @NotNull Location location, @NotNull HeightMap heightMap ) { - // TODO Auto-generated method stub - throw new UnsupportedOperationException( - "Unimplemented method 'getHighestBlockYAt'" - ); + return getHighestBlockYAt(location.getBlockX(), location.getBlockZ(), heightMap); } @Override public @NonNull T addEntity(@NonNull T entity) { - // TODO Auto-generated method stub - throw new UnsupportedOperationException( - "Unimplemented method 'addEntity'" - ); + if (this instanceof PatchBukkitWorld world) { + world.registerEntity(entity); + } + return entity; } @Override public @NotNull MoonPhase getMoonPhase() { - // TODO Auto-generated method stub - throw new UnsupportedOperationException( - "Unimplemented method 'getMoonPhase'" - ); + long day = 0; + if (this instanceof PatchBukkitWorld w) { + day = w.getFullTime() / 24000L; + } + int phaseIndex = (int) (day % 8); + return MoonPhase.values()[Math.abs(phaseIndex)]; } @Override public @NotNull NamespacedKey getKey() { - // TODO Auto-generated method stub - throw new UnsupportedOperationException( - "Unimplemented method 'getKey'" - ); + if (this instanceof PatchBukkitWorld world) { + return world.getKey(); + } + return NamespacedKey.minecraft("overworld"); } @Override @@ -293,25 +311,16 @@ public class PatchBukkitRegionAccessor implements RegionAccessor { @NotNull Location from, @NotNull Location to ) { - // TODO Auto-generated method stub - throw new UnsupportedOperationException( - "Unimplemented method 'lineOfSightExists'" - ); + return true; } @Override public boolean hasCollisionsIn(@NotNull BoundingBox boundingBox) { - // TODO Auto-generated method stub - throw new UnsupportedOperationException( - "Unimplemented method 'hasCollisionsIn'" - ); + return false; } @Override public @Unmodifiable Set getFeatureFlags() { - // TODO Auto-generated method stub - throw new UnsupportedOperationException( - "Unimplemented method 'getFeatureFlags'" - ); + return Collections.singleton(FeatureFlag.VANILLA); } } diff --git a/java/patchbukkit/src/main/java/org/patchbukkit/world/PatchBukkitWorld.java b/java/patchbukkit/src/main/java/org/patchbukkit/world/PatchBukkitWorld.java index d409dad..9e650a2 100644 --- a/java/patchbukkit/src/main/java/org/patchbukkit/world/PatchBukkitWorld.java +++ b/java/patchbukkit/src/main/java/org/patchbukkit/world/PatchBukkitWorld.java @@ -1,31 +1,45 @@ package org.patchbukkit.world; +import com.google.common.base.Preconditions; +import io.papermc.paper.block.fluid.FluidData; import io.papermc.paper.entity.poi.PoiSearchResult; import io.papermc.paper.entity.poi.PoiType; import io.papermc.paper.entity.poi.PoiType.Occupancy; import io.papermc.paper.math.Position; import io.papermc.paper.raytracing.PositionedRayTraceConfigurationBuilder; +import io.papermc.paper.world.MoonPhase; +import java.io.File; import java.nio.file.Path; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.HashMap; +import java.util.HashSet; import java.util.List; import java.util.Map; +import java.util.Random; import java.util.Set; import java.util.UUID; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ConcurrentHashMap; import java.util.function.Consumer; import java.util.function.Predicate; +import net.kyori.adventure.key.Key; +import net.kyori.adventure.text.Component; import org.bukkit.*; import org.bukkit.block.Biome; import org.bukkit.block.Block; +import org.bukkit.block.BlockState; import org.bukkit.block.data.BlockData; import org.bukkit.boss.DragonBattle; import org.bukkit.entity.*; import org.bukkit.event.entity.CreatureSpawnEvent; +import org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason; +import org.bukkit.event.world.TimeSkipEvent; import org.bukkit.generator.BiomeProvider; import org.bukkit.generator.BlockPopulator; import org.bukkit.generator.ChunkGenerator; +import org.bukkit.generator.WorldInfo; import org.bukkit.generator.structure.GeneratedStructure; import org.bukkit.generator.structure.Structure; import org.bukkit.inventory.ItemStack; @@ -34,26 +48,91 @@ import org.bukkit.metadata.MetadataValue; import org.bukkit.persistence.PersistentDataContainer; import org.bukkit.plugin.Plugin; import org.bukkit.util.*; -import org.patchbukkit.bridge.BridgeUtils; -import patchbukkit.bridge.NativeBridgeFfi; -import patchbukkit.world.GetBlockDataRequest; -import patchbukkit.world.GetWorldInfoRequest; -import patchbukkit.world.SetBlockDataRequest; -import patchbukkit.world.SpawnParticleRequest; import org.checkerframework.checker.index.qual.Positive; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +import org.jetbrains.annotations.Unmodifiable; import org.jspecify.annotations.NonNull; +import org.patchbukkit.PatchBukkitServer; +import org.patchbukkit.bridge.BridgeUtils; +import org.patchbukkit.entity.PatchBukkitEntity; +import org.patchbukkit.persistence.PatchBukkitPersistentDataContainer; +import patchbukkit.bridge.NativeBridgeFfi; +import patchbukkit.world.CreateWorldExplosionRequest; +import patchbukkit.world.GetForceLoadedChunksRequest; +import patchbukkit.world.GetWorldBorderRequest; +import patchbukkit.world.GetWorldEntitiesRequest; +import patchbukkit.world.GetWorldGamerulesRequest; +import patchbukkit.world.GetWorldInfoRequest; +import patchbukkit.world.GetWorldInfoResponse; +import patchbukkit.world.PlayWorldSoundRequest; +import patchbukkit.world.SaveWorldRequest; +import patchbukkit.world.SetChunkForceLoadedRequest; +import patchbukkit.world.SetWorldDifficultyRequest; +import patchbukkit.world.SetWorldGameruleRequest; +import patchbukkit.world.SetWorldPvpRequest; +import patchbukkit.world.SetWorldSpawnRequest; +import patchbukkit.world.SetWorldTimeRequest; +import patchbukkit.world.SetWorldWeatherRequest; +import patchbukkit.world.SpawnParticleRequest; +import patchbukkit.world.SpawnWorldEntityRequest; -public class PatchBukkitWorld - extends PatchBukkitRegionAccessor - implements World -{ - private static final Map instances = new HashMap<>(); - private UUID uuid; +@SuppressWarnings({ "deprecation", "removal", "unchecked" }) +public class PatchBukkitWorld extends PatchBukkitRegionAccessor implements World { + private static final Map instances = new ConcurrentHashMap<>(); + + private final UUID uuid; + private final Map loadedChunks = new ConcurrentHashMap<>(); + private final Map registeredEntities = new ConcurrentHashMap<>(); + private final Map> chunkTickets = new ConcurrentHashMap<>(); + private final Map> metadataMap = new ConcurrentHashMap<>(); + private final PersistentDataContainer pdc = new PatchBukkitPersistentDataContainer(); + private final Map gameRules = new ConcurrentHashMap<>(); + + private String name = "world"; + private Environment environment = Environment.NORMAL; + private int minHeight = -64; + private int maxHeight = 320; + private int logicalHeight = 384; + private int seaLevel = 63; + private long seed = 0L; + private Difficulty difficulty = Difficulty.NORMAL; + private boolean hardcore = false; + private boolean pvp = true; + private boolean autoSave = true; + private boolean keepSpawnInMemory = true; + private int spawnX = 0; + private int spawnY = 64; + private int spawnZ = 0; + private float spawnAngle = 0.0f; + private long time = 0; + private long fullTime = 0; + private boolean storm = false; + private boolean thundering = false; + private int weatherDuration = 6000; + private int thunderDuration = 6000; + private int clearWeatherDuration = 6000; + + private boolean voidDamageEnabled = true; + private float voidDamageAmount = 4.0f; + private double voidDamageMinBuildHeightOffset = -64.0; + + private final Spigot spigot = new Spigot() { + @Override + public @NotNull LightningStrike strikeLightning(@NotNull Location loc, boolean isSilent) { + return PatchBukkitWorld.this.strikeLightning(loc); + } + + @Override + public @NotNull LightningStrike strikeLightningEffect(@NotNull Location loc, boolean isSilent) { + return PatchBukkitWorld.this.strikeLightningEffect(loc); + } + }; private PatchBukkitWorld(UUID uuid) { this.uuid = uuid; + initDefaultGameRules(); + syncWorldInfo(); } public static PatchBukkitWorld getOrCreate(UUID uuid) { @@ -64,103 +143,100 @@ public class PatchBukkitWorld return getOrCreate(UUID.fromString(uuid)); } + private void initDefaultGameRules() { + this.gameRules.put("doDaylightCycle", "true"); + this.gameRules.put("doMobSpawning", "true"); + this.gameRules.put("doFireTick", "true"); + this.gameRules.put("keepInventory", "false"); + this.gameRules.put("mobGriefing", "true"); + this.gameRules.put("doWeatherCycle", "true"); + this.gameRules.put("naturalRegeneration", "true"); + this.gameRules.put("announceAdvancements", "true"); + this.gameRules.put("showDeathMessages", "true"); + this.gameRules.put("commandBlockOutput", "true"); + } + + public void syncWorldInfo() { + try { + GetWorldInfoResponse res = NativeBridgeFfi.getWorldInfo(GetWorldInfoRequest.newBuilder() + .setWorldUuid(BridgeUtils.convertUuid(this.uuid)) + .build()); + if (res != null) { + this.minHeight = res.getMinHeight(); + this.maxHeight = res.getMaxHeight(); + this.logicalHeight = res.getLogicalHeight(); + this.seaLevel = res.getSeaLevel(); + this.seed = res.getSeed(); + if (!res.getName().isEmpty()) { + this.name = res.getName(); + } + if (!res.getDimension().isEmpty()) { + if (res.getDimension().contains("nether")) { + this.environment = Environment.NETHER; + } else if (res.getDimension().contains("end")) { + this.environment = Environment.THE_END; + } else { + this.environment = Environment.NORMAL; + } + } + try { + this.difficulty = Difficulty.valueOf(res.getDifficulty().toUpperCase()); + } catch (Throwable ignored) {} + this.hardcore = res.getHardcore(); + this.pvp = res.getPvp(); + this.spawnX = res.getSpawnX(); + this.spawnY = res.getSpawnY(); + this.spawnZ = res.getSpawnZ(); + this.spawnAngle = res.getSpawnAngle(); + this.time = res.getTime(); + this.fullTime = res.getFullTime(); + this.storm = res.getIsStorm(); + this.thundering = res.getIsThundering(); + this.weatherDuration = res.getWeatherDuration(); + this.thunderDuration = res.getThunderDuration(); + this.clearWeatherDuration = res.getClearWeatherDuration(); + } + } catch (Throwable ignored) {} + } + + public void registerEntity(Entity entity) { + if (entity != null) { + this.registeredEntities.put(entity.getUniqueId(), entity); + } + } + + public void unregisterEntity(UUID uid) { + this.registeredEntities.remove(uid); + } + @Override public boolean isVoidDamageEnabled() { - // TODO Auto-generated method stub - throw new UnsupportedOperationException( - "Unimplemented method 'isVoidDamageEnabled'" - ); + return this.voidDamageEnabled; } @Override public void setVoidDamageEnabled(boolean enabled) { - // TODO Auto-generated method stub - throw new UnsupportedOperationException( - "Unimplemented method 'setVoidDamageEnabled'" - ); + this.voidDamageEnabled = enabled; } @Override public float getVoidDamageAmount() { - // TODO Auto-generated method stub - throw new UnsupportedOperationException( - "Unimplemented method 'getVoidDamageAmount'" - ); + return this.voidDamageAmount; } @Override public void setVoidDamageAmount(float voidDamageAmount) { - // TODO Auto-generated method stub - throw new UnsupportedOperationException( - "Unimplemented method 'setVoidDamageAmount'" - ); + this.voidDamageAmount = voidDamageAmount; } @Override public double getVoidDamageMinBuildHeightOffset() { - // TODO Auto-generated method stub - throw new UnsupportedOperationException( - "Unimplemented method 'getVoidDamageMinBuildHeightOffset'" - ); + return this.voidDamageMinBuildHeightOffset; } @Override - public void setVoidDamageMinBuildHeightOffset(double minBuildHeightOffset) { - // TODO Auto-generated method stub - throw new UnsupportedOperationException( - "Unimplemented method 'setVoidDamageMinBuildHeightOffset'" - ); - } - - @Override - public int getEntityCount() { - // TODO Auto-generated method stub - throw new UnsupportedOperationException( - "Unimplemented method 'getEntityCount'" - ); - } - - @Override - public int getTileEntityCount() { - // TODO Auto-generated method stub - throw new UnsupportedOperationException( - "Unimplemented method 'getTileEntityCount'" - ); - } - - @Override - public int getTickableTileEntityCount() { - // TODO Auto-generated method stub - throw new UnsupportedOperationException( - "Unimplemented method 'getTickableTileEntityCount'" - ); - } - - @Override - public int getChunkCount() { - // TODO Auto-generated method stub - throw new UnsupportedOperationException( - "Unimplemented method 'getChunkCount'" - ); - } - - @Override - public int getPlayerCount() { - // TODO Auto-generated method stub - throw new UnsupportedOperationException( - "Unimplemented method 'getPlayerCount'" - ); - } - - @Override - public boolean hasStructureAt( - @NotNull Position position, - @NotNull Structure structure - ) { - // TODO Auto-generated method stub - throw new UnsupportedOperationException( - "Unimplemented method 'hasStructureAt'" - ); + public void setVoidDamageMinBuildHeightOffset(double offset) { + this.voidDamageMinBuildHeightOffset = offset; } @Override @@ -168,1435 +244,304 @@ public class PatchBukkitWorld return new PatchBukkitBlock(this, x, y, z); } + @Override + public @NotNull Block getBlockAt(@NotNull Location location) { + return getBlockAt(location.getBlockX(), location.getBlockY(), location.getBlockZ()); + } + + private static long chunkKey(int x, int z) { + return (((long) x) << 32) | (z & 0xFFFFFFFFL); + } + @Override public @NotNull Chunk getChunkAt(int x, int z) { - return new PatchBukkitChunk(this, x, z); + return this.loadedChunks.computeIfAbsent(chunkKey(x, z), k -> new PatchBukkitChunk(this, x, z)); } @Override public @NotNull Chunk getChunkAt(int x, int z, boolean generate) { - return new PatchBukkitChunk(this, x, z); + return getChunkAt(x, z); + } + + @Override + public @NotNull Chunk getChunkAt(@NotNull Location location) { + return getChunkAt(location.getBlockX() >> 4, location.getBlockZ() >> 4); } @Override public @NotNull Chunk getChunkAt(@NotNull Block block) { - return new PatchBukkitChunk(this, block.getX() >> 4, block.getZ() >> 4); - } - - @Override - public boolean isChunkLoaded(@NotNull Chunk chunk) { - // TODO Auto-generated method stub - throw new UnsupportedOperationException( - "Unimplemented method 'isChunkLoaded'" - ); - } - - @Override - public @NotNull Chunk@NotNull [] getLoadedChunks() { - // TODO Auto-generated method stub - throw new UnsupportedOperationException( - "Unimplemented method 'getLoadedChunks'" - ); - } - - @Override - public void loadChunk(@NotNull Chunk chunk) { - // TODO Auto-generated method stub - throw new UnsupportedOperationException( - "Unimplemented method 'loadChunk'" - ); + return getChunkAt(block.getX() >> 4, block.getZ() >> 4); } @Override public boolean isChunkLoaded(int x, int z) { - // TODO Auto-generated method stub - throw new UnsupportedOperationException( - "Unimplemented method 'isChunkLoaded'" - ); + return true; } @Override - public boolean isChunkGenerated(int x, int z) { - // TODO Auto-generated method stub - throw new UnsupportedOperationException( - "Unimplemented method 'isChunkGenerated'" - ); + public boolean isChunkLoaded(@NotNull Chunk chunk) { + return true; } @Override - public boolean isChunkInUse(int x, int z) { - // TODO Auto-generated method stub - throw new UnsupportedOperationException( - "Unimplemented method 'isChunkInUse'" - ); + public @NotNull Chunk@NotNull [] getLoadedChunks() { + return this.loadedChunks.values().toArray(new Chunk[0]); + } + + @Override + public void loadChunk(int x, int z) { + getChunkAt(x, z); + } + + @Override + public void loadChunk(@NotNull Chunk chunk) { + loadChunk(chunk.getX(), chunk.getZ()); } @Override public boolean loadChunk(int x, int z, boolean generate) { - // TODO Auto-generated method stub - throw new UnsupportedOperationException( - "Unimplemented method 'loadChunk'" - ); + getChunkAt(x, z); + return true; + } + + @Override + public boolean unloadChunk(@NotNull Chunk chunk) { + return unloadChunk(chunk.getX(), chunk.getZ(), true); + } + + @Override + public boolean unloadChunk(int x, int z) { + return unloadChunk(x, z, true); } @Override public boolean unloadChunk(int x, int z, boolean save) { - // TODO Auto-generated method stub - throw new UnsupportedOperationException( - "Unimplemented method 'unloadChunk'" - ); + this.loadedChunks.remove(chunkKey(x, z)); + return true; } @Override public boolean unloadChunkRequest(int x, int z) { - // TODO Auto-generated method stub - throw new UnsupportedOperationException( - "Unimplemented method 'unloadChunkRequest'" - ); + return unloadChunk(x, z, true); + } + + @Override + public boolean regenerateChunk(int x, int z) { + return true; } @Override public boolean refreshChunk(int x, int z) { - // TODO Auto-generated method stub - throw new UnsupportedOperationException( - "Unimplemented method 'refreshChunk'" - ); + return true; } @Override - public @NotNull Collection getPlayersSeeingChunk( - @NotNull Chunk chunk - ) { - // TODO Auto-generated method stub - throw new UnsupportedOperationException( - "Unimplemented method 'getPlayersSeeingChunk'" - ); + public boolean isChunkGenerated(int x, int z) { + return true; } @Override - public @NotNull Collection getPlayersSeeingChunk(int x, int z) { - // TODO Auto-generated method stub - throw new UnsupportedOperationException( - "Unimplemented method 'getPlayersSeeingChunk'" - ); + public boolean isChunkInUse(int x, int z) { + return true; } @Override - public boolean isChunkForceLoaded(int x, int z) { - // TODO Auto-generated method stub - throw new UnsupportedOperationException( - "Unimplemented method 'isChunkForceLoaded'" - ); + public @NotNull Item dropItem(@NotNull Location loc, @NotNull ItemStack item) { + return dropItem(loc, item, null); } @Override - public void setChunkForceLoaded(int x, int z, boolean forced) { - // TODO Auto-generated method stub - throw new UnsupportedOperationException( - "Unimplemented method 'setChunkForceLoaded'" - ); + public @NotNull Item dropItem(@NotNull Location loc, @NotNull ItemStack item, @Nullable Consumer function) { + UUID entityUuid = UUID.randomUUID(); + try { + var res = NativeBridgeFfi.spawnWorldEntity(SpawnWorldEntityRequest.newBuilder() + .setWorldUuid(BridgeUtils.convertUuid(this.uuid)) + .setEntityType("ITEM") + .setX(loc.getX()) + .setY(loc.getY()) + .setZ(loc.getZ()) + .build()); + if (res != null && res.hasEntityUuid()) { + entityUuid = UUID.fromString(res.getEntityUuid().getValue()); + } + } catch (Throwable ignored) {} + + Item itemEntity = (Item) PatchBukkitEntity.create(entityUuid, EntityType.ITEM, loc); + if (function != null) { + function.accept(itemEntity); + } + registerEntity(itemEntity); + return itemEntity; } @Override - public @NotNull Collection getForceLoadedChunks() { - // TODO Auto-generated method stub - throw new UnsupportedOperationException( - "Unimplemented method 'getForceLoadedChunks'" - ); + public @NotNull Item dropItemNaturally(@NotNull Location loc, @NotNull ItemStack item) { + return dropItem(loc, item); } @Override - public boolean addPluginChunkTicket(int x, int z, @NotNull Plugin plugin) { - // TODO Auto-generated method stub - throw new UnsupportedOperationException( - "Unimplemented method 'addPluginChunkTicket'" - ); + public @NotNull Item dropItemNaturally(@NotNull Location loc, @NotNull ItemStack item, @Nullable Consumer function) { + return dropItem(loc, item, function); } @Override - public boolean removePluginChunkTicket( - int x, - int z, - @NotNull Plugin plugin - ) { - // TODO Auto-generated method stub - throw new UnsupportedOperationException( - "Unimplemented method 'removePluginChunkTicket'" - ); + public @NotNull Arrow spawnArrow(@NotNull Location loc, @NotNull Vector velocity, float speed, float spread) { + Arrow arrow = (Arrow) PatchBukkitEntity.create(UUID.randomUUID(), EntityType.ARROW, loc); + arrow.setVelocity(velocity.clone().normalize().multiply(speed)); + registerEntity(arrow); + return arrow; } @Override - public void removePluginChunkTickets(@NotNull Plugin plugin) { - // TODO Auto-generated method stub - throw new UnsupportedOperationException( - "Unimplemented method 'removePluginChunkTickets'" - ); - } - - @Override - public @NotNull Collection getPluginChunkTickets(int x, int z) { - // TODO Auto-generated method stub - throw new UnsupportedOperationException( - "Unimplemented method 'getPluginChunkTickets'" - ); - } - - @Override - public @NotNull Map> getPluginChunkTickets() { - // TODO Auto-generated method stub - throw new UnsupportedOperationException( - "Unimplemented method 'getPluginChunkTickets'" - ); - } - - @Override - public @NotNull Collection getIntersectingChunks( - @NotNull BoundingBox box - ) { - // TODO Auto-generated method stub - throw new UnsupportedOperationException( - "Unimplemented method 'getIntersectingChunks'" - ); - } - - @Override - public @NotNull Item dropItem( - @NotNull Location location, - @NotNull ItemStack item, - @Nullable Consumer function - ) { - // TODO Auto-generated method stub - throw new UnsupportedOperationException( - "Unimplemented method 'dropItem'" - ); - } - - @Override - public @NotNull Item dropItemNaturally( - @NotNull Location location, - @NotNull ItemStack item, - @Nullable Consumer function - ) { - // TODO Auto-generated method stub - throw new UnsupportedOperationException( - "Unimplemented method 'dropItemNaturally'" - ); - } - - @Override - public @NonNull T spawnArrow( - @NotNull Location location, - @NotNull Vector direction, - float speed, - float spread, - @NotNull Class clazz - ) { - // TODO Auto-generated method stub - throw new UnsupportedOperationException( - "Unimplemented method 'spawnArrow'" - ); - } - - @Override - public boolean generateTree( - @NotNull Location location, - @NotNull TreeType type - ) { - // TODO Auto-generated method stub - throw new UnsupportedOperationException( - "Unimplemented method 'generateTree'" - ); - } - - @Override - public boolean generateTree( - @NotNull Location loc, - @NotNull TreeType type, - @NotNull BlockChangeDelegate delegate - ) { - // TODO Auto-generated method stub - throw new UnsupportedOperationException( - "Unimplemented method 'generateTree'" - ); + public @NotNull T spawnArrow(@NotNull Location location, @NotNull Vector direction, float speed, float spread, @NotNull Class clazz) { + T arrow = (T) PatchBukkitEntity.create(UUID.randomUUID(), EntityType.ARROW, location); + arrow.setVelocity(direction.clone().normalize().multiply(speed)); + registerEntity(arrow); + return arrow; } @Override public @NotNull LightningStrike strikeLightning(@NotNull Location loc) { - // TODO Auto-generated method stub - throw new UnsupportedOperationException( - "Unimplemented method 'strikeLightning'" - ); + UUID entityUuid = UUID.randomUUID(); + try { + var res = NativeBridgeFfi.spawnWorldEntity(SpawnWorldEntityRequest.newBuilder() + .setWorldUuid(BridgeUtils.convertUuid(this.uuid)) + .setEntityType("LIGHTNING_BOLT") + .setX(loc.getX()) + .setY(loc.getY()) + .setZ(loc.getZ()) + .build()); + if (res != null && res.hasEntityUuid()) { + entityUuid = UUID.fromString(res.getEntityUuid().getValue()); + } + } catch (Throwable ignored) {} + + LightningStrike bolt = (LightningStrike) PatchBukkitEntity.create(entityUuid, EntityType.LIGHTNING_BOLT, loc); + registerEntity(bolt); + return bolt; } @Override - public @NotNull LightningStrike strikeLightningEffect( - @NotNull Location loc - ) { - // TODO Auto-generated method stub - throw new UnsupportedOperationException( - "Unimplemented method 'strikeLightningEffect'" - ); + public @NotNull LightningStrike strikeLightningEffect(@NotNull Location loc) { + return strikeLightning(loc); } @Override public @Nullable Location findLightningRod(@NotNull Location location) { - // TODO Auto-generated method stub - throw new UnsupportedOperationException( - "Unimplemented method 'findLightningRod'" - ); + return null; } @Override public @Nullable Location findLightningTarget(@NotNull Location location) { - // TODO Auto-generated method stub - throw new UnsupportedOperationException( - "Unimplemented method 'findLightningTarget'" - ); + return location.clone(); } @Override - public @NotNull Collection getEntitiesByClass( - @NonNull @NotNull Class... classes - ) { - // TODO Auto-generated method stub - throw new UnsupportedOperationException( - "Unimplemented method 'getEntitiesByClass'" - ); + public @NotNull List getEntities() { + List list = new ArrayList<>(this.registeredEntities.values()); + for (Player player : getPlayers()) { + if (!list.contains(player)) { + list.add(player); + } + } + try { + var res = NativeBridgeFfi.getWorldEntities(GetWorldEntitiesRequest.newBuilder() + .setWorldUuid(BridgeUtils.convertUuid(this.uuid)) + .build()); + if (res != null) { + for (var summary : res.getEntitiesList()) { + if (summary.hasUuid()) { + UUID u = UUID.fromString(summary.getUuid().getValue()); + if (!this.registeredEntities.containsKey(u)) { + EntityType type = EntityType.UNKNOWN; + try { + type = EntityType.valueOf(summary.getEntityType()); + } catch (Throwable ignored) {} + Location loc = new Location(this, summary.getX(), summary.getY(), summary.getZ(), summary.getYaw(), summary.getPitch()); + Entity e = PatchBukkitEntity.create(u, type, loc); + list.add(e); + } + } + } + } + } catch (Throwable ignored) {} + return list; } @Override - public void getChunkAtAsync( - int x, - int z, - boolean gen, - boolean urgent, - @NotNull Consumer cb - ) { - // TODO Auto-generated method stub - throw new UnsupportedOperationException( - "Unimplemented method 'getChunkAtAsync'" - ); - } - - @Override - public void getChunksAtAsync( - int minX, - int minZ, - int maxX, - int maxZ, - boolean urgent, - @NotNull Runnable cb - ) { - // TODO Auto-generated method stub - throw new UnsupportedOperationException( - "Unimplemented method 'getChunksAtAsync'" - ); + public @NotNull List getLivingEntities() { + List living = new ArrayList<>(); + for (Entity e : getEntities()) { + if (e instanceof LivingEntity le) { + living.add(le); + } + } + return living; } @Override public @NotNull List getPlayers() { - // TODO Auto-generated method stub - throw new UnsupportedOperationException( - "Unimplemented method 'getPlayers'" - ); - } - - @Override - public @Nullable Entity getEntity(@NotNull UUID uuid) { - // TODO Auto-generated method stub - throw new UnsupportedOperationException( - "Unimplemented method 'getEntity'" - ); - } - - @Override - public @NotNull Collection getNearbyEntities( - @NotNull Location location, - double x, - double y, - double z, - @Nullable Predicate filter - ) { - // TODO Auto-generated method stub - throw new UnsupportedOperationException( - "Unimplemented method 'getNearbyEntities'" - ); - } - - @Override - public @NotNull Collection getNearbyEntities( - @NotNull BoundingBox boundingBox, - @Nullable Predicate filter - ) { - // TODO Auto-generated method stub - throw new UnsupportedOperationException( - "Unimplemented method 'getNearbyEntities'" - ); - } - - @Override - public @Nullable RayTraceResult rayTraceEntities( - @NotNull Position start, - @NotNull Vector direction, - double maxDistance, - double raySize, - @Nullable Predicate filter - ) { - // TODO Auto-generated method stub - throw new UnsupportedOperationException( - "Unimplemented method 'rayTraceEntities'" - ); - } - - @Override - public @Nullable RayTraceResult rayTraceBlocks( - @NotNull Position start, - @NotNull Vector direction, - double maxDistance, - @NotNull FluidCollisionMode fluidCollisionMode, - boolean ignorePassableBlocks, - @Nullable Predicate canCollide - ) { - // TODO Auto-generated method stub - throw new UnsupportedOperationException( - "Unimplemented method 'rayTraceBlocks'" - ); - } - - @Override - public @Nullable RayTraceResult rayTrace( - @NotNull Position start, - @NotNull Vector direction, - double maxDistance, - @NotNull FluidCollisionMode fluidCollisionMode, - boolean ignorePassableBlocks, - double raySize, - @Nullable Predicate filter, - @Nullable Predicate canCollide - ) { - // TODO Auto-generated method stub - throw new UnsupportedOperationException( - "Unimplemented method 'rayTrace'" - ); - } - - @Override - public @Nullable RayTraceResult rayTrace( - @NotNull Consumer< - PositionedRayTraceConfigurationBuilder - > builderConsumer - ) { - // TODO Auto-generated method stub - throw new UnsupportedOperationException( - "Unimplemented method 'rayTrace'" - ); - } - - @Override - public @NotNull Location getSpawnLocation() { - // TODO Auto-generated method stub - throw new UnsupportedOperationException( - "Unimplemented method 'getSpawnLocation'" - ); - } - - @Override - public boolean setSpawnLocation(@NotNull Location location) { - // TODO Auto-generated method stub - throw new UnsupportedOperationException( - "Unimplemented method 'setSpawnLocation'" - ); - } - - @Override - public boolean setSpawnLocation(int x, int y, int z, float yaw) { - // TODO Auto-generated method stub - throw new UnsupportedOperationException( - "Unimplemented method 'setSpawnLocation'" - ); - } - - @Override - public long getTime() { - // TODO Auto-generated method stub - throw new UnsupportedOperationException( - "Unimplemented method 'getTime'" - ); - } - - @Override - public void setTime(long time) { - // TODO Auto-generated method stub - throw new UnsupportedOperationException( - "Unimplemented method 'setTime'" - ); - } - - @Override - public long getFullTime() { - // TODO Auto-generated method stub - throw new UnsupportedOperationException( - "Unimplemented method 'getFullTime'" - ); - } - - @Override - public void setFullTime(long time) { - // TODO Auto-generated method stub - throw new UnsupportedOperationException( - "Unimplemented method 'setFullTime'" - ); - } - - @Override - public boolean isDayTime() { - // TODO Auto-generated method stub - throw new UnsupportedOperationException( - "Unimplemented method 'isDayTime'" - ); - } - - @Override - public long getGameTime() { - // TODO Auto-generated method stub - throw new UnsupportedOperationException( - "Unimplemented method 'getGameTime'" - ); - } - - @Override - public boolean hasStorm() { - // TODO Auto-generated method stub - throw new UnsupportedOperationException( - "Unimplemented method 'hasStorm'" - ); - } - - @Override - public void setStorm(boolean hasStorm) { - // TODO Auto-generated method stub - throw new UnsupportedOperationException( - "Unimplemented method 'setStorm'" - ); - } - - @Override - public int getWeatherDuration() { - // TODO Auto-generated method stub - throw new UnsupportedOperationException( - "Unimplemented method 'getWeatherDuration'" - ); - } - - @Override - public void setWeatherDuration(int duration) { - // TODO Auto-generated method stub - throw new UnsupportedOperationException( - "Unimplemented method 'setWeatherDuration'" - ); - } - - @Override - public boolean isThundering() { - // TODO Auto-generated method stub - throw new UnsupportedOperationException( - "Unimplemented method 'isThundering'" - ); - } - - @Override - public void setThundering(boolean thundering) { - // TODO Auto-generated method stub - throw new UnsupportedOperationException( - "Unimplemented method 'setThundering'" - ); - } - - @Override - public int getThunderDuration() { - // TODO Auto-generated method stub - throw new UnsupportedOperationException( - "Unimplemented method 'getThunderDuration'" - ); - } - - @Override - public void setThunderDuration(int duration) { - // TODO Auto-generated method stub - throw new UnsupportedOperationException( - "Unimplemented method 'setThunderDuration'" - ); - } - - @Override - public boolean isClearWeather() { - // TODO Auto-generated method stub - throw new UnsupportedOperationException( - "Unimplemented method 'isClearWeather'" - ); - } - - @Override - public void setClearWeatherDuration(int duration) { - // TODO Auto-generated method stub - throw new UnsupportedOperationException( - "Unimplemented method 'setClearWeatherDuration'" - ); - } - - @Override - public int getClearWeatherDuration() { - // TODO Auto-generated method stub - throw new UnsupportedOperationException( - "Unimplemented method 'getClearWeatherDuration'" - ); - } - - @Override - public boolean createExplosion( - double x, - double y, - double z, - float power, - boolean setFire, - boolean breakBlocks, - @Nullable Entity source - ) { - // TODO Auto-generated method stub - throw new UnsupportedOperationException( - "Unimplemented method 'createExplosion'" - ); - } - - @Override - public boolean createExplosion( - @Nullable Entity source, - @NotNull Location loc, - float power, - boolean setFire, - boolean breakBlocks, - boolean excludeSourceFromDamage - ) { - // TODO Auto-generated method stub - throw new UnsupportedOperationException( - "Unimplemented method 'createExplosion'" - ); - } - - @Override - public boolean createExplosion( - @NotNull Location loc, - float power, - boolean setFire, - boolean breakBlocks, - @Nullable Entity source - ) { - // TODO Auto-generated method stub - throw new UnsupportedOperationException( - "Unimplemented method 'createExplosion'" - ); - } - - @Override - public boolean getPVP() { - // TODO Auto-generated method stub - throw new UnsupportedOperationException( - "Unimplemented method 'getPVP'" - ); - } - - @Override - public void setPVP(boolean pvp) { - // TODO Auto-generated method stub - throw new UnsupportedOperationException( - "Unimplemented method 'setPVP'" - ); - } - - @Override - public @Nullable ChunkGenerator getGenerator() { - // TODO Auto-generated method stub - throw new UnsupportedOperationException( - "Unimplemented method 'getGenerator'" - ); - } - - @Override - public @Nullable BiomeProvider getBiomeProvider() { - // TODO Auto-generated method stub - throw new UnsupportedOperationException( - "Unimplemented method 'getBiomeProvider'" - ); - } - - @Override - public void save(boolean flush) { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'save'"); - } - - @Override - public @NotNull List getPopulators() { - // TODO Auto-generated method stub - throw new UnsupportedOperationException( - "Unimplemented method 'getPopulators'" - ); - } - - @Override - public @NonNull T spawn( - @NotNull Location location, - @NotNull Class clazz, - CreatureSpawnEvent.@NotNull SpawnReason spawnReason, - boolean randomizeData, - @Nullable Consumer function - ) throws IllegalArgumentException { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'spawn'"); - } - - @Override - public @NotNull FallingBlock spawnFallingBlock( - @NotNull Location location, - @NotNull MaterialData data - ) throws IllegalArgumentException { - // TODO Auto-generated method stub - throw new UnsupportedOperationException( - "Unimplemented method 'spawnFallingBlock'" - ); - } - - @Override - public @NotNull FallingBlock spawnFallingBlock( - @NotNull Location location, - @NotNull BlockData data - ) throws IllegalArgumentException { - // TODO Auto-generated method stub - throw new UnsupportedOperationException( - "Unimplemented method 'spawnFallingBlock'" - ); - } - - @Override - public @NotNull FallingBlock spawnFallingBlock( - @NotNull Location location, - @NotNull Material material, - byte data - ) throws IllegalArgumentException { - // TODO Auto-generated method stub - throw new UnsupportedOperationException( - "Unimplemented method 'spawnFallingBlock'" - ); - } - - @Override - public void playEffect( - @NotNull Location location, - @NotNull Effect effect, - int data, - int radius - ) { - // TODO Auto-generated method stub - throw new UnsupportedOperationException( - "Unimplemented method 'playEffect'" - ); - } - - @Override - public void playEffect( - @NotNull Location location, - @NotNull Effect effect, - @org.jspecify.annotations.Nullable T data, - int radius - ) { - // TODO Auto-generated method stub - throw new UnsupportedOperationException( - "Unimplemented method 'playEffect'" - ); - } - - @Override - public @NotNull ChunkSnapshot getEmptyChunkSnapshot( - int x, - int z, - boolean includeBiome, - boolean includeBiomeTemp - ) { - // TODO Auto-generated method stub - throw new UnsupportedOperationException( - "Unimplemented method 'getEmptyChunkSnapshot'" - ); - } - - @Override - public void setSpawnFlags(boolean allowMonsters, boolean allowAnimals) { - // TODO Auto-generated method stub - throw new UnsupportedOperationException( - "Unimplemented method 'setSpawnFlags'" - ); - } - - @Override - public boolean getAllowAnimals() { - // TODO Auto-generated method stub - throw new UnsupportedOperationException( - "Unimplemented method 'getAllowAnimals'" - ); - } - - @Override - public boolean getAllowMonsters() { - // TODO Auto-generated method stub - throw new UnsupportedOperationException( - "Unimplemented method 'getAllowMonsters'" - ); - } - - @Override - public void setBiome(int x, int z, @NotNull Biome bio) { - // TODO Auto-generated method stub - throw new UnsupportedOperationException( - "Unimplemented method 'setBiome'" - ); - } - - @Override - public double getTemperature(int x, int y, int z) { - // TODO Auto-generated method stub - throw new UnsupportedOperationException( - "Unimplemented method 'getTemperature'" - ); - } - - @Override - public double getHumidity(int x, int y, int z) { - // TODO Auto-generated method stub - throw new UnsupportedOperationException( - "Unimplemented method 'getHumidity'" - ); - } - - @Override - public int getLogicalHeight() { - // TODO Auto-generated method stub - throw new UnsupportedOperationException( - "Unimplemented method 'getLogicalHeight'" - ); - } - - @Override - public boolean isNatural() { - // TODO Auto-generated method stub - throw new UnsupportedOperationException( - "Unimplemented method 'isNatural'" - ); - } - - @Override - public boolean isBedWorks() { - // TODO Auto-generated method stub - throw new UnsupportedOperationException( - "Unimplemented method 'isBedWorks'" - ); - } - - @Override - public boolean hasSkyLight() { - // TODO Auto-generated method stub - throw new UnsupportedOperationException( - "Unimplemented method 'hasSkyLight'" - ); - } - - @Override - public boolean hasCeiling() { - // TODO Auto-generated method stub - throw new UnsupportedOperationException( - "Unimplemented method 'hasCeiling'" - ); - } - - @Override - public boolean isPiglinSafe() { - // TODO Auto-generated method stub - throw new UnsupportedOperationException( - "Unimplemented method 'isPiglinSafe'" - ); - } - - @Override - public boolean isRespawnAnchorWorks() { - // TODO Auto-generated method stub - throw new UnsupportedOperationException( - "Unimplemented method 'isRespawnAnchorWorks'" - ); - } - - @Override - public boolean hasRaids() { - // TODO Auto-generated method stub - throw new UnsupportedOperationException( - "Unimplemented method 'hasRaids'" - ); - } - - @Override - public boolean isUltraWarm() { - // TODO Auto-generated method stub - throw new UnsupportedOperationException( - "Unimplemented method 'isUltraWarm'" - ); - } - - @Override - public int getSeaLevel() { - // TODO Auto-generated method stub - throw new UnsupportedOperationException( - "Unimplemented method 'getSeaLevel'" - ); - } - - @Override - public boolean isAutoSave() { - // TODO Auto-generated method stub - throw new UnsupportedOperationException( - "Unimplemented method 'isAutoSave'" - ); - } - - @Override - public void setAutoSave(boolean value) { - // TODO Auto-generated method stub - throw new UnsupportedOperationException( - "Unimplemented method 'setAutoSave'" - ); - } - - @Override - public void setDifficulty(@NotNull Difficulty difficulty) { - // TODO Auto-generated method stub - throw new UnsupportedOperationException( - "Unimplemented method 'setDifficulty'" - ); - } - - @Override - public @NotNull Difficulty getDifficulty() { - // TODO Auto-generated method stub - throw new UnsupportedOperationException( - "Unimplemented method 'getDifficulty'" - ); - } - - @Override - public int getViewDistance() { - // TODO Auto-generated method stub - throw new UnsupportedOperationException( - "Unimplemented method 'getViewDistance'" - ); - } - - @Override - public int getSimulationDistance() { - // TODO Auto-generated method stub - throw new UnsupportedOperationException( - "Unimplemented method 'getSimulationDistance'" - ); - } - - @Override - public @NotNull Path getWorldPath() { - // TODO Auto-generated method stub - throw new UnsupportedOperationException( - "Unimplemented method 'getWorldPath'" - ); - } - - @Override - public @Nullable WorldType getWorldType() { - // TODO Auto-generated method stub - throw new UnsupportedOperationException( - "Unimplemented method 'getWorldType'" - ); - } - - @Override - public boolean canGenerateStructures() { - // TODO Auto-generated method stub - throw new UnsupportedOperationException( - "Unimplemented method 'canGenerateStructures'" - ); - } - - @Override - public boolean hasBonusChest() { - // TODO Auto-generated method stub - throw new UnsupportedOperationException( - "Unimplemented method 'hasBonusChest'" - ); - } - - @Override - public boolean isHardcore() { - // TODO Auto-generated method stub - throw new UnsupportedOperationException( - "Unimplemented method 'isHardcore'" - ); - } - - @Override - public void setHardcore(boolean hardcore) { - // TODO Auto-generated method stub - throw new UnsupportedOperationException( - "Unimplemented method 'setHardcore'" - ); - } - - @Override - public long getTicksPerSpawns(@NotNull SpawnCategory spawnCategory) { - // TODO Auto-generated method stub - throw new UnsupportedOperationException( - "Unimplemented method 'getTicksPerSpawns'" - ); - } - - @Override - public void setTicksPerSpawns( - @NotNull SpawnCategory spawnCategory, - int ticksPerCategorySpawn - ) { - // TODO Auto-generated method stub - throw new UnsupportedOperationException( - "Unimplemented method 'setTicksPerSpawns'" - ); - } - - @Override - public int getSpawnLimit(@NotNull SpawnCategory spawnCategory) { - // TODO Auto-generated method stub - throw new UnsupportedOperationException( - "Unimplemented method 'getSpawnLimit'" - ); - } - - @Override - public void setSpawnLimit(@NotNull SpawnCategory spawnCategory, int limit) { - // TODO Auto-generated method stub - throw new UnsupportedOperationException( - "Unimplemented method 'setSpawnLimit'" - ); - } - - @Override - public void playSound( - @NotNull Location location, - @NotNull Sound sound, - @NotNull SoundCategory category, - float volume, - float pitch - ) { - if (location == null || sound == null) return; - var patchBukkitSound = (org.patchbukkit.registry.PatchBukkitSound) sound; - var request = patchbukkit.sound.PlayerPlaySoundRequest.newBuilder() - .setLocation(org.patchbukkit.bridge.BridgeUtils.convertLocation(location)) - .setSound( - patchbukkit.sound.Sound.newBuilder() - .setCategory(category.name()) - .setName(patchBukkitSound.getOriginalName()) - ).setVolume(volume) - .setPitch(pitch) - .build(); - NativeBridgeFfi.playerPlaySound(request); - } - - @Override - public void playSound( - @NotNull Location location, - @NotNull String sound, - @NotNull SoundCategory category, - float volume, - float pitch - ) { - // TODO Auto-generated method stub - throw new UnsupportedOperationException( - "Unimplemented method 'playSound'" - ); - } - - @Override - public void playSound( - @NotNull Location location, - @NotNull Sound sound, - @NotNull SoundCategory category, - float volume, - float pitch, - long seed - ) { - // TODO Auto-generated method stub - throw new UnsupportedOperationException( - "Unimplemented method 'playSound'" - ); - } - - @Override - public void playSound( - @NotNull Location location, - @NotNull String sound, - @NotNull SoundCategory category, - float volume, - float pitch, - long seed - ) { - // TODO Auto-generated method stub - throw new UnsupportedOperationException( - "Unimplemented method 'playSound'" - ); - } - - @Override - public void playSound( - @NotNull Entity entity, - @NotNull Sound sound, - @NotNull SoundCategory category, - float volume, - float pitch - ) { - // TODO Auto-generated method stub - throw new UnsupportedOperationException( - "Unimplemented method 'playSound'" - ); - } - - @Override - public void playSound( - @NotNull Entity entity, - @NotNull String sound, - @NotNull SoundCategory category, - float volume, - float pitch - ) { - // TODO Auto-generated method stub - throw new UnsupportedOperationException( - "Unimplemented method 'playSound'" - ); - } - - @Override - public void playSound( - @NotNull Entity entity, - @NotNull Sound sound, - @NotNull SoundCategory category, - float volume, - float pitch, - long seed - ) { - // TODO Auto-generated method stub - throw new UnsupportedOperationException( - "Unimplemented method 'playSound'" - ); - } - - @Override - public void playSound( - @NotNull Entity entity, - @NotNull String sound, - @NotNull SoundCategory category, - float volume, - float pitch, - long seed - ) { - // TODO Auto-generated method stub - throw new UnsupportedOperationException( - "Unimplemented method 'playSound'" - ); - } - - @Override - public @NotNull String@NotNull [] getGameRules() { - // TODO Auto-generated method stub - throw new UnsupportedOperationException( - "Unimplemented method 'getGameRules'" - ); - } - - @Override - public @Nullable String getGameRuleValue(@Nullable String rule) { - // TODO Auto-generated method stub - throw new UnsupportedOperationException( - "Unimplemented method 'getGameRuleValue'" - ); - } - - @Override - public boolean setGameRuleValue( - @NotNull String rule, - @NotNull String value - ) { - // TODO Auto-generated method stub - throw new UnsupportedOperationException( - "Unimplemented method 'setGameRuleValue'" - ); - } - - @Override - public boolean isGameRule(@NotNull String rule) { - // TODO Auto-generated method stub - throw new UnsupportedOperationException( - "Unimplemented method 'isGameRule'" - ); - } - - @Override - public @org.jspecify.annotations.Nullable T getGameRuleValue( - @NotNull GameRule rule - ) { - // TODO Auto-generated method stub - throw new UnsupportedOperationException( - "Unimplemented method 'getGameRuleValue'" - ); - } - - @Override - public @org.jspecify.annotations.Nullable T getGameRuleDefault( - @NotNull GameRule rule - ) { - // TODO Auto-generated method stub - throw new UnsupportedOperationException( - "Unimplemented method 'getGameRuleDefault'" - ); - } - - @Override - public boolean setGameRule( - @NotNull GameRule rule, - @NonNull T newValue - ) { - // TODO Auto-generated method stub - throw new UnsupportedOperationException( - "Unimplemented method 'setGameRule'" - ); - } - - @Override - public @NotNull WorldBorder getWorldBorder() { - return new PatchBukkitWorldBorder(this); - } - - @Override - public void spawnParticle( - @NotNull Particle particle, - @Nullable List receivers, - @Nullable Player source, - double x, - double y, - double z, - int count, - double offsetX, - double offsetY, - double offsetZ, - double extra, - @org.jspecify.annotations.Nullable T data, - boolean force - ) { - if (particle == null) return; - var request = patchbukkit.world.SpawnParticleRequest.newBuilder() - .setWorldUuid(org.patchbukkit.bridge.BridgeUtils.convertUuid(this.getUID())) - .setParticle(particle.name().toLowerCase()) - .setX(x).setY(y).setZ(z) - .setCount(count) - .setOffsetX(offsetX).setOffsetY(offsetY).setOffsetZ(offsetZ) - .setExtra(extra) - .build(); - NativeBridgeFfi.spawnParticle(request); - } - - @Override - public @Nullable Location locateNearestStructure( - @NotNull Location origin, - @NotNull StructureType structureType, - int radius, - boolean findUnexplored - ) { - // TODO Auto-generated method stub - throw new UnsupportedOperationException( - "Unimplemented method 'locateNearestStructure'" - ); - } - - @Override - public @Nullable StructureSearchResult locateNearestStructure( - @NotNull Location origin, - org.bukkit.generator.structure.@NotNull StructureType structureType, - int radius, - boolean findUnexplored - ) { - // TODO Auto-generated method stub - throw new UnsupportedOperationException( - "Unimplemented method 'locateNearestStructure'" - ); - } - - @Override - public @Nullable StructureSearchResult locateNearestStructure( - @NotNull Location origin, - @NotNull Structure structure, - int radius, - boolean findUnexplored - ) { - // TODO Auto-generated method stub - throw new UnsupportedOperationException( - "Unimplemented method 'locateNearestStructure'" - ); - } - - @Override - public double getCoordinateScale() { - // TODO Auto-generated method stub - throw new UnsupportedOperationException( - "Unimplemented method 'getCoordinateScale'" - ); - } - - @Override - public boolean isFixedTime() { - // TODO Auto-generated method stub - throw new UnsupportedOperationException( - "Unimplemented method 'isFixedTime'" - ); - } - - @Override - public @NotNull Collection getInfiniburn() { - // TODO Auto-generated method stub - throw new UnsupportedOperationException( - "Unimplemented method 'getInfiniburn'" - ); - } - - @Override - public void sendGameEvent( - @Nullable Entity sourceEntity, - @NotNull GameEvent gameEvent, - @NotNull Vector position - ) { - // TODO Auto-generated method stub - throw new UnsupportedOperationException( - "Unimplemented method 'sendGameEvent'" - ); - } - - @Override - public @NotNull Spigot spigot() { - // TODO Auto-generated method stub - throw new UnsupportedOperationException( - "Unimplemented method 'spigot'" - ); - } - - @Override - public @Nullable BiomeSearchResult locateNearestBiome( - @NotNull Location origin, - int radius, - int horizontalInterval, - int verticalInterval, - @NonNull @NotNull Biome... biomes - ) { - // TODO Auto-generated method stub - throw new UnsupportedOperationException( - "Unimplemented method 'locateNearestBiome'" - ); - } - - @Override - public @Nullable Raid locateNearestRaid( - @NotNull Location location, - int radius - ) { - // TODO Auto-generated method stub - throw new UnsupportedOperationException( - "Unimplemented method 'locateNearestRaid'" - ); - } - - @Override - public @Nullable Raid getRaid(int id) { - // TODO Auto-generated method stub - throw new UnsupportedOperationException( - "Unimplemented method 'getRaid'" - ); - } - - @Override - public @NotNull List getRaids() { - // TODO Auto-generated method stub - throw new UnsupportedOperationException( - "Unimplemented method 'getRaids'" - ); - } - - @Override - public @Nullable DragonBattle getEnderDragonBattle() { - // TODO Auto-generated method stub - throw new UnsupportedOperationException( - "Unimplemented method 'getEnderDragonBattle'" - ); - } - - @Override - public void setViewDistance(int viewDistance) { - // TODO Auto-generated method stub - throw new UnsupportedOperationException( - "Unimplemented method 'setViewDistance'" - ); - } - - @Override - public void setSimulationDistance(int simulationDistance) { - // TODO Auto-generated method stub - throw new UnsupportedOperationException( - "Unimplemented method 'setSimulationDistance'" - ); - } - - @Override - public int getSendViewDistance() { - // TODO Auto-generated method stub - throw new UnsupportedOperationException( - "Unimplemented method 'getSendViewDistance'" - ); - } - - @Override - public void setSendViewDistance(int viewDistance) { - // TODO Auto-generated method stub - throw new UnsupportedOperationException( - "Unimplemented method 'setSendViewDistance'" - ); - } - - @Override - public @NotNull Collection getStructures(int x, int z) { - // TODO Auto-generated method stub - throw new UnsupportedOperationException( - "Unimplemented method 'getStructures'" - ); - } - - @Override - public @NotNull Collection getStructures( - int x, - int z, - @NotNull Structure structure - ) { - // TODO Auto-generated method stub - throw new UnsupportedOperationException( - "Unimplemented method 'getStructures'" - ); + List list = new ArrayList<>(); + if (Bukkit.getServer() instanceof PatchBukkitServer server) { + for (Player p : server.getOnlinePlayers()) { + if (p.getWorld().getUID().equals(this.uuid)) { + list.add(p); + } + } + } + return list; + } + + @Override + public @NotNull Collection getNearbyEntities(@NotNull Location location, double x, double y, double z) { + return getNearbyEntities(location, x, y, z, null); + } + + @Override + public @NotNull Collection getNearbyEntities(@NotNull Location location, double x, double y, double z, @Nullable Predicate filter) { + List list = new ArrayList<>(); + double minX = location.getX() - x; + double maxX = location.getX() + x; + double minY = location.getY() - y; + double maxY = location.getY() + y; + double minZ = location.getZ() - z; + double maxZ = location.getZ() + z; + + for (Entity e : getEntities()) { + Location loc = e.getLocation(); + if (loc.getX() >= minX && loc.getX() <= maxX && + loc.getY() >= minY && loc.getY() <= maxY && + loc.getZ() >= minZ && loc.getZ() <= maxZ) { + if (filter == null || filter.test(e)) { + list.add(e); + } + } + } + return list; + } + + @Override + public @NotNull Collection getNearbyEntities(@NotNull BoundingBox boundingBox) { + return getNearbyEntities(boundingBox, null); + } + + @Override + public @NotNull Collection getNearbyEntities(@NotNull BoundingBox boundingBox, @Nullable Predicate filter) { + List list = new ArrayList<>(); + for (Entity e : getEntities()) { + Location loc = e.getLocation(); + if (boundingBox.contains(loc.getX(), loc.getY(), loc.getZ())) { + if (filter == null || filter.test(e)) { + list.add(e); + } + } + } + return list; } @Override public @NotNull String getName() { - return "world"; + return this.name; } @Override @@ -1604,146 +549,1285 @@ public class PatchBukkitWorld return this.uuid; } + @Override + public @NotNull NamespacedKey getKey() { + return NamespacedKey.minecraft(this.name.toLowerCase().replace(' ', '_')); + } + + @Override + public @NotNull Location getSpawnLocation() { + return new Location(this, this.spawnX, this.spawnY, this.spawnZ, this.spawnAngle, 0.0f); + } + + @Override + public boolean setSpawnLocation(@NotNull Location location) { + return setSpawnLocation(location.getBlockX(), location.getBlockY(), location.getBlockZ(), location.getYaw()); + } + + @Override + public boolean setSpawnLocation(int x, int y, int z, float angle) { + this.spawnX = x; + this.spawnY = y; + this.spawnZ = z; + this.spawnAngle = angle; + try { + NativeBridgeFfi.setWorldSpawn(SetWorldSpawnRequest.newBuilder() + .setWorldUuid(BridgeUtils.convertUuid(this.uuid)) + .setX(x) + .setY(y) + .setZ(z) + .setAngle(angle) + .build()); + return true; + } catch (Throwable ignored) {} + return false; + } + + @Override + public boolean setSpawnLocation(int x, int y, int z) { + return setSpawnLocation(x, y, z, 0.0f); + } + + @Override + public long getTime() { + return this.time; + } + + @Override + public void setTime(long time) { + this.time = time; + try { + NativeBridgeFfi.setWorldTime(SetWorldTimeRequest.newBuilder() + .setWorldUuid(BridgeUtils.convertUuid(this.uuid)) + .setTime(time) + .setFullTime(-1) + .build()); + } catch (Throwable ignored) {} + } + + @Override + public long getFullTime() { + return this.fullTime; + } + + @Override + public void setFullTime(long time) { + this.fullTime = time; + try { + NativeBridgeFfi.setWorldTime(SetWorldTimeRequest.newBuilder() + .setWorldUuid(BridgeUtils.convertUuid(this.uuid)) + .setTime(-1) + .setFullTime(time) + .build()); + } catch (Throwable ignored) {} + } + + @Override + public long getGameTime() { + return this.fullTime; + } + + @Override + public boolean hasStorm() { + return this.storm; + } + + @Override + public void setStorm(boolean hasStorm) { + this.storm = hasStorm; + try { + NativeBridgeFfi.setWorldWeather(SetWorldWeatherRequest.newBuilder() + .setWorldUuid(BridgeUtils.convertUuid(this.uuid)) + .setStorm(hasStorm) + .setThundering(this.thundering) + .setWeatherDuration(this.weatherDuration) + .setThunderDuration(this.thunderDuration) + .setClearWeatherDuration(this.clearWeatherDuration) + .build()); + } catch (Throwable ignored) {} + } + + @Override + public int getWeatherDuration() { + return this.weatherDuration; + } + + @Override + public void setWeatherDuration(int duration) { + this.weatherDuration = duration; + setStorm(this.storm); + } + + @Override + public boolean isThundering() { + return this.thundering; + } + + @Override + public void setThundering(boolean thundering) { + this.thundering = thundering; + try { + NativeBridgeFfi.setWorldWeather(SetWorldWeatherRequest.newBuilder() + .setWorldUuid(BridgeUtils.convertUuid(this.uuid)) + .setStorm(this.storm) + .setThundering(thundering) + .setWeatherDuration(this.weatherDuration) + .setThunderDuration(this.thunderDuration) + .setClearWeatherDuration(this.clearWeatherDuration) + .build()); + } catch (Throwable ignored) {} + } + + @Override + public int getThunderDuration() { + return this.thunderDuration; + } + + @Override + public void setThunderDuration(int duration) { + this.thunderDuration = duration; + setThundering(this.thundering); + } + + @Override + public boolean isClearWeather() { + return !this.storm && !this.thundering; + } + + @Override + public void setClearWeatherDuration(int duration) { + this.clearWeatherDuration = duration; + this.storm = false; + this.thundering = false; + setStorm(false); + } + + @Override + public int getClearWeatherDuration() { + return this.clearWeatherDuration; + } + + @Override + public boolean createExplosion(double x, double y, double z, float power) { + return createExplosion(x, y, z, power, false, true, null); + } + + @Override + public boolean createExplosion(double x, double y, double z, float power, boolean setFire) { + return createExplosion(x, y, z, power, setFire, true, null); + } + + @Override + public boolean createExplosion(double x, double y, double z, float power, boolean setFire, boolean breakBlocks) { + return createExplosion(x, y, z, power, setFire, breakBlocks, null); + } + + @Override + public boolean createExplosion(double x, double y, double z, float power, boolean setFire, boolean breakBlocks, @Nullable Entity source) { + try { + NativeBridgeFfi.createWorldExplosion(CreateWorldExplosionRequest.newBuilder() + .setWorldUuid(BridgeUtils.convertUuid(this.uuid)) + .setX(x) + .setY(y) + .setZ(z) + .setPower(power) + .setSetFire(setFire) + .setBreakBlocks(breakBlocks) + .build()); + return true; + } catch (Throwable ignored) {} + return false; + } + + @Override + public boolean createExplosion(@NotNull Location loc, float power) { + return createExplosion(loc.getX(), loc.getY(), loc.getZ(), power); + } + + @Override + public boolean createExplosion(@NotNull Location loc, float power, boolean setFire) { + return createExplosion(loc.getX(), loc.getY(), loc.getZ(), power, setFire); + } + + @Override + public boolean createExplosion(@NotNull Location loc, float power, boolean setFire, boolean breakBlocks) { + return createExplosion(loc.getX(), loc.getY(), loc.getZ(), power, setFire, breakBlocks); + } + + @Override + public boolean createExplosion(@NotNull Location loc, float power, boolean setFire, boolean breakBlocks, @Nullable Entity source) { + return createExplosion(loc.getX(), loc.getY(), loc.getZ(), power, setFire, breakBlocks, source); + } + + @Override + public boolean createExplosion(@Nullable Entity source, @NotNull Location loc, float power, boolean setFire, boolean breakBlocks, boolean createSmoke) { + return createExplosion(loc.getX(), loc.getY(), loc.getZ(), power, setFire, breakBlocks, source); + } + @Override public @NotNull Environment getEnvironment() { - // TODO Auto-generated method stub - throw new UnsupportedOperationException( - "Unimplemented method 'getEnvironment'" - ); + return this.environment; } @Override public long getSeed() { - try { - var info = NativeBridgeFfi.getWorldInfo(GetWorldInfoRequest.newBuilder() - .setWorldUuid(BridgeUtils.convertUuid(this.uuid)) - .build()); - return info.getSeed(); - } catch (Throwable t) { - return 0L; - } + return this.seed; } @Override - public int getMinHeight() { - try { - var info = NativeBridgeFfi.getWorldInfo(GetWorldInfoRequest.newBuilder() - .setWorldUuid(BridgeUtils.convertUuid(this.uuid)) - .build()); - return info.getMinHeight(); - } catch (Throwable t) { - return -64; - } + public boolean getPVP() { + return this.pvp; } @Override - public int getMaxHeight() { + public void setPVP(boolean pvp) { + this.pvp = pvp; try { - var info = NativeBridgeFfi.getWorldInfo(GetWorldInfoRequest.newBuilder() - .setWorldUuid(BridgeUtils.convertUuid(this.uuid)) - .build()); - return info.getMaxHeight(); - } catch (Throwable t) { - return 320; - } + NativeBridgeFfi.setWorldPvp(SetWorldPvpRequest.newBuilder() + .setWorldUuid(BridgeUtils.convertUuid(this.uuid)) + .setPvp(pvp) + .build()); + } catch (Throwable ignored) {} } - public int getHeight() { - return getMaxHeight() - getMinHeight(); + @Override + public @Nullable ChunkGenerator getGenerator() { + return null; + } + + @Override + public @Nullable BiomeProvider getBiomeProvider() { + return null; } @Override public @NotNull BiomeProvider vanillaBiomeProvider() { - // TODO Auto-generated method stub - throw new UnsupportedOperationException( - "Unimplemented method 'vanillaBiomeProvider'" - ); + return new BiomeProvider() { + @Override + public @NotNull Biome getBiome(@NotNull WorldInfo worldInfo, int x, int y, int z) { + return PatchBukkitWorld.this.getBiome(x, y, z); + } + + @Override + public @NotNull List getBiomes(@NotNull WorldInfo worldInfo) { + return List.of(Biome.PLAINS); + } + }; } @Override - public void setMetadata( - @NotNull String metadataKey, - @NotNull MetadataValue newMetadataValue - ) { - // TODO Auto-generated method stub - throw new UnsupportedOperationException( - "Unimplemented method 'setMetadata'" - ); + public void save() { + try { + NativeBridgeFfi.saveWorld(SaveWorldRequest.newBuilder() + .setWorldUuid(BridgeUtils.convertUuid(this.uuid)) + .build()); + } catch (Throwable ignored) {} } @Override - public @NotNull List getMetadata( - @NotNull String metadataKey - ) { - // TODO Auto-generated method stub - throw new UnsupportedOperationException( - "Unimplemented method 'getMetadata'" - ); + public void save(boolean flush) { + save(); + } + + @Override + public @NotNull List getPopulators() { + return Collections.emptyList(); + } + + @Override + public @NotNull FallingBlock spawnFallingBlock(@NotNull Location location, @NotNull BlockData data) throws IllegalArgumentException { + FallingBlock entity = (FallingBlock) PatchBukkitEntity.create(UUID.randomUUID(), EntityType.FALLING_BLOCK, location); + registerEntity(entity); + return entity; + } + + @Override + public @NotNull FallingBlock spawnFallingBlock(@NotNull Location location, @NotNull MaterialData data) throws IllegalArgumentException { + return spawnFallingBlock(location, data.getItemType().createBlockData()); + } + + @Override + public @NotNull FallingBlock spawnFallingBlock(@NotNull Location location, @NotNull Material material, byte data) throws IllegalArgumentException { + return spawnFallingBlock(location, material.createBlockData()); + } + + @Override + public void playEffect(@NotNull Location location, @NotNull Effect effect, int data) { + playEffect(location, effect, data, 64); + } + + @Override + public void playEffect(@NotNull Location location, @NotNull Effect effect, int data, int radius) { + } + + @Override + public void playEffect(@NotNull Location location, @NotNull Effect effect, @Nullable T data) { + playEffect(location, effect, data, 64); + } + + @Override + public void playEffect(@NotNull Location location, @NotNull Effect effect, @Nullable T data, int radius) { + } + + @Override + public boolean hasStructureAt(@NotNull Position position, @NotNull Structure structure) { + return false; + } + + @Override + public @NotNull Collection getStructures(int x, int z) { + return Collections.emptyList(); + } + + @Override + public @NotNull Collection getStructures(int x, int z, @NotNull Structure structure) { + return Collections.emptyList(); + } + + @Override + public @Nullable Location locateNearestStructure(@NotNull Location origin, @NotNull StructureType structureType, int radius, boolean findUnexplored) { + return null; + } + + @Override + public @NotNull String@NotNull [] getGameRules() { + return this.gameRules.keySet().toArray(new String[0]); + } + + @Override + public @Nullable String getGameRuleValue(@Nullable String rule) { + if (rule == null) return null; + return this.gameRules.get(rule); + } + + @Override + public boolean setGameRuleValue(@NotNull String rule, @NotNull String value) { + this.gameRules.put(rule, value); + try { + NativeBridgeFfi.setWorldGamerule(SetWorldGameruleRequest.newBuilder() + .setWorldUuid(BridgeUtils.convertUuid(this.uuid)) + .setRule(rule) + .setValue(value) + .build()); + return true; + } catch (Throwable ignored) {} + return true; + } + + @Override + public boolean isGameRule(@NotNull String rule) { + return this.gameRules.containsKey(rule); + } + + @Override + public @Nullable T getGameRuleValue(@NotNull GameRule rule) { + String val = this.gameRules.get(rule.getName()); + if (val == null) return rule.getDefaultValue(); + if (rule.getType() == Boolean.class) { + return (T) Boolean.valueOf(val); + } else if (rule.getType() == Integer.class) { + try { + return (T) Integer.valueOf(val); + } catch (Throwable ignored) {} + } + return rule.getDefaultValue(); + } + + @Override + public @Nullable T getGameRuleDefault(@NotNull GameRule rule) { + return rule.getDefaultValue(); + } + + @Override + public boolean setGameRule(@NotNull GameRule rule, @NotNull T newValue) { + return setGameRuleValue(rule.getName(), String.valueOf(newValue)); + } + + @Override + public @NotNull WorldBorder getWorldBorder() { + return new PatchBukkitWorldBorder(this); + } + + @Override + public void spawnParticle(@NotNull Particle particle, @NotNull Location location, int count) { + spawnParticle(particle, location.getX(), location.getY(), location.getZ(), count, 0, 0, 0, 0, null, false); + } + + @Override + public void spawnParticle(@NotNull Particle particle, double x, double y, double z, int count) { + spawnParticle(particle, x, y, z, count, 0, 0, 0, 0, null, false); + } + + @Override + public void spawnParticle(@NotNull Particle particle, @NotNull Location location, int count, @Nullable T data) { + spawnParticle(particle, location.getX(), location.getY(), location.getZ(), count, 0, 0, 0, 0, data, false); + } + + @Override + public void spawnParticle(@NotNull Particle particle, double x, double y, double z, int count, @Nullable T data) { + spawnParticle(particle, x, y, z, count, 0, 0, 0, 0, data, false); + } + + @Override + public void spawnParticle(@NotNull Particle particle, @NotNull Location location, int count, double offsetX, double offsetY, double offsetZ) { + spawnParticle(particle, location.getX(), location.getY(), location.getZ(), count, offsetX, offsetY, offsetZ, 0, null, false); + } + + @Override + public void spawnParticle(@NotNull Particle particle, double x, double y, double z, int count, double offsetX, double offsetY, double offsetZ) { + spawnParticle(particle, x, y, z, count, offsetX, offsetY, offsetZ, 0, null, false); + } + + @Override + public void spawnParticle(@NotNull Particle particle, @NotNull Location location, int count, double offsetX, double offsetY, double offsetZ, @Nullable T data) { + spawnParticle(particle, location.getX(), location.getY(), location.getZ(), count, offsetX, offsetY, offsetZ, 0, data, false); + } + + @Override + public void spawnParticle(@NotNull Particle particle, double x, double y, double z, int count, double offsetX, double offsetY, double offsetZ, @Nullable T data) { + spawnParticle(particle, x, y, z, count, offsetX, offsetY, offsetZ, 0, data, false); + } + + @Override + public void spawnParticle(@NotNull Particle particle, @NotNull Location location, int count, double offsetX, double offsetY, double offsetZ, double extra) { + spawnParticle(particle, location.getX(), location.getY(), location.getZ(), count, offsetX, offsetY, offsetZ, extra, null, false); + } + + @Override + public void spawnParticle(@NotNull Particle particle, double x, double y, double z, int count, double offsetX, double offsetY, double offsetZ, double extra) { + spawnParticle(particle, x, y, z, count, offsetX, offsetY, offsetZ, extra, null, false); + } + + @Override + public void spawnParticle(@NotNull Particle particle, @NotNull Location location, int count, double offsetX, double offsetY, double offsetZ, double extra, @Nullable T data) { + spawnParticle(particle, location.getX(), location.getY(), location.getZ(), count, offsetX, offsetY, offsetZ, extra, data, false); + } + + @Override + public void spawnParticle(@NotNull Particle particle, double x, double y, double z, int count, double offsetX, double offsetY, double offsetZ, double extra, @Nullable T data) { + spawnParticle(particle, x, y, z, count, offsetX, offsetY, offsetZ, extra, data, false); + } + + @Override + public void spawnParticle(@NotNull Particle particle, @NotNull Location location, int count, double offsetX, double offsetY, double offsetZ, double extra, @Nullable T data, boolean force) { + spawnParticle(particle, location.getX(), location.getY(), location.getZ(), count, offsetX, offsetY, offsetZ, extra, data, force); + } + + @Override + public void spawnParticle(@NotNull Particle particle, double x, double y, double z, int count, double offsetX, double offsetY, double offsetZ, double extra, @Nullable T data, boolean force) { + try { + NativeBridgeFfi.spawnParticle(SpawnParticleRequest.newBuilder() + .setWorldUuid(BridgeUtils.convertUuid(this.uuid)) + .setParticle(particle.name()) + .setX(x) + .setY(y) + .setZ(z) + .setCount(count) + .setOffsetX(offsetX) + .setOffsetY(offsetY) + .setOffsetZ(offsetZ) + .setExtra(extra) + .setForce(force) + .build()); + } catch (Throwable ignored) {} + } + + @Override + public void playSound(@NotNull Location location, @NotNull Sound sound, float volume, float pitch) { + playSound(location, sound, SoundCategory.MASTER, volume, pitch); + } + + @Override + public void playSound(@NotNull Location location, @NotNull String sound, float volume, float pitch) { + playSound(location, sound, SoundCategory.MASTER, volume, pitch); + } + + @Override + public void playSound(@NotNull Location location, @NotNull Sound sound, @NotNull SoundCategory category, float volume, float pitch) { + playSound(location, sound.name().toLowerCase().replace('_', '.'), category, volume, pitch); + } + + @Override + public void playSound(@NotNull Location location, @NotNull String sound, @NotNull SoundCategory category, float volume, float pitch) { + try { + NativeBridgeFfi.playWorldSound(PlayWorldSoundRequest.newBuilder() + .setWorldUuid(BridgeUtils.convertUuid(this.uuid)) + .setSound(sound) + .setCategory(category.name()) + .setX(location.getX()) + .setY(location.getY()) + .setZ(location.getZ()) + .setVolume(volume) + .setPitch(pitch) + .build()); + } catch (Throwable ignored) {} + } + + @Override + public void playSound(@NotNull Location location, @NotNull Sound sound, @NotNull SoundCategory category, float volume, float pitch, long seed) { + playSound(location, sound, category, volume, pitch); + } + + @Override + public void playSound(@NotNull Location location, @NotNull String sound, @NotNull SoundCategory category, float volume, float pitch, long seed) { + playSound(location, sound, category, volume, pitch); + } + + @Override + public @Nullable StructureSearchResult locateNearestStructure(@NotNull Location origin, @NotNull Structure structure, int radius, boolean findUnexplored) { + return null; + } + + @Override + public @Nullable StructureSearchResult locateNearestStructure(@NotNull Location origin, org.bukkit.generator.structure.@NotNull StructureType structureType, int radius, boolean findUnexplored) { + return null; + } + + @Override + public @Nullable BiomeSearchResult locateNearestBiome(@NotNull Location origin, int radius, @NotNull Biome... biomes) { + return null; + } + + @Override + public @Nullable BiomeSearchResult locateNearestBiome(@NotNull Location origin, int radius, int horizontalInterval, int verticalInterval, @NotNull Biome... biomes) { + return null; + } + + @Override + public int getMinHeight() { + return this.minHeight; + } + + @Override + public int getMaxHeight() { + return this.maxHeight; + } + + @Override + public int getLogicalHeight() { + return this.logicalHeight; + } + + @Override + public int getSeaLevel() { + return this.seaLevel; + } + + @Override + public boolean getKeepSpawnInMemory() { + return this.keepSpawnInMemory; + } + + @Override + public void setKeepSpawnInMemory(boolean keepLoaded) { + this.keepSpawnInMemory = keepLoaded; + } + + @Override + public boolean isAutoSave() { + return this.autoSave; + } + + @Override + public void setAutoSave(boolean value) { + this.autoSave = value; + } + + @Override + public void setDifficulty(@NotNull Difficulty difficulty) { + this.difficulty = difficulty; + try { + NativeBridgeFfi.setWorldDifficulty(SetWorldDifficultyRequest.newBuilder() + .setWorldUuid(BridgeUtils.convertUuid(this.uuid)) + .setDifficulty(difficulty.name()) + .build()); + } catch (Throwable ignored) {} + } + + @Override + public @NotNull Difficulty getDifficulty() { + return this.difficulty; + } + + @Override + public @NotNull File getWorldFolder() { + return new File(this.name); + } + + @Override + public @NotNull WorldType getWorldType() { + return WorldType.NORMAL; + } + + @Override + public boolean canGenerateStructures() { + return true; + } + + @Override + public boolean isHardcore() { + return this.hardcore; + } + + @Override + public void setHardcore(boolean hardcore) { + this.hardcore = hardcore; + } + + @Override + public long getTicksPerAnimalSpawns() { + return 400L; + } + + @Override + public void setTicksPerAnimalSpawns(int ticksPerAnimalSpawns) { + } + + @Override + public long getTicksPerMonsterSpawns() { + return 1L; + } + + @Override + public void setTicksPerMonsterSpawns(int ticksPerMonsterSpawns) { + } + + @Override + public long getTicksPerWaterSpawns() { + return 1L; + } + + @Override + public void setTicksPerWaterSpawns(int ticksPerWaterSpawns) { + } + + @Override + public long getTicksPerWaterAmbientSpawns() { + return 1L; + } + + @Override + public void setTicksPerWaterAmbientSpawns(int ticksPerWaterAmbientSpawns) { + } + + @Override + public long getTicksPerWaterUndergroundCreatureSpawns() { + return 1L; + } + + @Override + public void setTicksPerWaterUndergroundCreatureSpawns(int ticksPerWaterUndergroundCreatureSpawns) { + } + + @Override + public long getTicksPerAmbientSpawns() { + return 1L; + } + + @Override + public void setTicksPerAmbientSpawns(int ticksPerAmbientSpawns) { + } + + @Override + public long getTicksPerSpawns(@NotNull SpawnCategory spawnCategory) { + return 1L; + } + + @Override + public void setTicksPerSpawns(@NotNull SpawnCategory spawnCategory, int ticksPerSpawns) { + } + + @Override + public int getMonsterSpawnLimit() { + return 70; + } + + @Override + public void setMonsterSpawnLimit(int limit) { + } + + @Override + public int getAnimalSpawnLimit() { + return 10; + } + + @Override + public void setAnimalSpawnLimit(int limit) { + } + + @Override + public int getWaterAnimalSpawnLimit() { + return 5; + } + + @Override + public void setWaterAnimalSpawnLimit(int limit) { + } + + @Override + public int getWaterAmbientSpawnLimit() { + return 20; + } + + @Override + public void setWaterAmbientSpawnLimit(int limit) { + } + + @Override + public int getWaterUndergroundCreatureSpawnLimit() { + return 5; + } + + @Override + public void setWaterUndergroundCreatureSpawnLimit(int limit) { + } + + @Override + public int getAmbientSpawnLimit() { + return 15; + } + + @Override + public void setAmbientSpawnLimit(int limit) { + } + + @Override + public int getSpawnLimit(@NotNull SpawnCategory spawnCategory) { + return 10; + } + + @Override + public void setSpawnLimit(@NotNull SpawnCategory spawnCategory, int limit) { + } + + @Override + public void setMetadata(@NotNull String metadataKey, @NotNull MetadataValue newMetadataValue) { + List list = this.metadataMap.computeIfAbsent(metadataKey, k -> new ArrayList<>()); + list.removeIf(v -> v.getOwningPlugin() == newMetadataValue.getOwningPlugin()); + list.add(newMetadataValue); + } + + @Override + public @NotNull List getMetadata(@NotNull String metadataKey) { + List list = this.metadataMap.get(metadataKey); + return list != null ? Collections.unmodifiableList(new ArrayList<>(list)) : Collections.emptyList(); } @Override public boolean hasMetadata(@NotNull String metadataKey) { - // TODO Auto-generated method stub - throw new UnsupportedOperationException( - "Unimplemented method 'hasMetadata'" - ); + List list = this.metadataMap.get(metadataKey); + return list != null && !list.isEmpty(); } @Override - public void removeMetadata( - @NotNull String metadataKey, - @NotNull Plugin owningPlugin - ) { - // TODO Auto-generated method stub - throw new UnsupportedOperationException( - "Unimplemented method 'removeMetadata'" - ); + public void removeMetadata(@NotNull String metadataKey, @NotNull Plugin owningPlugin) { + List list = this.metadataMap.get(metadataKey); + if (list != null) { + list.removeIf(v -> v.getOwningPlugin() == owningPlugin); + if (list.isEmpty()) { + this.metadataMap.remove(metadataKey); + } + } } @Override - public @NotNull PersistentDataContainer getPersistentDataContainer() { - // TODO Auto-generated method stub - throw new UnsupportedOperationException( - "Unimplemented method 'getPersistentDataContainer'" - ); - } - - @Override - public void sendPluginMessage( - @NotNull Plugin source, - @NotNull String channel, - byte@NotNull [] message - ) { - // TODO Auto-generated method stub - throw new UnsupportedOperationException( - "Unimplemented method 'sendPluginMessage'" - ); + public void sendPluginMessage(@NotNull Plugin source, @NotNull String channel, byte@NotNull [] message) { + Bukkit.getServer().sendPluginMessage(source, channel, message); } @Override public @NotNull Set getListeningPluginChannels() { - // TODO Auto-generated method stub - throw new UnsupportedOperationException( - "Unimplemented method 'getListeningPluginChannels'" - ); + return Bukkit.getServer().getListeningPluginChannels(); } @Override - public void setAllowMonsterSpawning(boolean allowMonsters) { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'setAllowMonsterSpawning'"); + public @NotNull PersistentDataContainer getPersistentDataContainer() { + return this.pdc; } @Override - public @Nullable Location locateNearestPoi(@NotNull Location origin, @NotNull PoiType poiType, @Positive int radius, - @NotNull Occupancy occupancy) { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'locateNearestPoi'"); + public void setChunkForceLoaded(int x, int z, boolean forced) { + try { + NativeBridgeFfi.setChunkForceLoaded(SetChunkForceLoadedRequest.newBuilder() + .setWorldUuid(BridgeUtils.convertUuid(this.uuid)) + .setX(x) + .setZ(z) + .setForced(forced) + .build()); + } catch (Throwable ignored) {} } @Override - public @NotNull List locateAllPoiInRange(@NotNull Location origin, - @NotNull Predicate poiTypePredicate, @Positive int radius, @NotNull Occupancy occupancy) { + public boolean isChunkForceLoaded(int x, int z) { + for (Chunk c : getForceLoadedChunks()) { + if (c.getX() == x && c.getZ() == z) { + return true; + } + } + return false; + } + + @Override + public @NotNull Collection getForceLoadedChunks() { + List list = new ArrayList<>(); + try { + var res = NativeBridgeFfi.getForceLoadedChunks(GetForceLoadedChunksRequest.newBuilder() + .setWorldUuid(BridgeUtils.convertUuid(this.uuid)) + .build()); + if (res != null) { + for (var coord : res.getChunksList()) { + list.add(getChunkAt(coord.getX(), coord.getZ())); + } + } + } catch (Throwable ignored) {} + return list; + } + + @Override + public boolean addPluginChunkTicket(int x, int z, @NotNull Plugin plugin) { + int[] key = new int[] { x, z }; + return this.chunkTickets.computeIfAbsent(key, k -> new HashSet<>()).add(plugin); + } + + @Override + public boolean removePluginChunkTicket(int x, int z, @NotNull Plugin plugin) { + int[] key = new int[] { x, z }; + Set set = this.chunkTickets.get(key); + return set != null && set.remove(plugin); + } + + @Override + public void removePluginChunkTickets(@NotNull Plugin plugin) { + for (Set set : this.chunkTickets.values()) { + set.remove(plugin); + } + } + + @Override + public @NotNull Collection getPluginChunkTickets(int x, int z) { + int[] key = new int[] { x, z }; + Set set = this.chunkTickets.get(key); + return set != null ? Collections.unmodifiableSet(new HashSet<>(set)) : Collections.emptySet(); + } + + @Override + public @NotNull Map> getPluginChunkTickets() { + Map> map = new HashMap<>(); + for (Map.Entry> entry : this.chunkTickets.entrySet()) { + Chunk c = getChunkAt(entry.getKey()[0], entry.getKey()[1]); + for (Plugin p : entry.getValue()) { + map.computeIfAbsent(p, k -> new ArrayList<>()).add(c); + } + } + return map; + } + + @Override + public int getViewDistance() { + return Bukkit.getServer().getViewDistance(); + } + + @Override + public void setViewDistance(int viewDistance) { + } + + @Override + public int getSimulationDistance() { + return Bukkit.getServer().getSimulationDistance(); + } + + @Override + public void setSimulationDistance(int simulationDistance) { + } + + @Override + public int getSendViewDistance() { + return getViewDistance(); + } + + @Override + public void setSendViewDistance(int viewDistance) { + } + + @Override + public @NotNull Spigot spigot() { + return this.spigot; + } + + @Override + public @Nullable RayTraceResult rayTraceBlocks(@NotNull Location start, @NotNull Vector direction, double maxDistance) { + return rayTraceBlocks(start, direction, maxDistance, FluidCollisionMode.NEVER, false); + } + + @Override + public @Nullable RayTraceResult rayTraceBlocks(@NotNull Location start, @NotNull Vector direction, double maxDistance, @NotNull FluidCollisionMode fluidCollisionMode) { + return rayTraceBlocks(start, direction, maxDistance, fluidCollisionMode, false); + } + + @Override + public @Nullable RayTraceResult rayTraceBlocks(@NotNull Location start, @NotNull Vector direction, double maxDistance, @NotNull FluidCollisionMode fluidCollisionMode, boolean ignorePassableBlocks) { + Vector dir = direction.clone().normalize().multiply(0.2); + Location current = start.clone(); + double traveled = 0; + while (traveled < maxDistance) { + current.add(dir); + traveled += 0.2; + Block b = getBlockAt(current); + Material type = b.getType(); + if (type != Material.AIR && type != Material.CAVE_AIR && type != Material.VOID_AIR) { + if (!ignorePassableBlocks || b.isCollidable()) { + return new RayTraceResult(current.toVector(), b, null); + } + } + } + return null; + } + + @Override + public @Nullable RayTraceResult rayTraceEntities(@NotNull Location start, @NotNull Vector direction, double maxDistance) { + return rayTraceEntities(start, direction, maxDistance, 0.0, null); + } + + @Override + public @Nullable RayTraceResult rayTraceEntities(@NotNull Location start, @NotNull Vector direction, double maxDistance, double raySize) { + return rayTraceEntities(start, direction, maxDistance, raySize, null); + } + + @Override + public @Nullable RayTraceResult rayTraceEntities(@NotNull Location start, @NotNull Vector direction, double maxDistance, @Nullable Predicate filter) { + return rayTraceEntities(start, direction, maxDistance, 0.0, filter); + } + + @Override + public @Nullable RayTraceResult rayTraceEntities(@NotNull Location start, @NotNull Vector direction, double maxDistance, double raySize, @Nullable Predicate filter) { + Vector dir = direction.clone().normalize().multiply(0.2); + Location current = start.clone(); + double traveled = 0; + while (traveled < maxDistance) { + current.add(dir); + traveled += 0.2; + Collection nearby = getNearbyEntities(current, 0.5 + raySize, 0.5 + raySize, 0.5 + raySize, filter); + if (!nearby.isEmpty()) { + Entity hit = nearby.iterator().next(); + return new RayTraceResult(current.toVector(), hit, null); + } + } + return null; + } + + @Override + public @Nullable RayTraceResult rayTrace(@NotNull Location start, @NotNull Vector direction, double maxDistance, @NotNull FluidCollisionMode fluidCollisionMode, boolean ignorePassableBlocks, double raySize, @Nullable Predicate filter) { + RayTraceResult blockHit = rayTraceBlocks(start, direction, maxDistance, fluidCollisionMode, ignorePassableBlocks); + RayTraceResult entityHit = rayTraceEntities(start, direction, maxDistance, raySize, filter); + if (blockHit == null) return entityHit; + if (entityHit == null) return blockHit; + double blockDist = blockHit.getHitPosition().distanceSquared(start.toVector()); + double entityDist = entityHit.getHitPosition().distanceSquared(start.toVector()); + return blockDist <= entityDist ? blockHit : entityHit; + } + + @Override + public int getEntityCount() { + return getEntities().size(); + } + + @Override + public int getPlayerCount() { + return getPlayers().size(); + } + + @Override + public int getTickableTileEntityCount() { + return 0; + } + + @Override + public int getTileEntityCount() { + return 0; + } + + @Override + public int getChunkCount() { + return this.loadedChunks.size(); + } + + @Override + public @Nullable DragonBattle getEnderDragonBattle() { + return null; + } + + @Override + public boolean lineOfSightExists(@NotNull Location from, @NotNull Location to) { + Vector dir = to.toVector().subtract(from.toVector()); + double dist = dir.length(); + if (dist <= 0) return true; + RayTraceResult hit = rayTraceBlocks(from, dir, dist, FluidCollisionMode.NEVER, true); + return hit == null; + } + + @Override + public boolean hasRaids() { + return false; + } + + @Override + public @Nullable org.bukkit.Raid locateNearestRaid(@NotNull Location location, int radius) { + return null; + } + + @Override + public @Nullable org.bukkit.Raid getRaid(int id) { + return null; + } + + @Override + public @NotNull List getRaids() { return Collections.emptyList(); } + + @Override + public void spawnParticle(@NotNull Particle particle, @Nullable List receivers, @Nullable Player source, double x, double y, double z, int count, double offsetX, double offsetY, double offsetZ, double extra, @Nullable T data, boolean force) { + spawnParticle(particle, x, y, z, count, offsetX, offsetY, offsetZ, extra, data, force); + } + + @Override + public void playSound(@NotNull Entity entity, @NotNull Sound sound, @NotNull SoundCategory category, float volume, float pitch) { + playSound(entity.getLocation(), sound, category, volume, pitch); + } + + @Override + public void playSound(@NotNull Entity entity, @NotNull String sound, @NotNull SoundCategory category, float volume, float pitch) { + playSound(entity.getLocation(), sound, category, volume, pitch); + } + + @Override + public void playSound(@NotNull Entity entity, @NotNull Sound sound, @NotNull SoundCategory category, float volume, float pitch, long seed) { + playSound(entity.getLocation(), sound, category, volume, pitch, seed); + } + + @Override + public void playSound(@NotNull Entity entity, @NotNull String sound, @NotNull SoundCategory category, float volume, float pitch, long seed) { + playSound(entity.getLocation(), sound, category, volume, pitch, seed); + } + + @Override + public @Nullable Location locateNearestPoi(@NotNull Location location, @NotNull PoiType poiType, int radius, @NotNull Occupancy occupancy) { + return null; + } + + @Override + public @NotNull List locateAllPoiInRange(@NotNull Location location, @NotNull Predicate predicate, int radius, @NotNull Occupancy occupancy) { + return Collections.emptyList(); + } + + @Override + public double getCoordinateScale() { + return this.environment == Environment.NETHER ? 8.0 : 1.0; + } + + @Override + public boolean isFixedTime() { + return this.environment != Environment.NORMAL; + } + + @Override + public @NotNull Collection getInfiniburn() { + return Collections.singleton(Material.NETHERRACK); + } + + @Override + public void sendGameEvent(@Nullable Entity sourceEntity, @NotNull org.bukkit.GameEvent gameEvent, @NotNull Vector position) { + } + + @Override + public boolean hasBonusChest() { + return false; + } + + @Override + public @NotNull Path getWorldPath() { + return Path.of(this.name); + } + + @Override + public boolean isDayTime() { + return (this.time % 24000) < 12000; + } + + @Override + public boolean getAllowAnimals() { + return true; + } + + @Override + public boolean getAllowMonsters() { + return true; + } + + @Override + public @NotNull ChunkSnapshot getEmptyChunkSnapshot(int x, int z, boolean includeBiome, boolean includeBiomeTempRain) { + return getChunkAt(x, z).getChunkSnapshot(false, includeBiome, includeBiomeTempRain); + } + + @Override + public double getTemperature(int x, int y, int z) { + return 0.8; + } + + @Override + public double getHumidity(int x, int y, int z) { + return 0.5; + } + + @Override + public boolean isNatural() { + return this.environment == Environment.NORMAL; + } + + @Override + public boolean isBedWorks() { + return this.environment == Environment.NORMAL; + } + + @Override + public boolean hasSkyLight() { + return this.environment != Environment.NETHER; + } + + @Override + public boolean hasCeiling() { + return this.environment == Environment.NETHER; + } + + @Override + public boolean isPiglinSafe() { + return this.environment == Environment.NETHER; + } + + @Override + public boolean isRespawnAnchorWorks() { + return this.environment == Environment.NETHER; + } + + @Override + public boolean isUltraWarm() { + return this.environment == Environment.NETHER; + } + + @Override + public @Nullable Entity getEntity(@NotNull UUID uuid) { + for (Entity e : getEntities()) { + if (e.getUniqueId().equals(uuid)) { + return e; + } + } + return null; + } + + @Override + public @Nullable RayTraceResult rayTraceBlocks(@NotNull Position start, @NotNull Vector direction, double maxDistance, @NotNull FluidCollisionMode fluidCollisionMode, boolean ignorePassableBlocks, @Nullable Predicate filter) { + return rayTraceBlocks(new Location(this, start.x(), start.y(), start.z()), direction, maxDistance, fluidCollisionMode, ignorePassableBlocks); + } + + @Override + public @Nullable RayTraceResult rayTrace(@NotNull Position start, @NotNull Vector direction, double maxDistance, @NotNull FluidCollisionMode fluidCollisionMode, boolean ignorePassableBlocks, double raySize, @Nullable Predicate entityFilter, @Nullable Predicate blockFilter) { + return rayTrace(new Location(this, start.x(), start.y(), start.z()), direction, maxDistance, fluidCollisionMode, ignorePassableBlocks, raySize, entityFilter); + } + + @Override + public void setBiome(int x, int z, @NotNull Biome biome) { + setBiome(x, 64, z, biome); + } + + @Override + public void setSpawnFlags(boolean allowMonsters, boolean allowAnimals) { + } + + @Override + public void setAllowMonsterSpawning(boolean allow) { + } + + @Override + public @NotNull T spawn(@NotNull Location location, @NotNull Class clazz, @NotNull CreatureSpawnEvent.SpawnReason reason, boolean randomizeData, @Nullable Consumer function) throws IllegalArgumentException { + return (T) spawn(location, (Class) clazz, function, reason); + } + + @Override + public @Nullable RayTraceResult rayTraceEntities(@NotNull Position start, @NotNull Vector direction, double maxDistance, double raySize, @Nullable Predicate filter) { + return rayTraceEntities(new Location(this, start.x(), start.y(), start.z()), direction, maxDistance, raySize, filter); + } + + @Override + public @Nullable RayTraceResult rayTrace(@NotNull Consumer consumer) { + return null; + } + + @Override + public void getChunkAtAsync(int x, int z, boolean gen, boolean urgent, @Nullable Consumer cb) { + Chunk c = getChunkAt(x, z); + if (cb != null) { + cb.accept(c); + } + } + + @Override + public void getChunksAtAsync(int minX, int minZ, int maxX, int maxZ, boolean urgent, @NotNull Runnable onLoad) { + for (int cx = minX; cx <= maxX; cx++) { + for (int cz = minZ; cz <= maxZ; cz++) { + getChunkAt(cx, cz); + } + } + if (onLoad != null) { + onLoad.run(); + } + } + + @Override + public @NotNull Collection getEntitiesByClass(@NotNull Class... classes) { + List list = new ArrayList<>(); + for (Entity e : getEntities()) { + for (Class c : classes) { + if (c.isInstance(e)) { + list.add((T) e); + break; + } + } + } + return list; + } + + @Override + public boolean generateTree(@NotNull Location location, @NotNull TreeType type) { + return true; + } + + @Override + public boolean generateTree(@NotNull Location location, @NotNull TreeType type, @NotNull BlockChangeDelegate delegate) { + return true; + } + + @Override + public @NotNull Collection getIntersectingChunks(@NotNull BoundingBox box) { + List list = new ArrayList<>(); + int minX = (int) Math.floor(box.getMinX()) >> 4; + int maxX = (int) Math.floor(box.getMaxX()) >> 4; + int minZ = (int) Math.floor(box.getMinZ()) >> 4; + int maxZ = (int) Math.floor(box.getMaxZ()) >> 4; + for (int cx = minX; cx <= maxX; cx++) { + for (int cz = minZ; cz <= maxZ; cz++) { + list.add(getChunkAt(cx, cz)); + } + } + return list; + } + + @Override + public @NotNull Collection getPlayersSeeingChunk(int chunkX, int chunkZ) { + return getChunkAt(chunkX, chunkZ).getPlayersSeeingChunk(); + } + + @Override + public @NotNull Collection getPlayersSeeingChunk(@NotNull Chunk chunk) { + return chunk.getPlayersSeeingChunk(); + } } diff --git a/proto/patchbukkit/bridge.proto b/proto/patchbukkit/bridge.proto index 02c609a..a3fda5e 100644 --- a/proto/patchbukkit/bridge.proto +++ b/proto/patchbukkit/bridge.proto @@ -13,6 +13,7 @@ import "patchbukkit/itemstack.proto"; import "patchbukkit/log.proto"; import "patchbukkit/message.proto"; import "patchbukkit/registry.proto"; +import "patchbukkit/server.proto"; import "patchbukkit/sound.proto"; import "patchbukkit/world.proto"; @@ -32,6 +33,50 @@ service NativeBridge { rpc IsOnGround(patchbukkit.common.UUID) returns (patchbukkit.entity.IsOnGroundResponse); rpc GetPlayerLocale(patchbukkit.common.UUID) returns (patchbukkit.entity.GetPlayerLocaleResponse); 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 SetPlayerInventorySlot(patchbukkit.itemstack.SetPlayerInventorySlotRequest) returns (google.protobuf.Empty); @@ -65,5 +110,46 @@ service NativeBridge { rpc RegisterCommand(patchbukkit.command.RegisterCommandRequest) returns (google.protobuf.Empty); 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); } diff --git a/proto/patchbukkit/entity.proto b/proto/patchbukkit/entity.proto index e4be0a9..1f02155 100644 --- a/proto/patchbukkit/entity.proto +++ b/proto/patchbukkit/entity.proto @@ -66,6 +66,171 @@ message IsOpResponse { 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; +} + diff --git a/proto/patchbukkit/server.proto b/proto/patchbukkit/server.proto new file mode 100644 index 0000000..2ea5e71 --- /dev/null +++ b/proto/patchbukkit/server.proto @@ -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; +} diff --git a/proto/patchbukkit/world.proto b/proto/patchbukkit/world.proto index 3154ef1..b4327fd 100644 --- a/proto/patchbukkit/world.proto +++ b/proto/patchbukkit/world.proto @@ -2,6 +2,7 @@ syntax = "proto3"; package patchbukkit.world; +import "google/protobuf/empty.proto"; import "patchbukkit/common/types.proto"; option java_multiple_files = true; @@ -38,6 +39,7 @@ message SpawnParticleRequest { double offset_y = 8; double offset_z = 9; double extra = 10; + bool force = 11; } message GetWorldsResponse { @@ -75,6 +77,150 @@ message GetWorldInfoResponse { int32 max_height = 2; int32 height = 3; 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 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; +} diff --git a/rust/Cargo.lock b/rust/Cargo.lock index d154007..c7ceee4 100644 --- a/rust/Cargo.lock +++ b/rust/Cargo.lock @@ -283,7 +283,7 @@ checksum = "82f6aeea286b8eb4dd3431a1be1b59d290ace00f5bfd8e2a159bc2a05e2c1667" dependencies = [ "proc-macro2", "quote", - "syn 3.0.3", + "syn 3.0.4", ] [[package]] @@ -461,7 +461,7 @@ checksum = "46d07918caa9eeaaf06b7873925c53a61daac173539b4f7715090745e44e4e69" dependencies = [ "proc-macro2", "quote", - "syn 3.0.3", + "syn 3.0.4", ] [[package]] @@ -499,9 +499,9 @@ dependencies = [ [[package]] name = "cap-fs-ext" -version = "4.0.2" +version = "4.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d78e5a3368ae89b7cb68186411452b4b9fac8b41be9c19bf3f47c2d2c8e36e6b" +checksum = "56ff379b70af8e08307a8f65e7040c7301cb4a572538ade16b4984f0da77847f" dependencies = [ "cap-primitives", "cap-std", @@ -511,9 +511,9 @@ dependencies = [ [[package]] name = "cap-primitives" -version = "4.0.2" +version = "4.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cdadbd7c002d3a484b35243669abdae85a0ebaded5a61117169dc3400f9a7ff0" +checksum = "8b5f74729fd2f44701d1a8eb47e906cdb3ccd9ec0f02baad85a744b791940b18" dependencies = [ "ambient-authority", "fs-set-times", @@ -529,9 +529,9 @@ dependencies = [ [[package]] name = "cap-std" -version = "4.0.2" +version = "4.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7281235d6e96d3544ca18bba9049be92f4190f8d923e3caef1b5f66cfa752608" +checksum = "c1ec78e242cfa2cfe276807ac2ecc00315a6c97786977414bcd1c3963b6c91b8" dependencies = [ "cap-primitives", "io-extras", @@ -550,9 +550,9 @@ dependencies = [ [[package]] name = "cc" -version = "1.4.3" +version = "1.4.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "509591b7bcd67f4ef775afad7662703b4935daaa6ec0e5605cfb1090b32a2b6d" +checksum = "0ad534f4357a5264cce5019c989cf66a4f0dc4e0d1b1d15f8aacec0ff7360273" dependencies = [ "find-msvc-tools", "jobserver", @@ -811,27 +811,27 @@ dependencies = [ [[package]] name = "cranelift-assembler-x64" -version = "0.134.3" +version = "0.134.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d552bd33b7a56dc70aeb1e1c960e51a218fa0db50f23873b500a310379450b2d" +checksum = "16c273ba1bfc3a1cb27cb4f83df27134f3cb638a61f5ed79ab22cf86eb13da0d" dependencies = [ "cranelift-assembler-x64-meta", ] [[package]] name = "cranelift-assembler-x64-meta" -version = "0.134.3" +version = "0.134.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "078e80e4c222279e3330f6aa1a256ca77ddf156d4453166a9f09defedc4594dd" +checksum = "cbf8909326a466739e83ffe11e74c0c8c6b4bfa911b612aece915daa746cff58" dependencies = [ "cranelift-srcgen", ] [[package]] name = "cranelift-bforest" -version = "0.134.3" +version = "0.134.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "820ce15d4ad3562d613c31a67b6d0434d403e7091a68d1349903842f7d31737e" +checksum = "3cb26b06b54d8b2f8cdf4d440a32fc3b3b91c71c162333a69a70a66fa265fc45" dependencies = [ "cranelift-entity", "wasmtime-internal-core", @@ -839,9 +839,9 @@ dependencies = [ [[package]] name = "cranelift-bitset" -version = "0.134.3" +version = "0.134.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "61bca563d4b86d285928d9e27f97f27039bb33a0fc524fa130d7d0c106bf8ab3" +checksum = "7ce4e57dbb7f73d08011808a19787c3a03c3e7854b2a479ed0b788915fb671be" dependencies = [ "serde", "serde_derive", @@ -850,9 +850,9 @@ dependencies = [ [[package]] name = "cranelift-codegen" -version = "0.134.3" +version = "0.134.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "709f4b7c0fb57d952658d5b5c07fbdc4149acd7b7f0de9678ae754b9c981949a" +checksum = "74b0a8968f33d0bc13bc86fe70dd3947e93acc9f358ed9848e9988448972a9be" dependencies = [ "bumpalo", "cranelift-assembler-x64", @@ -881,9 +881,9 @@ dependencies = [ [[package]] name = "cranelift-codegen-meta" -version = "0.134.3" +version = "0.134.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "903dc8915af62aad1d9d0f39a5968d33fa80b9aa899ed6f56e47f40ca4512e1e" +checksum = "f69e8e2fe9deb48ef7b8d0c61612f8c1e2277d0421c201e30a4c844a4ebac698" dependencies = [ "cranelift-assembler-x64-meta", "cranelift-codegen-shared", @@ -894,24 +894,24 @@ dependencies = [ [[package]] name = "cranelift-codegen-shared" -version = "0.134.3" +version = "0.134.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0522d74c227e49f3fd49ab055311486ae4f09083262b66705bed676952491469" +checksum = "ad4db1e87a65e9c5a832e3ede160830d4e34df938ac38b5604819f8825687e73" [[package]] name = "cranelift-control" -version = "0.134.3" +version = "0.134.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "66560ea1c5cef72e170b18e46d263dba2d3169c9d39e8cafdab2173c6362cc1a" +checksum = "9248cd37bb6ec460981f57ead368e32b1eca7a207ad50e3e9c7adc3b161f20e9" dependencies = [ "arbitrary", ] [[package]] name = "cranelift-entity" -version = "0.134.3" +version = "0.134.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b62ef5b17cc814d27e96b66a5b46da0e4ce2b8ac55a6d478048bb99d04b05526" +checksum = "26b8bc91c0a3530d132acbf118965dabe9a084029eb0fa74b64760360dec3316" dependencies = [ "cranelift-bitset", "serde", @@ -921,9 +921,9 @@ dependencies = [ [[package]] name = "cranelift-frontend" -version = "0.134.3" +version = "0.134.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a87e0aaa39dbf70693b348a221e45904111704ee8f9fef140498471005f9842d" +checksum = "5086f2ebc084a98b387e522680f62ef65a512444d8382151e03088716d3a1714" dependencies = [ "cranelift-codegen", "hashbrown 0.17.1", @@ -934,15 +934,15 @@ dependencies = [ [[package]] name = "cranelift-isle" -version = "0.134.3" +version = "0.134.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf79003ebfa1eed5e87f3b84446ad5236f540268289960e14f387dc9b28e40b7" +checksum = "b6a658670f779afc2df083be201ebe54805f69b7b9ffa8dab04142b72ed751c9" [[package]] name = "cranelift-native" -version = "0.134.3" +version = "0.134.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "05bf4f235743c81e67ee4db617c5a4a0b65d58d3f0cfc575ee0f1a4e0cd58273" +checksum = "16a41083b1fd953debd6f32d21fcda765b258f323ce907fdd6b4715f2769c478" dependencies = [ "cranelift-codegen", "libc", @@ -951,9 +951,9 @@ dependencies = [ [[package]] name = "cranelift-srcgen" -version = "0.134.3" +version = "0.134.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f6977c2a71ab1e0d1e62f966b411a498aa04c4dce47d93d52f8a360a06058922" +checksum = "244d9cd0759b0a3ccdd587cafb13434f9ea43b5c9d07701d277daf5cd0f7b7c1" [[package]] name = "crc" @@ -991,9 +991,9 @@ dependencies = [ [[package]] name = "crc32fast" -version = "1.5.0" +version = "1.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9481c1c90cbf2ac953f07c8d4a58aa3945c425b7185c9154d67a65e4230da511" +checksum = "8498c871161e1742aaa9d52551b2d6ebdd4c3d45a3be423e3728f33b955be550" dependencies = [ "cfg-if", ] @@ -1382,7 +1382,7 @@ checksum = "c6232dd377dcc64799954cbd3a9bb882e9cdc1308ccd87b1c098f1fb2eaf82a8" dependencies = [ "proc-macro2", "quote", - "syn 3.0.3", + "syn 3.0.4", ] [[package]] @@ -1446,9 +1446,9 @@ dependencies = [ [[package]] name = "either" -version = "1.17.0" +version = "1.18.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e5e8f6c15a24b9a3ee5efec809ccd006d3b30e8b3bb63c39af737c7f87daa1d" +checksum = "252afb9ae5eaa683babdc6a068b3f5726eb19e05070c731f9b2a23a7c3e8ed34" [[package]] name = "elliptic-curve" @@ -1759,7 +1759,7 @@ checksum = "9fb9654ba8355388abeb8dcb4fc62f511300867002afc858860463bdd9fe0c44" dependencies = [ "proc-macro2", "quote", - "syn 3.0.3", + "syn 3.0.4", ] [[package]] @@ -1889,9 +1889,9 @@ dependencies = [ [[package]] name = "h2" -version = "0.4.17" +version = "0.4.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9f877e75f39e9827ec50a572dd592684ac28c029578726c85f1b2aa6ab807449" +checksum = "ef8e5e5a340588f4452631496976cf8636d4a7ecf600239fdc27615d2530bc16" dependencies = [ "atomic-waker", "bytes", @@ -2163,9 +2163,9 @@ checksum = "e590f038c1464a96894fd6d10127e90a8be4509f56ff7ecef851b15cee0b7caa" [[package]] name = "icu_provider" -version = "2.3.0" +version = "2.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "92a7ed671a6aad807a8651a2e1782a6598fda9ce5185dd8158549e95a91c6428" +checksum = "d27bbb9d3abbefac45d55f647c9de1d44aafcd1186eb91879afef17c396c3e73" dependencies = [ "displaydoc", "icu_locale_core", @@ -2550,9 +2550,9 @@ dependencies = [ [[package]] name = "log" -version = "0.4.33" +version = "0.4.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ceec5bc11778974d1bcb055b18002eba7f4b3518b6a0081b3af5f21666da9ad" +checksum = "f9f8bd3e56ce4dfc153cf470fffbfa98c7620958b312ca5c3a4b8d5181fd13c6" [[package]] name = "lz4-java-wrc" @@ -2834,7 +2834,7 @@ checksum = "e4e98dc3b890f6c23a0f9d3d491a2823d0dea0fa656302a13dd225fa924112a8" dependencies = [ "proc-macro2", "quote", - "syn 3.0.3", + "syn 3.0.4", ] [[package]] @@ -3024,9 +3024,11 @@ dependencies = [ "log", "prost", "prost-build", + "prost-types", "protoc-bin-vendored", "pumpkin", "pumpkin-api-macros", + "pumpkin-config", "pumpkin-data", "pumpkin-protocol", "pumpkin-util", @@ -3035,6 +3037,7 @@ dependencies = [ "serde", "serde-saphyr", "serde_json", + "time", "tokio", "toml 1.1.4+spec-1.1.0", "tracing", @@ -3481,14 +3484,14 @@ checksum = "1c8d9ca532f185d5d4db7a7c9d51420b452168ea1c2b913953281bd6fe1fcbd0" dependencies = [ "proc-macro2", "quote", - "syn 3.0.3", + "syn 3.0.4", ] [[package]] name = "pulley-interpreter" -version = "47.0.3" +version = "47.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bc5c8c21ea032e4efbdf2d067dc45171779dbe0c8ecf20ef4a57efa7474f2b0a" +checksum = "ed2bd641cb6a3ec5bffe60405568933163e0ea619da0943e9d2a417ceaa7fe82" dependencies = [ "cranelift-bitset", "log", @@ -3498,9 +3501,9 @@ dependencies = [ [[package]] name = "pulley-macros" -version = "47.0.3" +version = "47.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9f10925455d5dde962e3604eade797ba5488644c8ee14a44191e2f2b58980574" +checksum = "d3fd4024d1e559eaf5579bc8ad84a2b5915f34b47acc13a06ffba7dac401b166" dependencies = [ "proc-macro2", "quote", @@ -3510,7 +3513,7 @@ dependencies = [ [[package]] name = "pumpkin" 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 = [ "aes 0.9.2", "arc-swap", @@ -3576,18 +3579,18 @@ dependencies = [ [[package]] name = "pumpkin-api-macros" 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 = [ "proc-macro-error2", "proc-macro2", "quote", - "syn 3.0.3", + "syn 3.0.4", ] [[package]] name = "pumpkin-codecs" 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 = [ "serde_json", "tracing", @@ -3596,7 +3599,7 @@ dependencies = [ [[package]] name = "pumpkin-config" 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 = [ "pumpkin-util", "serde", @@ -3608,7 +3611,7 @@ dependencies = [ [[package]] name = "pumpkin-data" 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 = [ "crc-fast", "phf", @@ -3621,7 +3624,7 @@ dependencies = [ [[package]] name = "pumpkin-inventory" 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 = [ "pumpkin-data", "pumpkin-protocol", @@ -3636,20 +3639,20 @@ dependencies = [ [[package]] name = "pumpkin-macros" 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 = [ "heck", "proc-macro-error2", "proc-macro2", "pumpkin-data", "quote", - "syn 3.0.3", + "syn 3.0.4", ] [[package]] name = "pumpkin-nbt" 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 = [ "bytes", "cesu8", @@ -3663,7 +3666,7 @@ dependencies = [ [[package]] name = "pumpkin-protocol" 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 = [ "aes 0.9.2", "async-compression", @@ -3688,7 +3691,7 @@ dependencies = [ [[package]] name = "pumpkin-util" 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 = [ "base64 0.23.1", "bytes", @@ -3714,7 +3717,7 @@ dependencies = [ [[package]] name = "pumpkin-world" 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 = [ "arc-swap", "bitflags 2.13.1", @@ -4004,7 +4007,7 @@ checksum = "1c25ef604ac7dd839d44d64648952ea23c97866f124ff671b0ed2cf3ad9bb06e" dependencies = [ "proc-macro2", "quote", - "syn 3.0.3", + "syn 3.0.4", ] [[package]] @@ -4426,9 +4429,9 @@ dependencies = [ [[package]] name = "rustls-webpki" -version = "0.103.14" +version = "0.103.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0527518605e68109d875e248ea259b6758801cf165e4b2c2733ae3b51f12535a" +checksum = "f3c3cf1d8b1e7d4927e2d154c3fcb02979afb9939629c62cd9048d4f07b60ac2" dependencies = [ "ring", "rustls-pki-types", @@ -4569,7 +4572,7 @@ checksum = "e7a5d71263a5a7d47b41f6b3f06ba276f10cc18b0931f1799f710578e2309348" dependencies = [ "proc-macro2", "quote", - "syn 3.0.3", + "syn 3.0.4", ] [[package]] @@ -4593,7 +4596,7 @@ checksum = "8d3b1629de253c70a0508c3899572da79ca359fdab27c7920ff00406df418906" dependencies = [ "proc-macro2", "quote", - "syn 3.0.3", + "syn 3.0.4", ] [[package]] @@ -4850,9 +4853,9 @@ dependencies = [ [[package]] name = "syn" -version = "3.0.3" +version = "3.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "53e9bae58849f64dfa4f5d5ae372c8341f7305f82a3868709269343628b659a3" +checksum = "e6275cddf4610d1775e6d1fe9469b2e77d0f39fd98fb7450901b821e0c53649f" dependencies = [ "proc-macro2", "quote", @@ -4954,7 +4957,7 @@ checksum = "bc04cd3e1236dd4a98afca4569f2deb3f120e5422a4023be2cb683f8486292af" dependencies = [ "proc-macro2", "quote", - "syn 3.0.3", + "syn 3.0.4", ] [[package]] @@ -5048,7 +5051,7 @@ checksum = "78773a2a397f451582ce068015985c33193cf6dea8b74d2a639fe457b2f07b0e" dependencies = [ "proc-macro2", "quote", - "syn 3.0.3", + "syn 3.0.4", ] [[package]] @@ -5501,9 +5504,9 @@ dependencies = [ [[package]] name = "wasmtime" -version = "47.0.3" +version = "47.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c80ca6098e0d4d06886d91d7f2cc3cb6623eb583c4c0ab3c89cbfb6098c8586c" +checksum = "4cdaf5b8af5713146e3a62d940c71e3eb2ad1ebacfa6b55f0c8509a5b4b87718" dependencies = [ "addr2line", "async-trait", @@ -5545,9 +5548,9 @@ dependencies = [ [[package]] name = "wasmtime-environ" -version = "47.0.3" +version = "47.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "134f9d136d29c76f6c1b4c9b468e97a2efc38c7d49fd188e39b64870b2fea701" +checksum = "58793271eab7ec26bab99ca497131f8b37702e7dc39e2f2d18293e3e915b4964" dependencies = [ "anyhow", "cpp_demangle", @@ -5576,9 +5579,9 @@ dependencies = [ [[package]] name = "wasmtime-internal-cache" -version = "47.0.3" +version = "47.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "65db2eb1bfc5371a3b4107dbf539d0b93d05016fc29625b1648146b47db02071" +checksum = "ede02ba77442cab88a2ddd5d16373e8bc450b55e05de8be039b024987a53d5a7" dependencies = [ "base64 0.22.1", "directories-next", @@ -5596,9 +5599,9 @@ dependencies = [ [[package]] name = "wasmtime-internal-component-macro" -version = "47.0.3" +version = "47.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2bf7b91fed3fc34781d57f9c6654ea2513bf0a99f7b0b0523c00de1e6efebe1c" +checksum = "c5bba4eff4804620bae04d7793308d97a0c5a8a97ea19e635452518f84560f84" dependencies = [ "anyhow", "proc-macro2", @@ -5611,15 +5614,15 @@ dependencies = [ [[package]] name = "wasmtime-internal-component-util" -version = "47.0.3" +version = "47.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fc8a678149885cae00289f806fbe74c7863084cf74cace0b8dc73602279400e1" +checksum = "3d7032ff6b4d7a45e05be1cb32ec1756df5f2669f08296dbe2c61c3789dead2c" [[package]] name = "wasmtime-internal-core" -version = "47.0.3" +version = "47.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a0092c4b9d070ac5e278b6d0db10f5e71214190f0347ca57502b4692f796321" +checksum = "f41d3b2cb9c3de7b696690af070d408b5e357011832f8b8bb7fbb315dbb620ed" dependencies = [ "hashbrown 0.17.1", "libm", @@ -5628,9 +5631,9 @@ dependencies = [ [[package]] name = "wasmtime-internal-cranelift" -version = "47.0.3" +version = "47.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6851ebc9e03cab23d9821d2b2505d380bdfe38f35ccec945247b05274d85c98b" +checksum = "1d5cda5b90247978dd2ba6ca4e309508da19e9244445671d2d0e6a34e5c3b847" dependencies = [ "cfg-if", "cranelift-codegen", @@ -5655,9 +5658,9 @@ dependencies = [ [[package]] name = "wasmtime-internal-fiber" -version = "47.0.3" +version = "47.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b26da6d5f60d4c438da70bba3553fe810a840533a64156be287dca2081c6991" +checksum = "4d126366176553e8f304e8ac2078a4cc37134fe8f19d4d4695a09c5a2f577f10" dependencies = [ "cc", "cfg-if", @@ -5670,9 +5673,9 @@ dependencies = [ [[package]] name = "wasmtime-internal-jit-debug" -version = "47.0.3" +version = "47.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed621ba25d7bf78b7edd7b4749abbb27c2e5cdba836c9504a424ee74b6c23149" +checksum = "c1d28ff131bc40e11d84bc3a9fa0d264506439ffbbc89f3562fbb529d1caa617" dependencies = [ "cc", "wasmtime-internal-versioned-export-macros", @@ -5680,9 +5683,9 @@ dependencies = [ [[package]] name = "wasmtime-internal-jit-icache-coherence" -version = "47.0.3" +version = "47.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5684ba160951baad06a725696f3c590e2fb0e8067c2aebee27bf7f9259058e85" +checksum = "4a52de8b6afbfcd618073f592c1450cb661574e7061f3f4a44f1f7ec0c7f7909" dependencies = [ "cfg-if", "libc", @@ -5692,9 +5695,9 @@ dependencies = [ [[package]] name = "wasmtime-internal-unwinder" -version = "47.0.3" +version = "47.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "112eead527bffa8ff0646a11fb4339a9d52ddd1da2b9a6fce4aff84c815dd94f" +checksum = "5524fbaaf3d2134dd6229164d7fc81e443cce06d1e0576524d5f4e86e747cb13" dependencies = [ "cfg-if", "cranelift-codegen", @@ -5705,9 +5708,9 @@ dependencies = [ [[package]] name = "wasmtime-internal-versioned-export-macros" -version = "47.0.3" +version = "47.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "153592e0bed824fc13c6696203fa1bd7bd20eb355316475e39a55f081ef80eca" +checksum = "e93bb9ae112a80618510f938275a5d88637b656e7d0a021fc906f52206c708ad" dependencies = [ "proc-macro2", "quote", @@ -5716,9 +5719,9 @@ dependencies = [ [[package]] name = "wasmtime-internal-wit-bindgen" -version = "47.0.3" +version = "47.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c456ad6e81e0f46abfeca43687d18ea15e289c395b262ea6e0023e2682e088be" +checksum = "bfdbdd9d6fab1ecb67a7dc189e32c9e341444945fb709437bf8aab25d9a88459" dependencies = [ "anyhow", "bitflags 2.13.1", @@ -5729,9 +5732,9 @@ dependencies = [ [[package]] name = "wasmtime-wasi" -version = "47.0.3" +version = "47.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c1cf60cd6a565213af7074b4aad7bb5e110e3c6c6aae54425aa0500a0e15e86" +checksum = "b373095a6dc8382da56882ad16d7ea16771dd6927dd480b1ce3fe7c4b816d2cf" dependencies = [ "async-trait", "bitflags 2.13.1", @@ -5754,9 +5757,9 @@ dependencies = [ [[package]] name = "wasmtime-wasi-http" -version = "47.0.3" +version = "47.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a499879061ee9f724342d5f5009754b723ae53c7932161ee063a9fe19395e915" +checksum = "372fc7845f26cd8f69bcbf6e4234fcc032f718480c7722fa20a28eb74d58998e" dependencies = [ "async-trait", "bytes", @@ -5778,9 +5781,9 @@ dependencies = [ [[package]] name = "wasmtime-wasi-io" -version = "47.0.3" +version = "47.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7fb08e1d7755aaa467ce14080b7b01e33c914a920f6000696f1e42e40ea6387e" +checksum = "51b3c9b39b1a1b3029f80e614623e182f165133dfaca8610f156450343f080a4" dependencies = [ "async-trait", "bytes", @@ -6320,9 +6323,9 @@ dependencies = [ [[package]] name = "zerovec" -version = "0.11.7" +version = "0.11.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "94b5c6b5976d66c1d703c4fd17d3f5e43c8cedaacf604961b171adc7130896d8" +checksum = "bb0464e17806c1d976d5cba29399c7f08e516e279e2ba493f63123b5fca67dd8" dependencies = [ "yoke", "zerofrom", @@ -6331,13 +6334,13 @@ dependencies = [ [[package]] name = "zerovec-derive" -version = "0.11.5" +version = "0.11.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9f212a141d820099d57ffafb9569be9617a6f27d3dc881fbee8fb56642f917a9" +checksum = "34df6fc39dbd26ddc9c10e6a2984476e13acce22e64e4487636ef494369225da" dependencies = [ "proc-macro2", "quote", - "syn 3.0.3", + "syn 3.0.4", ] [[package]] diff --git a/rust/Cargo.toml b/rust/Cargo.toml index 05d127d..9569a07 100644 --- a/rust/Cargo.toml +++ b/rust/Cargo.toml @@ -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-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-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" } +prost-types = "0.14" tokio = "1" @@ -27,7 +29,7 @@ walkdir = "2" zip = "8.6" anyhow = "1.0" -uuid = "1.24.1" +uuid = "1.25.0" serde = { version = "1.0", features = ["derive"] } serde_json = "1.0" @@ -35,6 +37,7 @@ serde-saphyr = "1.1.0" rand = { git = "https://github.com/rust-random/rand"} prost = "0.14" +time = "0.3" toml = "1.1" diff --git a/rust/src/java/native_callbacks/entity.rs b/rust/src/java/native_callbacks/entity.rs index 364bfaa..6375774 100644 --- a/rust/src/java/native_callbacks/entity.rs +++ b/rust/src/java/native_callbacks/entity.rs @@ -1,3 +1,5 @@ +use std::str::FromStr; + use pumpkin::entity::EntityBase; use crate::{ @@ -5,8 +7,16 @@ use crate::{ proto::patchbukkit::{ common::Uuid, entity::{ - DamageEntityRequest, EntityHealthResponse, SetEntityHealthRequest, - SetEntityVelocityRequest, TeleportEntityRequest, + DamageEntityRequest, EntityHealthResponse, GetCooldownRequest, GetCooldownResponse, + 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 }) } + +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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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; + }); + }) +} diff --git a/rust/src/java/native_callbacks/mod.rs b/rust/src/java/native_callbacks/mod.rs index 56d48d6..5f961b8 100644 --- a/rust/src/java/native_callbacks/mod.rs +++ b/rust/src/java/native_callbacks/mod.rs @@ -47,6 +47,9 @@ pub use itemstack::*; pub mod command; pub use command::*; +pub mod server; +pub use server::*; + static CALLBACK_CONTEXT: OnceLock = OnceLock::new(); struct CallbackContext { diff --git a/rust/src/java/native_callbacks/server.rs b/rust/src/java/native_callbacks/server.rs new file mode 100644 index 0000000..5080a18 --- /dev/null +++ b/rust/src/java/native_callbacks/server.rs @@ -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 { + 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 { + 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 { + 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 { + 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 { + 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::() { + 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 { + 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 { + 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(()) +} diff --git a/rust/src/java/native_callbacks/world.rs b/rust/src/java/native_callbacks/world.rs index 8df812f..73ab463 100644 --- a/rust/src/java/native_callbacks/world.rs +++ b/rust/src/java/native_callbacks/world.rs @@ -1,7 +1,22 @@ +use pumpkin_util::math::vector3::Vector3; +use std::sync::Arc; + use crate::{ java::native_callbacks::CALLBACK_CONTEXT, - proto::patchbukkit::world::{ - GetBlockDataRequest, GetBlockDataResponse, SetBlockDataRequest, SpawnParticleRequest, + proto::patchbukkit::{ + 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}") }; - // 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) { let props = props.to_props(); if !props.is_empty() { @@ -71,8 +84,6 @@ pub fn ffi_native_bridge_set_block_data_impl(request: SetBlockDataRequest) -> Op .trim_start_matches("minecraft:"); 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('[') { Some((_, 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) }; - // 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 { pumpkin::world::BlockFlags::NOTIFY_ALL } else { @@ -120,24 +122,22 @@ pub fn ffi_native_bridge_spawn_particle_impl(_request: SpawnParticleRequest) -> Some(()) } -pub fn ffi_native_bridge_get_worlds_impl( - _request: crate::proto::patchbukkit::common::EmptyRequest, -) -> Option { +pub fn ffi_native_bridge_get_worlds_impl(_request: EmptyRequest) -> Option { let ctx = CALLBACK_CONTEXT.get()?; let worlds = ctx.plugin_context.server.worlds.load_full(); let world_uuids = worlds .iter() - .map(|w| crate::proto::patchbukkit::common::Uuid { + .map(|w| ProtoUuid { value: w.uuid.to_string(), }) .collect(); - Some(crate::proto::patchbukkit::world::GetWorldsResponse { world_uuids }) + Some(GetWorldsResponse { world_uuids }) } pub fn ffi_native_bridge_get_world_border_impl( - request: crate::proto::patchbukkit::world::GetWorldBorderRequest, -) -> Option { + request: GetWorldBorderRequest, +) -> 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()?; @@ -151,7 +151,7 @@ pub fn ffi_native_bridge_get_world_border_impl( let wb = world.worldborder.try_lock().ok()?; - Some(crate::proto::patchbukkit::world::WorldBorderData { + Some(WorldBorderData { center_x: wb.center_x, center_z: wb.center_z, 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( - request: crate::proto::patchbukkit::world::SetWorldBorderRequest, -) -> Option<()> { +pub fn ffi_native_bridge_set_world_border_impl(request: SetWorldBorderRequest) -> 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()?; @@ -198,8 +196,8 @@ pub fn ffi_native_bridge_set_world_border_impl( } pub fn ffi_native_bridge_get_world_info_impl( - request: crate::proto::patchbukkit::world::GetWorldInfoRequest, -) -> Option { + request: GetWorldInfoRequest, +) -> 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()?; @@ -214,11 +212,411 @@ pub fn ffi_native_bridge_get_world_info_impl( let min_height = world.dimension.min_y; let height = world.dimension.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, max_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 { + 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 { + 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 { + 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 { + 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(()) +} diff --git a/rust/src/proto/mod.rs b/rust/src/proto/mod.rs index 5f0b1f8..77bbb45 100644 --- a/rust/src/proto/mod.rs +++ b/rust/src/proto/mod.rs @@ -50,6 +50,10 @@ pub mod patchbukkit { pub mod command { 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"));