From 3e3a48e0c142b41df50036e7937a351650d5d4cd Mon Sep 17 00:00:00 2001 From: Alexander Medvedev Date: Fri, 31 Jul 2026 20:45:09 +0200 Subject: [PATCH] more stuff --- .../org/patchbukkit/PatchBukkitServer.java | 5 +- .../patchbukkit/entity/PatchBukkitEntity.java | 4 + .../entity/PatchBukkitHumanEntity.java | 17 +- .../patchbukkit/entity/PatchBukkitPlayer.java | 8 +- .../patchbukkit/world/PatchBukkitWorld.java | 5 +- .../world/PatchBukkitWorldBorder.java | 222 ++++++++++++++++++ proto/patchbukkit/bridge.proto | 6 + proto/patchbukkit/entity.proto | 17 ++ proto/patchbukkit/world.proto | 23 ++ rust/Cargo.lock | 22 +- rust/src/java/native_callbacks/entity.rs | 41 ++++ rust/src/java/native_callbacks/world.rs | 61 +++++ 12 files changed, 404 insertions(+), 27 deletions(-) create mode 100644 java/patchbukkit/src/main/java/org/patchbukkit/world/PatchBukkitWorldBorder.java diff --git a/java/patchbukkit/src/main/java/org/patchbukkit/PatchBukkitServer.java b/java/patchbukkit/src/main/java/org/patchbukkit/PatchBukkitServer.java index f04f8d3..9736f29 100644 --- a/java/patchbukkit/src/main/java/org/patchbukkit/PatchBukkitServer.java +++ b/java/patchbukkit/src/main/java/org/patchbukkit/PatchBukkitServer.java @@ -641,10 +641,7 @@ public class PatchBukkitServer implements Server { @Override public @NotNull WorldBorder createWorldBorder() { - // TODO Auto-generated method stub - throw new UnsupportedOperationException( - "Unimplemented method 'createWorldBorder'" - ); + return new org.patchbukkit.world.PatchBukkitWorldBorder(null); } @Override 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 3c1b0ba..b68cbc3 100644 --- a/java/patchbukkit/src/main/java/org/patchbukkit/entity/PatchBukkitEntity.java +++ b/java/patchbukkit/src/main/java/org/patchbukkit/entity/PatchBukkitEntity.java @@ -287,6 +287,10 @@ public class PatchBukkitEntity implements Entity { @Override public @NotNull Vector getVelocity() { + var resp = NativeBridgeFfi.getEntityVelocity(BridgeUtils.convertUuid(this.uuid)); + if (resp != null) { + return new Vector(resp.getX(), resp.getY(), resp.getZ()); + } return this.velocity.clone(); } 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 e48457f..063973b 100644 --- a/java/patchbukkit/src/main/java/org/patchbukkit/entity/PatchBukkitHumanEntity.java +++ b/java/patchbukkit/src/main/java/org/patchbukkit/entity/PatchBukkitHumanEntity.java @@ -50,6 +50,8 @@ import org.checkerframework.checker.index.qual.NonNegative; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Range; +import org.patchbukkit.bridge.BridgeUtils; +import patchbukkit.bridge.NativeBridgeFfi; import com.destroystokyo.paper.block.TargetBlockInfo; import com.destroystokyo.paper.block.TargetBlockInfo.FluidMode; @@ -1128,14 +1130,21 @@ public class PatchBukkitHumanEntity @Override public GameMode getGameMode() { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'getGameMode'"); + var resp = NativeBridgeFfi.getGamemode(BridgeUtils.convertUuid(this.getUniqueId())); + if (resp != null) { + return GameMode.getByValue(resp.getGamemode()); + } + return GameMode.SURVIVAL; } @Override public void setGameMode(GameMode mode) { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'setGameMode'"); + if (mode == null) return; + var request = patchbukkit.entity.SetGamemodeRequest.newBuilder() + .setUuid(BridgeUtils.convertUuid(this.getUniqueId())) + .setGamemode(mode.getValue()) + .build(); + NativeBridgeFfi.setGamemode(request); } @Override 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 83603ff..1465a69 100644 --- a/java/patchbukkit/src/main/java/org/patchbukkit/entity/PatchBukkitPlayer.java +++ b/java/patchbukkit/src/main/java/org/patchbukkit/entity/PatchBukkitPlayer.java @@ -1402,16 +1402,16 @@ public class PatchBukkitPlayer throw new UnsupportedOperationException("Unimplemented method 'setScoreboard'"); } + private WorldBorder customWorldBorder; + @Override public @org.jspecify.annotations.Nullable WorldBorder getWorldBorder() { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'getWorldBorder'"); + return this.customWorldBorder != null ? this.customWorldBorder : getWorld().getWorldBorder(); } @Override public void setWorldBorder(@org.jspecify.annotations.Nullable WorldBorder border) { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'setWorldBorder'"); + this.customWorldBorder = border; } @Override 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 a20e9e4..0d6d5b3 100644 --- a/java/patchbukkit/src/main/java/org/patchbukkit/world/PatchBukkitWorld.java +++ b/java/patchbukkit/src/main/java/org/patchbukkit/world/PatchBukkitWorld.java @@ -1379,10 +1379,7 @@ public class PatchBukkitWorld @Override public @NotNull WorldBorder getWorldBorder() { - // TODO Auto-generated method stub - throw new UnsupportedOperationException( - "Unimplemented method 'getWorldBorder'" - ); + return new PatchBukkitWorldBorder(this); } @Override diff --git a/java/patchbukkit/src/main/java/org/patchbukkit/world/PatchBukkitWorldBorder.java b/java/patchbukkit/src/main/java/org/patchbukkit/world/PatchBukkitWorldBorder.java new file mode 100644 index 0000000..27402f3 --- /dev/null +++ b/java/patchbukkit/src/main/java/org/patchbukkit/world/PatchBukkitWorldBorder.java @@ -0,0 +1,222 @@ +package org.patchbukkit.world; + +import org.bukkit.Location; +import org.bukkit.World; +import org.bukkit.WorldBorder; + +import patchbukkit.bridge.NativeBridgeFfi; +import patchbukkit.world.GetWorldBorderRequest; +import patchbukkit.world.SetWorldBorderRequest; +import patchbukkit.world.WorldBorderData; +import org.patchbukkit.bridge.BridgeUtils; + +import java.util.concurrent.TimeUnit; + +public class PatchBukkitWorldBorder implements WorldBorder { + private final World world; + + public PatchBukkitWorldBorder(World world) { + this.world = world; + } + + private WorldBorderData getData() { + if (this.world == null) { + return WorldBorderData.newBuilder() + .setCenterX(0.0) + .setCenterZ(0.0) + .setSize(59999968.0) + .setTargetSize(59999968.0) + .setSpeed(0) + .setWarningTime(15) + .setWarningBlocks(5) + .setDamagePerBlock(0.2) + .setDamageBuffer(5.0) + .setMaxCenterCoordinate(29999984) + .build(); + } + return NativeBridgeFfi.getWorldBorder( + GetWorldBorderRequest.newBuilder() + .setWorldUuid(BridgeUtils.convertUuid(this.world.getUID())) + .build() + ); + } + + private void updateData(WorldBorderData.Builder builder) { + if (this.world == null) { + return; + } + NativeBridgeFfi.setWorldBorder( + SetWorldBorderRequest.newBuilder() + .setWorldUuid(BridgeUtils.convertUuid(this.world.getUID())) + .setBorder(builder) + .build() + ); + } + + @Override + public World getWorld() { + return this.world; + } + + @Override + public void reset() { + if (this.world == null) return; + WorldBorderData.Builder builder = getData().toBuilder(); + builder.setCenterX(0.0); + builder.setCenterZ(0.0); + builder.setSize(59999968.0); + builder.setTargetSize(59999968.0); + builder.setSpeed(0); + builder.setWarningTime(15); + builder.setWarningBlocks(5); + builder.setDamagePerBlock(0.2); + builder.setDamageBuffer(5.0); + updateData(builder); + } + + @Override + public double getSize() { + return getData().getSize(); + } + + @Override + public void setSize(double newSize) { + if (this.world == null) return; + WorldBorderData.Builder builder = getData().toBuilder(); + builder.setSize(newSize); + builder.setTargetSize(newSize); + builder.setSpeed(0); + updateData(builder); + } + + @Override + public void setSize(double newSize, long seconds) { + setSize(newSize, TimeUnit.SECONDS, seconds); + } + + @Override + public void setSize(double newSize, TimeUnit unit, long time) { + if (this.world == null) return; + long timeInMillis = unit.toMillis(time); + WorldBorderData.Builder builder = getData().toBuilder(); + builder.setTargetSize(newSize); + builder.setSpeed(timeInMillis); + updateData(builder); + } + + @Override + public void changeSize(double newSize, long seconds) { + setSize(getSize() + newSize, seconds); + } + + public void changeSize(double newSize, TimeUnit unit, long time) { + setSize(getSize() + newSize, unit, time); + } + + @Override + public Location getCenter() { + WorldBorderData data = getData(); + return new Location(this.world, data.getCenterX(), 0.0, data.getCenterZ()); + } + + @Override + public void setCenter(double x, double z) { + if (this.world == null) return; + WorldBorderData.Builder builder = getData().toBuilder(); + builder.setCenterX(x); + builder.setCenterZ(z); + updateData(builder); + } + + @Override + public void setCenter(Location location) { + setCenter(location.getX(), location.getZ()); + } + + @Override + public double getDamageBuffer() { + return getData().getDamageBuffer(); + } + + @Override + public void setDamageBuffer(double blocks) { + if (this.world == null) return; + WorldBorderData.Builder builder = getData().toBuilder(); + builder.setDamageBuffer(blocks); + updateData(builder); + } + + @Override + public double getDamageAmount() { + return getData().getDamagePerBlock(); + } + + @Override + public void setDamageAmount(double damage) { + if (this.world == null) return; + WorldBorderData.Builder builder = getData().toBuilder(); + builder.setDamagePerBlock(damage); + updateData(builder); + } + + @Override + public int getWarningTime() { + return getData().getWarningTime(); + } + + @Override + public void setWarningTime(int seconds) { + if (this.world == null) return; + WorldBorderData.Builder builder = getData().toBuilder(); + builder.setWarningTime(seconds); + updateData(builder); + } + + @Override + public int getWarningTimeTicks() { + return getWarningTime() * 20; + } + + @Override + public void setWarningTimeTicks(int ticks) { + setWarningTime(ticks / 20); + } + + @Override + public int getWarningDistance() { + return getData().getWarningBlocks(); + } + + @Override + public void setWarningDistance(int distance) { + if (this.world == null) return; + WorldBorderData.Builder builder = getData().toBuilder(); + builder.setWarningBlocks(distance); + updateData(builder); + } + + @Override + public boolean isInside(Location location) { + if (location == null) return false; + if (this.world != null && location.getWorld() != null && !this.world.equals(location.getWorld())) { + return false; + } + WorldBorderData data = getData(); + double radius = data.getSize() / 2.0; + double minX = data.getCenterX() - radius; + double maxX = data.getCenterX() + radius; + double minZ = data.getCenterZ() - radius; + double maxZ = data.getCenterZ() + radius; + return location.getX() >= minX && location.getX() <= maxX && location.getZ() >= minZ && location.getZ() <= maxZ; + } + + @Override + public double getMaxCenterCoordinate() { + return getData().getMaxCenterCoordinate(); + } + + @Override + public double getMaxSize() { + return getData().getMaxCenterCoordinate() * 2.0; + } +} diff --git a/proto/patchbukkit/bridge.proto b/proto/patchbukkit/bridge.proto index ef83899..e636b9d 100644 --- a/proto/patchbukkit/bridge.proto +++ b/proto/patchbukkit/bridge.proto @@ -21,13 +21,19 @@ service NativeBridge { rpc GetEntityHealth(patchbukkit.common.UUID) returns (patchbukkit.entity.EntityHealthResponse); rpc SetEntityHealth(patchbukkit.entity.SetEntityHealthRequest) returns (google.protobuf.Empty); rpc DamageEntity(patchbukkit.entity.DamageEntityRequest) returns (google.protobuf.Empty); + rpc GetEntityVelocity(patchbukkit.common.UUID) returns (patchbukkit.entity.EntityVelocityResponse); rpc SetEntityVelocity(patchbukkit.entity.SetEntityVelocityRequest) returns (google.protobuf.Empty); + rpc SetEntityPose(patchbukkit.entity.SetEntityPoseRequest) returns (google.protobuf.Empty); rpc TeleportEntity(patchbukkit.entity.TeleportEntityRequest) returns (google.protobuf.Empty); + rpc GetGamemode(patchbukkit.common.UUID) returns (patchbukkit.entity.GetGamemodeResponse); + rpc SetGamemode(patchbukkit.entity.SetGamemodeRequest) returns (google.protobuf.Empty); rpc GetBlockData(patchbukkit.world.GetBlockDataRequest) returns (patchbukkit.world.GetBlockDataResponse); rpc SetBlockData(patchbukkit.world.SetBlockDataRequest) returns (google.protobuf.Empty); rpc SpawnParticle(patchbukkit.world.SpawnParticleRequest) returns (google.protobuf.Empty); rpc GetWorlds(patchbukkit.common.EmptyRequest) returns (patchbukkit.world.GetWorldsResponse); + rpc GetWorldBorder(patchbukkit.world.GetWorldBorderRequest) returns (patchbukkit.world.WorldBorderData); + rpc SetWorldBorder(patchbukkit.world.SetWorldBorderRequest) returns (google.protobuf.Empty); rpc RegisterEvent(patchbukkit.events.RegisterEventRequest) returns (google.protobuf.Empty); rpc CallEvent(patchbukkit.events.CallEventRequest) returns (patchbukkit.events.CallEventResponse); diff --git a/proto/patchbukkit/entity.proto b/proto/patchbukkit/entity.proto index a66c856..3e9b48a 100644 --- a/proto/patchbukkit/entity.proto +++ b/proto/patchbukkit/entity.proto @@ -39,3 +39,20 @@ message SetEntityPoseRequest { string pose = 2; } +message EntityVelocityResponse { + double x = 1; + double y = 2; + double z = 3; +} + +message GetGamemodeResponse { + int32 gamemode = 1; +} + +message SetGamemodeRequest { + patchbukkit.common.UUID uuid = 1; + int32 gamemode = 2; +} + + + diff --git a/proto/patchbukkit/world.proto b/proto/patchbukkit/world.proto index e0108b8..f7fe801 100644 --- a/proto/patchbukkit/world.proto +++ b/proto/patchbukkit/world.proto @@ -44,3 +44,26 @@ message GetWorldsResponse { repeated patchbukkit.common.UUID world_uuids = 1; } +message WorldBorderData { + double center_x = 1; + double center_z = 2; + double size = 3; + double target_size = 4; + int64 speed = 5; + int32 warning_time = 6; + int32 warning_blocks = 7; + double damage_per_block = 8; + double damage_buffer = 9; + int32 max_center_coordinate = 10; +} + +message GetWorldBorderRequest { + patchbukkit.common.UUID world_uuid = 1; +} + +message SetWorldBorderRequest { + patchbukkit.common.UUID world_uuid = 1; + WorldBorderData border = 2; +} + + diff --git a/rust/Cargo.lock b/rust/Cargo.lock index 1728875..85169bb 100644 --- a/rust/Cargo.lock +++ b/rust/Cargo.lock @@ -2773,7 +2773,7 @@ dependencies = [ [[package]] name = "pumpkin" version = "0.1.0-dev+26.2" -source = "git+https://github.com/Pumpkin-MC/Pumpkin.git?branch=master#a3b66be27b2f70afc8dbe43923ab2cfbc36e94dd" +source = "git+https://github.com/Pumpkin-MC/Pumpkin.git?branch=master#75994f3302fa76078087b9a113130541e4d3fc37" dependencies = [ "arc-swap", "base64 0.23.0", @@ -2831,7 +2831,7 @@ dependencies = [ [[package]] name = "pumpkin-api-macros" version = "0.1.0-dev+26.2" -source = "git+https://github.com/Pumpkin-MC/Pumpkin.git?branch=master#a3b66be27b2f70afc8dbe43923ab2cfbc36e94dd" +source = "git+https://github.com/Pumpkin-MC/Pumpkin.git?branch=master#75994f3302fa76078087b9a113130541e4d3fc37" dependencies = [ "proc-macro-error2", "proc-macro2", @@ -2842,7 +2842,7 @@ dependencies = [ [[package]] name = "pumpkin-codecs" version = "0.1.0-dev+26.2" -source = "git+https://github.com/Pumpkin-MC/Pumpkin.git?branch=master#a3b66be27b2f70afc8dbe43923ab2cfbc36e94dd" +source = "git+https://github.com/Pumpkin-MC/Pumpkin.git?branch=master#75994f3302fa76078087b9a113130541e4d3fc37" dependencies = [ "serde_json", "tracing", @@ -2851,7 +2851,7 @@ dependencies = [ [[package]] name = "pumpkin-config" version = "0.1.0-dev+26.2" -source = "git+https://github.com/Pumpkin-MC/Pumpkin.git?branch=master#a3b66be27b2f70afc8dbe43923ab2cfbc36e94dd" +source = "git+https://github.com/Pumpkin-MC/Pumpkin.git?branch=master#75994f3302fa76078087b9a113130541e4d3fc37" dependencies = [ "pumpkin-util", "serde", @@ -2863,7 +2863,7 @@ dependencies = [ [[package]] name = "pumpkin-data" version = "0.1.0-dev+26.2" -source = "git+https://github.com/Pumpkin-MC/Pumpkin.git?branch=master#a3b66be27b2f70afc8dbe43923ab2cfbc36e94dd" +source = "git+https://github.com/Pumpkin-MC/Pumpkin.git?branch=master#75994f3302fa76078087b9a113130541e4d3fc37" dependencies = [ "crc-fast", "phf", @@ -2876,7 +2876,7 @@ dependencies = [ [[package]] name = "pumpkin-inventory" version = "0.1.0-dev+26.2" -source = "git+https://github.com/Pumpkin-MC/Pumpkin.git?branch=master#a3b66be27b2f70afc8dbe43923ab2cfbc36e94dd" +source = "git+https://github.com/Pumpkin-MC/Pumpkin.git?branch=master#75994f3302fa76078087b9a113130541e4d3fc37" dependencies = [ "pumpkin-data", "pumpkin-protocol", @@ -2891,7 +2891,7 @@ dependencies = [ [[package]] name = "pumpkin-macros" version = "0.1.0-dev+26.2" -source = "git+https://github.com/Pumpkin-MC/Pumpkin.git?branch=master#a3b66be27b2f70afc8dbe43923ab2cfbc36e94dd" +source = "git+https://github.com/Pumpkin-MC/Pumpkin.git?branch=master#75994f3302fa76078087b9a113130541e4d3fc37" dependencies = [ "heck", "proc-macro-error2", @@ -2903,7 +2903,7 @@ dependencies = [ [[package]] name = "pumpkin-nbt" version = "0.1.0-dev+26.2" -source = "git+https://github.com/Pumpkin-MC/Pumpkin.git?branch=master#a3b66be27b2f70afc8dbe43923ab2cfbc36e94dd" +source = "git+https://github.com/Pumpkin-MC/Pumpkin.git?branch=master#75994f3302fa76078087b9a113130541e4d3fc37" dependencies = [ "bytes", "cesu8", @@ -2918,7 +2918,7 @@ dependencies = [ [[package]] name = "pumpkin-protocol" version = "0.1.0-dev+26.2" -source = "git+https://github.com/Pumpkin-MC/Pumpkin.git?branch=master#a3b66be27b2f70afc8dbe43923ab2cfbc36e94dd" +source = "git+https://github.com/Pumpkin-MC/Pumpkin.git?branch=master#75994f3302fa76078087b9a113130541e4d3fc37" dependencies = [ "aes", "async-compression", @@ -2943,7 +2943,7 @@ dependencies = [ [[package]] name = "pumpkin-util" version = "0.1.0-dev+26.2" -source = "git+https://github.com/Pumpkin-MC/Pumpkin.git?branch=master#a3b66be27b2f70afc8dbe43923ab2cfbc36e94dd" +source = "git+https://github.com/Pumpkin-MC/Pumpkin.git?branch=master#75994f3302fa76078087b9a113130541e4d3fc37" dependencies = [ "base64 0.23.0", "bytes", @@ -2970,7 +2970,7 @@ dependencies = [ [[package]] name = "pumpkin-world" version = "0.1.0-dev+26.2" -source = "git+https://github.com/Pumpkin-MC/Pumpkin.git?branch=master#a3b66be27b2f70afc8dbe43923ab2cfbc36e94dd" +source = "git+https://github.com/Pumpkin-MC/Pumpkin.git?branch=master#75994f3302fa76078087b9a113130541e4d3fc37" dependencies = [ "arc-swap", "bitflags 2.13.1", diff --git a/rust/src/java/native_callbacks/entity.rs b/rust/src/java/native_callbacks/entity.rs index d25842e..dc76785 100644 --- a/rust/src/java/native_callbacks/entity.rs +++ b/rust/src/java/native_callbacks/entity.rs @@ -49,6 +49,17 @@ pub fn ffi_native_bridge_damage_entity_impl(request: DamageEntityRequest) -> Opt }) } +pub fn ffi_native_bridge_get_entity_velocity_impl(request: Uuid) -> Option { + with_player(Some(&request), |player| { + let vel = player.living_entity.entity.velocity.load(); + crate::proto::patchbukkit::entity::EntityVelocityResponse { + x: vel.x, + y: vel.y, + z: vel.z, + } + }) +} + pub fn ffi_native_bridge_set_entity_velocity_impl(request: SetEntityVelocityRequest) -> Option<()> { with_player(request.uuid.as_ref(), |player| { let velocity = pumpkin_util::math::vector3::Vector3::new(request.x, request.y, request.z); @@ -56,6 +67,36 @@ pub fn ffi_native_bridge_set_entity_velocity_impl(request: SetEntityVelocityRequ }) } +pub fn ffi_native_bridge_set_entity_pose_impl( + _request: crate::proto::patchbukkit::entity::SetEntityPoseRequest, +) -> Option<()> { + Some(()) +} + +pub fn ffi_native_bridge_get_gamemode_impl(request: Uuid) -> Option { + with_player(Some(&request), |player| { + let gamemode = player.gamemode.load(); + crate::proto::patchbukkit::entity::GetGamemodeResponse { + gamemode: gamemode as i32, + } + }) +} + +pub fn ffi_native_bridge_set_gamemode_impl( + request: crate::proto::patchbukkit::entity::SetGamemodeRequest, +) -> Option<()> { + let ctx = CALLBACK_CONTEXT.get()?; + with_player(request.uuid.as_ref(), |player| { + let gamemode_val = request.gamemode as i8; + if let Ok(gamemode) = pumpkin_util::gamemode::GameMode::try_from(gamemode_val) { + let player = player.clone(); + ctx.runtime.spawn(async move { + player.set_gamemode(gamemode).await; + }); + } + }) +} + pub fn ffi_native_bridge_teleport_entity_impl(request: TeleportEntityRequest) -> Option<()> { let ctx = CALLBACK_CONTEXT.get()?; let loc = request.location?; diff --git a/rust/src/java/native_callbacks/world.rs b/rust/src/java/native_callbacks/world.rs index fbf26b8..7c34e58 100644 --- a/rust/src/java/native_callbacks/world.rs +++ b/rust/src/java/native_callbacks/world.rs @@ -71,3 +71,64 @@ pub fn ffi_native_bridge_get_worlds_impl( Some(crate::proto::patchbukkit::world::GetWorldsResponse { world_uuids }) } + +pub fn ffi_native_bridge_get_world_border_impl( + request: crate::proto::patchbukkit::world::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()?; + + 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 wb = world.worldborder.blocking_lock(); + + Some(crate::proto::patchbukkit::world::WorldBorderData { + center_x: wb.center_x, + center_z: wb.center_z, + size: wb.old_diameter, + target_size: wb.new_diameter, + speed: wb.speed, + warning_time: wb.warning_time, + warning_blocks: wb.warning_blocks, + damage_per_block: wb.damage_per_block as f64, + damage_buffer: wb.buffer as f64, + max_center_coordinate: wb.portal_teleport_boundary, + }) +} + +pub fn ffi_native_bridge_set_world_border_impl( + request: crate::proto::patchbukkit::world::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()?; + let border_data = request.border?; + + 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 wb = world.worldborder.blocking_lock(); + wb.center_x = border_data.center_x; + wb.center_z = border_data.center_z; + wb.old_diameter = border_data.size; + wb.new_diameter = border_data.target_size; + wb.speed = border_data.speed; + wb.warning_time = border_data.warning_time; + wb.warning_blocks = border_data.warning_blocks; + wb.damage_per_block = border_data.damage_per_block as f32; + wb.buffer = border_data.damage_buffer as f32; + wb.portal_teleport_boundary = border_data.max_center_coordinate; + + Some(()) +} +