Code Patterns

Copy-paste examples for common plugin tasks. Commands, events, ECS, GUI, and more.

← Back to Patterns
command

Loot Spawn Command

/spawn command using AbstractCommand

Example Code

java
package dev.myplugin.example;

// import dev.myplugin.example.TroubleInTrorkTownPlugin;  // Anonymized
// import dev.myplugin.example.InstanceConfig;  // Anonymized
// import dev.myplugin.example.SpawnPoint;  // Anonymized
// import dev.myplugin.example.IncludedLootItem;  // Anonymized
// import dev.myplugin.example.LootItem;  // Anonymized
// import dev.myplugin.example.LootSpawnPoint;  // Anonymized
// import dev.myplugin.example.LootTable;  // Anonymized
import com.hypixel.hytale.component.AddReason;
import com.hypixel.hytale.component.Holder;
import com.hypixel.hytale.component.Ref;
import com.hypixel.hytale.logger.HytaleLogger;
import com.hypixel.hytale.server.core.Message;
import com.hypixel.hytale.server.core.command.system.CommandContext;
import com.hypixel.hytale.server.core.command.system.arguments.system.OptionalArg;
import com.hypixel.hytale.server.core.command.system.arguments.types.ArgTypes;
import com.hypixel.hytale.server.core.command.system.basecommands.AbstractAsyncCommand;
import com.hypixel.hytale.server.core.command.system.basecommands.AbstractCommandCollection;
import com.hypixel.hytale.server.core.inventory.ItemStack;
import com.hypixel.hytale.server.core.modules.entity.DespawnComponent;
import com.hypixel.hytale.server.core.modules.entity.component.TransformComponent;
import com.hypixel.hytale.server.core.modules.entity.item.ItemComponent;
import com.hypixel.hytale.server.core.universe.world.World;
import com.hypixel.hytale.server.core.universe.world.storage.EntityStore;
import org.checkerframework.checker.nullness.compatqual.NonNullDecl;

import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ThreadLocalRandom;

import static ar.ncode.plugin.TroubleInTrorkTownPlugin.gameModeStateForWorld;
import static ar.ncode.plugin.TroubleInTrorkTownPlugin.lootTables;

public class ExampleCommand extends AbstractCommandCollection {

	public ExampleCommand() {
		super("spawn", "Spawn all the configured loot boxes in the world.");
		addSubCommand(new LootForceSpawnCommand());
		addSubCommand(new LootShowSpawnPointsCommand());
		addSubCommand(new LootAddSpawnPositionCommand());

	}

	public static class LootForceSpawnCommand extends AbstractAsyncCommand {

		public LootForceSpawnCommand() {
			super("force", "Add a loot spawn at your current position.");
		}

		private static void addLoot(World world, LootSpawnPoint lootSpawnPoint) {
			for (String lootTableId : lootSpawnPoint.getLootTables()) {
				LootTable lootTable = lootTables.get().getLootTableById(lootTableId);

				if (lootTable == null) {
					continue;
				}

				for (LootItem item : lootTable.getItems()) {
					if (!chance(item.getProbability())) {
						continue;
					}

					spawnItemInWorld(world, lootSpawnPoint, item.getItemId(), item.getAmount());

					for (IncludedLootItem included : item.getIncludes()) {
						spawnItemInWorld(world, lootSpawnPoint, included.getItemId(), included.getAmount());
					}
				}
			}
		}

		private static void spawnItemInWorld(World world, LootSpawnPoint lootSpawnPoint, String itemId, int amount) {
			var gameModeState = gameModeStateForWorld.get(world.getWorldConfig().getUuid());

			ItemStack itemToSpawn = new ItemStack(itemId, amount);

			// Magic numbers to spread items around a bit
			Holder<EntityStore> itemEntityHolder = ItemComponent.generateItemDrop(
					world.getEntityStore().getStore(),
					itemToSpawn,
					lootSpawnPoint.getSpawnPoint().getPosition().clone(),
					lootSpawnPoint.getSpawnPoint().getRotation().clone(),
					0,
					0,
					0
			);

			if (itemEntityHolder == null) {
				return;
			}

			itemEntityHolder.removeComponent(DespawnComponent.getComponentType());

			ItemComponent itemComponent = itemEntityHolder.getComponent(ItemComponent.getComponentType());
			if (itemComponent != null) {
				itemComponent.setPickupDelay(0.5F);
			}

			Ref<EntityStore> item = world.getEntityStore().getStore().addEntity(itemEntityHolder, AddReason.SPAWN);
			gameModeState.trackItem(item);
		}

