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<String, BenchRecipeRegistry>();
private static final Map<String, String[]> itemGeneratedRecipes = new Object2ObjectOpenHashMap<String, String[]>();
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 = (BenchRecipeRegistry)registries.get(benchId);
return benchRecipeRegistry == null ? null : benchRecipeRegistry.getRecipesForCategory(benchCategoryId);
}
public static boolean isValidCraftingMaterialForBench(BenchState benchState, ItemStack itemStack) {
BenchRecipeRegistry 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;
}
}
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((Class)LoadedAssetsEvent.class, CraftingRecipe.class, CraftingPlugin::onRecipeLoad);
this.getEventRegistry().register((Class)RemovedAssetsEvent.class, CraftingRecipe.class, CraftingPlugin::onRecipeRemove);
this.getEventRegistry().register((Class)LoadedAssetsEvent.class, Item.class, CraftingPlugin::onItemAssetLoad);
this.getEventRegistry().register((Class)RemovedAssetsEvent.class, Item.class, CraftingPlugin::onItemAssetRemove);
Interaction.CODEC.register("LearnRecipe", LearnRecipeInteraction.class, LearnRecipeInteraction.CODEC);
CommandManager.get().registerSystemCommand(new RecipeCommand());
entityStoreRegistry.registerSystem(new PlayerAddedSystem());
}
private static void onItemAssetLoad(LoadedAssetsEvent<String, Item, DefaultAssetMap<String, Item>> event) {
List<CraftingRecipe> recipesToLoad = new ObjectArrayList<CraftingRecipe>();
for(Item item : event.getLoadedAssets().values()) {
if (item.hasRecipesToGenerate()) {
List<CraftingRecipe> generatedRecipes = new ObjectArrayList<CraftingRecipe>();
item.collectRecipesToGenerate(generatedRecipes);
List<String> generatedIds = new ObjectArrayList<String>();
for(CraftingRecipe generatedRecipe : generatedRecipes) {
String id = generatedRecipe.getId();
generatedIds.add(id);
}
itemGeneratedRecipes.put(item.getId(), (String[])generatedIds.toArray((x$0) -> new String[x$0]));
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 = (String[])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 = (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, (String)null);
}
@Nonnull
public static List<CraftingRecipe> getBenchRecipes(BenchType benchType, String benchId, @Nullable String category) {
BenchRecipeRegistry registry = (BenchRecipeRegistry)registries.get(benchId);
if (registry == null) {
return List.of();
} else {
List<CraftingRecipe> list = new ObjectArrayList<CraftingRecipe>();
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 = (Player)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 = (Player)componentAccessor.getComponent(ref, Player.getComponentType());
assert playerComponent != null;
PlayerConfigData playerConfigData = playerComponent.getPlayerConfigData();
Set<String> knownRecipes = new ObjectOpenHashSet<String>(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 = (PlayerRef)componentAccessor.getComponent(ref, PlayerRef.getComponentType());
assert playerRefComponent != null;
Player playerComponent = (Player)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<String, com.hypixel.hytale.protocol.CraftingRecipe>();
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)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());
public PlayerAddedSystem() {
}
public Query<EntityStore> getQuery() {
return QUERY;
}
public void onEntityAdded(@Nonnull Ref<EntityStore> ref, @Nonnull AddReason reason, @Nonnull Store<EntityStore> store, @Nonnull CommandBuffer<EntityStore> commandBuffer) {
CraftingPlugin.sendKnownRecipes(ref, commandBuffer);
}
public void onEntityRemove(@Nonnull Ref<EntityStore> ref, @Nonnull RemoveReason reason, @Nonnull Store<EntityStore> store, @Nonnull CommandBuffer<EntityStore> commandBuffer) {
}
}
}