mirror of
https://github.com/CivMC/Civ.git
synced 2026-07-15 23:20:44 +00:00
phase 6
This commit is contained in:
@@ -0,0 +1,154 @@
|
||||
package net.civmc.zorweth;
|
||||
|
||||
import com.sk89q.worldedit.extent.clipboard.Clipboard;
|
||||
import com.sk89q.worldedit.math.BlockVector3;
|
||||
import com.sk89q.worldedit.regions.Region;
|
||||
import com.sk89q.worldedit.world.block.BlockState;
|
||||
import java.util.logging.Level;
|
||||
import net.civmc.zorweth.transfer.DestinationRocketTransfer;
|
||||
import net.civmc.zorweth.transfer.RocketBlockPosition;
|
||||
import net.kyori.adventure.text.Component;
|
||||
import net.kyori.adventure.text.format.NamedTextColor;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.Material;
|
||||
import org.bukkit.World;
|
||||
import org.bukkit.block.Block;
|
||||
import org.bukkit.block.Dispenser;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.event.EventHandler;
|
||||
import org.bukkit.event.EventPriority;
|
||||
import org.bukkit.event.Listener;
|
||||
import org.bukkit.event.player.PlayerJoinEvent;
|
||||
import org.bukkit.persistence.PersistentDataType;
|
||||
|
||||
public final class DestinationTransferListener implements Listener {
|
||||
|
||||
private final ZorwethPlugin plugin;
|
||||
|
||||
public DestinationTransferListener(final ZorwethPlugin plugin) {
|
||||
this.plugin = plugin;
|
||||
}
|
||||
|
||||
@EventHandler(priority = EventPriority.LOWEST, ignoreCancelled = false)
|
||||
public void onPlayerJoin(final PlayerJoinEvent event) {
|
||||
final Player player = event.getPlayer();
|
||||
Bukkit.getScheduler().runTaskAsynchronously(this.plugin, () -> handleJoin(player));
|
||||
}
|
||||
|
||||
private void handleJoin(final Player player) {
|
||||
final DestinationRocketTransfer transfer;
|
||||
try {
|
||||
transfer = this.plugin.getRocketTransferDao().getPendingDestinationTransfer(
|
||||
player.getUniqueId(), this.plugin.getServerName());
|
||||
} catch (final Exception exception) {
|
||||
this.plugin.getLogger().log(Level.SEVERE, "Failed to look up destination rocket transfer", exception);
|
||||
kick(player);
|
||||
return;
|
||||
}
|
||||
if (transfer == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
final boolean claimed;
|
||||
try {
|
||||
claimed = this.plugin.getRocketTransferDao().claimDestinationTransfer(transfer.transferId(), player.getUniqueId());
|
||||
} catch (final Exception exception) {
|
||||
this.plugin.getLogger().log(Level.SEVERE, "Failed to claim destination rocket transfer", exception);
|
||||
kick(player);
|
||||
return;
|
||||
}
|
||||
if (!claimed) {
|
||||
kick(player);
|
||||
return;
|
||||
}
|
||||
|
||||
Bukkit.getScheduler().runTask(this.plugin, () -> {
|
||||
try {
|
||||
if (!ensureDestinationRocketPasted(transfer)) {
|
||||
kick(player);
|
||||
}
|
||||
} catch (final RuntimeException exception) {
|
||||
this.plugin.getLogger().log(Level.SEVERE, "Failed to paste destination rocket", exception);
|
||||
kick(player);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private boolean ensureDestinationRocketPasted(final DestinationRocketTransfer transfer) {
|
||||
final World world = Bukkit.getWorld(transfer.destinationWorld());
|
||||
if (world == null) {
|
||||
this.plugin.getLogger().warning("Destination world is not loaded: " + transfer.destinationWorld());
|
||||
return false;
|
||||
}
|
||||
|
||||
final Block computer = getDestinationComputerBlock(world, transfer.destinationOrigin());
|
||||
final String existingTransferId = getSchematicPasteMarker(computer);
|
||||
if (transfer.transferId().toString().equals(existingTransferId)) {
|
||||
return true;
|
||||
}
|
||||
if (existingTransferId != null) {
|
||||
this.plugin.getLogger().warning("Destination rocket already has schematic marker for " + existingTransferId
|
||||
+ " at " + computer.getLocation());
|
||||
return false;
|
||||
}
|
||||
|
||||
pasteRocket(world, transfer.destinationOrigin());
|
||||
markDestinationComputer(getDestinationComputerBlock(world, transfer.destinationOrigin()), transfer);
|
||||
return true;
|
||||
}
|
||||
|
||||
private String getSchematicPasteMarker(final Block computer) {
|
||||
if (computer.getType() != Material.DISPENSER) {
|
||||
return null;
|
||||
}
|
||||
final Dispenser dispenser = (Dispenser) computer.getState(false);
|
||||
return dispenser.getPersistentDataContainer().get(RocketTransferKeys.SCHEMATIC_PASTED_LOCAL,
|
||||
PersistentDataType.STRING);
|
||||
}
|
||||
|
||||
private void pasteRocket(final World world, final RocketBlockPosition origin) {
|
||||
final Clipboard clipboard = this.plugin.getRocketClipboard();
|
||||
final Region region = clipboard.getRegion();
|
||||
final BlockVector3 schematicNorthWestCorner = region.getMinimumPoint();
|
||||
for (final BlockVector3 position : region) {
|
||||
final BlockVector3 relative = position.subtract(schematicNorthWestCorner);
|
||||
final BlockState block = clipboard.getBlock(position);
|
||||
final Block target = world.getBlockAt(
|
||||
origin.x() + relative.getX(),
|
||||
origin.y() + relative.getY(),
|
||||
origin.z() + relative.getZ()
|
||||
);
|
||||
target.setBlockData(Bukkit.createBlockData(block.getAsString()), false);
|
||||
}
|
||||
}
|
||||
|
||||
private void markDestinationComputer(final Block computer, final DestinationRocketTransfer transfer) {
|
||||
if (computer.getType() != Material.DISPENSER) {
|
||||
throw new IllegalStateException("Destination schematic did not produce a flight computer at "
|
||||
+ computer.getLocation());
|
||||
}
|
||||
final Dispenser dispenser = (Dispenser) computer.getState(false);
|
||||
dispenser.getPersistentDataContainer().set(FlightComputer.ROCKET_COMPUTER_KEY, PersistentDataType.BOOLEAN, true);
|
||||
dispenser.getPersistentDataContainer().set(RocketTransferKeys.DESTINATION_TRANSFER_ID,
|
||||
PersistentDataType.STRING, transfer.transferId().toString());
|
||||
dispenser.getPersistentDataContainer().set(RocketTransferKeys.SCHEMATIC_PASTED_LOCAL,
|
||||
PersistentDataType.STRING, transfer.transferId().toString());
|
||||
dispenser.update(true, false);
|
||||
}
|
||||
|
||||
private Block getDestinationComputerBlock(final World world, final RocketBlockPosition origin) {
|
||||
return world.getBlockAt(
|
||||
origin.x() + FlightComputer.RELATIVE_POSITION.getX(),
|
||||
origin.y() + FlightComputer.RELATIVE_POSITION.getY(),
|
||||
origin.z() + FlightComputer.RELATIVE_POSITION.getZ()
|
||||
);
|
||||
}
|
||||
|
||||
private void kick(final Player player) {
|
||||
Bukkit.getScheduler().runTask(this.plugin, () -> {
|
||||
if (player.isOnline()) {
|
||||
player.kick(Component.text(this.plugin.getTransferFailureMessage(), NamedTextColor.RED));
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -48,6 +48,7 @@ public final class ZorwethPlugin extends JavaPlugin {
|
||||
getServer().getMessenger().registerOutgoingPluginChannel(this, "BungeeCord");
|
||||
this.rocketClipboard = loadRocketClipboard();
|
||||
getServer().getPluginManager().registerEvents(new FlightComputer(this), this);
|
||||
getServer().getPluginManager().registerEvents(new DestinationTransferListener(this), this);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -2,8 +2,11 @@ package net.civmc.zorweth.database;
|
||||
|
||||
import java.sql.Connection;
|
||||
import java.sql.PreparedStatement;
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
import java.util.UUID;
|
||||
import javax.sql.DataSource;
|
||||
import net.civmc.zorweth.transfer.DestinationRocketTransfer;
|
||||
import net.civmc.zorweth.transfer.RocketBlockPosition;
|
||||
import net.civmc.zorweth.transfer.RocketChestTransfer;
|
||||
import net.civmc.zorweth.transfer.RocketManifest;
|
||||
@@ -54,6 +57,69 @@ public final class RocketTransferDao {
|
||||
}
|
||||
}
|
||||
|
||||
public DestinationRocketTransfer getPendingDestinationTransfer(final UUID playerUuid, final String destinationServer)
|
||||
throws SQLException {
|
||||
try (Connection connection = this.dataSource.getConnection();
|
||||
PreparedStatement statement = connection.prepareStatement("""
|
||||
SELECT rt.transfer_id, rt.state, rt.destination_world,
|
||||
rt.destination_origin_x, rt.destination_origin_y, rt.destination_origin_z
|
||||
FROM rocket_transfer_players rtp
|
||||
JOIN rocket_transfers rt ON rt.transfer_id = rtp.transfer_id
|
||||
WHERE rtp.player_uuid = ?
|
||||
AND rt.destination_server = ?
|
||||
AND rt.state IN ('SOURCE_CLEARED', 'CLAIMED')
|
||||
AND rtp.state IN ('PENDING', 'CLAIMED')
|
||||
ORDER BY rt.created_at DESC
|
||||
LIMIT 1
|
||||
""")) {
|
||||
statement.setString(1, playerUuid.toString());
|
||||
statement.setString(2, destinationServer);
|
||||
try (ResultSet resultSet = statement.executeQuery()) {
|
||||
if (!resultSet.next()) {
|
||||
return null;
|
||||
}
|
||||
return new DestinationRocketTransfer(
|
||||
UUID.fromString(resultSet.getString("transfer_id")),
|
||||
RocketTransferState.valueOf(resultSet.getString("state")),
|
||||
resultSet.getString("destination_world"),
|
||||
new RocketBlockPosition(
|
||||
resultSet.getInt("destination_origin_x"),
|
||||
resultSet.getInt("destination_origin_y"),
|
||||
resultSet.getInt("destination_origin_z")
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public boolean claimDestinationTransfer(final UUID transferId, final UUID playerUuid) throws SQLException {
|
||||
try (Connection connection = this.dataSource.getConnection()) {
|
||||
connection.setAutoCommit(false);
|
||||
try {
|
||||
final RocketTransferState transferState = getTransferStateForUpdate(connection, transferId);
|
||||
if (transferState == null || (transferState != RocketTransferState.SOURCE_CLEARED
|
||||
&& transferState != RocketTransferState.CLAIMED)) {
|
||||
connection.rollback();
|
||||
return false;
|
||||
}
|
||||
if (transferState == RocketTransferState.SOURCE_CLEARED) {
|
||||
updateTransferState(connection, transferId, RocketTransferState.CLAIMED);
|
||||
}
|
||||
if (!claimPassenger(connection, transferId, playerUuid)) {
|
||||
connection.rollback();
|
||||
return false;
|
||||
}
|
||||
connection.commit();
|
||||
return true;
|
||||
} catch (final SQLException exception) {
|
||||
connection.rollback();
|
||||
throw exception;
|
||||
} finally {
|
||||
connection.setAutoCommit(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void insertTransfer(final Connection connection, final RocketManifest manifest,
|
||||
final RocketBlockPosition destinationOrigin) throws SQLException {
|
||||
try (PreparedStatement statement = connection.prepareStatement("""
|
||||
@@ -82,6 +148,61 @@ public final class RocketTransferDao {
|
||||
}
|
||||
}
|
||||
|
||||
private RocketTransferState getTransferStateForUpdate(final Connection connection, final UUID transferId)
|
||||
throws SQLException {
|
||||
try (PreparedStatement statement = connection.prepareStatement("""
|
||||
SELECT state
|
||||
FROM rocket_transfers
|
||||
WHERE transfer_id = ?
|
||||
FOR UPDATE
|
||||
""")) {
|
||||
statement.setString(1, transferId.toString());
|
||||
try (ResultSet resultSet = statement.executeQuery()) {
|
||||
if (!resultSet.next()) {
|
||||
return null;
|
||||
}
|
||||
return RocketTransferState.valueOf(resultSet.getString("state"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void updateTransferState(final Connection connection, final UUID transferId, final RocketTransferState state)
|
||||
throws SQLException {
|
||||
try (PreparedStatement statement = connection.prepareStatement("""
|
||||
UPDATE rocket_transfers
|
||||
SET state = ?
|
||||
WHERE transfer_id = ?
|
||||
""")) {
|
||||
statement.setString(1, state.name());
|
||||
statement.setString(2, transferId.toString());
|
||||
statement.executeUpdate();
|
||||
}
|
||||
}
|
||||
|
||||
private boolean claimPassenger(final Connection connection, final UUID transferId, final UUID playerUuid)
|
||||
throws SQLException {
|
||||
try (PreparedStatement statement = connection.prepareStatement("""
|
||||
UPDATE rocket_transfer_players
|
||||
SET state = 'CLAIMED'
|
||||
WHERE transfer_id = ? AND player_uuid = ? AND state IN ('PENDING', 'CLAIMED')
|
||||
""")) {
|
||||
statement.setString(1, transferId.toString());
|
||||
statement.setString(2, playerUuid.toString());
|
||||
statement.executeUpdate();
|
||||
}
|
||||
try (PreparedStatement statement = connection.prepareStatement("""
|
||||
SELECT 1
|
||||
FROM rocket_transfer_players
|
||||
WHERE transfer_id = ? AND player_uuid = ? AND state = 'CLAIMED'
|
||||
""")) {
|
||||
statement.setString(1, transferId.toString());
|
||||
statement.setString(2, playerUuid.toString());
|
||||
try (ResultSet resultSet = statement.executeQuery()) {
|
||||
return resultSet.next();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void insertPassengers(final Connection connection, final Iterable<RocketPassengerTransfer> passengers)
|
||||
throws SQLException {
|
||||
try (PreparedStatement statement = connection.prepareStatement("""
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
package net.civmc.zorweth.transfer;
|
||||
|
||||
import java.util.Objects;
|
||||
import java.util.UUID;
|
||||
|
||||
public record DestinationRocketTransfer(
|
||||
UUID transferId,
|
||||
RocketTransferState state,
|
||||
String destinationWorld,
|
||||
RocketBlockPosition destinationOrigin
|
||||
) {
|
||||
|
||||
public DestinationRocketTransfer {
|
||||
Objects.requireNonNull(transferId, "transferId");
|
||||
Objects.requireNonNull(state, "state");
|
||||
Objects.requireNonNull(destinationWorld, "destinationWorld");
|
||||
Objects.requireNonNull(destinationOrigin, "destinationOrigin");
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user