Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions xplat/src/main/java/dev/emi/emi/api/EmiApi.java
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@
import java.util.stream.Collectors;
import java.util.stream.Stream;

import dev.emi.emi.api.search.EmiSearchManagerApi;
import dev.emi.emi.search.EmiSearchManager;
import org.jetbrains.annotations.Nullable;

import com.google.common.collect.Lists;
Expand Down Expand Up @@ -65,6 +67,10 @@ public static void setSearchText(String text) {
EmiScreenManager.search.setText(text);
}

public static EmiSearchManagerApi createSearchManager() {
return new EmiSearchManager();
}

public static boolean isSearchFocused() {
return EmiScreenManager.search.isFocused();
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package dev.emi.emi.api.search;

import dev.emi.emi.api.stack.EmiIngredient;

import java.util.List;
import java.util.concurrent.CompletableFuture;

/**
* A search manager controls searching for items using EMI infrastructure.
*/
public interface EmiSearchManagerApi {
/**
* {@return the current list of stacks matching the last search query}
*/
List<? extends EmiIngredient> getStacks();

/**
* Search for ingredients matching the given query string. The list returned by {@link EmiSearchManagerApi#getStacks()}
* will not update until after the returned future completes.
* @param query the query string to use when searching
* @return a future that completes with the updated list
*/
CompletableFuture<List<? extends EmiIngredient>> search(String query);
}
2 changes: 1 addition & 1 deletion xplat/src/main/java/dev/emi/emi/mixin/ItemStackMixin.java
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ public class ItemStackMixin {

@Inject(at = @At("RETURN"), method = "getTooltip")
private void getTooltip(PlayerEntity player, TooltipContext context, CallbackInfoReturnable<List<Text>> info) {
if (EmiConfig.appendItemModId && EmiConfig.appendModId && Thread.currentThread() != EmiSearch.searchThread) {
if (EmiConfig.appendItemModId && EmiConfig.appendModId && !EmiSearch.isSearchThread()) {
List<Text> text = info.getReturnValue();
String namespace = EmiPort.getItemRegistry().getId(((ItemStack) (Object) this).getItem()).getNamespace();
String mod = EmiUtil.getModName(namespace);
Expand Down
2 changes: 1 addition & 1 deletion xplat/src/main/java/dev/emi/emi/screen/ConfigScreen.java
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@ public void setActiveBind(EmiBind bind, int offset) {
@Override
public void close() {
EmiConfig.writeConfig();
EmiSearch.update();
EmiScreenManager.updateSearch();
MinecraftClient.getInstance().setScreen(last);
}

Expand Down
17 changes: 12 additions & 5 deletions xplat/src/main/java/dev/emi/emi/screen/EmiScreenManager.java
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
import java.util.stream.Collectors;
import java.util.stream.Stream;

import dev.emi.emi.search.EmiSearchManager;
import org.jetbrains.annotations.Nullable;
import org.lwjgl.glfw.GLFW;

Expand Down Expand Up @@ -125,17 +126,23 @@ public class EmiScreenManager {
() -> true, (w) -> EmiApi.viewRecipeTree(),
List.of(EmiPort.translatable("tooltip.emi.recipe_tree")));

public static EmiSearchManager searchManager = new EmiSearchManager();

public static boolean isDisabled() {
return !EmiReloadManager.isLoaded() || !EmiConfig.enabled;
}

public static void updateSearch() {
searchManager.search(search.getText());
}

public static void recalculate() {
updateCraftables();
SidebarPanel searchPanel = getSearchPanel();
if (searchPanel != null && searchPanel.space != null) {
if (searchedStacks != EmiSearch.stacks) {
if (searchedStacks != searchManager.getStacks()) {
searchPanel.space.batcher.repopulate();
searchedStacks = EmiSearch.stacks;
searchedStacks = searchManager.getStacks();
}
}

Expand Down Expand Up @@ -238,7 +245,7 @@ private static void updateCraftables() {
if (searchPanel != null && searchPanel.space != null) {
searchPanel.space.batcher.repopulate();
if (searchPanel.getType() == SidebarType.CRAFTABLES) {
EmiSearch.update();
EmiScreenManager.updateSearch();
}
}
EmiFavorites.updateSynthetic(inv);
Expand Down Expand Up @@ -808,7 +815,7 @@ private static void renderExclusionAreas(EmiDrawContext context, int mouseX, int
private static void renderSlotOverlays(EmiDrawContext context, int mouseX, int mouseY, float delta, EmiScreenBase base) {
CompiledQuery query = null;
if (EmiScreenManager.search.highlight) {
query = EmiSearch.compiledQuery;
query = searchManager.getCompiledQuery();
}
Set<Slot> ignoredSlots = Sets.newHashSet();
Set<EmiStack> synfavs = Sets.newHashSet();
Expand Down Expand Up @@ -1380,7 +1387,7 @@ public void setSidebarPage(int page) {
}
}
if (isSearch()) {
EmiSearch.search(search.getText());
searchManager.search(search.getText());
}
if (space != null) {
space.batcher.repopulate();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -131,7 +131,7 @@ public EmiSearchWidget(TextRenderer textRenderer, int x, int y, int width, int h
styles.add(new Pair<Integer, Style>(string.length(), Style.EMPTY.withFormatting(Formatting.WHITE)));
}
this.styles = styles;
EmiSearch.search(string);
EmiScreenManager.searchManager.search(string);
});
}

Expand Down
86 changes: 13 additions & 73 deletions xplat/src/main/java/dev/emi/emi/search/EmiSearch.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

import java.util.List;
import java.util.Set;
import java.util.concurrent.Executor;
import java.util.concurrent.Executors;
import java.util.function.Function;
import java.util.function.Supplier;
import java.util.regex.Matcher;
Expand Down Expand Up @@ -32,14 +34,21 @@

public class EmiSearch {
public static final Pattern TOKENS = Pattern.compile("(-?[@#]?\\/(\\\\.|[^\\\\\\/])+\\/|[^\\s]+)");
private static volatile SearchWorker currentWorker = null;
public static volatile Thread searchThread = null;
public static volatile List<? extends EmiIngredient> stacks = EmiStackList.stacks;
public static volatile CompiledQuery compiledQuery;
private static Thread searchThread;
public static final Executor executor = Executors.newSingleThreadExecutor(task -> {
Thread t = new Thread(task, "EMI Search Thread");
t.setDaemon(true);
searchThread = t;
return t;
});
public static Set<EmiStack> bakedStacks;
public static SuffixArray<SearchStack> names, tooltips, mods;
public static SuffixArray<EmiStack> aliases;

public static boolean isSearchThread() {
return searchThread == Thread.currentThread();
}

public static void bake() {
SuffixArray<SearchStack> names = new SuffixArray<>();
SuffixArray<SearchStack> tooltips = new SuffixArray<>();
Expand Down Expand Up @@ -110,31 +119,6 @@ public static void bake() {
EmiSearch.bakedStacks = bakedStacks;
}

public static void update() {
search(EmiScreenManager.search.getText());
}

public static void search(String query) {
synchronized (EmiSearch.class) {
SearchWorker worker = new SearchWorker(query, EmiScreenManager.getSearchSource());
currentWorker = worker;

searchThread = new Thread(worker);
searchThread.setDaemon(true);
searchThread.start();
}
}

public static void apply(SearchWorker worker, List<? extends EmiIngredient> stacks) {
synchronized (EmiSearch.class) {
if (worker == currentWorker) {
EmiSearch.stacks = stacks;
currentWorker = null;
searchThread = null;
}
}
}

public static class CompiledQuery {
public final Query fullQuery;

Expand Down Expand Up @@ -223,48 +207,4 @@ private static void addQuery(String s, boolean negated, List<Query> queries, Fun
queries.add(q);
}
}

private static class SearchWorker implements Runnable {
private final String query;
private final List<? extends EmiIngredient> source;

public SearchWorker(String query, List<? extends EmiIngredient> source) {
this.query = query;
this.source = source;
}

@Override
public void run() {
try {
CompiledQuery compiled = new CompiledQuery(query);
compiledQuery = compiled;
if (compiled.isEmpty()) {
apply(this, source);
return;
}
List<EmiIngredient> stacks = Lists.newArrayList();
int processed = 0;
for (EmiIngredient stack : source) {
if (processed++ >= 1024) {
processed = 0;
if (this != currentWorker) {
return;
}
}
List<EmiStack> ess = stack.getEmiStacks();
// TODO properly support ingredients?
if (ess.size() == 1) {
EmiStack es = ess.get(0);
if (compiled.test(es)) {
stacks.add(stack);
}
}
}
apply(this, List.copyOf(stacks));
} catch (Exception e) {
EmiLog.error("Error when attempting to search:");
e.printStackTrace();
}
}
}
}
98 changes: 98 additions & 0 deletions xplat/src/main/java/dev/emi/emi/search/EmiSearchManager.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
package dev.emi.emi.search;

import com.google.common.collect.Lists;
import dev.emi.emi.api.search.EmiSearchManagerApi;
import dev.emi.emi.api.stack.EmiIngredient;
import dev.emi.emi.api.stack.EmiStack;
import dev.emi.emi.registry.EmiStackList;
import dev.emi.emi.runtime.EmiLog;
import dev.emi.emi.screen.EmiScreenManager;

import java.util.List;
import java.util.concurrent.CompletableFuture;

public class EmiSearchManager implements EmiSearchManagerApi {
private EmiSearch.CompiledQuery compiledQuery;
private List<? extends EmiIngredient> stacks = EmiStackList.stacks;
private volatile SearchWorker currentWorker;

public CompletableFuture<List<? extends EmiIngredient>> search(String query) {
synchronized (this) {
SearchWorker worker = new SearchWorker(query, EmiScreenManager.getSearchSource());
currentWorker = worker;

EmiSearch.executor.execute(worker);
return worker.getCompletionFuture();
}
}

public List<? extends EmiIngredient> getStacks() {
return this.stacks;
}

public EmiSearch.CompiledQuery getCompiledQuery() {
return this.compiledQuery;
}

class SearchWorker implements Runnable {
private final String query;
private final List<? extends EmiIngredient> source;
private final CompletableFuture<List<? extends EmiIngredient>> completion;

SearchWorker(String query, List<? extends EmiIngredient> source) {
this.query = query;
this.source = source;
this.completion = new CompletableFuture<>();
}

public CompletableFuture<List<? extends EmiIngredient>> getCompletionFuture() {
return completion;
}

private void apply(List<? extends EmiIngredient> stacks) {
synchronized (EmiSearchManager.this) {
if(this == currentWorker) {
EmiSearchManager.this.stacks = stacks;
EmiSearchManager.this.currentWorker = null;
}
}
completion.complete(stacks);
}

@Override
public void run() {
try {
EmiSearch.CompiledQuery compiled = new EmiSearch.CompiledQuery(query);
compiledQuery = compiled;
if (compiled.isEmpty()) {
apply(source);
return;
}
List<EmiIngredient> stacks = Lists.newArrayList();
int processed = 0;
for (EmiIngredient stack : source) {
if (processed++ >= 1024) {
processed = 0;
if (this != currentWorker) {
apply(source);
return;
}
}
List<EmiStack> ess = stack.getEmiStacks();
// TODO properly support ingredients?
if (ess.size() == 1) {
EmiStack es = ess.get(0);
if (compiled.test(es)) {
stacks.add(stack);
}
}
}
apply(List.copyOf(stacks));
} catch (Exception e) {
EmiLog.error("Error when attempting to search:");
e.printStackTrace();
apply(source);
}
}
}
}