Skip to content

Commit

Permalink
Added custom blocks API with initial oraxen integration
Browse files Browse the repository at this point in the history
  • Loading branch information
WillFP committed Jul 30, 2024
1 parent c89edd0 commit 4a8935f
Show file tree
Hide file tree
Showing 13 changed files with 689 additions and 1 deletion.
149 changes: 149 additions & 0 deletions eco-api/src/main/java/com/willfp/eco/core/blocks/Blocks.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,149 @@
package com.willfp.eco.core.blocks;

import com.willfp.eco.core.blocks.impl.EmptyTestableBlock;
import com.willfp.eco.core.blocks.impl.MaterialTestableBlock;
import com.willfp.eco.core.blocks.impl.UnrestrictedMaterialTestableBlock;
import com.willfp.eco.core.blocks.provider.BlockProvider;
import com.willfp.eco.util.NamespacedKeyUtils;
import org.bukkit.Location;
import org.bukkit.Material;
import org.bukkit.NamespacedKey;
import org.bukkit.block.Block;
import org.jetbrains.annotations.NotNull;

import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;

/**
* Class to manage all custom and vanilla blocks.
*/
public final class Blocks {
/**
* All entities.
*/
private static final Map<NamespacedKey, TestableBlock> REGISTRY = new ConcurrentHashMap<>();

/**
* All block providers.
*/
private static final Map<String, BlockProvider> PROVIDERS = new ConcurrentHashMap<>();

/**
* The lookup handler.
*/
private static final BlocksLookupHandler BLOCKS_LOOKUP_HANDLER = new BlocksLookupHandler(Blocks::doParse);

/**
* Register a new custom block.
*
* @param key The key of the block.
* @param block The block.
*/
public static void registerCustomBlock(@NotNull final NamespacedKey key,
@NotNull final TestableBlock block) {
REGISTRY.put(key, block);
}

/**
* Register a new block provider.
*
* @param provider The provider.
*/
public static void registerBlockProvider(@NotNull final BlockProvider provider) {
PROVIDERS.put(provider.getNamespace(), provider);
}

/**
* Remove a block.
*
* @param key The key of the block.
*/
public static void removeCustomBlock(@NotNull final NamespacedKey key) {
REGISTRY.remove(key);
}

/**
* This is the backbone of the eco block system.
* <p>
* You can look up a TestableBlock for any material or custom block,
* and it will return it.
* <p>
* If you want to get a Block instance from this, then just call
* {@link TestableBlock#place(Location)}.
*
* @param key The lookup string.
* @return The testable block, or an empty testable block if not found.
*/
@NotNull
public static TestableBlock lookup(@NotNull final String key) {
return BLOCKS_LOOKUP_HANDLER.parseKey(key);
}

@NotNull
private static TestableBlock doParse(@NotNull final String[] args) {
if (args.length == 0) {
return new EmptyTestableBlock();
}

String[] split = args[0].toLowerCase().split(":");
if (split.length == 1) {
if (args[0].startsWith("*")) {
Material type = Material.getMaterial(args[0].substring(1));
return (type == null) ? new EmptyTestableBlock() : new UnrestrictedMaterialTestableBlock(type);
} else {
Material type = Material.getMaterial(args[0].toUpperCase());
return (type == null) ? new EmptyTestableBlock() : new MaterialTestableBlock(type);
}
}

NamespacedKey namespacedKey = NamespacedKeyUtils.create(split[0], split[1]);
TestableBlock block = REGISTRY.get(namespacedKey);

if (block != null) {
return block;
}

BlockProvider provider = PROVIDERS.get(split[0]);
if (provider == null) {
return new EmptyTestableBlock();
}

block = provider.provideForKey(split[1]);
if (block == null) {
return new EmptyTestableBlock();
}

registerCustomBlock(namespacedKey, block);
return block;
}

/**
* Get if block is a custom block.
*
* @param block The block to check.
* @return If is custom.
*/
public static boolean isCustomBlock(@NotNull final Block block) {
for (TestableBlock testable : REGISTRY.values()) {
if (testable.matches(block)) {
return true;
}
}
return false;
}

/**
* Get all registered custom blocks.
*
* @return A set of all blocks.
*/
public static Set<TestableBlock> getCustomBlocks() {
return new HashSet<>(REGISTRY.values());
}

private Blocks() {
throw new UnsupportedOperationException("This is a utility class and cannot be instantiated");
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
package com.willfp.eco.core.blocks;

import com.willfp.eco.core.blocks.impl.EmptyTestableBlock;
import com.willfp.eco.core.blocks.impl.GroupedTestableBlocks;
import com.willfp.eco.core.lookup.LookupHandler;
import org.jetbrains.annotations.NotNull;

import java.util.Collection;
import java.util.function.Function;

/**
* Handle block lookup strings.
*/
public class BlocksLookupHandler implements LookupHandler<TestableBlock> {
/**
* The parser.
*/
private final Function<String[], @NotNull TestableBlock> parser;

/**
* Create new lookup handler.
*
* @param parser The parser.
*/
public BlocksLookupHandler(@NotNull final Function<String[], @NotNull TestableBlock> parser) {
this.parser = parser;
}

@Override
public @NotNull TestableBlock parse(@NotNull final String[] args) {
return parser.apply(args);
}

@Override
public boolean validate(@NotNull final TestableBlock object) {
return !(object instanceof EmptyTestableBlock);
}

@Override
public @NotNull TestableBlock getFailsafe() {
return new EmptyTestableBlock();
}

@Override
public @NotNull TestableBlock join(@NotNull final Collection<TestableBlock> options) {
return new GroupedTestableBlocks(options);
}
}
84 changes: 84 additions & 0 deletions eco-api/src/main/java/com/willfp/eco/core/blocks/CustomBlock.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
package com.willfp.eco.core.blocks;

import org.apache.commons.lang.Validate;
import org.bukkit.Location;
import org.bukkit.NamespacedKey;
import org.bukkit.block.Block;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

import java.util.function.Function;
import java.util.function.Predicate;

/**
* A custom block has 3 components.
*
* <ul>
* <li>The key to identify it</li>
* <li>The test to check if any block is this custom block</li>
* <li>The supplier to spawn the custom {@link Block}</li>
* </ul>
*/
public class CustomBlock implements TestableBlock {
/**
* The key.
*/
private final NamespacedKey key;

/**
* The test for block to pass.
*/
private final Predicate<@NotNull Block> test;

/**
* The provider to spawn the block.
*/
private final Function<Location, Block> provider;

/**
* Create a new custom block.
*
* @param key The block key.
* @param test The test.
* @param provider The provider to spawn the block.
*/
public CustomBlock(@NotNull final NamespacedKey key,
@NotNull final Predicate<@NotNull Block> test,
@NotNull final Function<Location, Block> provider) {
this.key = key;
this.test = test;
this.provider = provider;
}

@Override
public boolean matches(@Nullable final Block other) {
if (other == null) {
return false;
}

return test.test(other);
}

@Override
public @NotNull Block place(@NotNull final Location location) {
Validate.notNull(location.getWorld());

return provider.apply(location);
}

/**
* Register the block.
*/
public void register() {
Blocks.registerCustomBlock(this.getKey(), this);
}

/**
* Get the key.
*
* @return The key.
*/
public NamespacedKey getKey() {
return this.key;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package com.willfp.eco.core.blocks;

import com.willfp.eco.core.lookup.Testable;
import org.bukkit.Location;
import org.bukkit.block.Block;
import org.bukkit.entity.Entity;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

/**
* A block with a test.
*/
public interface TestableBlock extends Testable<Block> {
/**
* If a Block matches the test.
*
* @param other The other block.
* @return If the block matches.
*/
@Override
boolean matches(@Nullable Block other);

/**
* Place the block.
*
* @param location The location.
* @return The block.
*/
@NotNull
Block place(@NotNull Location location);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
package com.willfp.eco.core.blocks.impl;

import com.willfp.eco.core.blocks.TestableBlock;
import org.bukkit.Location;
import org.bukkit.block.Block;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

/**
* Empty block.
*/
public class EmptyTestableBlock implements TestableBlock {
/**
* Create a new empty testable block.
*/
public EmptyTestableBlock() {

}

@Override
public boolean matches(@Nullable final Block other) {
return false;
}

@Override
public @NotNull Block place(@NotNull final Location location) {
return location.getBlock();
}
}
Loading

0 comments on commit 4a8935f

Please sign in to comment.