diff --git a/ansible/files/paper-config/plugins/FactoryMod/config.yml b/ansible/files/paper-config/plugins/FactoryMod/config.yml index fd26308a5..671b49c5d 100644 --- a/ansible/files/paper-config/plugins/FactoryMod/config.yml +++ b/ansible/files/paper-config/plugins/FactoryMod/config.yml @@ -1828,7 +1828,11 @@ recipes: production_time: 4s name: Build Rocket type: BUILD_ROCKET - flight_computer_reinforcement: DIAMOND + flight_computer_reinforcement: + diamond: + ==: org.bukkit.inventory.ItemStack + v: 3839 + type: DIAMOND input: daylight_sensor: v: 3839 diff --git a/ansible/files/paper-config/plugins/Zorweth/config.yml b/ansible/files/paper-config/plugins/Zorweth/config.yml new file mode 100644 index 000000000..65b876f50 --- /dev/null +++ b/ansible/files/paper-config/plugins/Zorweth/config.yml @@ -0,0 +1,16 @@ +server-name: main +destination-server: zorweth +destination-world: world +transfer-failure-message: Unable to complete rocket transfer. Please reconnect and try again. + +database: + ==: vg.civcraft.mc.civmodcore.dao.DatabaseCredentials + hostname: ${CIV_MYSQL_HOST} + dbname: zorweth + username: ${CIV_MYSQL_USERNAME} + password: ${CIV_MYSQL_PASSWORD} + poolsize: 10 + port: 3306 + connection_timeout: 10000 + idle_timeout: 600000 + max_lifetime: 7200000 diff --git a/ansible/files/proxy-config/plugins/zorweth/config.yml b/ansible/files/proxy-config/plugins/zorweth/config.yml new file mode 100644 index 000000000..e46bb279e --- /dev/null +++ b/ansible/files/proxy-config/plugins/zorweth/config.yml @@ -0,0 +1,14 @@ +database: + driver: mariadb + host: ${CIV_MYSQL_HOST} + port: 3306 + user: ${CIV_MYSQL_USERNAME} + password: ${CIV_MYSQL_PASSWORD} + database: zorweth + poolsize: 5 + connection_timeout: 10000 + idle_timeout: 600000 + max_lifetime: 7200000 + +holding-server: "" +failure-message: "Unable to verify your rocket transfer. Please reconnect and try again." diff --git a/ansible/files/zorweth-config/plugins/CastleGates/config.yml b/ansible/files/zorweth-config/plugins/CastleGates/config.yml index 24778fbdf..7fe0d96ba 100644 --- a/ansible/files/zorweth-config/plugins/CastleGates/config.yml +++ b/ansible/files/zorweth-config/plugins/CastleGates/config.yml @@ -1,7 +1,7 @@ Database: Host: ${CIV_MYSQL_HOST} Port: 3306 - Schema: castlegates + Schema: ${CIV_DATABASE_PREFIX}castlegates User: ${CIV_MYSQL_USERNAME} Password: ${CIV_MYSQL_PASSWORD} Settings: diff --git a/ansible/files/zorweth-config/plugins/ExilePearl/config.yml b/ansible/files/zorweth-config/plugins/ExilePearl/config.yml index 5f32a9092..49634854a 100644 --- a/ansible/files/zorweth-config/plugins/ExilePearl/config.yml +++ b/ansible/files/zorweth-config/plugins/ExilePearl/config.yml @@ -245,7 +245,7 @@ storage: type: 1 mysql: host: ${CIV_MYSQL_HOST} - dbname: exilepearl + dbname: ${CIV_DATABASE_PREFIX}exilepearl port: 3306 username: ${CIV_MYSQL_USERNAME} password: ${CIV_MYSQL_PASSWORD} diff --git a/ansible/files/zorweth-config/plugins/FactoryMod/config.yml b/ansible/files/zorweth-config/plugins/FactoryMod/config.yml index dcb974cda..671b49c5d 100644 --- a/ansible/files/zorweth-config/plugins/FactoryMod/config.yml +++ b/ansible/files/zorweth-config/plugins/FactoryMod/config.yml @@ -1344,6 +1344,18 @@ factories: - make_redstone_kit - repair_redstone - repair_redstone_kit + space_factory: + type: FCC + name: Space Factory + citadelBreakReduction: 1.0 + setupcost: + daylight_sensor: + v: 3839 + ==: org.bukkit.inventory.ItemStack + type: DAYLIGHT_DETECTOR + amount: 1 + recipes: + - build_rocket bastion_factory: type: FCC name: Bastion Factory @@ -1812,6 +1824,21 @@ factories: - repair_copper_workshop_kit #RECIPES recipes: + build_rocket: + production_time: 4s + name: Build Rocket + type: BUILD_ROCKET + flight_computer_reinforcement: + diamond: + ==: org.bukkit.inventory.ItemStack + v: 3839 + type: DIAMOND + input: + daylight_sensor: + v: 3839 + ==: org.bukkit.inventory.ItemStack + type: DAYLIGHT_DETECTOR + amount: 1 facet_heliodor: production_time: 4s name: Facet Heliodor Gem diff --git a/ansible/files/zorweth-config/plugins/Zorweth/config.yml b/ansible/files/zorweth-config/plugins/Zorweth/config.yml new file mode 100644 index 000000000..016f4c75b --- /dev/null +++ b/ansible/files/zorweth-config/plugins/Zorweth/config.yml @@ -0,0 +1,16 @@ +server-name: zorweth +destination-server: main +destination-world: world +transfer-failure-message: Unable to complete rocket transfer. Please reconnect and try again. + +database: + ==: vg.civcraft.mc.civmodcore.dao.DatabaseCredentials + hostname: ${CIV_MYSQL_HOST} + dbname: zorweth + username: ${CIV_MYSQL_USERNAME} + password: ${CIV_MYSQL_PASSWORD} + poolsize: 10 + port: 3306 + connection_timeout: 10000 + idle_timeout: 600000 + max_lifetime: 7200000 diff --git a/containers/provisioning/mariadb/docker-entrypoint-initdb.d/0100_create_databases.sql b/containers/provisioning/mariadb/docker-entrypoint-initdb.d/0100_create_databases.sql index e75f07998..1f74a8dfa 100755 --- a/containers/provisioning/mariadb/docker-entrypoint-initdb.d/0100_create_databases.sql +++ b/containers/provisioning/mariadb/docker-entrypoint-initdb.d/0100_create_databases.sql @@ -78,6 +78,7 @@ CREATE DATABASE zorweth_essenceglue; CREATE DATABASE zorweth_jukealert; CREATE DATABASE zorweth_realisticbiomes; CREATE DATABASE zorweth_exilepearl; +CREATE DATABASE zorweth_castlegates; GRANT ALL PRIVILEGES ON `zorweth_bastion`.* TO 'mariadb'@'%'; GRANT ALL PRIVILEGES ON `zorweth_citadel`.* TO 'mariadb'@'%'; @@ -89,3 +90,7 @@ GRANT ALL PRIVILEGES ON `zorweth_essenceglue`.* TO 'mariadb'@'%'; GRANT ALL PRIVILEGES ON `zorweth_jukealert`.* TO 'mariadb'@'%'; GRANT ALL PRIVILEGES ON `zorweth_realisticbiomes`.* TO 'mariadb'@'%'; GRANT ALL PRIVILEGES ON `zorweth_exilepearl`.* TO 'mariadb'@'%'; +GRANT ALL PRIVILEGES ON `zorweth_castlegates`.* TO 'mariadb'@'%'; + +CREATE DATABASE zorweth; +GRANT ALL PRIVILEGES ON `zorweth`.* TO 'mariadb'@'%'; diff --git a/plugins/factorymod-paper/src/main/java/com/github/igotyou/FactoryMod/recipes/space/BuildRocketRecipe.java b/plugins/factorymod-paper/src/main/java/com/github/igotyou/FactoryMod/recipes/space/BuildRocketRecipe.java index 9fbb6173f..596ac768f 100644 --- a/plugins/factorymod-paper/src/main/java/com/github/igotyou/FactoryMod/recipes/space/BuildRocketRecipe.java +++ b/plugins/factorymod-paper/src/main/java/com/github/igotyou/FactoryMod/recipes/space/BuildRocketRecipe.java @@ -9,7 +9,7 @@ import com.sk89q.worldedit.math.BlockVector3; import com.sk89q.worldedit.regions.Region; import com.sk89q.worldedit.world.block.BlockState; import java.util.List; -import net.civmc.zorweth.FlightComputer; +import net.civmc.zorweth.flight.FlightComputer; import net.civmc.zorweth.ZorwethPlugin; import org.bukkit.Bukkit; import org.bukkit.Color; diff --git a/plugins/zorweth-paper/src/main/java/net/civmc/zorweth/DestinationTransferListener.java b/plugins/zorweth-paper/src/main/java/net/civmc/zorweth/DestinationTransferListener.java index e11ed41c8..a1db23451 100644 --- a/plugins/zorweth-paper/src/main/java/net/civmc/zorweth/DestinationTransferListener.java +++ b/plugins/zorweth-paper/src/main/java/net/civmc/zorweth/DestinationTransferListener.java @@ -5,6 +5,7 @@ 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.flight.FlightComputer; import net.civmc.zorweth.transfer.DestinationRocketTransfer; import net.civmc.zorweth.transfer.RocketBlockPosition; import net.kyori.adventure.text.Component; @@ -29,7 +30,7 @@ public final class DestinationTransferListener implements Listener { this.plugin = plugin; } - @EventHandler(priority = EventPriority.LOWEST, ignoreCancelled = false) + @EventHandler(priority = EventPriority.LOWEST) public void onPlayerJoin(final PlayerJoinEvent event) { final Player player = event.getPlayer(); Bukkit.getScheduler().runTaskAsynchronously(this.plugin, () -> handleJoin(player)); diff --git a/plugins/zorweth-paper/src/main/java/net/civmc/zorweth/StasisHandler.java b/plugins/zorweth-paper/src/main/java/net/civmc/zorweth/StasisHandler.java new file mode 100644 index 000000000..cf8543720 --- /dev/null +++ b/plugins/zorweth-paper/src/main/java/net/civmc/zorweth/StasisHandler.java @@ -0,0 +1,140 @@ +package net.civmc.zorweth; + +import java.util.Objects; +import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; +import org.bukkit.entity.Entity; +import org.bukkit.entity.Player; +import org.bukkit.event.EventHandler; +import org.bukkit.event.EventPriority; +import org.bukkit.event.Listener; +import org.bukkit.event.block.BlockBreakEvent; +import org.bukkit.event.block.BlockPlaceEvent; +import org.bukkit.event.entity.EntityDamageEvent; +import org.bukkit.event.entity.EntityPickupItemEvent; +import org.bukkit.event.inventory.InventoryClickEvent; +import org.bukkit.event.player.PlayerArmorStandManipulateEvent; +import org.bukkit.event.player.PlayerDropItemEvent; +import org.bukkit.event.player.PlayerInteractEntityEvent; +import org.bukkit.event.player.PlayerInteractEvent; +import org.bukkit.event.player.PlayerMoveEvent; +import org.bukkit.event.player.PlayerQuitEvent; + +public final class StasisHandler implements Listener { + +private final Set playersInStasis = ConcurrentHashMap.newKeySet(); + + public void putInStasis(final Player player) { + Objects.requireNonNull(player); + if (!player.isConnected()) { + throw new IllegalArgumentException("player is not connected"); + } + this.playersInStasis.add(player); + } + + public void removeStasis(final Player player) { + Objects.requireNonNull(player); + this.playersInStasis.remove(player); + } + + @EventHandler(priority = EventPriority.HIGHEST) + public void onEntityDamage(final EntityDamageEvent event) { + final Entity entity = event.getEntity(); + if (!(entity instanceof Player player) || !isInStasis(player)) { + return; + } + + event.setDamage(0); + event.setCancelled(true); + } + + @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) + public void onPlayerMove(final PlayerMoveEvent event) { + if (!isInStasis(event.getPlayer())) { + return; + } + + event.setCancelled(true); + } + + @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) + public void onPlayerInteract(final PlayerInteractEvent event) { + if (!isInStasis(event.getPlayer())) { + return; + } + + event.setCancelled(true); + } + + @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) + public void onPlayerInteractEntity(final PlayerInteractEntityEvent event) { + if (!isInStasis(event.getPlayer())) { + return; + } + + event.setCancelled(true); + } + + @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) + public void onPlayerArmorStandManipulate(final PlayerArmorStandManipulateEvent event) { + if (!isInStasis(event.getPlayer())) { + return; + } + + event.setCancelled(true); + } + + @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) + public void onBlockBreak(final BlockBreakEvent event) { + if (!isInStasis(event.getPlayer())) { + return; + } + + event.setCancelled(true); + } + + @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) + public void onBlockPlace(final BlockPlaceEvent event) { + if (!isInStasis(event.getPlayer())) { + return; + } + + event.setCancelled(true); + } + + @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) + public void onEntityPickupItem(final EntityPickupItemEvent event) { + if (!(event.getEntity() instanceof Player player) || !isInStasis(player)) { + return; + } + + event.setCancelled(true); + } + + @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) + public void onPlayerDropItem(final PlayerDropItemEvent event) { + if (!isInStasis(event.getPlayer())) { + return; + } + + event.setCancelled(true); + } + + @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) + public void onInventoryClick(final InventoryClickEvent event) { + if (!(event.getWhoClicked() instanceof Player player) || !isInStasis(player)) { + return; + } + + event.setCancelled(true); + } + + @EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true) + public void onPlayerQuit(final PlayerQuitEvent event) { + removeStasis(event.getPlayer()); + } + + private boolean isInStasis(final Player player) { + return this.playersInStasis.contains(player); + } +} diff --git a/plugins/zorweth-paper/src/main/java/net/civmc/zorweth/ZorwethPlugin.java b/plugins/zorweth-paper/src/main/java/net/civmc/zorweth/ZorwethPlugin.java index f1c7143dd..53d6fd9a5 100644 --- a/plugins/zorweth-paper/src/main/java/net/civmc/zorweth/ZorwethPlugin.java +++ b/plugins/zorweth-paper/src/main/java/net/civmc/zorweth/ZorwethPlugin.java @@ -17,6 +17,7 @@ import java.util.logging.Level; import javax.sql.DataSource; import net.civmc.zorweth.database.RocketTransferDao; import net.civmc.zorweth.database.ZorwethDatabase; +import net.civmc.zorweth.flight.FlightComputer; import org.bukkit.plugin.java.JavaPlugin; import vg.civcraft.mc.civmodcore.dao.DatabaseCredentials; @@ -26,6 +27,7 @@ public final class ZorwethPlugin extends JavaPlugin { private Clipboard rocketClipboard; private HikariDataSource dataSource; + private StasisHandler invincibilityHandler; private RocketTransferDao rocketTransferDao; private String serverName; private String destinationServer; @@ -47,6 +49,8 @@ public final class ZorwethPlugin extends JavaPlugin { } getServer().getMessenger().registerOutgoingPluginChannel(this, "BungeeCord"); this.rocketClipboard = loadRocketClipboard(); + this.invincibilityHandler = new StasisHandler(); + getServer().getPluginManager().registerEvents(this.invincibilityHandler, this); getServer().getPluginManager().registerEvents(new FlightComputer(this), this); getServer().getPluginManager().registerEvents(new DestinationTransferListener(this), this); } @@ -71,6 +75,10 @@ public final class ZorwethPlugin extends JavaPlugin { return this.rocketTransferDao; } + public StasisHandler getInvincibilityHandler() { + return this.invincibilityHandler; + } + public String getServerName() { return this.serverName; } diff --git a/plugins/zorweth-paper/src/main/java/net/civmc/zorweth/database/RocketTransferDao.java b/plugins/zorweth-paper/src/main/java/net/civmc/zorweth/database/RocketTransferDao.java index 10dd07760..7e7053446 100644 --- a/plugins/zorweth-paper/src/main/java/net/civmc/zorweth/database/RocketTransferDao.java +++ b/plugins/zorweth-paper/src/main/java/net/civmc/zorweth/database/RocketTransferDao.java @@ -131,7 +131,7 @@ public final class RocketTransferDao { ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) """)) { statement.setString(1, manifest.transferId().toString()); - statement.setString(2, RocketTransferState.PREPARED.name()); + statement.setString(2, RocketTransferState.SOURCE_CLEARED.name()); statement.setString(3, manifest.sourceServer()); statement.setString(4, manifest.destinationServer()); statement.setString(5, manifest.sourceWorld()); diff --git a/plugins/zorweth-paper/src/main/java/net/civmc/zorweth/database/ZorwethDatabase.java b/plugins/zorweth-paper/src/main/java/net/civmc/zorweth/database/ZorwethDatabase.java index 6b914e3b5..c216c99de 100644 --- a/plugins/zorweth-paper/src/main/java/net/civmc/zorweth/database/ZorwethDatabase.java +++ b/plugins/zorweth-paper/src/main/java/net/civmc/zorweth/database/ZorwethDatabase.java @@ -87,6 +87,9 @@ public final class ZorwethDatabase { INDEX idx_player_server_state_last_server (last_server) ) """); + migrator.registerMigration("zorweth", 1, """ + ALTER TABLE rocket_transfers ADD COLUMN IF NOT EXISTS fuel FLOAT NOT NULL + """); try (Connection connection = dataSource.getConnection()) { migrator.migrate(connection); diff --git a/plugins/zorweth-paper/src/main/java/net/civmc/zorweth/FlightComputer.java b/plugins/zorweth-paper/src/main/java/net/civmc/zorweth/flight/FlightComputer.java similarity index 83% rename from plugins/zorweth-paper/src/main/java/net/civmc/zorweth/FlightComputer.java rename to plugins/zorweth-paper/src/main/java/net/civmc/zorweth/flight/FlightComputer.java index f5d798dc3..14114e6bb 100644 --- a/plugins/zorweth-paper/src/main/java/net/civmc/zorweth/FlightComputer.java +++ b/plugins/zorweth-paper/src/main/java/net/civmc/zorweth/flight/FlightComputer.java @@ -1,11 +1,10 @@ -package net.civmc.zorweth; +package net.civmc.zorweth.flight; import com.google.common.io.ByteArrayDataOutput; import com.google.common.io.ByteStreams; 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 io.papermc.paper.datacomponent.DataComponentTypes; import java.util.ArrayList; import java.util.HashMap; @@ -13,6 +12,10 @@ import java.util.List; import java.util.Map; import java.util.UUID; import java.util.logging.Level; + +import net.civmc.zorweth.RocketTransferKeys; +import net.civmc.zorweth.StasisHandler; +import net.civmc.zorweth.ZorwethPlugin; import net.civmc.zorweth.transfer.RocketBlockPosition; import net.civmc.zorweth.transfer.RocketEntityPosition; import net.civmc.zorweth.transfer.RocketChestTransfer; @@ -21,7 +24,6 @@ import net.civmc.zorweth.transfer.RocketManifestChest; import net.civmc.zorweth.transfer.RocketManifestPassenger; import net.civmc.zorweth.transfer.RocketManifestSerializer; import net.civmc.zorweth.transfer.RocketPassengerTransfer; -import net.civmc.zorweth.transfer.RocketTransferState; import net.kyori.adventure.text.Component; import net.kyori.adventure.text.format.NamedTextColor; import net.kyori.adventure.text.format.TextDecoration; @@ -30,7 +32,6 @@ import org.bukkit.Location; import org.bukkit.Material; import org.bukkit.NamespacedKey; import org.bukkit.World; -import org.bukkit.attribute.Attribute; import org.bukkit.block.Block; import org.bukkit.block.BlockFace; import org.bukkit.block.Chest; @@ -80,8 +81,11 @@ public final class FlightComputer implements Listener { private final Map coordinateAnvils = new HashMap<>(); private final Map coordinateComputers = new HashMap<>(); - public FlightComputer(final ZorwethPlugin plugin) { + private final StasisHandler invincibilityHandler; + + public FlightComputer(final ZorwethPlugin plugin, StasisHandler invincibilityHandler) { this.plugin = plugin; + this.invincibilityHandler = invincibilityHandler; } public static boolean isFlightComputerPosition(final BlockVector3 relative) { @@ -188,7 +192,7 @@ public final class FlightComputer implements Listener { final List mismatches = new ArrayList<>(); for (final BlockVector3 position : region) { final BlockVector3 relative = position.subtract(schematicNorthWestCorner); - final BlockState expectedState = clipboard.getBlock(position); + final com.sk89q.worldedit.world.block.BlockState expectedState = clipboard.getBlock(position); final Material expected = Bukkit.createBlockData(expectedState.getAsString()).getMaterial(); final Block actualBlock = origin.getRelative(relative.getX(), relative.getY(), relative.getZ()); final Material actual = actualBlock.getType(); @@ -254,7 +258,7 @@ public final class FlightComputer implements Listener { for (final BlockVector3 position : region) { final BlockVector3 relative = position.subtract(schematicNorthWestCorner); - final BlockState expectedState = clipboard.getBlock(position); + final com.sk89q.worldedit.world.block.BlockState expectedState = clipboard.getBlock(position); final Material expected = Bukkit.createBlockData(expectedState.getAsString()).getMaterial(); final Block actualBlock = origin.getRelative(relative.getX(), relative.getY(), relative.getZ()); final Material actual = actualBlock.getType(); @@ -312,16 +316,22 @@ public final class FlightComputer implements Listener { final Coordinates destination = getDestination(computer); if (destination == null) { - return new RocketManifestResult(createManifest(origin, new Coordinates(0, 0), passengers, chests), + return new RocketManifestResult(null, Component.text("Destination not set.", NamedTextColor.RED)); } - return new RocketManifestResult(createManifest(origin, destination, passengers, chests), null); + final double cargoMass = calculateCargoMass(chests); + final double dryMass = ROCKET_DRY_MASS_KG + cargoMass + passengers.size() * SITTING_PLAYER_MASS_KG; + final double wetMass = getFuelKg(computer) + dryMass; + final double remainingFuel = wetMass / (Math.exp(DELTA_V_METERS_PER_SECOND / EXHAUST_VELOCITY_METERS_PER_SECOND)) - dryMass; + + return new RocketManifestResult(createManifest(origin, destination, passengers, chests, remainingFuel), null); } private RocketManifest createManifest(final Block origin, final Coordinates destination, final List passengers, - final List chests) { + final List chests, + final double fuelKg) { return new RocketManifest( UUID.randomUUID(), this.plugin.getServerName(), @@ -332,7 +342,8 @@ public final class FlightComputer implements Listener { destination.x(), destination.z(), passengers, - chests + chests, + fuelKg ); } @@ -501,7 +512,7 @@ public final class FlightComputer implements Listener { Component.text("Passengers: " + manifest.passengers().size(), NamedTextColor.GRAY) .decoration(TextDecoration.ITALIC, false), Component.text("Required fuel: " + roundUpTenths(fuelStatus.requiredFuelKg()) + " kg (" - + fuelStatus.requiredFuelItems() + " charcoal)", NamedTextColor.GRAY) + + fuelStatus.requiredFuelItems() + " charcoal)", NamedTextColor.GRAY) .decoration(TextDecoration.ITALIC, false), Component.text("Once engines have been started, there is no going back.", NamedTextColor.GRAY) .decoration(TextDecoration.ITALIC, false) @@ -555,18 +566,48 @@ public final class FlightComputer implements Listener { return; } + clearPassengerState(manifest); + setSourceClearedMarkers(manifest); + clearRocket(computer); + + for (RocketPassengerTransfer passenger : passengers) { + invincibilityHandler.putInStasis(Bukkit.getPlayer(passenger.playerUuid())); + } + clicker.sendMessage(Component.text("Preparing rocket transfer.", NamedTextColor.GREEN)); Bukkit.getScheduler().runTaskAsynchronously(this.plugin, () -> { - try { - this.plugin.getRocketTransferDao().insertPreparedTransfer(manifest, destinationOrigin, passengers, chests); - } catch (final Exception exception) { - this.plugin.getLogger().log(Level.SEVERE, "Failed to insert prepared rocket transfer", exception); - Bukkit.getScheduler().runTask(this.plugin, () -> clicker.sendMessage( - Component.text("Failed to prepare rocket transfer.", NamedTextColor.RED))); - return; + for (int i = 0; i < 5; i++) { + if (i > 0) { + try { + Thread.sleep((1 << (i - 1)) * 1000L); + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + return; + } + } + try { + this.plugin.getRocketTransferDao().insertPreparedTransfer(manifest, destinationOrigin, passengers, chests); + break; + } catch (final Exception exception) { + this.plugin.getLogger().log(Level.SEVERE, "Failed to insert prepared rocket transfer", exception); + } + if (i == 4) { + this.plugin.getLogger().log(Level.SEVERE, "Failed to retry launch"); + clicker.sendMessage(Component.text("Rocket launch failed, please contact admins.", NamedTextColor.RED)); + + for (RocketPassengerTransfer passenger : passengers) { + Player player = Bukkit.getPlayer(passenger.playerUuid()); + if (player != null) { + invincibilityHandler.removeStasis(player); + } + } + } } - Bukkit.getScheduler().runTask(this.plugin, () -> clearSourceAndTransition(computer, clicker, manifest, fuelStatus)); + Bukkit.getScheduler().runTask(this.plugin, () -> { + connectOrKickPassengers(manifest); + clicker.sendMessage(Component.text("Ignition.", NamedTextColor.GREEN)); + }); }); } @@ -579,45 +620,6 @@ public final class FlightComputer implements Listener { return new RocketBlockPosition(manifest.destinationRequestedX(), y, manifest.destinationRequestedZ()); } - private void clearSourceAndTransition(final Block computer, final Player clicker, final RocketManifest manifest, - final FuelStatus fuelStatus) { - for (final RocketManifestPassenger passenger : manifest.passengers()) { - if (Bukkit.getPlayer(passenger.playerUuid()) == null) { - markPreparedTransferCancelled(manifest); - clicker.sendMessage(Component.text("Passenger disconnected before launch completed.", NamedTextColor.RED)); - return; - } - } - - final int originalFuelItems = getFuelItems(computer); - clearPassengerState(manifest); - clearChestState(manifest); - setFuelItems(computer, originalFuelItems - fuelStatus.requiredFuelItems()); - setSourceClearedMarkers(manifest); - - Bukkit.getScheduler().runTaskAsynchronously(this.plugin, () -> { - final boolean cleared; - try { - cleared = this.plugin.getRocketTransferDao().transitionTransferState( - manifest, RocketTransferState.PREPARED, RocketTransferState.SOURCE_CLEARED); - } catch (final Exception exception) { - this.plugin.getLogger().log(Level.SEVERE, "Failed to mark rocket transfer source-cleared", exception); - Bukkit.getScheduler().runTask(this.plugin, () -> restoreCancelledSourceTransfer(computer, clicker, manifest, - originalFuelItems)); - return; - } - - Bukkit.getScheduler().runTask(this.plugin, () -> { - if (!cleared) { - restoreCancelledSourceTransfer(computer, clicker, manifest, originalFuelItems); - return; - } - connectOrKickPassengers(manifest); - clicker.sendMessage(Component.text("Launch committed. Transfer is now destination-owned.", NamedTextColor.GREEN)); - }); - }); - } - private void clearPassengerState(final RocketManifest manifest) { for (final RocketManifestPassenger passenger : manifest.passengers()) { final Player player = Bukkit.getPlayer(passenger.playerUuid()); @@ -634,15 +636,6 @@ public final class FlightComputer implements Listener { } } - private void clearChestState(final RocketManifest manifest) { - for (final RocketManifestChest chest : manifest.chests()) { - final Block block = getSourceBlock(manifest, chest.relativePosition()); - if (block.getState(false) instanceof Chest sourceChest) { - sourceChest.getBlockInventory().clear(); - } - } - } - private void setSourceClearedMarkers(final RocketManifest manifest) { for (final RocketManifestPassenger passenger : manifest.passengers()) { final Player player = Bukkit.getPlayer(passenger.playerUuid()); @@ -655,47 +648,7 @@ public final class FlightComputer implements Listener { } } - private void restoreCancelledSourceTransfer(final Block computer, final Player clicker, final RocketManifest manifest, - final int originalFuelItems) { - restorePassengerState(manifest); - restoreChestState(manifest); - setFuelItems(computer, originalFuelItems); - clearSourceMarkers(manifest); - markPreparedTransferCancelled(manifest); - clicker.sendMessage(Component.text("Rocket transfer failed before destination ownership; source state restored.", - NamedTextColor.RED)); - } - - private void restorePassengerState(final RocketManifest manifest) { - for (final RocketManifestPassenger passenger : manifest.passengers()) { - final Player player = Bukkit.getPlayer(passenger.playerUuid()); - if (player == null) { - continue; - } - player.getInventory().setContents(passenger.inventoryContents()); - final double maxHealth = player.getAttribute(Attribute.MAX_HEALTH) == null - ? passenger.health() - : player.getAttribute(Attribute.MAX_HEALTH).getValue(); - player.setHealth(Math.min(passenger.health(), maxHealth)); - player.setLevel(passenger.xpLevel()); - player.setExp(passenger.xpProgress()); - player.setFoodLevel(passenger.foodLevel()); - player.setSaturation(passenger.saturation()); - player.setExhaustion(passenger.exhaustion()); - player.getInventory().setHeldItemSlot(passenger.heldSlot()); - player.setGameMode(passenger.gameMode()); - } - } - - private void restoreChestState(final RocketManifest manifest) { - for (final RocketManifestChest chest : manifest.chests()) { - final Block block = getSourceBlock(manifest, chest.relativePosition()); - if (block.getState(false) instanceof Chest sourceChest) { - sourceChest.getBlockInventory().setStorageContents(chest.contents()); - } - } - } - + // TODO ?? private void clearSourceMarkers(final RocketManifest manifest) { for (final RocketManifestPassenger passenger : manifest.passengers()) { final Player player = Bukkit.getPlayer(passenger.playerUuid()); @@ -707,29 +660,6 @@ public final class FlightComputer implements Listener { } } - private void markPreparedTransferCancelled(final RocketManifest manifest) { - Bukkit.getScheduler().runTaskAsynchronously(this.plugin, () -> { - try { - this.plugin.getRocketTransferDao().transitionTransferState( - manifest, RocketTransferState.PREPARED, RocketTransferState.CANCELLED); - } catch (final Exception exception) { - this.plugin.getLogger().log(Level.WARNING, "Failed to cancel prepared rocket transfer", exception); - } - }); - } - - private Block getSourceBlock(final RocketManifest manifest, final RocketBlockPosition relative) { - final World world = Bukkit.getWorld(manifest.sourceWorld()); - if (world == null) { - throw new IllegalStateException("Source world is not loaded: " + manifest.sourceWorld()); - } - return world.getBlockAt( - manifest.sourceOrigin().x() + relative.x(), - manifest.sourceOrigin().y() + relative.y(), - manifest.sourceOrigin().z() + relative.z() - ); - } - private void connectOrKickPassengers(final RocketManifest manifest) { for (final RocketManifestPassenger passenger : manifest.passengers()) { final Player player = Bukkit.getPlayer(passenger.playerUuid()); @@ -782,13 +712,13 @@ public final class FlightComputer implements Listener { } final int amount = hand.getAmount(); - setFuelItems(computer, getFuelItems(computer) + amount); + setFuelKg(computer, getFuelKg(computer) + (amount * CHARCOAL_FUEL_KG)); player.getInventory().setItemInMainHand(null); player.sendMessage(Component.text("Added " + amount + " charcoal to the rocket.", NamedTextColor.GREEN)); } private void siphonFuel(final Player player, final Block computer) { - final int stored = getFuelItems(computer); + final double stored = getFuelKg(computer); if (stored <= 0) { player.sendMessage(Component.text("This rocket has no fuel to siphon.", NamedTextColor.RED)); return; @@ -809,13 +739,13 @@ public final class FlightComputer implements Listener { return; } - final int removed = Math.min(stored, space); - if (hand == null || hand.getType().isAir()) { + final int removed = Math.min((int) (stored / CHARCOAL_FUEL_KG), space); + if (hand.getType().isAir()) { player.getInventory().setItemInMainHand(new ItemStack(Material.CHARCOAL, removed)); } else { hand.setAmount(hand.getAmount() + removed); } - setFuelItems(computer, stored - removed); + setFuelKg(computer, stored - (removed * CHARCOAL_FUEL_KG)); player.sendMessage(Component.text("Siphoned " + removed + " charcoal from the rocket.", NamedTextColor.GREEN)); } @@ -829,10 +759,10 @@ public final class FlightComputer implements Listener { final int sittingPlayers = Math.max(1, passengers.size()); final double nonFuelMass = ROCKET_DRY_MASS_KG + cargoMass + sittingPlayers * SITTING_PLAYER_MASS_KG; final double requiredFuelKg = nonFuelMass * (Math.exp(DELTA_V_METERS_PER_SECOND / EXHAUST_VELOCITY_METERS_PER_SECOND) - 1.0); - final int fuelItems = getFuelItems(computer); + final double fuelKg = getFuelKg(computer); return new FuelStatus( - fuelItems, - fuelItems * CHARCOAL_FUEL_KG, + (int) (fuelKg / CHARCOAL_FUEL_KG), + fuelKg, requiredFuelKg, (int) Math.ceil(requiredFuelKg / CHARCOAL_FUEL_KG), cargoMass, @@ -854,22 +784,23 @@ public final class FlightComputer implements Listener { if (item == null || item.getType().isAir()) { continue; } + // TODO increase weight for fuel mass += (double) item.getAmount() / item.getMaxStackSize(); } return mass; } - private int getFuelItems(final Block computer) { + private double getFuelKg(final Block computer) { final Dispenser dispenser = (Dispenser) computer.getState(false); - return dispenser.getPersistentDataContainer().getOrDefault(ROCKET_FUEL_KEY, PersistentDataType.INTEGER, 0); + return dispenser.getPersistentDataContainer().getOrDefault(ROCKET_FUEL_KEY, PersistentDataType.DOUBLE, 0D); } - private void setFuelItems(final Block computer, final int fuelItems) { + private void setFuelKg(final Block computer, final double fuelKg) { final Dispenser dispenser = (Dispenser) computer.getState(false); - if (fuelItems <= 0) { + if (fuelKg <= 0) { dispenser.getPersistentDataContainer().remove(ROCKET_FUEL_KEY); } else { - dispenser.getPersistentDataContainer().set(ROCKET_FUEL_KEY, PersistentDataType.INTEGER, fuelItems); + dispenser.getPersistentDataContainer().set(ROCKET_FUEL_KEY, PersistentDataType.DOUBLE, fuelKg); } dispenser.update(true, false); } @@ -934,4 +865,17 @@ public final class FlightComputer implements Listener { private record Coordinates(int x, int z) { } + + private void clearRocket(Block computer) { + final Clipboard clipboard = this.plugin.getRocketClipboard(); + final Region region = clipboard.getRegion(); + final BlockVector3 schematicNorthWestCorner = region.getMinimumPoint(); + final Block origin = getRocketOrigin(computer); + + for (final BlockVector3 position : region) { + final BlockVector3 relative = position.subtract(schematicNorthWestCorner); + final Block actualBlock = origin.getRelative(relative.getX(), relative.getY(), relative.getZ()); + actualBlock.setType(Material.AIR); + } + } } diff --git a/plugins/zorweth-paper/src/main/java/net/civmc/zorweth/transfer/RocketInventorySerialization.java b/plugins/zorweth-paper/src/main/java/net/civmc/zorweth/transfer/RocketInventorySerialization.java deleted file mode 100644 index 12ddca909..000000000 --- a/plugins/zorweth-paper/src/main/java/net/civmc/zorweth/transfer/RocketInventorySerialization.java +++ /dev/null @@ -1,30 +0,0 @@ -package net.civmc.zorweth.transfer; - -import java.util.Arrays; -import java.util.Objects; -import org.bukkit.inventory.ItemStack; - -public final class RocketInventorySerialization { - - private RocketInventorySerialization() { - } - - public static byte[] serializeInventory(final ItemStack[] contents) { - Objects.requireNonNull(contents, "contents"); - return ItemStack.serializeItemsAsBytes(Arrays.copyOf(contents, contents.length)); - } - - public static ItemStack[] deserializeInventory(final byte[] serializedInventory, final int expectedLength) { - Objects.requireNonNull(serializedInventory, "serializedInventory"); - if (expectedLength < 0) { - throw new IllegalArgumentException("expectedLength must not be negative"); - } - - final ItemStack[] contents = ItemStack.deserializeItemsFromBytes(serializedInventory); - if (contents.length != expectedLength) { - throw new IllegalArgumentException("Serialized inventory has " + contents.length - + " slots, expected " + expectedLength); - } - return contents; - } -} diff --git a/plugins/zorweth-paper/src/main/java/net/civmc/zorweth/transfer/RocketManifest.java b/plugins/zorweth-paper/src/main/java/net/civmc/zorweth/transfer/RocketManifest.java index e349ae663..6f93faa48 100644 --- a/plugins/zorweth-paper/src/main/java/net/civmc/zorweth/transfer/RocketManifest.java +++ b/plugins/zorweth-paper/src/main/java/net/civmc/zorweth/transfer/RocketManifest.java @@ -14,7 +14,8 @@ public record RocketManifest( int destinationRequestedX, int destinationRequestedZ, List passengers, - List chests + List chests, + double fuel ) { public RocketManifest { diff --git a/plugins/zorweth-paper/src/main/java/net/civmc/zorweth/transfer/RocketManifestSerializer.java b/plugins/zorweth-paper/src/main/java/net/civmc/zorweth/transfer/RocketManifestSerializer.java index aa9d2b930..16ce92565 100644 --- a/plugins/zorweth-paper/src/main/java/net/civmc/zorweth/transfer/RocketManifestSerializer.java +++ b/plugins/zorweth-paper/src/main/java/net/civmc/zorweth/transfer/RocketManifestSerializer.java @@ -1,5 +1,6 @@ package net.civmc.zorweth.transfer; +import org.bukkit.inventory.ItemStack; import java.util.ArrayList; import java.util.List; @@ -15,7 +16,7 @@ public final class RocketManifestSerializer { manifest.transferId(), passenger.playerUuid(), passenger.relativePosition(), - RocketInventorySerialization.serializeInventory(passenger.inventoryContents()), + ItemStack.serializeItemsAsBytes(passenger.inventoryContents()), passenger.health(), passenger.xpLevel(), passenger.xpProgress(), @@ -36,7 +37,7 @@ public final class RocketManifestSerializer { chests.add(new RocketChestTransfer( manifest.transferId(), chest.relativePosition(), - RocketInventorySerialization.serializeInventory(chest.contents()), + ItemStack.serializeItemsAsBytes(chest.contents()), RocketTransferCargoState.PENDING )); } diff --git a/plugins/zorweth-paper/src/main/java/net/civmc/zorweth/transfer/RocketTransferState.java b/plugins/zorweth-paper/src/main/java/net/civmc/zorweth/transfer/RocketTransferState.java index 7311399b2..2bf821800 100644 --- a/plugins/zorweth-paper/src/main/java/net/civmc/zorweth/transfer/RocketTransferState.java +++ b/plugins/zorweth-paper/src/main/java/net/civmc/zorweth/transfer/RocketTransferState.java @@ -1,7 +1,6 @@ package net.civmc.zorweth.transfer; public enum RocketTransferState { - PREPARED, SOURCE_CLEARED, CLAIMED, APPLIED,