345 lines
16 KiB
Java
345 lines
16 KiB
Java
package com.hypixel.hytale.builtin.crafting;
|
|
|
|
import com.hypixel.hytale.assetstore.AssetRegistry;
|
|
import com.hypixel.hytale.assetstore.event.LoadedAssetsEvent;
|
|
import com.hypixel.hytale.assetstore.event.RemovedAssetsEvent;
|
|
import com.hypixel.hytale.assetstore.map.DefaultAssetMap;
|
|
import com.hypixel.hytale.builtin.crafting.commands.RecipeCommand;
|
|
import com.hypixel.hytale.builtin.crafting.component.CraftingManager;
|
|
import com.hypixel.hytale.builtin.crafting.interaction.LearnRecipeInteraction;
|
|
import com.hypixel.hytale.builtin.crafting.interaction.OpenBenchPageInteraction;
|
|
import com.hypixel.hytale.builtin.crafting.interaction.OpenProcessingBenchInteraction;
|
|
import com.hypixel.hytale.builtin.crafting.state.BenchState;
|
|
import com.hypixel.hytale.builtin.crafting.state.ProcessingBenchState;
|
|
import com.hypixel.hytale.builtin.crafting.system.PlayerCraftingSystems;
|
|
import com.hypixel.hytale.builtin.crafting.window.FieldCraftingWindow;
|
|
import com.hypixel.hytale.common.util.ArrayUtil;
|
|
import com.hypixel.hytale.component.AddReason;
|
|
import com.hypixel.hytale.component.Archetype;
|
|
import com.hypixel.hytale.component.CommandBuffer;
|
|
import com.hypixel.hytale.component.ComponentAccessor;
|
|
import com.hypixel.hytale.component.ComponentRegistryProxy;
|
|
import com.hypixel.hytale.component.ComponentType;
|
|
import com.hypixel.hytale.component.Ref;
|
|
import com.hypixel.hytale.component.RemoveReason;
|
|
import com.hypixel.hytale.component.Store;
|
|
import com.hypixel.hytale.component.query.Query;
|
|
import com.hypixel.hytale.component.system.RefSystem;
|
|
import com.hypixel.hytale.protocol.BenchRequirement;
|
|
import com.hypixel.hytale.protocol.BenchType;
|
|
import com.hypixel.hytale.protocol.ItemResourceType;
|
|
import com.hypixel.hytale.protocol.packets.interface_.UpdateKnownRecipes;
|
|
import com.hypixel.hytale.protocol.packets.window.WindowType;
|
|
import com.hypixel.hytale.server.core.asset.type.blocktype.config.bench.Bench;
|
|
import com.hypixel.hytale.server.core.asset.type.blocktype.config.bench.BenchUpgradeRequirement;
|
|
import com.hypixel.hytale.server.core.asset.type.item.config.CraftingRecipe;
|
|
import com.hypixel.hytale.server.core.asset.type.item.config.Item;
|
|
import com.hypixel.hytale.server.core.command.system.CommandManager;
|
|
import com.hypixel.hytale.server.core.entity.entities.Player;
|
|
import com.hypixel.hytale.server.core.entity.entities.player.data.PlayerConfigData;
|
|
import com.hypixel.hytale.server.core.entity.entities.player.windows.Window;
|
|
import com.hypixel.hytale.server.core.inventory.ItemStack;
|
|
import com.hypixel.hytale.server.core.inventory.MaterialQuantity;
|
|
import com.hypixel.hytale.server.core.modules.interaction.interaction.config.Interaction;
|
|
import com.hypixel.hytale.server.core.modules.interaction.interaction.config.RootInteraction;
|
|
import com.hypixel.hytale.server.core.plugin.JavaPlugin;
|
|
import com.hypixel.hytale.server.core.plugin.JavaPluginInit;
|
|
import com.hypixel.hytale.server.core.universe.PlayerRef;
|
|
import com.hypixel.hytale.server.core.universe.world.meta.BlockStateRegistry;
|
|
import com.hypixel.hytale.server.core.universe.world.storage.EntityStore;
|
|
import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap;
|
|
import it.unimi.dsi.fastutil.objects.ObjectArrayList;
|
|
import it.unimi.dsi.fastutil.objects.ObjectOpenHashSet;
|
|
import java.util.HashSet;
|
|
import java.util.List;
|
|
import java.util.Map;
|
|
import java.util.Set;
|
|
import javax.annotation.Nonnull;
|
|
import javax.annotation.Nullable;
|
|
|
|
public class CraftingPlugin extends JavaPlugin {
|
|
private static CraftingPlugin instance;
|
|
private static final Map<String, BenchRecipeRegistry> registries = new Object2ObjectOpenHashMap();
|
|
private static final Map<String, String[]> itemGeneratedRecipes = new Object2ObjectOpenHashMap();
|
|
private ComponentType<EntityStore, CraftingManager> craftingManagerComponentType;
|
|
|
|
public CraftingPlugin(@Nonnull JavaPluginInit init) {
|
|
super(init);
|
|
instance = this;
|
|
}
|
|
|
|
@Nullable
|
|
public static Set<String> getAvailableRecipesForCategory(String benchId, String benchCategoryId) {
|
|
BenchRecipeRegistry benchRecipeRegistry = registries.get(benchId);
|
|
return benchRecipeRegistry == null ? null : benchRecipeRegistry.getRecipesForCategory(benchCategoryId);
|
|
}
|
|
|
|
public static boolean isValidCraftingMaterialForBench(BenchState benchState, ItemStack itemStack) {
|
|
BenchRecipeRegistry benchRecipeRegistry = registries.get(benchState.getBench().getId());
|
|
return benchRecipeRegistry == null ? false : benchRecipeRegistry.isValidCraftingMaterial(itemStack);
|
|
}
|
|
|
|
public static boolean isValidUpgradeMaterialForBench(BenchState benchState, ItemStack itemStack) {
|
|
BenchUpgradeRequirement nextLevelUpgradeMaterials = benchState.getNextLevelUpgradeMaterials();
|
|
if (nextLevelUpgradeMaterials == null) {
|
|
return false;
|
|
} else {
|
|
for (MaterialQuantity upgradeMaterial : nextLevelUpgradeMaterials.getInput()) {
|
|
if (itemStack.getItemId().equals(upgradeMaterial.getItemId())) {
|
|
return true;
|
|
}
|
|
|
|
ItemResourceType[] resourceTypeId = itemStack.getItem().getResourceTypes();
|
|
if (resourceTypeId != null) {
|
|
for (ItemResourceType resTypeId : resourceTypeId) {
|
|
if (resTypeId.id.equals(upgradeMaterial.getResourceTypeId())) {
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
}
|
|
|
|
@Override
|
|
protected void setup() {
|
|
AssetRegistry.getAssetStore(Interaction.class)
|
|
.loadAssets(
|
|
"Hytale:Hytale",
|
|
List.of(OpenBenchPageInteraction.SIMPLE_CRAFTING, OpenBenchPageInteraction.DIAGRAM_CRAFTING, OpenBenchPageInteraction.STRUCTURAL_CRAFTING)
|
|
);
|
|
AssetRegistry.getAssetStore(RootInteraction.class)
|
|
.loadAssets(
|
|
"Hytale:Hytale",
|
|
List.of(
|
|
OpenBenchPageInteraction.SIMPLE_CRAFTING_ROOT, OpenBenchPageInteraction.DIAGRAM_CRAFTING_ROOT, OpenBenchPageInteraction.STRUCTURAL_CRAFTING_ROOT
|
|
)
|
|
);
|
|
ComponentRegistryProxy<EntityStore> entityStoreRegistry = this.getEntityStoreRegistry();
|
|
this.craftingManagerComponentType = entityStoreRegistry.registerComponent(CraftingManager.class, CraftingManager::new);
|
|
entityStoreRegistry.registerSystem(new PlayerCraftingSystems.PlayerCraftingSystem(this.craftingManagerComponentType));
|
|
entityStoreRegistry.registerSystem(new PlayerCraftingSystems.CraftingManagerAddSystem(this.craftingManagerComponentType));
|
|
this.getCodecRegistry(Interaction.CODEC)
|
|
.register("OpenBenchPage", OpenBenchPageInteraction.class, OpenBenchPageInteraction.CODEC)
|
|
.register("OpenProcessingBench", OpenProcessingBenchInteraction.class, OpenProcessingBenchInteraction.CODEC);
|
|
Bench.registerRootInteraction(BenchType.Crafting, OpenBenchPageInteraction.SIMPLE_CRAFTING_ROOT);
|
|
Bench.registerRootInteraction(BenchType.DiagramCrafting, OpenBenchPageInteraction.DIAGRAM_CRAFTING_ROOT);
|
|
Bench.registerRootInteraction(BenchType.StructuralCrafting, OpenBenchPageInteraction.STRUCTURAL_CRAFTING_ROOT);
|
|
BlockStateRegistry blockStateRegistry = this.getBlockStateRegistry();
|
|
blockStateRegistry.registerBlockState(ProcessingBenchState.class, "processingBench", ProcessingBenchState.CODEC);
|
|
blockStateRegistry.registerBlockState(BenchState.class, "crafting", BenchState.CODEC);
|
|
Window.CLIENT_REQUESTABLE_WINDOW_TYPES.put(WindowType.PocketCrafting, FieldCraftingWindow::new);
|
|
this.getEventRegistry().register(LoadedAssetsEvent.class, CraftingRecipe.class, CraftingPlugin::onRecipeLoad);
|
|
this.getEventRegistry().register(RemovedAssetsEvent.class, CraftingRecipe.class, CraftingPlugin::onRecipeRemove);
|
|
this.getEventRegistry().register(LoadedAssetsEvent.class, Item.class, CraftingPlugin::onItemAssetLoad);
|
|
this.getEventRegistry().register(RemovedAssetsEvent.class, Item.class, CraftingPlugin::onItemAssetRemove);
|
|
Interaction.CODEC.register("LearnRecipe", LearnRecipeInteraction.class, LearnRecipeInteraction.CODEC);
|
|
CommandManager.get().registerSystemCommand(new RecipeCommand());
|
|
entityStoreRegistry.registerSystem(new CraftingPlugin.PlayerAddedSystem());
|
|
}
|
|
|
|
private static void onItemAssetLoad(LoadedAssetsEvent<String, Item, DefaultAssetMap<String, Item>> event) {
|
|
List<CraftingRecipe> recipesToLoad = new ObjectArrayList();
|
|
|
|
for (Item item : event.getLoadedAssets().values()) {
|
|
if (item.hasRecipesToGenerate()) {
|
|
List<CraftingRecipe> generatedRecipes = new ObjectArrayList();
|
|
item.collectRecipesToGenerate(generatedRecipes);
|
|
List<String> generatedIds = new ObjectArrayList();
|
|
|
|
for (CraftingRecipe generatedRecipe : generatedRecipes) {
|
|
String id = generatedRecipe.getId();
|
|
generatedIds.add(id);
|
|
}
|
|
|
|
itemGeneratedRecipes.put(item.getId(), generatedIds.toArray(String[]::new));
|
|
recipesToLoad.addAll(generatedRecipes);
|
|
}
|
|
}
|
|
|
|
if (!recipesToLoad.isEmpty()) {
|
|
CraftingRecipe.getAssetStore().loadAssets("Hytale:Hytale", recipesToLoad);
|
|
}
|
|
}
|
|
|
|
private static void onItemAssetRemove(@Nonnull RemovedAssetsEvent<String, Item, DefaultAssetMap<String, Item>> event) {
|
|
for (String id : event.getRemovedAssets()) {
|
|
String[] generatedRecipes = itemGeneratedRecipes.get(id);
|
|
if (generatedRecipes != null) {
|
|
CraftingRecipe.getAssetStore().removeAssets(List.of(generatedRecipes));
|
|
}
|
|
}
|
|
}
|
|
|
|
private static void onRecipeLoad(LoadedAssetsEvent<String, CraftingRecipe, DefaultAssetMap<String, CraftingRecipe>> event) {
|
|
for (CraftingRecipe recipe : event.getLoadedAssets().values()) {
|
|
for (BenchRecipeRegistry registry : registries.values()) {
|
|
registry.removeRecipe(recipe.getId());
|
|
}
|
|
|
|
if (recipe.getBenchRequirement() != null) {
|
|
for (BenchRequirement benchRequirement : recipe.getBenchRequirement()) {
|
|
BenchRecipeRegistry benchRecipeRegistry = registries.computeIfAbsent(benchRequirement.id, BenchRecipeRegistry::new);
|
|
benchRecipeRegistry.addRecipe(benchRequirement, recipe);
|
|
}
|
|
}
|
|
}
|
|
|
|
computeBenchRecipeRegistries();
|
|
}
|
|
|
|
private static void onRecipeRemove(RemovedAssetsEvent<String, CraftingRecipe, DefaultAssetMap<String, CraftingRecipe>> event) {
|
|
for (String removedRecipeId : event.getRemovedAssets()) {
|
|
for (BenchRecipeRegistry registry : registries.values()) {
|
|
registry.removeRecipe(removedRecipeId);
|
|
}
|
|
}
|
|
|
|
computeBenchRecipeRegistries();
|
|
}
|
|
|
|
private static void computeBenchRecipeRegistries() {
|
|
for (BenchRecipeRegistry registry : registries.values()) {
|
|
registry.recompute();
|
|
}
|
|
}
|
|
|
|
@Nonnull
|
|
public static List<CraftingRecipe> getBenchRecipes(@Nonnull Bench bench) {
|
|
return getBenchRecipes(bench.getType(), bench.getId());
|
|
}
|
|
|
|
@Nonnull
|
|
public static List<CraftingRecipe> getBenchRecipes(BenchType benchType, String name) {
|
|
return getBenchRecipes(benchType, name, null);
|
|
}
|
|
|
|
@Nonnull
|
|
public static List<CraftingRecipe> getBenchRecipes(BenchType benchType, String benchId, @Nullable String category) {
|
|
BenchRecipeRegistry registry = registries.get(benchId);
|
|
if (registry == null) {
|
|
return List.of();
|
|
} else {
|
|
List<CraftingRecipe> list = new ObjectArrayList();
|
|
|
|
for (CraftingRecipe recipe : registry.getAllRecipes()) {
|
|
BenchRequirement[] benchRequirement = recipe.getBenchRequirement();
|
|
if (benchRequirement != null) {
|
|
for (BenchRequirement requirement : benchRequirement) {
|
|
if (requirement.type == benchType && requirement.id.equals(benchId) && (category == null || hasCategory(recipe, category))) {
|
|
list.add(recipe);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return list;
|
|
}
|
|
}
|
|
|
|
private static boolean hasCategory(@Nonnull CraftingRecipe recipe, String category) {
|
|
for (BenchRequirement benchRequirement : recipe.getBenchRequirement()) {
|
|
if (benchRequirement.categories != null && ArrayUtil.contains(benchRequirement.categories, category)) {
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
public static boolean learnRecipe(@Nonnull Ref<EntityStore> ref, @Nonnull String recipeId, @Nonnull ComponentAccessor<EntityStore> componentAccessor) {
|
|
Player playerComponent = componentAccessor.getComponent(ref, Player.getComponentType());
|
|
|
|
assert playerComponent != null;
|
|
|
|
PlayerConfigData playerConfigData = playerComponent.getPlayerConfigData();
|
|
Set<String> knownRecipes = new HashSet<>(playerConfigData.getKnownRecipes());
|
|
if (knownRecipes.add(recipeId)) {
|
|
playerConfigData.setKnownRecipes(knownRecipes);
|
|
sendKnownRecipes(ref, componentAccessor);
|
|
return true;
|
|
} else {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
public static boolean forgetRecipe(@Nonnull Ref<EntityStore> ref, @Nonnull String itemId, @Nonnull ComponentAccessor<EntityStore> componentAccessor) {
|
|
Player playerComponent = componentAccessor.getComponent(ref, Player.getComponentType());
|
|
|
|
assert playerComponent != null;
|
|
|
|
PlayerConfigData playerConfigData = playerComponent.getPlayerConfigData();
|
|
Set<String> knownRecipes = new ObjectOpenHashSet(playerConfigData.getKnownRecipes());
|
|
if (knownRecipes.remove(itemId)) {
|
|
playerConfigData.setKnownRecipes(knownRecipes);
|
|
sendKnownRecipes(ref, componentAccessor);
|
|
return true;
|
|
} else {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
public static void sendKnownRecipes(@Nonnull Ref<EntityStore> ref, @Nonnull ComponentAccessor<EntityStore> componentAccessor) {
|
|
PlayerRef playerRefComponent = componentAccessor.getComponent(ref, PlayerRef.getComponentType());
|
|
|
|
assert playerRefComponent != null;
|
|
|
|
Player playerComponent = componentAccessor.getComponent(ref, Player.getComponentType());
|
|
|
|
assert playerComponent != null;
|
|
|
|
PlayerConfigData playerConfigData = playerComponent.getPlayerConfigData();
|
|
DefaultAssetMap<String, Item> itemAssetMap = Item.getAssetMap();
|
|
Map<String, com.hypixel.hytale.protocol.CraftingRecipe> knownRecipes = new Object2ObjectOpenHashMap();
|
|
|
|
for (String id : playerConfigData.getKnownRecipes()) {
|
|
Item item = itemAssetMap.getAsset(id);
|
|
if (item != null) {
|
|
for (BenchRecipeRegistry registry : registries.values()) {
|
|
for (String recipeId : registry.getIncomingRecipesForItem(item.getId())) {
|
|
CraftingRecipe recipe = CraftingRecipe.getAssetMap().getAsset(recipeId);
|
|
if (recipe != null) {
|
|
knownRecipes.put(id, recipe.toPacket(id));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
playerRefComponent.getPacketHandler().writeNoCache(new UpdateKnownRecipes(knownRecipes));
|
|
}
|
|
|
|
public ComponentType<EntityStore, CraftingManager> getCraftingManagerComponentType() {
|
|
return this.craftingManagerComponentType;
|
|
}
|
|
|
|
public static CraftingPlugin get() {
|
|
return instance;
|
|
}
|
|
|
|
public static class PlayerAddedSystem extends RefSystem<EntityStore> {
|
|
private static final Query<EntityStore> QUERY = Archetype.of(Player.getComponentType(), PlayerRef.getComponentType());
|
|
|
|
@Override
|
|
public Query<EntityStore> getQuery() {
|
|
return QUERY;
|
|
}
|
|
|
|
@Override
|
|
public void onEntityAdded(
|
|
@Nonnull Ref<EntityStore> ref, @Nonnull AddReason reason, @Nonnull Store<EntityStore> store, @Nonnull CommandBuffer<EntityStore> commandBuffer
|
|
) {
|
|
CraftingPlugin.sendKnownRecipes(ref, commandBuffer);
|
|
}
|
|
|
|
@Override
|
|
public void onEntityRemove(
|
|
@Nonnull Ref<EntityStore> ref, @Nonnull RemoveReason reason, @Nonnull Store<EntityStore> store, @Nonnull CommandBuffer<EntityStore> commandBuffer
|
|
) {
|
|
}
|
|
}
|
|
}
|