		public static boolean chance(int probability) {
			if (probability < 0 || probability > 100) {
				throw new IllegalArgumentException("Probability must be between 0 and 100");
			}

			// nextInt(100) returns a value from 0 (inclusive) to 100 (exclusive)
			return ThreadLocalRandom.current().nextInt(100) < probability;
		}

		public static void spawnLootForWorld(World world) {
			world.execute(() -> {
				String worldName = world.getWorldConfig().getDisplayName().replace(" ", "_").toLowerCase();
				InstanceConfig instanceConfig =
						TroubleInTrorkTownPlugin.instanceConfig.get(worldName).get();

				for (LootSpawnPoint lootSpawnPoint : instanceConfig.getLootSpawnPoints()) {
					if (!chance(lootSpawnPoint.getProbability())) {
						continue;
					}

					addLoot(world, lootSpawnPoint);
				}
			});
		}

		protected void executeSync(@NonNullDecl CommandContext ctx) {
			Ref<EntityStore> reference = ctx.senderAsPlayerRef();

			if (reference == null || !reference.isValid()) {
				ctx.sendMessage(Message.raw("You can't use this command from the console."));
				return;
			}

			var world = reference.getStore().getExternalData().getWorld();
			spawnLootForWorld(world);
		}

		@NonNullDecl
		@Override
		protected CompletableFuture<Void> executeAsync(@NonNullDecl CommandContext commandContext) {
			return CompletableFuture.runAsync(() -> executeSync(commandContext));
		}
	}

	public static class LootAddSpawnPositionCommand extends AbstractAsyncCommand {

		private static final HytaleLogger LOGGER = HytaleLogger.forEnclosingClass();

		OptionalArg<Integer> probabilityArg = this.withOptionalArg("probability", "Define the probability of this lootbox" +
				" spawning", ArgTypes.INTEGER);

		public LootAddSpawnPositionCommand() {
			super("add", "Adds a loot position at the player's current location.");
		}

		protected void executeSync(@NonNullDecl CommandContext ctx) {
			Ref<EntityStore> reference = ctx.senderAsPlayerRef();

			if (reference == null || !reference.isValid()) {
				ctx.sendMessage(Message.raw("You can't use this command from the console."));
				return;
			}

			var world = reference.getStore().getExternalData().getWorld();
			world.execute(() -> {
				var transformComponent = reference.getStore().getComponent(reference, TransformComponent.getComponentType());
				if (transformComponent == null) {
					ctx.sendMessage(Message.raw("An error occurred while trying to access your player information."));
					return;
				}

				// Here you would add the logic to actually store the loot position
				LootSpawnPoint lootSpawnPoint = new LootSpawnPoint();
				lootSpawnPoint.setSpawnPoint(new SpawnPoint(
						transformComponent.getPosition().clone(),
						transformComponent.getRotation().clone()
				));

				if (probabilityArg.get(ctx) != null) {
					lootSpawnPoint.setProbability(probabilityArg.get(ctx));
				}

				String worldName = world.getWorldConfig().getDisplayName().replace(" ", "_").toLowerCase();
				InstanceConfig instanceConfig = TroubleInTrorkTownPlugin.instanceConfig.get(worldName).get();
				LootSpawnPoint[] lootSpawnPoints = instanceConfig.getLootSpawnPoints();
				if (lootSpawnPoints == null) {
					lootSpawnPoints = new LootSpawnPoint[0];
				}

				LootSpawnPoint[] newLootSpawnPoints = new LootSpawnPoint[lootSpawnPoints.length + 1];
				System.arraycopy(lootSpawnPoints, 0, newLootSpawnPoints, 0, lootSpawnPoints.length);
				newLootSpawnPoints[lootSpawnPoints.length] = lootSpawnPoint;
				instanceConfig.setLootSpawnPoints(newLootSpawnPoints);

				TroubleInTrorkTownPlugin.instanceConfig.get(worldName).save();

				ctx.sendMessage(Message.raw("Loot position added at your current location."));
			});
		}

		@NonNullDecl
		@Override
		protected CompletableFuture<Void> executeAsync(@NonNullDecl CommandContext commandContext) {
			return CompletableFuture.runAsync(() -> executeSync(commandContext));
		}
	}

}