From 67171a5ff24ed77e6c4cc889543e8dfb543e8fe5 Mon Sep 17 00:00:00 2001 From: shedaniel Date: Sun, 20 Dec 2020 19:59:49 +0800 Subject: wip more Signed-off-by: shedaniel --- runtime/build.gradle | 14 + .../me/shedaniel/rei/REIModMenuEntryPoint.java | 45 + .../me/shedaniel/rei/RoughlyEnoughItemsCore.java | 608 ++++++++++++ .../shedaniel/rei/RoughlyEnoughItemsNetwork.java | 143 +++ .../me/shedaniel/rei/RoughlyEnoughItemsState.java | 136 +++ .../me/shedaniel/rei/compat/LBASupportPlugin.java | 59 ++ .../shedaniel/rei/gui/ConfigReloadingScreen.java | 71 ++ .../shedaniel/rei/gui/ContainerScreenOverlay.java | 840 ++++++++++++++++ .../java/me/shedaniel/rei/gui/DelegateScreen.java | 284 ++++++ .../me/shedaniel/rei/gui/OverlaySearchField.java | 184 ++++ .../shedaniel/rei/gui/PreRecipeViewingScreen.java | 215 +++++ .../shedaniel/rei/gui/RecipeDisplayExporter.java | 131 +++ .../java/me/shedaniel/rei/gui/RecipeScreen.java | 43 + .../me/shedaniel/rei/gui/RecipeViewingScreen.java | 585 +++++++++++ .../me/shedaniel/rei/gui/TransformingScreen.java | 150 +++ .../rei/gui/VillagerRecipeViewingScreen.java | 452 +++++++++ .../shedaniel/rei/gui/WarningAndErrorScreen.java | 292 ++++++ .../gui/config/entry/FilteringAddRuleScreen.java | 195 ++++ .../rei/gui/config/entry/FilteringEntry.java | 110 +++ .../config/entry/FilteringRuleOptionsScreen.java | 240 +++++ .../rei/gui/config/entry/FilteringRulesScreen.java | 247 +++++ .../rei/gui/config/entry/FilteringScreen.java | 521 ++++++++++ .../rei/gui/config/entry/NoFilteringEntry.java | 90 ++ .../gui/config/entry/RecipeScreenTypeEntry.java | 112 +++ .../rei/gui/config/entry/ReloadPluginsEntry.java | 95 ++ .../rei/gui/credits/CreditsEntryListWidget.java | 212 ++++ .../shedaniel/rei/gui/credits/CreditsScreen.java | 153 +++ .../java/me/shedaniel/rei/gui/modules/Menu.java | 282 ++++++ .../me/shedaniel/rei/gui/modules/MenuEntry.java | 45 + .../entries/EntryStackSubsetsMenuEntry.java | 140 +++ .../rei/gui/modules/entries/GameModeMenuEntry.java | 108 +++ .../gui/modules/entries/SubSubsetsMenuEntry.java | 233 +++++ .../rei/gui/modules/entries/WeatherMenuEntry.java | 109 +++ .../rei/gui/plugin/DefaultRuntimePlugin.java | 268 ++++++ .../rei/gui/plugin/entry/FluidEntryDefinition.java | 195 ++++ .../rei/gui/plugin/entry/ItemEntryDefinition.java | 296 ++++++ .../rei/gui/toast/CopyRecipeIdentifierToast.java | 76 ++ .../rei/gui/toast/ExportRecipeIdentifierToast.java | 76 ++ .../shedaniel/rei/gui/widget/DraggableWidget.java | 104 ++ .../rei/gui/widget/EntryListEntryWidget.java | 116 +++ .../shedaniel/rei/gui/widget/EntryListWidget.java | 587 ++++++++++++ .../me/shedaniel/rei/gui/widget/EntryWidget.java | 425 ++++++++ .../rei/gui/widget/FavoritesListWidget.java | 1013 ++++++++++++++++++++ .../shedaniel/rei/gui/widget/LateRenderable.java | 30 + .../me/shedaniel/rei/gui/widget/QueuedTooltip.java | 109 +++ .../rei/gui/widget/RecipeChoosePageWidget.java | 209 ++++ .../me/shedaniel/rei/gui/widget/TabWidget.java | 131 +++ .../me/shedaniel/rei/impl/AbstractEntryStack.java | 121 +++ .../rei/impl/AmountIgnoredEntryStackWrapper.java | 62 ++ .../main/java/me/shedaniel/rei/impl/Animator.java | 97 ++ .../shedaniel/rei/impl/BaseBoundsHandlerImpl.java | 116 +++ .../me/shedaniel/rei/impl/ClientHelperImpl.java | 431 +++++++++ .../me/shedaniel/rei/impl/ConfigManagerImpl.java | 331 +++++++ .../me/shedaniel/rei/impl/ConfigObjectImpl.java | 488 ++++++++++ .../me/shedaniel/rei/impl/DisplayHelperImpl.java | 121 +++ .../me/shedaniel/rei/impl/EntryRegistryImpl.java | 201 ++++ .../me/shedaniel/rei/impl/EntryTypeDeferred.java | 66 ++ .../shedaniel/rei/impl/EntryTypeRegistryImpl.java | 85 ++ .../rei/impl/FavoriteEntryTypeRegistryImpl.java | 107 +++ .../rei/impl/FluidSupportProviderImpl.java | 72 ++ .../me/shedaniel/rei/impl/InternalWidgets.java | 348 +++++++ .../java/me/shedaniel/rei/impl/IssuesDetector.java | 95 ++ .../me/shedaniel/rei/impl/RecipeHelperImpl.java | 648 +++++++++++++ .../java/me/shedaniel/rei/impl/ScreenHelper.java | 265 +++++ .../java/me/shedaniel/rei/impl/SearchArgument.java | 161 ++++ .../me/shedaniel/rei/impl/SimpleFluidRenderer.java | 85 ++ .../me/shedaniel/rei/impl/TextTransformations.java | 44 + .../me/shedaniel/rei/impl/TypedEntryStack.java | 120 +++ .../main/java/me/shedaniel/rei/impl/Weather.java | 61 ++ .../rei/impl/entry/EmptyEntryDefinition.java | 135 +++ .../rei/impl/filtering/AbstractFilteringRule.java | 40 + .../rei/impl/filtering/FilteringContext.java | 47 + .../rei/impl/filtering/FilteringContextImpl.java | 104 ++ .../rei/impl/filtering/FilteringContextType.java | 34 + .../rei/impl/filtering/FilteringResult.java | 93 ++ .../rei/impl/filtering/FilteringResultImpl.java | 55 ++ .../rei/impl/filtering/FilteringRule.java | 91 ++ .../impl/filtering/rules/ManualFilteringRule.java | 82 ++ .../impl/filtering/rules/SearchFilteringRule.java | 170 ++++ .../rei/impl/search/AlwaysMatchingArgument.java | 53 + .../me/shedaniel/rei/impl/search/Argument.java | 67 ++ .../rei/impl/search/ArgumentsRegistry.java | 51 + .../me/shedaniel/rei/impl/search/MatchStatus.java | 86 ++ .../me/shedaniel/rei/impl/search/MatchType.java | 41 + .../me/shedaniel/rei/impl/search/ModArgument.java | 69 ++ .../shedaniel/rei/impl/search/RegexArgument.java | 82 ++ .../me/shedaniel/rei/impl/search/TagArgument.java | 87 ++ .../me/shedaniel/rei/impl/search/TextArgument.java | 59 ++ .../shedaniel/rei/impl/search/TooltipArgument.java | 61 ++ .../rei/impl/subsets/SubsetsRegistryImpl.java | 109 +++ .../me/shedaniel/rei/impl/widgets/ArrowWidget.java | 80 ++ .../rei/impl/widgets/BurningFireWidget.java | 80 ++ .../shedaniel/rei/impl/widgets/ButtonWidget.java | 312 ++++++ .../shedaniel/rei/impl/widgets/DrawableWidget.java | 52 + .../widgets/FillRectangleDrawableConsumer.java | 67 ++ .../me/shedaniel/rei/impl/widgets/LabelWidget.java | 289 ++++++ .../me/shedaniel/rei/impl/widgets/PanelWidget.java | 154 +++ .../rei/impl/widgets/TexturedDrawableConsumer.java | 77 ++ .../shedaniel/rei/impl/widgets/package-info.java | 27 + .../autocrafting/DefaultCategoryHandler.java | 152 +++ .../me/shedaniel/rei/server/InputSlotCrafter.java | 205 ++++ .../shedaniel/rei/tests/plugin/REITestPlugin.java | 80 ++ .../roughlyenoughitems/textures/gui/button.png | Bin 0 -> 7903 bytes .../textures/gui/button_dark.png | Bin 0 -> 5111 bytes .../roughlyenoughitems/textures/gui/display.png | Bin 0 -> 2124 bytes .../textures/gui/display_dark.png | Bin 0 -> 2149 bytes .../roughlyenoughitems/textures/gui/kirb.png | Bin 0 -> 630 bytes .../textures/gui/recipecontainer.png | Bin 0 -> 2014 bytes .../textures/gui/recipecontainer_dark.png | Bin 0 -> 2516 bytes .../roughlyenoughitems/textures/gui/screenshot.png | Bin 0 -> 25515 bytes .../roughlyenoughitems/textures/gui/toasts.png | Bin 0 -> 256 bytes runtime/src/main/resources/fabric.mod.json | 55 ++ runtime/src/main/resources/icon.png | Bin 0 -> 27270 bytes .../roughlyenoughitems-runtime.accessWidener | 22 + 114 files changed, 18172 insertions(+) create mode 100644 runtime/build.gradle create mode 100644 runtime/src/main/java/me/shedaniel/rei/REIModMenuEntryPoint.java create mode 100644 runtime/src/main/java/me/shedaniel/rei/RoughlyEnoughItemsCore.java create mode 100644 runtime/src/main/java/me/shedaniel/rei/RoughlyEnoughItemsNetwork.java create mode 100644 runtime/src/main/java/me/shedaniel/rei/RoughlyEnoughItemsState.java create mode 100644 runtime/src/main/java/me/shedaniel/rei/compat/LBASupportPlugin.java create mode 100644 runtime/src/main/java/me/shedaniel/rei/gui/ConfigReloadingScreen.java create mode 100644 runtime/src/main/java/me/shedaniel/rei/gui/ContainerScreenOverlay.java create mode 100644 runtime/src/main/java/me/shedaniel/rei/gui/DelegateScreen.java create mode 100644 runtime/src/main/java/me/shedaniel/rei/gui/OverlaySearchField.java create mode 100644 runtime/src/main/java/me/shedaniel/rei/gui/PreRecipeViewingScreen.java create mode 100644 runtime/src/main/java/me/shedaniel/rei/gui/RecipeDisplayExporter.java create mode 100644 runtime/src/main/java/me/shedaniel/rei/gui/RecipeScreen.java create mode 100644 runtime/src/main/java/me/shedaniel/rei/gui/RecipeViewingScreen.java create mode 100644 runtime/src/main/java/me/shedaniel/rei/gui/TransformingScreen.java create mode 100644 runtime/src/main/java/me/shedaniel/rei/gui/VillagerRecipeViewingScreen.java create mode 100644 runtime/src/main/java/me/shedaniel/rei/gui/WarningAndErrorScreen.java create mode 100644 runtime/src/main/java/me/shedaniel/rei/gui/config/entry/FilteringAddRuleScreen.java create mode 100644 runtime/src/main/java/me/shedaniel/rei/gui/config/entry/FilteringEntry.java create mode 100644 runtime/src/main/java/me/shedaniel/rei/gui/config/entry/FilteringRuleOptionsScreen.java create mode 100644 runtime/src/main/java/me/shedaniel/rei/gui/config/entry/FilteringRulesScreen.java create mode 100644 runtime/src/main/java/me/shedaniel/rei/gui/config/entry/FilteringScreen.java create mode 100644 runtime/src/main/java/me/shedaniel/rei/gui/config/entry/NoFilteringEntry.java create mode 100644 runtime/src/main/java/me/shedaniel/rei/gui/config/entry/RecipeScreenTypeEntry.java create mode 100644 runtime/src/main/java/me/shedaniel/rei/gui/config/entry/ReloadPluginsEntry.java create mode 100644 runtime/src/main/java/me/shedaniel/rei/gui/credits/CreditsEntryListWidget.java create mode 100644 runtime/src/main/java/me/shedaniel/rei/gui/credits/CreditsScreen.java create mode 100644 runtime/src/main/java/me/shedaniel/rei/gui/modules/Menu.java create mode 100644 runtime/src/main/java/me/shedaniel/rei/gui/modules/MenuEntry.java create mode 100644 runtime/src/main/java/me/shedaniel/rei/gui/modules/entries/EntryStackSubsetsMenuEntry.java create mode 100644 runtime/src/main/java/me/shedaniel/rei/gui/modules/entries/GameModeMenuEntry.java create mode 100644 runtime/src/main/java/me/shedaniel/rei/gui/modules/entries/SubSubsetsMenuEntry.java create mode 100644 runtime/src/main/java/me/shedaniel/rei/gui/modules/entries/WeatherMenuEntry.java create mode 100644 runtime/src/main/java/me/shedaniel/rei/gui/plugin/DefaultRuntimePlugin.java create mode 100644 runtime/src/main/java/me/shedaniel/rei/gui/plugin/entry/FluidEntryDefinition.java create mode 100644 runtime/src/main/java/me/shedaniel/rei/gui/plugin/entry/ItemEntryDefinition.java create mode 100644 runtime/src/main/java/me/shedaniel/rei/gui/toast/CopyRecipeIdentifierToast.java create mode 100644 runtime/src/main/java/me/shedaniel/rei/gui/toast/ExportRecipeIdentifierToast.java create mode 100644 runtime/src/main/java/me/shedaniel/rei/gui/widget/DraggableWidget.java create mode 100644 runtime/src/main/java/me/shedaniel/rei/gui/widget/EntryListEntryWidget.java create mode 100644 runtime/src/main/java/me/shedaniel/rei/gui/widget/EntryListWidget.java create mode 100644 runtime/src/main/java/me/shedaniel/rei/gui/widget/EntryWidget.java create mode 100644 runtime/src/main/java/me/shedaniel/rei/gui/widget/FavoritesListWidget.java create mode 100644 runtime/src/main/java/me/shedaniel/rei/gui/widget/LateRenderable.java create mode 100644 runtime/src/main/java/me/shedaniel/rei/gui/widget/QueuedTooltip.java create mode 100644 runtime/src/main/java/me/shedaniel/rei/gui/widget/RecipeChoosePageWidget.java create mode 100644 runtime/src/main/java/me/shedaniel/rei/gui/widget/TabWidget.java create mode 100644 runtime/src/main/java/me/shedaniel/rei/impl/AbstractEntryStack.java create mode 100644 runtime/src/main/java/me/shedaniel/rei/impl/AmountIgnoredEntryStackWrapper.java create mode 100644 runtime/src/main/java/me/shedaniel/rei/impl/Animator.java create mode 100644 runtime/src/main/java/me/shedaniel/rei/impl/BaseBoundsHandlerImpl.java create mode 100644 runtime/src/main/java/me/shedaniel/rei/impl/ClientHelperImpl.java create mode 100644 runtime/src/main/java/me/shedaniel/rei/impl/ConfigManagerImpl.java create mode 100644 runtime/src/main/java/me/shedaniel/rei/impl/ConfigObjectImpl.java create mode 100644 runtime/src/main/java/me/shedaniel/rei/impl/DisplayHelperImpl.java create mode 100644 runtime/src/main/java/me/shedaniel/rei/impl/EntryRegistryImpl.java create mode 100644 runtime/src/main/java/me/shedaniel/rei/impl/EntryTypeDeferred.java create mode 100644 runtime/src/main/java/me/shedaniel/rei/impl/EntryTypeRegistryImpl.java create mode 100644 runtime/src/main/java/me/shedaniel/rei/impl/FavoriteEntryTypeRegistryImpl.java create mode 100644 runtime/src/main/java/me/shedaniel/rei/impl/FluidSupportProviderImpl.java create mode 100644 runtime/src/main/java/me/shedaniel/rei/impl/InternalWidgets.java create mode 100644 runtime/src/main/java/me/shedaniel/rei/impl/IssuesDetector.java create mode 100644 runtime/src/main/java/me/shedaniel/rei/impl/RecipeHelperImpl.java create mode 100644 runtime/src/main/java/me/shedaniel/rei/impl/ScreenHelper.java create mode 100644 runtime/src/main/java/me/shedaniel/rei/impl/SearchArgument.java create mode 100644 runtime/src/main/java/me/shedaniel/rei/impl/SimpleFluidRenderer.java create mode 100644 runtime/src/main/java/me/shedaniel/rei/impl/TextTransformations.java create mode 100644 runtime/src/main/java/me/shedaniel/rei/impl/TypedEntryStack.java create mode 100644 runtime/src/main/java/me/shedaniel/rei/impl/Weather.java create mode 100644 runtime/src/main/java/me/shedaniel/rei/impl/entry/EmptyEntryDefinition.java create mode 100644 runtime/src/main/java/me/shedaniel/rei/impl/filtering/AbstractFilteringRule.java create mode 100644 runtime/src/main/java/me/shedaniel/rei/impl/filtering/FilteringContext.java create mode 100644 runtime/src/main/java/me/shedaniel/rei/impl/filtering/FilteringContextImpl.java create mode 100644 runtime/src/main/java/me/shedaniel/rei/impl/filtering/FilteringContextType.java create mode 100644 runtime/src/main/java/me/shedaniel/rei/impl/filtering/FilteringResult.java create mode 100644 runtime/src/main/java/me/shedaniel/rei/impl/filtering/FilteringResultImpl.java create mode 100644 runtime/src/main/java/me/shedaniel/rei/impl/filtering/FilteringRule.java create mode 100644 runtime/src/main/java/me/shedaniel/rei/impl/filtering/rules/ManualFilteringRule.java create mode 100644 runtime/src/main/java/me/shedaniel/rei/impl/filtering/rules/SearchFilteringRule.java create mode 100644 runtime/src/main/java/me/shedaniel/rei/impl/search/AlwaysMatchingArgument.java create mode 100644 runtime/src/main/java/me/shedaniel/rei/impl/search/Argument.java create mode 100644 runtime/src/main/java/me/shedaniel/rei/impl/search/ArgumentsRegistry.java create mode 100644 runtime/src/main/java/me/shedaniel/rei/impl/search/MatchStatus.java create mode 100644 runtime/src/main/java/me/shedaniel/rei/impl/search/MatchType.java create mode 100644 runtime/src/main/java/me/shedaniel/rei/impl/search/ModArgument.java create mode 100644 runtime/src/main/java/me/shedaniel/rei/impl/search/RegexArgument.java create mode 100644 runtime/src/main/java/me/shedaniel/rei/impl/search/TagArgument.java create mode 100644 runtime/src/main/java/me/shedaniel/rei/impl/search/TextArgument.java create mode 100644 runtime/src/main/java/me/shedaniel/rei/impl/search/TooltipArgument.java create mode 100644 runtime/src/main/java/me/shedaniel/rei/impl/subsets/SubsetsRegistryImpl.java create mode 100644 runtime/src/main/java/me/shedaniel/rei/impl/widgets/ArrowWidget.java create mode 100644 runtime/src/main/java/me/shedaniel/rei/impl/widgets/BurningFireWidget.java create mode 100644 runtime/src/main/java/me/shedaniel/rei/impl/widgets/ButtonWidget.java create mode 100644 runtime/src/main/java/me/shedaniel/rei/impl/widgets/DrawableWidget.java create mode 100644 runtime/src/main/java/me/shedaniel/rei/impl/widgets/FillRectangleDrawableConsumer.java create mode 100644 runtime/src/main/java/me/shedaniel/rei/impl/widgets/LabelWidget.java create mode 100644 runtime/src/main/java/me/shedaniel/rei/impl/widgets/PanelWidget.java create mode 100644 runtime/src/main/java/me/shedaniel/rei/impl/widgets/TexturedDrawableConsumer.java create mode 100644 runtime/src/main/java/me/shedaniel/rei/impl/widgets/package-info.java create mode 100644 runtime/src/main/java/me/shedaniel/rei/plugin/autocrafting/DefaultCategoryHandler.java create mode 100644 runtime/src/main/java/me/shedaniel/rei/server/InputSlotCrafter.java create mode 100644 runtime/src/main/java/me/shedaniel/rei/tests/plugin/REITestPlugin.java create mode 100644 runtime/src/main/resources/assets/roughlyenoughitems/textures/gui/button.png create mode 100644 runtime/src/main/resources/assets/roughlyenoughitems/textures/gui/button_dark.png create mode 100644 runtime/src/main/resources/assets/roughlyenoughitems/textures/gui/display.png create mode 100644 runtime/src/main/resources/assets/roughlyenoughitems/textures/gui/display_dark.png create mode 100644 runtime/src/main/resources/assets/roughlyenoughitems/textures/gui/kirb.png create mode 100644 runtime/src/main/resources/assets/roughlyenoughitems/textures/gui/recipecontainer.png create mode 100644 runtime/src/main/resources/assets/roughlyenoughitems/textures/gui/recipecontainer_dark.png create mode 100644 runtime/src/main/resources/assets/roughlyenoughitems/textures/gui/screenshot.png create mode 100644 runtime/src/main/resources/assets/roughlyenoughitems/textures/gui/toasts.png create mode 100644 runtime/src/main/resources/fabric.mod.json create mode 100644 runtime/src/main/resources/icon.png create mode 100644 runtime/src/main/resources/roughlyenoughitems-runtime.accessWidener (limited to 'runtime') diff --git a/runtime/build.gradle b/runtime/build.gradle new file mode 100644 index 000000000..d7264220b --- /dev/null +++ b/runtime/build.gradle @@ -0,0 +1,14 @@ +archivesBaseName = "runtime" + +repositories { + maven { url "https://mod-buildcraft.com/maven" } +} + +loom { + accessWidener = file("src/main/resources/roughlyenoughitems-runtime.accessWidener") +} + +dependencies { + compile project(path: ":api", configuration: "dev") + modCompileOnly("alexiil.mc.lib:libblockattributes-fluids:0.8.3-pre.3") +} \ No newline at end of file diff --git a/runtime/src/main/java/me/shedaniel/rei/REIModMenuEntryPoint.java b/runtime/src/main/java/me/shedaniel/rei/REIModMenuEntryPoint.java new file mode 100644 index 000000000..f282a6259 --- /dev/null +++ b/runtime/src/main/java/me/shedaniel/rei/REIModMenuEntryPoint.java @@ -0,0 +1,45 @@ +/* + * This file is licensed under the MIT License, part of Roughly Enough Items. + * Copyright (c) 2018, 2019, 2020 shedaniel + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package me.shedaniel.rei; + +import com.google.common.collect.ImmutableMap; +import io.github.prospector.modmenu.api.ConfigScreenFactory; +import io.github.prospector.modmenu.api.ModMenuApi; +import me.shedaniel.rei.api.ConfigManager; + +import java.util.Map; + +public class REIModMenuEntryPoint implements ModMenuApi { + @Override + public ConfigScreenFactory getModConfigScreenFactory() { + return parent -> ConfigManager.getInstance().getConfigScreen(parent); + } + + @Override + public Map> getProvidedConfigScreenFactories() { + return ImmutableMap.of( + "roughlyenoughitems", getModConfigScreenFactory() + ); + } +} diff --git a/runtime/src/main/java/me/shedaniel/rei/RoughlyEnoughItemsCore.java b/runtime/src/main/java/me/shedaniel/rei/RoughlyEnoughItemsCore.java new file mode 100644 index 000000000..93055513f --- /dev/null +++ b/runtime/src/main/java/me/shedaniel/rei/RoughlyEnoughItemsCore.java @@ -0,0 +1,608 @@ +/* + * This file is licensed under the MIT License, part of Roughly Enough Items. + * Copyright (c) 2018, 2019, 2020 shedaniel + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package me.shedaniel.rei; + +import com.google.common.collect.BiMap; +import com.google.common.collect.HashBiMap; +import com.google.common.collect.Lists; +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; +import me.shedaniel.cloth.api.client.events.v0.ClothClientHooks; +import me.shedaniel.clothconfig2.api.LazyResettable; +import me.shedaniel.math.Point; +import me.shedaniel.math.Rectangle; +import me.shedaniel.math.api.Executor; +import me.shedaniel.rei.api.*; +import me.shedaniel.rei.api.entry.*; +import me.shedaniel.rei.api.favorites.FavoriteEntry; +import me.shedaniel.rei.api.favorites.FavoriteEntryType; +import me.shedaniel.rei.api.favorites.FavoriteMenuEntry; +import me.shedaniel.rei.api.fluid.FluidSupportProvider; +import me.shedaniel.rei.api.plugins.REIPluginV0; +import me.shedaniel.rei.api.subsets.SubsetsRegistry; +import me.shedaniel.rei.api.widgets.*; +import me.shedaniel.rei.gui.ContainerScreenOverlay; +import me.shedaniel.rei.gui.widget.EntryWidget; +import me.shedaniel.rei.gui.widget.QueuedTooltip; +import me.shedaniel.rei.gui.widget.Widget; +import me.shedaniel.rei.impl.*; +import me.shedaniel.rei.impl.entry.EmptyEntryDefinition; +import me.shedaniel.rei.impl.subsets.SubsetsRegistryImpl; +import me.shedaniel.rei.impl.widgets.*; +import me.shedaniel.rei.tests.plugin.REITestPlugin; +import net.fabricmc.api.ClientModInitializer; +import net.fabricmc.api.EnvType; +import net.fabricmc.api.Environment; +import net.fabricmc.fabric.api.client.networking.v1.ClientPlayNetworking; +import net.fabricmc.loader.api.FabricLoader; +import net.fabricmc.loader.api.ModContainer; +import net.minecraft.client.Minecraft; +import net.minecraft.client.gui.components.EditBox; +import net.minecraft.client.gui.components.ImageButton; +import net.minecraft.client.gui.components.events.GuiEventListener; +import net.minecraft.client.gui.screens.Screen; +import net.minecraft.client.gui.screens.inventory.AbstractContainerScreen; +import net.minecraft.client.gui.screens.inventory.CraftingScreen; +import net.minecraft.client.gui.screens.inventory.CreativeModeInventoryScreen; +import net.minecraft.client.gui.screens.inventory.InventoryScreen; +import net.minecraft.client.gui.screens.recipebook.GhostRecipe; +import net.minecraft.client.gui.screens.recipebook.RecipeBookComponent; +import net.minecraft.client.gui.screens.recipebook.RecipeUpdateListener; +import net.minecraft.client.resources.language.I18n; +import net.minecraft.network.chat.Component; +import net.minecraft.network.chat.FormattedText; +import net.minecraft.network.chat.TextComponent; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.util.GsonHelper; +import net.minecraft.util.Unit; +import net.minecraft.world.InteractionResult; +import net.minecraft.world.inventory.CraftingMenu; +import net.minecraft.world.inventory.Slot; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.item.Items; +import net.minecraft.world.item.crafting.Ingredient; +import net.minecraft.world.item.crafting.RecipeManager; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.jetbrains.annotations.ApiStatus; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.*; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.function.BiFunction; +import java.util.function.Function; +import java.util.function.Supplier; +import java.util.stream.Stream; + +import static me.shedaniel.rei.impl.Internals.attachInstance; + +@ApiStatus.Internal +@Environment(EnvType.CLIENT) +public class RoughlyEnoughItemsCore implements ClientModInitializer { + @ApiStatus.Internal public static final Logger LOGGER = LogManager.getFormatterLogger("REI"); + private static final BiMap PLUGINS = HashBiMap.create(); + private static final ExecutorService SYNC_RECIPES = Executors.newSingleThreadScheduledExecutor(r -> new Thread(r, "REI-SyncRecipes")); + @ApiStatus.Experimental + public static boolean isLeftModePressed = false; + + static { + attachInstance(EntryTypeRegistryImpl.getInstance(), EntryTypeRegistry.class); + Map> typeCache = new ConcurrentHashMap<>(); + attachInstance((Function>) id -> { + if (id.equals(BuiltinEntryTypes.EMPTY_ID)) { + return BuiltinEntryTypes.EMPTY; + } else if (id.equals(BuiltinEntryTypes.RENDERING_ID)) { + return BuiltinEntryTypes.RENDERING; + } + return typeCache.computeIfAbsent(id, EntryTypeDeferred::new); + }, "entryTypeDeferred"); + attachInstance(new RecipeHelperImpl(), RecipeHelper.class); + attachInstance(new EntryRegistryImpl(), EntryRegistry.class); + attachInstance(new DisplayHelperImpl(), DisplayHelper.class); + attachInstance(new FluidSupportProviderImpl(), FluidSupportProvider.class); + attachInstance(new SubsetsRegistryImpl(), SubsetsRegistry.class); + attachInstance(new Internals.EntryStackProvider() { + @Override + public EntryStack empty() { + return TypedEntryStack.EMPTY; + } + + @Override + public EntryStack of(EntryDefinition definition, T value) { + return new TypedEntryStack<>(definition, value); + } + + @Override + public EntryType emptyType(ResourceLocation id) { + return new EntryType() { + @Override + public @NotNull ResourceLocation getId() { + return id; + } + + @Override + public @NotNull EntryDefinition getDefinition() { + return EmptyEntryDefinition.EMPTY; + } + }; + } + + @Override + public EntryType renderingType(ResourceLocation id) { + return new EntryType() { + @Override + public @NotNull ResourceLocation getId() { + return id; + } + + @Override + public @NotNull EntryDefinition getDefinition() { + return EmptyEntryDefinition.RENDERING; + } + }; + } + }, Internals.EntryStackProvider.class); + attachInstance(new Internals.WidgetsProvider() { + @Override + public boolean isRenderingPanel(Panel panel) { + return PanelWidget.isRendering(panel); + } + + @Override + public Widget createDrawableWidget(DrawableConsumer drawable) { + return new DrawableWidget(drawable); + } + + @Override + public me.shedaniel.rei.api.widgets.Slot createSlot(Point point) { + return new EntryWidget(point); + } + + @Override + public Button createButton(Rectangle bounds, Component text) { + return new ButtonWidget(bounds, text); + } + + @Override + public Panel createPanelWidget(Rectangle bounds) { + return new PanelWidget(bounds); + } + + @Override + public Label createLabel(Point point, FormattedText text) { + return new LabelWidget(point, text); + } + + @Override + public Arrow createArrow(Rectangle rectangle) { + return new ArrowWidget(rectangle); + } + + @Override + public BurningFire createBurningFire(Rectangle rectangle) { + return new BurningFireWidget(rectangle); + } + + @Override + public DrawableConsumer createTexturedConsumer(ResourceLocation texture, int x, int y, int width, int height, float u, float v, int uWidth, int vHeight, int textureWidth, int textureHeight) { + return new TexturedDrawableConsumer(texture, x, y, width, height, u, v, uWidth, vHeight, textureWidth, textureHeight); + } + + @Override + public DrawableConsumer createFillRectangleConsumer(Rectangle rectangle, int color) { + return new FillRectangleDrawableConsumer(rectangle, color); + } + }, Internals.WidgetsProvider.class); + attachInstance((Supplier) FavoriteEntryTypeRegistryImpl::getInstance, "favoriteEntryTypeRegistry"); + attachInstance((BiFunction, Supplier, FavoriteEntry>) (supplier, toJson) -> new FavoriteEntry() { + LazyResettable value = new LazyResettable<>(supplier); + + @Override + public FavoriteEntry getUnwrapped() { + FavoriteEntry entry = value.get(); + if (entry == null) { + value.reset(); + } + return Objects.requireNonNull(entry).getUnwrapped(); + } + + @Override + public UUID getUuid() { + return getUnwrapped().getUuid(); + } + + @Override + public boolean isInvalid() { + try { + return getUnwrapped().isInvalid(); + } catch (Exception e) { + return true; + } + } + + @Override + public Renderer getRenderer(boolean showcase) { + return getUnwrapped().getRenderer(showcase); + } + + @Override + public boolean doAction(int button) { + return getUnwrapped().doAction(button); + } + + @Override + public @NotNull Optional>> getMenuEntries() { + return getUnwrapped().getMenuEntries(); + } + + @Override + public int hashIgnoreAmount() { + return getUnwrapped().hashIgnoreAmount(); + } + + @Override + public FavoriteEntry copy() { + return FavoriteEntry.delegate(supplier, toJson); + } + + @Override + public ResourceLocation getType() { + return getUnwrapped().getType(); + } + + @Override + public @NotNull JsonObject toJson(@NotNull JsonObject to) { + if (toJson == null) { + return getUnwrapped().toJson(to); + } + + JsonObject object = toJson.get(); + for (Map.Entry entry : object.entrySet()) { + to.add(entry.getKey(), entry.getValue()); + } + return to; + } + + @Override + public boolean isSame(FavoriteEntry other) { + return getUnwrapped().isSame(other.getUnwrapped()); + } + }, "delegateFavoriteEntry"); + attachInstance((Function) (object) -> { + String type = GsonHelper.getAsString(object, FavoriteEntry.TYPE_KEY); + switch (type) { + case "stack": + case "item": + case "fluid": + case "empty": + return FavoriteEntry.fromEntryStack(EntryStack.readFromJson(object)); + default: + ResourceLocation id = new ResourceLocation(type); + return Objects.requireNonNull(Objects.requireNonNull(FavoriteEntryType.registry().get(id)).fromJson(object)); + } + }, "favoriteEntryFromJson"); + attachInstance((BiFunction<@Nullable Point, Collection, Tooltip>) QueuedTooltip::create, "tooltipProvider"); + attachInstance((Function<@Nullable Boolean, ClickAreaHandler.Result>) successful -> new ClickAreaHandler.Result() { + private List categories = Lists.newArrayList(); + + @Override + public ClickAreaHandler.Result category(ResourceLocation category) { + this.categories.add(category); + return this; + } + + @Override + public boolean isSuccessful() { + return successful; + } + + @Override + public Stream getCategories() { + return categories.stream(); + } + }, "clickAreaHandlerResult"); + } + + /** + * Registers a REI plugin + * + * @param plugin the plugin instance + * @return the plugin itself + */ + @ApiStatus.Internal + public static REIPluginEntry registerPlugin(REIPluginEntry plugin) { + PLUGINS.put(plugin.getPluginIdentifier(), plugin); + RoughlyEnoughItemsCore.LOGGER.debug("Registered plugin %s from %s", plugin.getPluginIdentifier().toString(), plugin.getClass().getSimpleName()); + return plugin; + } + + public static List getPlugins() { + return new ArrayList<>(PLUGINS.values()); + } + + public static Optional getPluginIdentifier(REIPluginEntry plugin) { + return Optional.ofNullable(PLUGINS.inverse().get(plugin)); + } + + public static boolean hasPermissionToUsePackets() { + try { + Minecraft.getInstance().getConnection().getSuggestionsProvider().hasPermission(0); + return hasOperatorPermission() && canUsePackets(); + } catch (NullPointerException e) { + return true; + } + } + + public static boolean hasOperatorPermission() { + try { + return Minecraft.getInstance().getConnection().getSuggestionsProvider().hasPermission(1); + } catch (NullPointerException e) { + return true; + } + } + + public static boolean canUsePackets() { + return ClientPlayNetworking.canSend(RoughlyEnoughItemsNetwork.CREATE_ITEMS_PACKET) && ClientPlayNetworking.canSend(RoughlyEnoughItemsNetwork.CREATE_ITEMS_GRAB_PACKET) && ClientPlayNetworking.canSend(RoughlyEnoughItemsNetwork.DELETE_ITEMS_PACKET); + } + + @ApiStatus.Internal + public static void syncRecipes(long[] lastSync) { + if (lastSync != null) { + if (lastSync[0] > 0 && System.currentTimeMillis() - lastSync[0] <= 5000) { + RoughlyEnoughItemsCore.LOGGER.warn("Suppressing Sync Recipes!"); + return; + } + lastSync[0] = System.currentTimeMillis(); + } + RecipeManager recipeManager = Minecraft.getInstance().getConnection().getRecipeManager(); + if (ConfigObject.getInstance().doesRegisterRecipesInAnotherThread()) { + CompletableFuture.runAsync(() -> ((RecipeHelperImpl) RecipeHelper.getInstance()).tryRecipesLoaded(recipeManager), SYNC_RECIPES); + } else { + ((RecipeHelperImpl) RecipeHelper.getInstance()).tryRecipesLoaded(recipeManager); + } + } + + @ApiStatus.Internal + public static boolean isDebugModeEnabled() { + return System.getProperty("rei.test", "false").equals("true"); + } + + public static boolean canDeleteItems() { + return hasPermissionToUsePackets() || Minecraft.getInstance().gameMode.hasInfiniteItems(); + } + + @SuppressWarnings("deprecation") + @Override + public void onInitializeClient() { + attachInstance(new ConfigManagerImpl(), ConfigManager.class); + + IssuesDetector.detect(); + registerClothEvents(); + discoverPluginEntries(); + for (ModContainer modContainer : FabricLoader.getInstance().getAllMods()) { + if (modContainer.getMetadata().containsCustomElement("roughlyenoughitems:plugins")) + RoughlyEnoughItemsCore.LOGGER.error("REI plugin from " + modContainer.getMetadata().getId() + " is not loaded because it is too old!"); + } + + RoughlyEnoughItemsState.checkRequiredFabricModules(); + Executor.run(() -> () -> { + ClientPlayNetworking.registerGlobalReceiver(RoughlyEnoughItemsNetwork.CREATE_ITEMS_MESSAGE_PACKET, (client, handler, buf, responseSender) -> { + ItemStack stack = buf.readItem(); + String player = buf.readUtf(32767); + if (client.player != null) { + client.player.displayClientMessage(new TextComponent(I18n.get("text.rei.cheat_items").replaceAll("\\{item_name}", EntryStacks.of(stack.copy()).asFormattedText().getString()).replaceAll("\\{item_count}", stack.copy().getCount() + "").replaceAll("\\{player_name}", player)), false); + } + }); + ClientPlayNetworking.registerGlobalReceiver(RoughlyEnoughItemsNetwork.NOT_ENOUGH_ITEMS_PACKET, (client, handler, buf, responseSender) -> { + Screen currentScreen = Minecraft.getInstance().screen; + if (currentScreen instanceof CraftingScreen) { + RecipeBookComponent recipeBookGui = ((RecipeUpdateListener) currentScreen).getRecipeBookComponent(); + GhostRecipe ghostSlots = recipeBookGui.ghostRecipe; + ghostSlots.clear(); + + List> input = Lists.newArrayList(); + int mapSize = buf.readInt(); + for (int i = 0; i < mapSize; i++) { + List list = Lists.newArrayList(); + int count = buf.readInt(); + for (int j = 0; j < count; j++) { + list.add(buf.readItem()); + } + input.add(list); + } + + ghostSlots.addIngredient(Ingredient.of(Items.STONE), 381203812, 12738291); + CraftingMenu container = ((CraftingScreen) currentScreen).getMenu(); + for (int i = 0; i < input.size(); i++) { + List stacks = input.get(i); + if (!stacks.isEmpty()) { + Slot slot = container.getSlot(i + container.getResultSlotIndex() + 1); + ghostSlots.addIngredient(Ingredient.of(stacks.toArray(new ItemStack[0])), slot.x, slot.y); + } + } + } + }); + }); + } + + private void discoverPluginEntries() { + for (REIPluginEntry reiPlugin : FabricLoader.getInstance().getEntrypoints("rei_plugins", REIPluginEntry.class)) { + try { + if (!REIPluginV0.class.isAssignableFrom(reiPlugin.getClass())) + throw new IllegalArgumentException("REI plugin is too old!"); + registerPlugin(reiPlugin); + } catch (Exception e) { + e.printStackTrace(); + RoughlyEnoughItemsCore.LOGGER.error("Can't load REI plugins from %s: %s", reiPlugin.getClass(), e.getLocalizedMessage()); + } + } + for (REIPluginV0 reiPlugin : FabricLoader.getInstance().getEntrypoints("rei_plugins_v0", REIPluginV0.class)) { + try { + registerPlugin(reiPlugin); + } catch (Exception e) { + e.printStackTrace(); + RoughlyEnoughItemsCore.LOGGER.error("Can't load REI plugins from %s: %s", reiPlugin.getClass(), e.getLocalizedMessage()); + } + } + + // Test Only + loadTestPlugins(); + } + + private void loadTestPlugins() { + if (isDebugModeEnabled()) { + registerPlugin(new REITestPlugin()); + } + if (FabricLoader.getInstance().isModLoaded("libblockattributes-fluids")) { + try { + registerPlugin((REIPluginEntry) Class.forName("me.shedaniel.rei.compat.LBASupportPlugin").getConstructor().newInstance()); + } catch (Throwable throwable) { + throwable.printStackTrace(); + } + } + } + + private boolean shouldReturn(Screen screen) { + if (screen == null) return true; + if (screen != Minecraft.getInstance().screen) return true; + return shouldReturn(screen.getClass()); + } + + private boolean shouldReturn(Class screen) { + try { + for (OverlayDecider decider : DisplayHelper.getInstance().getAllOverlayDeciders()) { + if (!decider.isHandingScreen(screen)) + continue; + InteractionResult result = decider.shouldScreenBeOverlaid(screen); + if (result != InteractionResult.PASS) + return result == InteractionResult.FAIL || REIHelper.getInstance().getPreviousContainerScreen() == null; + } + } catch (ConcurrentModificationException ignored) { + } + return true; + } + + private void registerClothEvents() { + final ResourceLocation recipeButtonTex = new ResourceLocation("textures/gui/recipe_button.png"); + long[] lastSync = {-1}; + ClothClientHooks.SYNC_RECIPES.register((minecraftClient, recipeManager, synchronizeRecipesS2CPacket) -> syncRecipes(lastSync)); + ClothClientHooks.SCREEN_ADD_BUTTON.register((minecraftClient, screen, abstractButtonWidget) -> { + if (ConfigObject.getInstance().doesDisableRecipeBook() && screen instanceof AbstractContainerScreen && abstractButtonWidget instanceof ImageButton) + if (((ImageButton) abstractButtonWidget).resourceLocation.equals(recipeButtonTex)) + return InteractionResult.FAIL; + return InteractionResult.PASS; + }); + ClothClientHooks.SCREEN_INIT_POST.register((minecraftClient, screen, screenHooks) -> { + if (shouldReturn(screen)) + return; + if (screen instanceof InventoryScreen && minecraftClient.gameMode.hasInfiniteItems()) + return; + if (screen instanceof AbstractContainerScreen) + ScreenHelper.setPreviousContainerScreen((AbstractContainerScreen) screen); + boolean alreadyAdded = false; + for (GuiEventListener element : Lists.newArrayList(screenHooks.cloth$getChildren())) + if (ContainerScreenOverlay.class.isAssignableFrom(element.getClass())) + if (alreadyAdded) + screenHooks.cloth$getChildren().remove(element); + else + alreadyAdded = true; + if (!alreadyAdded) + screenHooks.cloth$getChildren().add(ScreenHelper.getLastOverlay(true, false)); + }); + ClothClientHooks.SCREEN_RENDER_POST.register((matrices, minecraftClient, screen, i, i1, v) -> { + if (shouldReturn(screen)) + return; + ScreenHelper.getLastOverlay().render(matrices, i, i1, v); + }); + ClothClientHooks.SCREEN_MOUSE_DRAGGED.register((minecraftClient, screen, v, v1, i, v2, v3) -> { + if (shouldReturn(screen)) + return InteractionResult.PASS; + if (ScreenHelper.isOverlayVisible() && ScreenHelper.getLastOverlay().mouseDragged(v, v1, i, v2, v3)) + return InteractionResult.SUCCESS; + return InteractionResult.PASS; + }); + ClothClientHooks.SCREEN_MOUSE_CLICKED.register((minecraftClient, screen, v, v1, i) -> { + isLeftModePressed = true; + if (ScreenHelper.getOptionalOverlay().isPresent()) + if (screen instanceof CreativeModeInventoryScreen) + if (ScreenHelper.isOverlayVisible() && ScreenHelper.getLastOverlay().mouseClicked(v, v1, i)) { + screen.setFocused(ScreenHelper.getLastOverlay()); + if (i == 0) + screen.setDragging(true); + return InteractionResult.SUCCESS; + } + return InteractionResult.PASS; + }); + ClothClientHooks.SCREEN_MOUSE_RELEASED.register((minecraftClient, screen, v, v1, i) -> { + isLeftModePressed = false; + if (shouldReturn(screen)) + return InteractionResult.PASS; + if (ScreenHelper.getOptionalOverlay().isPresent()) + if (ScreenHelper.isOverlayVisible() && ScreenHelper.getLastOverlay().mouseReleased(v, v1, i)) { + return InteractionResult.SUCCESS; + } + return InteractionResult.PASS; + }); + ClothClientHooks.SCREEN_MOUSE_SCROLLED.register((minecraftClient, screen, v, v1, v2) -> { + if (shouldReturn(screen)) + return InteractionResult.PASS; + if (ScreenHelper.isOverlayVisible() && ScreenHelper.getLastOverlay().mouseScrolled(v, v1, v2)) + return InteractionResult.SUCCESS; + return InteractionResult.PASS; + }); + ClothClientHooks.SCREEN_CHAR_TYPED.register((minecraftClient, screen, character, keyCode) -> { + if (shouldReturn(screen)) + return InteractionResult.PASS; + if (ScreenHelper.getLastOverlay().charTyped(character, keyCode)) + return InteractionResult.SUCCESS; + return InteractionResult.PASS; + }); + ClothClientHooks.SCREEN_LATE_RENDER.register((matrices, minecraftClient, screen, i, i1, v) -> { + if (shouldReturn(screen)) + return; + if (!ScreenHelper.isOverlayVisible()) + return; + ScreenHelper.getLastOverlay().lateRender(matrices, i, i1, v); + }); + ClothClientHooks.SCREEN_KEY_PRESSED.register((minecraftClient, screen, i, i1, i2) -> { + if (shouldReturn(screen)) + return InteractionResult.PASS; + if (screen instanceof AbstractContainerScreen && ConfigObject.getInstance().doesDisableRecipeBook() && ConfigObject.getInstance().doesFixTabCloseContainer()) + if (i == 258 && minecraftClient.options.keyInventory.matches(i, i1)) { + minecraftClient.player.closeContainer(); + return InteractionResult.SUCCESS; + } + if (screen.getFocused() != null && screen.getFocused() instanceof EditBox || (screen.getFocused() instanceof RecipeBookComponent && ((RecipeBookComponent) screen.getFocused()).searchBox != null && ((RecipeBookComponent) screen.getFocused()).searchBox.isFocused())) + return InteractionResult.PASS; + if (ScreenHelper.getLastOverlay().keyPressed(i, i1, i2)) + return InteractionResult.SUCCESS; + return InteractionResult.PASS; + }); + } + +} diff --git a/runtime/src/main/java/me/shedaniel/rei/RoughlyEnoughItemsNetwork.java b/runtime/src/main/java/me/shedaniel/rei/RoughlyEnoughItemsNetwork.java new file mode 100644 index 000000000..7d73ac6c3 --- /dev/null +++ b/runtime/src/main/java/me/shedaniel/rei/RoughlyEnoughItemsNetwork.java @@ -0,0 +1,143 @@ +/* + * This file is licensed under the MIT License, part of Roughly Enough Items. + * Copyright (c) 2018, 2019, 2020 shedaniel + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package me.shedaniel.rei; + +import com.google.common.collect.Lists; +import io.netty.buffer.Unpooled; +import me.shedaniel.math.api.Executor; +import me.shedaniel.rei.server.InputSlotCrafter; +import net.fabricmc.api.ModInitializer; +import net.fabricmc.fabric.api.networking.v1.ServerPlayNetworking; +import net.fabricmc.loader.api.FabricLoader; +import net.minecraft.ChatFormatting; +import net.minecraft.Util; +import net.minecraft.core.NonNullList; +import net.minecraft.network.FriendlyByteBuf; +import net.minecraft.network.chat.TranslatableComponent; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.util.Mth; +import net.minecraft.world.entity.player.Inventory; +import net.minecraft.world.inventory.AbstractContainerMenu; +import net.minecraft.world.inventory.InventoryMenu; +import net.minecraft.world.inventory.RecipeBookMenu; +import net.minecraft.world.item.ItemStack; + +import java.util.List; + +public class RoughlyEnoughItemsNetwork implements ModInitializer { + public static final ResourceLocation DELETE_ITEMS_PACKET = new ResourceLocation("roughlyenoughitems", "delete_item"); + public static final ResourceLocation CREATE_ITEMS_PACKET = new ResourceLocation("roughlyenoughitems", "create_item"); + public static final ResourceLocation CREATE_ITEMS_GRAB_PACKET = new ResourceLocation("roughlyenoughitems", "create_item_grab"); + public static final ResourceLocation CREATE_ITEMS_MESSAGE_PACKET = new ResourceLocation("roughlyenoughitems", "ci_msg"); + public static final ResourceLocation MOVE_ITEMS_PACKET = new ResourceLocation("roughlyenoughitems", "move_items"); + public static final ResourceLocation NOT_ENOUGH_ITEMS_PACKET = new ResourceLocation("roughlyenoughitems", "og_not_enough"); + + @Override + public void onInitialize() { + RoughlyEnoughItemsState.checkRequiredFabricModules(); + Executor.run(() -> () -> { + FabricLoader.getInstance().getEntrypoints("rei_containers", Runnable.class).forEach(Runnable::run); + ServerPlayNetworking.registerGlobalReceiver(DELETE_ITEMS_PACKET, (server, player, handler, buf, responseSender) -> { + if (player.getServer().getProfilePermissions(player.getGameProfile()) < player.getServer().getOperatorUserPermissionLevel()) { + player.displayClientMessage(new TranslatableComponent("text.rei.no_permission_cheat").withStyle(ChatFormatting.RED), false); + return; + } + if (!player.getInventory().getCarried().isEmpty()) { + player.getInventory().setCarried(ItemStack.EMPTY); + player.broadcastCarriedItem(); + } + }); + ServerPlayNetworking.registerGlobalReceiver(CREATE_ITEMS_PACKET, (server, player, handler, buf, responseSender) -> { + if (player.getServer().getProfilePermissions(player.getGameProfile()) < player.getServer().getOperatorUserPermissionLevel()) { + player.displayClientMessage(new TranslatableComponent("text.rei.no_permission_cheat").withStyle(ChatFormatting.RED), false); + return; + } + ItemStack stack = buf.readItem(); + if (player.getInventory().add(stack.copy())) { + responseSender.sendPacket(RoughlyEnoughItemsNetwork.CREATE_ITEMS_MESSAGE_PACKET, new FriendlyByteBuf(Unpooled.buffer()).writeItem(stack.copy()).writeUtf(player.getScoreboardName(), 32767)); + } else + player.displayClientMessage(new TranslatableComponent("text.rei.failed_cheat_items"), false); + }); + ServerPlayNetworking.registerGlobalReceiver(CREATE_ITEMS_GRAB_PACKET, (server, player, handler, buf, responseSender) -> { + if (player.getServer().getProfilePermissions(player.getGameProfile()) < player.getServer().getOperatorUserPermissionLevel()) { + player.displayClientMessage(new TranslatableComponent("text.rei.no_permission_cheat").withStyle(ChatFormatting.RED), false); + return; + } + + Inventory inventory = player.getInventory(); + ItemStack itemStack = buf.readItem(); + ItemStack stack = itemStack.copy(); + if (!inventory.getCarried().isEmpty() && ItemStack.isSameIgnoreDurability(inventory.getCarried(), stack) && ItemStack.tagMatches(inventory.getCarried(), stack)) { + stack.setCount(Mth.clamp(stack.getCount() + inventory.getCarried().getCount(), 1, stack.getMaxStackSize())); + } else if (!inventory.getCarried().isEmpty()) { + return; + } + inventory.setCarried(stack.copy()); + player.broadcastCarriedItem(); + responseSender.sendPacket(RoughlyEnoughItemsNetwork.CREATE_ITEMS_MESSAGE_PACKET, new FriendlyByteBuf(Unpooled.buffer()).writeItem(itemStack.copy()).writeUtf(player.getScoreboardName(), 32767)); + }); + ServerPlayNetworking.registerGlobalReceiver(MOVE_ITEMS_PACKET, (server, player, handler, packetByteBuf, responseSender) -> { + ResourceLocation category = packetByteBuf.readResourceLocation(); + AbstractContainerMenu container = player.containerMenu; + InventoryMenu playerContainer = player.inventoryMenu; + try { + boolean shift = packetByteBuf.readBoolean(); + NonNullList> input = NonNullList.create(); + int mapSize = packetByteBuf.readInt(); + for (int i = 0; i < mapSize; i++) { + List list = Lists.newArrayList(); + int count = packetByteBuf.readInt(); + for (int j = 0; j < count; j++) { + list.add(packetByteBuf.readItem()); + } + input.add(list); + } + try { + InputSlotCrafter.start(category, container, player, input, shift); + } catch (InputSlotCrafter.NotEnoughMaterialsException e) { + if (!(container instanceof RecipeBookMenu)) + return; + FriendlyByteBuf buf = new FriendlyByteBuf(Unpooled.buffer()); + buf.writeInt(input.size()); + for (List stacks : input) { + buf.writeInt(stacks.size()); + for (ItemStack stack : stacks) { + buf.writeItem(stack); + } + } + responseSender.sendPacket(NOT_ENOUGH_ITEMS_PACKET, buf); + } catch (IllegalStateException e) { + player.sendMessage(new TranslatableComponent(e.getMessage()).withStyle(ChatFormatting.RED), Util.NIL_UUID); + } catch (Exception e) { + player.sendMessage(new TranslatableComponent("error.rei.internal.error", e.getMessage()).withStyle(ChatFormatting.RED), Util.NIL_UUID); + e.printStackTrace(); + } + } catch (Exception e) { + e.printStackTrace(); + } + }); + }); + } + +} diff --git a/runtime/src/main/java/me/shedaniel/rei/RoughlyEnoughItemsState.java b/runtime/src/main/java/me/shedaniel/rei/RoughlyEnoughItemsState.java new file mode 100644 index 000000000..9378fafaf --- /dev/null +++ b/runtime/src/main/java/me/shedaniel/rei/RoughlyEnoughItemsState.java @@ -0,0 +1,136 @@ +/* + * This file is licensed under the MIT License, part of Roughly Enough Items. + * Copyright (c) 2018, 2019, 2020 shedaniel + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package me.shedaniel.rei; + +import com.google.common.collect.ImmutableSet; +import net.fabricmc.api.EnvType; +import net.fabricmc.loader.api.FabricLoader; +import net.minecraft.util.Tuple; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.jetbrains.annotations.ApiStatus; + +import java.util.ArrayList; +import java.util.LinkedHashSet; +import java.util.List; +import java.util.Set; + +@ApiStatus.Internal +public class RoughlyEnoughItemsState { + private RoughlyEnoughItemsState() {} + + public static final Logger LOGGER = LogManager.getFormatterLogger("REI"); + + private static List> errors = new ArrayList<>(); + private static List> warnings = new ArrayList<>(); + private static Set errorSet = new LinkedHashSet<>(); + private static Set warningSet = new LinkedHashSet<>(); + private static List continueCallbacks = new ArrayList<>(); + + public static void error(String reason) { + if (FabricLoader.getInstance().getEnvironmentType() == EnvType.SERVER || FabricLoader.getInstance().isDevelopmentEnvironment()) + throw new RuntimeException(reason); + if (RoughlyEnoughItemsState.errorSet.add(reason + " " + null)) { + RoughlyEnoughItemsState.errors.add(new Tuple<>(reason, null)); + LOGGER.error(reason); + } + } + + public static void checkRequiredFabricModules() { + ImmutableSet requiredModules = FabricLoader.getInstance().getEnvironmentType() == EnvType.CLIENT ? + ImmutableSet.builder() + .add("fabric-api-base") + .add("fabric-resource-loader-v0") + .add("fabric-networking-api-v1") + .add("fabric-lifecycle-events-v1") + .add("fabric-rendering-fluids-v1") + .build() : + ImmutableSet.builder() + .add("fabric-api-base") + .add("fabric-resource-loader-v0") + .add("fabric-networking-api-v1") + .add("fabric-lifecycle-events-v1") + .build(); + for (String module : requiredModules) { + boolean moduleLoaded = FabricLoader.getInstance().isModLoaded(module); + if (!moduleLoaded) { + RoughlyEnoughItemsState.error("Fabric API is not installed!", "https://www.curseforge.com/minecraft/mc-mods/fabric-api/files/all"); + return; + } + } + } + + public static void error(String reason, String link) { + if (FabricLoader.getInstance().getEnvironmentType() == EnvType.SERVER || FabricLoader.getInstance().isDevelopmentEnvironment()) + throw new RuntimeException(reason + " " + link); + if (RoughlyEnoughItemsState.errorSet.add(reason + " " + link)) { + RoughlyEnoughItemsState.errors.add(new Tuple<>(reason, link)); + LOGGER.error(reason + " " + link); + } + } + + public static void warn(String reason) { + if (RoughlyEnoughItemsState.warningSet.add(reason + " " + null)) { + RoughlyEnoughItemsState.warnings.add(new Tuple<>(reason, null)); + LOGGER.warn(reason); + } + } + + public static void warn(String reason, String link) { + if (RoughlyEnoughItemsState.warningSet.add(reason + " " + link)) { + RoughlyEnoughItemsState.warnings.add(new Tuple<>(reason, link)); + LOGGER.warn(reason + " " + link); + } + } + + public static void onContinue(Runnable runnable) { + continueCallbacks.add(runnable); + } + + public static List> getErrors() { + return errors; + } + + public static List> getWarnings() { + return warnings; + } + + public static void clear() { + errors.clear(); + errorSet.clear(); + warnings.clear(); + warningSet.clear(); + } + + public static void continues() { + for (Runnable callback : continueCallbacks) { + try { + callback.run(); + } catch (Throwable throwable) { + throwable.printStackTrace(); + } + } + continueCallbacks.clear();