├── .github └── workflows │ └── build.yml ├── .gitignore ├── LICENSE ├── README.md ├── build.gradle ├── gradle.properties ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── jitpack.yml ├── owo-sentinel ├── build.gradle ├── gradle.properties └── src │ └── main │ ├── java │ └── io │ │ └── wispforest │ │ └── owosentinel │ │ ├── DownloadTask.java │ │ ├── Maldenhagen.java │ │ ├── OwoSentinel.java │ │ ├── SentinelConsole.java │ │ └── SentinelWindow.java │ └── resources │ ├── fabric.mod.json │ └── owo_sentinel_icon.png ├── owo-ui.xsd ├── settings.gradle ├── src ├── main │ ├── java │ │ └── io │ │ │ └── wispforest │ │ │ └── owo │ │ │ ├── Owo.java │ │ │ ├── blockentity │ │ │ ├── LinearProcess.java │ │ │ └── LinearProcessExecutor.java │ │ │ ├── client │ │ │ ├── OwoClient.java │ │ │ ├── screens │ │ │ │ ├── OwoScreenHandler.java │ │ │ │ ├── ScreenInternals.java │ │ │ │ ├── ScreenUtils.java │ │ │ │ ├── ScreenhandlerMessageData.java │ │ │ │ ├── SlotGenerator.java │ │ │ │ ├── SyncedProperty.java │ │ │ │ └── ValidatingSlot.java │ │ │ └── texture │ │ │ │ ├── AnimatedTextureDrawable.java │ │ │ │ └── SpriteSheetMetadata.java │ │ │ ├── command │ │ │ ├── EnumArgumentType.java │ │ │ └── debug │ │ │ │ ├── CcaDataCommand.java │ │ │ │ ├── DumpdataCommand.java │ │ │ │ ├── HealCommand.java │ │ │ │ ├── MakeLootContainerCommand.java │ │ │ │ └── OwoDebugCommands.java │ │ │ ├── compat │ │ │ ├── emi │ │ │ │ └── OwoEmiPlugin.java │ │ │ ├── modmenu │ │ │ │ └── OwoModMenuPlugin.java │ │ │ └── rei │ │ │ │ ├── OwoReiPlugin.java │ │ │ │ ├── ReiUIAdapter.java │ │ │ │ └── ReiWidgetComponent.java │ │ │ ├── config │ │ │ ├── ConfigAP.java │ │ │ ├── ConfigSynchronizer.java │ │ │ ├── ConfigWrapper.java │ │ │ ├── Option.java │ │ │ ├── OwoConfigCommand.java │ │ │ ├── annotation │ │ │ │ ├── Config.java │ │ │ │ ├── ExcludeFromScreen.java │ │ │ │ ├── Expanded.java │ │ │ │ ├── Hook.java │ │ │ │ ├── Modmenu.java │ │ │ │ ├── Nest.java │ │ │ │ ├── PredicateConstraint.java │ │ │ │ ├── RangeConstraint.java │ │ │ │ ├── RegexConstraint.java │ │ │ │ ├── RestartRequired.java │ │ │ │ ├── SectionHeader.java │ │ │ │ ├── Sync.java │ │ │ │ └── WithAlpha.java │ │ │ └── ui │ │ │ │ ├── ConfigScreen.java │ │ │ │ ├── ConfigScreenProviders.java │ │ │ │ ├── OptionComponentFactory.java │ │ │ │ ├── OptionComponents.java │ │ │ │ ├── RestartRequiredScreen.java │ │ │ │ └── component │ │ │ │ ├── ConfigEnumButton.java │ │ │ │ ├── ConfigSlider.java │ │ │ │ ├── ConfigTextBox.java │ │ │ │ ├── ConfigToggleButton.java │ │ │ │ ├── ListOptionContainer.java │ │ │ │ ├── OptionValueProvider.java │ │ │ │ └── SearchAnchorComponent.java │ │ │ ├── ext │ │ │ ├── DerivedComponentMap.java │ │ │ └── OwoItem.java │ │ │ ├── itemgroup │ │ │ ├── Icon.java │ │ │ ├── ItemGroupReference.java │ │ │ ├── OwoItemGroup.java │ │ │ ├── OwoItemSettings.java │ │ │ ├── OwoItemSettingsExtension.java │ │ │ ├── gui │ │ │ │ ├── ItemGroupButton.java │ │ │ │ ├── ItemGroupButtonWidget.java │ │ │ │ └── ItemGroupTab.java │ │ │ └── json │ │ │ │ ├── OwoItemGroupLoader.java │ │ │ │ └── WrapperGroup.java │ │ │ ├── mixin │ │ │ ├── CachedRegistryInfoGetterAccessor.java │ │ │ ├── ClientCommonNetworkHandlerAccessor.java │ │ │ ├── ClientConfigurationNetworkHandlerMixin.java │ │ │ ├── ClientConnectionMixin.java │ │ │ ├── ClientLoginNetworkHandlerAccessor.java │ │ │ ├── ComponentTypeBuilderMixin.java │ │ │ ├── Copenhagen.java │ │ │ ├── DataResultMixin.java │ │ │ ├── DrawContextMixin.java │ │ │ ├── ForwardingDynamicOpsAccessor.java │ │ │ ├── GlResourceManagerMixin.java │ │ │ ├── MainMixin.java │ │ │ ├── MinecraftClientMixin.java │ │ │ ├── NbtCompoundMixin.java │ │ │ ├── PacketByteBufMixin.java │ │ │ ├── RegistryOpsAccessor.java │ │ │ ├── ScreenAccessor.java │ │ │ ├── ScreenHandlerInvoker.java │ │ │ ├── ScreenHandlerMixin.java │ │ │ ├── ServerCommonNetworkHandlerAccessor.java │ │ │ ├── ServerPlayerEntityMixin.java │ │ │ ├── ServerPlayerInteractionManagerMixin.java │ │ │ ├── SetComponentsLootFunctionAccessor.java │ │ │ ├── TagGroupLoaderMixin.java │ │ │ ├── ext │ │ │ │ ├── ItemMixin.java │ │ │ │ ├── ItemStackMixin.java │ │ │ │ ├── MergedComponentMapAccessor.java │ │ │ │ └── MergedComponentMapMixin.java │ │ │ ├── itemgroup │ │ │ │ ├── CreativeInventoryScreenAccessor.java │ │ │ │ ├── CreativeInventoryScreenMixin.java │ │ │ │ ├── ItemGroupAccessor.java │ │ │ │ ├── ItemMixin.java │ │ │ │ ├── ItemSettingsMixin.java │ │ │ │ ├── MixinCreativeInventoryScreenMixin.java │ │ │ │ └── StatusEffectsDisplayMixin.java │ │ │ ├── offline │ │ │ │ ├── AdvancementProgressAccessor.java │ │ │ │ ├── PlayerAdvancementTrackerMixin.java │ │ │ │ ├── ProgressMapAccessor.java │ │ │ │ └── WorldSaveHandlerMixin.java │ │ │ ├── recipe_remainders │ │ │ │ ├── CraftingResultSlotMixin.java │ │ │ │ ├── JsonDataLoaderMixin.java │ │ │ │ └── ServerRecipeManagerAccessor.java │ │ │ ├── registry │ │ │ │ ├── ReferenceAccessor.java │ │ │ │ └── SimpleRegistryMixin.java │ │ │ ├── shader │ │ │ │ └── ShaderProgramAccessor.java │ │ │ ├── text │ │ │ │ ├── LanguageMixin.java │ │ │ │ ├── TextCodecsMixin.java │ │ │ │ ├── TranslatableTextContentMixin.java │ │ │ │ ├── TranslationStorageMixin.java │ │ │ │ └── stapi │ │ │ │ │ └── SystemDelegatedLanguageFixin.java │ │ │ ├── tweaks │ │ │ │ ├── EulaReaderMixin.java │ │ │ │ ├── LevelInfoMixin.java │ │ │ │ ├── OperatingSystemMixin.java │ │ │ │ └── TextFieldWidgetMixin.java │ │ │ └── ui │ │ │ │ ├── ChatScreenMixin.java │ │ │ │ ├── ClickableWidgetMixin.java │ │ │ │ ├── DrawContextInvoker.java │ │ │ │ ├── EditBoxWidgetMixin.java │ │ │ │ ├── EntityRenderDispatcherMixin.java │ │ │ │ ├── EntityRendererMixin.java │ │ │ │ ├── HandledScreenMixin.java │ │ │ │ ├── MinecraftClientMixin.java │ │ │ │ ├── RenderPhaseMixin.java │ │ │ │ ├── ScreenMixin.java │ │ │ │ ├── SliderWidgetMixin.java │ │ │ │ ├── SlotAccessor.java │ │ │ │ ├── SlotMixin.java │ │ │ │ ├── TextFieldWidgetMixin.java │ │ │ │ ├── access │ │ │ │ ├── BaseOwoHandledScreenAccessor.java │ │ │ │ ├── BlockEntityAccessor.java │ │ │ │ ├── ButtonWidgetAccessor.java │ │ │ │ ├── CheckboxWidgetAccessor.java │ │ │ │ ├── ClickableWidgetAccessor.java │ │ │ │ ├── EditBoxAccessor.java │ │ │ │ ├── EditBoxWidgetAccessor.java │ │ │ │ ├── TextBoxComponentAccessor.java │ │ │ │ └── TextFieldWidgetAccessor.java │ │ │ │ └── layers │ │ │ │ ├── HandledScreenAccessor.java │ │ │ │ ├── KeyboardMixin.java │ │ │ │ ├── MouseMixin.java │ │ │ │ └── ScreenMixin.java │ │ │ ├── moddata │ │ │ ├── ModDataConsumer.java │ │ │ └── ModDataLoader.java │ │ │ ├── network │ │ │ ├── ClientAccess.java │ │ │ ├── NetworkException.java │ │ │ ├── OwoClientConnectionExtension.java │ │ │ ├── OwoHandshake.java │ │ │ ├── OwoNetChannel.java │ │ │ ├── QueuedChannelSet.java │ │ │ ├── ServerAccess.java │ │ │ └── serialization │ │ │ │ └── SealedPolymorphic.java │ │ │ ├── offline │ │ │ ├── DataSavedEvents.java │ │ │ ├── OfflineAdvancementLookup.java │ │ │ ├── OfflineAdvancementState.java │ │ │ └── OfflineDataLookup.java │ │ │ ├── ops │ │ │ ├── ItemOps.java │ │ │ ├── LootOps.java │ │ │ ├── TextOps.java │ │ │ └── WorldOps.java │ │ │ ├── particles │ │ │ ├── ClientParticles.java │ │ │ └── systems │ │ │ │ ├── ParticleSystem.java │ │ │ │ ├── ParticleSystemController.java │ │ │ │ └── ParticleSystemExecutor.java │ │ │ ├── registration │ │ │ ├── ComplexRegistryAction.java │ │ │ ├── RegistryHelper.java │ │ │ ├── annotations │ │ │ │ ├── AssignedName.java │ │ │ │ ├── IterationIgnored.java │ │ │ │ └── RegistryNamespace.java │ │ │ └── reflect │ │ │ │ ├── AutoRegistryContainer.java │ │ │ │ ├── BlockEntityRegistryContainer.java │ │ │ │ ├── BlockRegistryContainer.java │ │ │ │ ├── EntityRegistryContainer.java │ │ │ │ ├── FieldProcessingSubject.java │ │ │ │ ├── FieldRegistrationHandler.java │ │ │ │ ├── ItemRegistryContainer.java │ │ │ │ └── SimpleFieldProcessingSubject.java │ │ │ ├── renderdoc │ │ │ ├── RenderDoc.java │ │ │ ├── RenderdocLibrary.java │ │ │ └── RenderdocScreen.java │ │ │ ├── serialization │ │ │ ├── CodecUtils.java │ │ │ ├── EndecRecipeSerializer.java │ │ │ ├── OwoComponentTypeBuilder.java │ │ │ ├── RegistriesAttribute.java │ │ │ ├── endec │ │ │ │ ├── DefaultedListEndec.java │ │ │ │ ├── EitherEndec.java │ │ │ │ ├── MinecraftEndecs.java │ │ │ │ └── StructEitherEndec.java │ │ │ └── format │ │ │ │ ├── ContextHolder.java │ │ │ │ ├── DynamicOpsWithContext.java │ │ │ │ ├── edm │ │ │ │ └── EdmOps.java │ │ │ │ └── nbt │ │ │ │ ├── IdentityHolder.java │ │ │ │ ├── NbtDeserializer.java │ │ │ │ ├── NbtEndec.java │ │ │ │ └── NbtSerializer.java │ │ │ ├── shader │ │ │ └── OwoBlurRenderer.java │ │ │ ├── text │ │ │ ├── CustomTextRegistry.java │ │ │ ├── InsertingTextContent.java │ │ │ ├── LanguageAccess.java │ │ │ ├── TextLanguage.java │ │ │ └── TranslationContext.java │ │ │ ├── ui │ │ │ ├── base │ │ │ │ ├── BaseComponent.java │ │ │ │ ├── BaseOwoHandledScreen.java │ │ │ │ ├── BaseOwoScreen.java │ │ │ │ ├── BaseOwoToast.java │ │ │ │ ├── BaseOwoTooltipComponent.java │ │ │ │ ├── BaseParentComponent.java │ │ │ │ ├── BaseUIModelHandledScreen.java │ │ │ │ └── BaseUIModelScreen.java │ │ │ ├── component │ │ │ │ ├── BlockComponent.java │ │ │ │ ├── BoxComponent.java │ │ │ │ ├── ButtonComponent.java │ │ │ │ ├── CheckboxComponent.java │ │ │ │ ├── ColorPickerComponent.java │ │ │ │ ├── Components.java │ │ │ │ ├── DiscreteSliderComponent.java │ │ │ │ ├── DropdownComponent.java │ │ │ │ ├── EntityComponent.java │ │ │ │ ├── ItemComponent.java │ │ │ │ ├── LabelComponent.java │ │ │ │ ├── SliderComponent.java │ │ │ │ ├── SlimSliderComponent.java │ │ │ │ ├── SmallCheckboxComponent.java │ │ │ │ ├── SpacerComponent.java │ │ │ │ ├── SpriteComponent.java │ │ │ │ ├── TextAreaComponent.java │ │ │ │ ├── TextBoxComponent.java │ │ │ │ ├── TextureComponent.java │ │ │ │ └── VanillaWidgetComponent.java │ │ │ ├── container │ │ │ │ ├── CollapsibleContainer.java │ │ │ │ ├── Containers.java │ │ │ │ ├── DraggableContainer.java │ │ │ │ ├── FlowLayout.java │ │ │ │ ├── GridLayout.java │ │ │ │ ├── OverlayContainer.java │ │ │ │ ├── RenderEffectWrapper.java │ │ │ │ ├── ScrollContainer.java │ │ │ │ ├── StackLayout.java │ │ │ │ └── WrappingParentComponent.java │ │ │ ├── core │ │ │ │ ├── Animatable.java │ │ │ │ ├── AnimatableProperty.java │ │ │ │ ├── Animation.java │ │ │ │ ├── Color.java │ │ │ │ ├── Component.java │ │ │ │ ├── CursorStyle.java │ │ │ │ ├── Easing.java │ │ │ │ ├── HorizontalAlignment.java │ │ │ │ ├── Insets.java │ │ │ │ ├── OwoUIAdapter.java │ │ │ │ ├── OwoUIDrawContext.java │ │ │ │ ├── OwoUIPipelines.java │ │ │ │ ├── OwoUIRenderLayers.java │ │ │ │ ├── ParentComponent.java │ │ │ │ ├── PositionedRectangle.java │ │ │ │ ├── Positioning.java │ │ │ │ ├── Size.java │ │ │ │ ├── Sizing.java │ │ │ │ ├── Surface.java │ │ │ │ └── VerticalAlignment.java │ │ │ ├── event │ │ │ │ ├── CharTyped.java │ │ │ │ ├── ClientRenderCallback.java │ │ │ │ ├── FocusGained.java │ │ │ │ ├── FocusLost.java │ │ │ │ ├── KeyPress.java │ │ │ │ ├── MouseDown.java │ │ │ │ ├── MouseDrag.java │ │ │ │ ├── MouseEnter.java │ │ │ │ ├── MouseLeave.java │ │ │ │ ├── MouseScroll.java │ │ │ │ ├── MouseUp.java │ │ │ │ └── WindowResizeCallback.java │ │ │ ├── hud │ │ │ │ ├── Hud.java │ │ │ │ ├── HudContainer.java │ │ │ │ └── HudInspectorScreen.java │ │ │ ├── inject │ │ │ │ ├── ComponentStub.java │ │ │ │ └── GreedyInputComponent.java │ │ │ ├── layers │ │ │ │ ├── Layer.java │ │ │ │ └── Layers.java │ │ │ ├── parsing │ │ │ │ ├── ConfigureHotReloadScreen.java │ │ │ │ ├── IncompatibleUIModelException.java │ │ │ │ ├── UIModel.java │ │ │ │ ├── UIModelLoader.java │ │ │ │ ├── UIModelParsingException.java │ │ │ │ └── UIParsing.java │ │ │ └── util │ │ │ │ ├── CommandOpenedScreen.java │ │ │ │ ├── CursorAdapter.java │ │ │ │ ├── Delta.java │ │ │ │ ├── DisposableScreen.java │ │ │ │ ├── FocusHandler.java │ │ │ │ ├── MatrixStackTransformer.java │ │ │ │ ├── MountingHelper.java │ │ │ │ ├── NinePatchTexture.java │ │ │ │ ├── ScissorStack.java │ │ │ │ ├── SpriteUtilInvoker.java │ │ │ │ ├── UIErrorToast.java │ │ │ │ └── UISounds.java │ │ │ └── util │ │ │ ├── EventSource.java │ │ │ ├── EventStream.java │ │ │ ├── ImplementedInventory.java │ │ │ ├── KawaiiUtil.java │ │ │ ├── Maldenhagen.java │ │ │ ├── NumberReflection.java │ │ │ ├── Observable.java │ │ │ ├── OwoFreezer.java │ │ │ ├── RecipeRemainderStorage.java │ │ │ ├── ReflectionUtils.java │ │ │ ├── Scary.java │ │ │ ├── ServicesFrozenException.java │ │ │ ├── StackTraceSupplier.java │ │ │ ├── TagInjector.java │ │ │ ├── VectorRandomUtils.java │ │ │ ├── VectorSerializer.java │ │ │ ├── Wisdom.java │ │ │ └── pond │ │ │ ├── OwoCreativeInventoryScreenExtensions.java │ │ │ ├── OwoEntityRenderDispatcherExtension.java │ │ │ ├── OwoItemExtensions.java │ │ │ ├── OwoScreenExtension.java │ │ │ ├── OwoScreenHandlerExtension.java │ │ │ ├── OwoSimpleRegistryExtensions.java │ │ │ ├── OwoSlotExtension.java │ │ │ ├── OwoTessellatorExtension.java │ │ │ ├── OwoTextRendererExtension.java │ │ │ └── package-info.java │ └── resources │ │ ├── META-INF │ │ └── services │ │ │ └── javax.annotation.processing.Processor │ │ ├── architectury.common.json │ │ ├── assets │ │ └── owo │ │ │ ├── icon.png │ │ │ ├── lang │ │ │ ├── en_us.json │ │ │ └── tt_ru.json │ │ │ ├── nine_patch_textures │ │ │ ├── button │ │ │ │ ├── active.json │ │ │ │ ├── disabled.json │ │ │ │ └── hovered.json │ │ │ ├── panel │ │ │ │ ├── dark.json │ │ │ │ ├── default.json │ │ │ │ └── inset.json │ │ │ ├── scrollbar │ │ │ │ ├── track.json │ │ │ │ ├── vanilla_flat.json │ │ │ │ ├── vanilla_horizontal.json │ │ │ │ ├── vanilla_horizontal_disabled.json │ │ │ │ ├── vanilla_vertical.json │ │ │ │ └── vanilla_vertical_disabled.json │ │ │ └── slim_slider_track.json │ │ │ ├── owo_ui │ │ │ ├── config.xml │ │ │ ├── configure_hot_reload.xml │ │ │ └── restart_required.xml │ │ │ ├── shaders │ │ │ └── core │ │ │ │ ├── blur.fsh │ │ │ │ ├── blur.vsh │ │ │ │ └── spectrum.fsh │ │ │ ├── sounds.json │ │ │ ├── sounds │ │ │ └── ui_interaction.ogg │ │ │ └── textures │ │ │ └── gui │ │ │ ├── buttons.png │ │ │ ├── config_search.png │ │ │ ├── dark_panel.png │ │ │ ├── dropdown_icons.png │ │ │ ├── icons.png │ │ │ ├── panel.png │ │ │ ├── panel_inset.png │ │ │ ├── scrollbar.png │ │ │ ├── slim_slider.png │ │ │ ├── smol_checkbox.png │ │ │ └── tabs.png │ │ ├── fabric.mod.json │ │ ├── owo.accesswidener │ │ └── owo.mixins.json └── testmod │ ├── java │ └── io │ │ └── wispforest │ │ └── uwu │ │ ├── EpicScreenHandler.java │ │ ├── FabledBananasClass.java │ │ ├── Uwu.java │ │ ├── blockentity │ │ └── ProcessBlockEntity.java │ │ ├── client │ │ ├── ComponentTestScreen.java │ │ ├── EpicHandledModelScreen.java │ │ ├── EpicHandledScreen.java │ │ ├── ParseFailScreen.java │ │ ├── ScissorTestScreen.java │ │ ├── SelectUwuScreenScreen.java │ │ ├── SizingTestScreen.java │ │ ├── SmolComponentTestScreen.java │ │ ├── TestConfigScreen.java │ │ ├── TestParseScreen.java │ │ ├── TooManyComponentsScreen.java │ │ ├── UwuClient.java │ │ └── UwuConfigScreen.java │ │ ├── config │ │ ├── UowouConfigModel.java │ │ └── UwuConfigModel.java │ │ ├── items │ │ ├── UwuCounterItem.java │ │ ├── UwuItems.java │ │ ├── UwuScreenShardItem.java │ │ └── UwuTestStickItem.java │ │ ├── mixin │ │ └── TitleScreenMixin.java │ │ ├── network │ │ ├── DispatchedInterface.java │ │ ├── DispatchedSubclassOne.java │ │ ├── DispatchedSubclassTwo.java │ │ ├── KeycodePacket.java │ │ ├── MaldingPacket.java │ │ ├── NullablePacket.java │ │ ├── SealedSubclassOne.java │ │ ├── SealedSubclassTwo.java │ │ ├── SealedTestClass.java │ │ ├── StringPacket.java │ │ ├── UwuNetworkExample.java │ │ ├── UwuNetworkTest.java │ │ └── UwuOptionalNetExample.java │ │ ├── recipe │ │ └── UwuShapedRecipe.java │ │ ├── rei │ │ ├── UiCategory.java │ │ └── UwuReiPlugin.java │ │ └── text │ │ └── BasedTextContent.java │ └── resources │ ├── assets │ ├── uowou │ │ ├── models │ │ │ └── item │ │ │ │ └── owo_ingot.json │ │ └── textures │ │ │ └── item │ │ │ └── owo_ingot.png │ └── uwu │ │ ├── lang │ │ └── en_us.json │ │ ├── models │ │ └── item │ │ │ ├── counter.json │ │ │ ├── screen_shard.json │ │ │ └── test_stick.json │ │ ├── owo_ui │ │ ├── config.xml │ │ ├── expand_gap_test.xml │ │ ├── focus_cycle_test.xml │ │ ├── parse_fail.xml │ │ ├── smol_components.xml │ │ ├── test_element_one.xml │ │ └── test_element_two.xml │ │ └── textures │ │ └── gui │ │ ├── animated_icon_test.png │ │ ├── group.png │ │ ├── icon.png │ │ ├── sprites │ │ ├── bottom_selected.png │ │ ├── bottom_selected_first_column.png │ │ ├── bottom_unselected.png │ │ ├── scroller.png │ │ ├── scroller_disabled.png │ │ ├── top_selected.png │ │ ├── top_selected_first_column.png │ │ └── top_unselected.png │ │ └── translucent_owo.png │ ├── data │ └── uwu │ │ ├── item_group_tabs │ │ ├── crab_group.json │ │ ├── food_and_drink_button.json │ │ ├── ingredients_extension.json │ │ └── ingredients_extension_2.json │ │ ├── recipe │ │ ├── test_recipe.json │ │ └── uwu_shaped_recipe.json │ │ └── tags │ │ └── item │ │ └── tab_2_content.json │ ├── fabric.mod.json │ └── uwu.mixins.json └── stylesheet.css /.github/workflows/build.yml: -------------------------------------------------------------------------------- 1 | # Automatically build the project and run any configured tests for every push 2 | # and submitted pull request. This can help catch issues that only occur on 3 | # certain platforms or Java versions, and provides a first line of defence 4 | # against bad commits. 5 | 6 | name: build 7 | on: [pull_request, push] 8 | 9 | jobs: 10 | build: 11 | strategy: 12 | matrix: 13 | # Use these Java versions 14 | java: [ 15 | 21 # Minimum supported by Minecraft 16 | ] 17 | # and run on both Linux and Windows 18 | os: [ubuntu-22.04] 19 | runs-on: ${{ matrix.os }} 20 | steps: 21 | - name: checkout repository 22 | uses: actions/checkout@v2 23 | - name: validate gradle wrapper 24 | uses: gradle/wrapper-validation-action@v1 25 | - name: setup jdk ${{ matrix.java }} 26 | uses: actions/setup-java@v1 27 | with: 28 | java-version: ${{ matrix.java }} 29 | - name: make gradle wrapper executable 30 | if: ${{ runner.os != 'Windows' }} 31 | run: chmod +x ./gradlew 32 | - name: build 33 | run: ./gradlew build 34 | - name: capture build artifacts 35 | if: ${{ runner.os == 'Linux' && matrix.java == '21' }} # Only upload artifacts built from latest java on one OS 36 | uses: actions/upload-artifact@v4 37 | with: 38 | name: Artifacts 39 | path: build/libs/ 40 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2021 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /gradle.properties: -------------------------------------------------------------------------------- 1 | # Done to increase the memory available to gradle. 2 | org.gradle.jvmargs=-Xmx2G 3 | # Fabric Properties 4 | # check these on https://fabricmc.net/develop 5 | minecraft_base_version=1.21.5 6 | minecraft_version=1.21.5 7 | yarn_mappings=1.21.5+build.1 8 | loader_version=0.16.13 9 | # Mod Properties 10 | mod_version=0.12.21 11 | maven_group=io.wispforest 12 | archives_base_name=owo-lib 13 | # Dependencies 14 | fabric_version=0.119.9+1.21.5 15 | 16 | # https://maven.shedaniel.me/me/shedaniel/RoughlyEnoughItems-fabric/ 17 | rei_version=18.0.800 18 | 19 | # https://maven.terraformersmc.com/releases/dev/emi/emi-fabric/ 20 | emi_version=1.1.18+1.21.1 21 | 22 | # https://search.maven.org/artifact/blue.endless/jankson 23 | jankson_version=1.2.2 24 | 25 | # https://maven.terraformersmc.com/releases/com/terraformersmc/modmenu 26 | modmenu_version=14.0.0-rc.2 27 | 28 | # https://maven.nucleoid.xyz/xyz/nucleoid/server-translations-api/ 29 | stapi_version=2.5.0+1.21.5-rc1 30 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wisp-forest/owo-lib/ef83709dae78614695e2842cd792990e3db28900/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-8.12-bin.zip 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | -------------------------------------------------------------------------------- /jitpack.yml: -------------------------------------------------------------------------------- 1 | jdk: 2 | - openjdk21 -------------------------------------------------------------------------------- /owo-sentinel/build.gradle: -------------------------------------------------------------------------------- 1 | loom { 2 | runConfigs.client.ideConfigGenerated = true 3 | 4 | mods { 5 | "owo-sentinel" { 6 | sourceSet sourceSets.main 7 | } 8 | } 9 | } -------------------------------------------------------------------------------- /owo-sentinel/gradle.properties: -------------------------------------------------------------------------------- 1 | archives_base_name=owo-sentinel 2 | -------------------------------------------------------------------------------- /owo-sentinel/src/main/java/io/wispforest/owosentinel/DownloadTask.java: -------------------------------------------------------------------------------- 1 | package io.wispforest.owosentinel; 2 | 3 | import javax.swing.*; 4 | import java.util.function.Consumer; 5 | 6 | public class DownloadTask extends SwingWorker { 7 | 8 | private final Runnable whenDone; 9 | private final Consumer logger; 10 | 11 | public DownloadTask(Consumer logger, Runnable whenDone) { 12 | this.logger = logger; 13 | this.whenDone = whenDone; 14 | } 15 | 16 | @Override 17 | protected void done() { 18 | whenDone.run(); 19 | } 20 | 21 | @Override 22 | protected Void doInBackground() { 23 | try { 24 | OwoSentinel.downloadAndInstall(logger); 25 | } catch (Exception e) { 26 | logger.accept("Download failed!"); 27 | OwoSentinel.LOGGER.error("Download failed", e); 28 | } 29 | return null; 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /owo-sentinel/src/main/java/io/wispforest/owosentinel/Maldenhagen.java: -------------------------------------------------------------------------------- 1 | package io.wispforest.owosentinel; 2 | 3 | import net.fabricmc.loader.api.LanguageAdapter; 4 | import net.fabricmc.loader.api.ModContainer; 5 | 6 | public class Maldenhagen implements LanguageAdapter { 7 | @Override 8 | public T create(ModContainer mod, String value, Class type) { 9 | throw new UnsupportedOperationException(); 10 | } 11 | 12 | static { 13 | OwoSentinel.launch(); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /owo-sentinel/src/main/java/io/wispforest/owosentinel/SentinelConsole.java: -------------------------------------------------------------------------------- 1 | package io.wispforest.owosentinel; 2 | 3 | import java.util.Locale; 4 | import java.util.Scanner; 5 | 6 | public class SentinelConsole { 7 | public static void run() throws Exception { 8 | System.out.println("oωo-lib is required to run the following mods:"); 9 | 10 | for (String dependent : OwoSentinel.listOwoDependents()) { 11 | System.out.println("- " + dependent); 12 | } 13 | 14 | System.out.println("\n" + OwoSentinel.OWO_EXPLANATION); 15 | System.out.print("Download and install (Y/n): "); 16 | 17 | Scanner in = new Scanner(System.in); 18 | boolean install = false; 19 | 20 | try { 21 | String answer = in.next(); 22 | 23 | install = answer.isBlank() || answer.toLowerCase(Locale.ROOT).startsWith("y"); 24 | } catch (Exception e) { 25 | System.out.println(""); 26 | } 27 | 28 | if (install) { 29 | OwoSentinel.downloadAndInstall(System.out::println); 30 | } else { 31 | System.out.println("You can install oωo-lib at https://modrinth.com/mod/owo-lib."); 32 | } 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /owo-sentinel/src/main/resources/fabric.mod.json: -------------------------------------------------------------------------------- 1 | { 2 | "schemaVersion": 1, 3 | "id": "owo-sentinel", 4 | "version": "${version}", 5 | "name": "oωo-sentinel", 6 | "description": "makes u download oωo", 7 | "authors": [ 8 | "glisco" 9 | ], 10 | "contact": {}, 11 | "license": "MIT", 12 | "icon": "owo_sentinel_icon.png", 13 | "environment": "*", 14 | "provides": [ 15 | "owo", 16 | "owo-lib" 17 | ], 18 | "languageAdapters": { 19 | "maldenhagen": "io.wispforest.owosentinel.Maldenhagen" 20 | }, 21 | "depends": { 22 | "fabricloader": "*", 23 | "minecraft": ">=1.18" 24 | }, 25 | "custom": { 26 | "modmenu": { 27 | "links": { 28 | "modmenu.discord": "https://discord.gg/xrwHKktV2d" 29 | }, 30 | "badges": [ 31 | "library" 32 | ] 33 | } 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /owo-sentinel/src/main/resources/owo_sentinel_icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wisp-forest/owo-lib/ef83709dae78614695e2842cd792990e3db28900/owo-sentinel/src/main/resources/owo_sentinel_icon.png -------------------------------------------------------------------------------- /settings.gradle: -------------------------------------------------------------------------------- 1 | pluginManagement { 2 | repositories { 3 | jcenter() 4 | maven { 5 | name = 'Fabric' 6 | url = 'https://maven.fabricmc.net/' 7 | } 8 | gradlePluginPortal() 9 | } 10 | } 11 | 12 | include 'owo-sentinel' -------------------------------------------------------------------------------- /src/main/java/io/wispforest/owo/client/screens/ScreenhandlerMessageData.java: -------------------------------------------------------------------------------- 1 | package io.wispforest.owo.client.screens; 2 | 3 | import io.wispforest.endec.Endec; 4 | import org.jetbrains.annotations.ApiStatus; 5 | 6 | import java.util.function.Consumer; 7 | 8 | @ApiStatus.Internal 9 | public record ScreenhandlerMessageData(int id, boolean clientbound, Endec endec, Consumer handler) {} 10 | -------------------------------------------------------------------------------- /src/main/java/io/wispforest/owo/client/screens/SyncedProperty.java: -------------------------------------------------------------------------------- 1 | package io.wispforest.owo.client.screens; 2 | 3 | import io.wispforest.endec.Endec; 4 | import io.wispforest.endec.SerializationContext; 5 | import io.wispforest.owo.serialization.RegistriesAttribute; 6 | import io.wispforest.owo.util.Observable; 7 | import net.minecraft.network.PacketByteBuf; 8 | import net.minecraft.screen.ScreenHandler; 9 | import org.jetbrains.annotations.ApiStatus; 10 | 11 | public class SyncedProperty extends Observable { 12 | private final int index; 13 | private final Endec endec; 14 | private final ScreenHandler owner; 15 | private boolean needsSync; 16 | 17 | @ApiStatus.Internal 18 | public SyncedProperty(int index, Endec endec, T initial, ScreenHandler owner) { 19 | super(initial); 20 | 21 | this.index = index; 22 | this.endec = endec; 23 | this.owner = owner; 24 | } 25 | 26 | public int index() { 27 | return index; 28 | } 29 | 30 | @ApiStatus.Internal 31 | public boolean needsSync() { 32 | return needsSync; 33 | } 34 | 35 | @ApiStatus.Internal 36 | public void write(PacketByteBuf buf) { 37 | needsSync = false; 38 | buf.write(serializationContext(), this.endec, value); 39 | } 40 | 41 | @ApiStatus.Internal 42 | public void read(PacketByteBuf buf) { 43 | this.set(buf.read(serializationContext(), this.endec)); 44 | } 45 | 46 | @Override 47 | protected void notifyObservers(T value) { 48 | super.notifyObservers(value); 49 | 50 | this.needsSync = true; 51 | } 52 | 53 | public void markDirty() { 54 | notifyObservers(value); 55 | } 56 | 57 | private SerializationContext serializationContext() { 58 | var player = this.owner.player(); 59 | if (player == null) return SerializationContext.empty(); 60 | 61 | return SerializationContext.attributes(RegistriesAttribute.of(player.getRegistryManager())); 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /src/main/java/io/wispforest/owo/client/screens/ValidatingSlot.java: -------------------------------------------------------------------------------- 1 | package io.wispforest.owo.client.screens; 2 | 3 | import net.minecraft.inventory.Inventory; 4 | import net.minecraft.item.ItemStack; 5 | import net.minecraft.screen.slot.Slot; 6 | 7 | import java.util.function.Predicate; 8 | 9 | /** 10 | * A slot that uses the provided {@code insertCondition} 11 | * to decide which items can be inserted 12 | */ 13 | public class ValidatingSlot extends Slot { 14 | 15 | private final Predicate insertCondition; 16 | 17 | public ValidatingSlot(Inventory inventory, int index, int x, int y, Predicate insertCondition) { 18 | super(inventory, index, x, y); 19 | this.insertCondition = insertCondition; 20 | } 21 | 22 | @Override 23 | public boolean canInsert(ItemStack stack) { 24 | return insertCondition.test(stack); 25 | } 26 | 27 | } 28 | -------------------------------------------------------------------------------- /src/main/java/io/wispforest/owo/client/texture/SpriteSheetMetadata.java: -------------------------------------------------------------------------------- 1 | package io.wispforest.owo.client.texture; 2 | 3 | 4 | /** 5 | * A simple container to define the sprite sheet an {@link AnimatedTextureDrawable} uses 6 | * 7 | *

Originally from Animawid, adapted for oωo

8 | * 9 | * @author Tempora 10 | * @author glisco 11 | */ 12 | public record SpriteSheetMetadata(int width, int height, int frameWidth, int frameHeight, int offset) { 13 | 14 | /** 15 | * Creates a new SpriteSheetMetadata object. 16 | * 17 | * @param width The width of the Sprite Sheet. 18 | * @param height The height of the Sprite Sheet. 19 | * @param frameWidth The width of each individual frame 20 | * @param frameHeight The width of each individual frame 21 | */ 22 | public SpriteSheetMetadata(int width, int height, int frameWidth, int frameHeight) { 23 | this(width, height, frameWidth, frameHeight, 0); 24 | } 25 | 26 | /** 27 | * Convenience constructor that assumes both the spritesheet and frames are square 28 | */ 29 | public SpriteSheetMetadata(int size, int frameSize) { 30 | this(size, size, frameSize, frameSize, 0); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/main/java/io/wispforest/owo/compat/emi/OwoEmiPlugin.java: -------------------------------------------------------------------------------- 1 | package io.wispforest.owo.compat.emi; 2 | 3 | import dev.emi.emi.api.EmiPlugin; 4 | import dev.emi.emi.api.EmiRegistry; 5 | import dev.emi.emi.api.widget.Bounds; 6 | import io.wispforest.owo.itemgroup.OwoItemGroup; 7 | import io.wispforest.owo.mixin.itemgroup.CreativeInventoryScreenAccessor; 8 | import io.wispforest.owo.ui.base.BaseOwoHandledScreen; 9 | import io.wispforest.owo.util.pond.OwoCreativeInventoryScreenExtensions; 10 | import net.minecraft.client.gui.screen.ingame.CreativeInventoryScreen; 11 | 12 | public class OwoEmiPlugin implements EmiPlugin { 13 | @Override 14 | public void register(EmiRegistry registry) { 15 | registry.addExclusionArea(CreativeInventoryScreen.class, (screen, consumer) -> { 16 | var group = CreativeInventoryScreenAccessor.owo$getSelectedTab(); 17 | if (!(group instanceof OwoItemGroup owoGroup)) return; 18 | if (owoGroup.getButtons().isEmpty()) return; 19 | 20 | int x = ((OwoCreativeInventoryScreenExtensions) screen).owo$getRootX(); 21 | int y = ((OwoCreativeInventoryScreenExtensions) screen).owo$getRootY(); 22 | 23 | int stackHeight = owoGroup.getButtonStackHeight(); 24 | y -= 13 * (stackHeight - 4); 25 | 26 | for (int i = 0; i < owoGroup.getButtons().size(); i++) { 27 | int xOffset = x + 198 + (i / stackHeight) * 26; 28 | int yOffset = y + 10 + (i % stackHeight) * 30; 29 | consumer.accept(new Bounds(xOffset, yOffset, 24, 24)); 30 | } 31 | }); 32 | 33 | registry.addGenericExclusionArea((screen, consumer) -> { 34 | if (!(screen instanceof BaseOwoHandledScreen owoHandledScreen)) return; 35 | 36 | owoHandledScreen.componentsForExclusionAreas() 37 | .map(component -> new Bounds(component.x(), component.y(), component.width(), component.height())) 38 | .forEach(consumer); 39 | }); 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/main/java/io/wispforest/owo/compat/modmenu/OwoModMenuPlugin.java: -------------------------------------------------------------------------------- 1 | package io.wispforest.owo.compat.modmenu; 2 | 3 | import com.google.common.collect.ForwardingMap; 4 | import com.terraformersmc.modmenu.api.ConfigScreenFactory; 5 | import com.terraformersmc.modmenu.api.ModMenuApi; 6 | import io.wispforest.owo.config.ui.ConfigScreenProviders; 7 | import net.minecraft.util.Util; 8 | import org.jetbrains.annotations.ApiStatus; 9 | import org.jetbrains.annotations.NotNull; 10 | 11 | import java.util.HashMap; 12 | import java.util.Map; 13 | 14 | @ApiStatus.Internal 15 | public class OwoModMenuPlugin implements ModMenuApi { 16 | 17 | private static final Map> OWO_FACTORIES = new ForwardingMap<>() { 18 | @Override 19 | protected @NotNull Map> delegate() { 20 | return Util.make( 21 | new HashMap<>(), 22 | map -> ConfigScreenProviders.forEach((s, provider) -> map.put(s, provider::apply)) 23 | ); 24 | } 25 | }; 26 | 27 | @Override 28 | public Map> getProvidedConfigScreenFactories() { 29 | return OWO_FACTORIES; 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/main/java/io/wispforest/owo/config/annotation/Config.java: -------------------------------------------------------------------------------- 1 | package io.wispforest.owo.config.annotation; 2 | 3 | import java.lang.annotation.ElementType; 4 | import java.lang.annotation.Retention; 5 | import java.lang.annotation.RetentionPolicy; 6 | import java.lang.annotation.Target; 7 | 8 | /** 9 | * Applied to a class to mark is as a config model. This means an 10 | * implementation of {@link io.wispforest.owo.config.ConfigWrapper} 11 | * will be generated which can subsequently be used to manage 12 | * the config data described by the annotated class 13 | * 14 | * @see io.wispforest.owo.config.ConfigWrapper 15 | */ 16 | @Retention(RetentionPolicy.RUNTIME) 17 | @Target(ElementType.TYPE) 18 | public @interface Config { 19 | /** 20 | * @return The name of the wrapper class to generate 21 | */ 22 | String wrapperName(); 23 | 24 | /** 25 | * @return The name under which to save the config 26 | */ 27 | String name(); 28 | 29 | /** 30 | * @return {@code true} if all fields should be treated 31 | * as if they were annotated with {@link Hook} 32 | */ 33 | boolean defaultHook() default false; 34 | 35 | /** 36 | * @return {@code true} if this config should automatically 37 | * be saved whenever it is modified 38 | */ 39 | boolean saveOnModification() default true; 40 | } 41 | -------------------------------------------------------------------------------- /src/main/java/io/wispforest/owo/config/annotation/ExcludeFromScreen.java: -------------------------------------------------------------------------------- 1 | package io.wispforest.owo.config.annotation; 2 | 3 | import java.lang.annotation.ElementType; 4 | import java.lang.annotation.Retention; 5 | import java.lang.annotation.RetentionPolicy; 6 | import java.lang.annotation.Target; 7 | 8 | /** 9 | * Applied to a field to declare that 10 | * it should be ignored when generating 11 | * the config screen 12 | */ 13 | @Retention(RetentionPolicy.RUNTIME) 14 | @Target(ElementType.FIELD) 15 | public @interface ExcludeFromScreen {} 16 | -------------------------------------------------------------------------------- /src/main/java/io/wispforest/owo/config/annotation/Expanded.java: -------------------------------------------------------------------------------- 1 | package io.wispforest.owo.config.annotation; 2 | 3 | import java.lang.annotation.ElementType; 4 | import java.lang.annotation.Retention; 5 | import java.lang.annotation.RetentionPolicy; 6 | import java.lang.annotation.Target; 7 | 8 | /** 9 | * Declares that the annotated, collapsible 10 | * config option (list or nested object) should start 11 | * expanded when the config screen is opened 12 | */ 13 | @Retention(RetentionPolicy.RUNTIME) 14 | @Target(ElementType.FIELD) 15 | public @interface Expanded {} 16 | -------------------------------------------------------------------------------- /src/main/java/io/wispforest/owo/config/annotation/Hook.java: -------------------------------------------------------------------------------- 1 | package io.wispforest.owo.config.annotation; 2 | 3 | import java.lang.annotation.ElementType; 4 | import java.lang.annotation.Target; 5 | 6 | /** 7 | * Applied to a field to declare that a method for 8 | * registering subscribers should be generated 9 | */ 10 | @Target(ElementType.FIELD) 11 | public @interface Hook {} 12 | -------------------------------------------------------------------------------- /src/main/java/io/wispforest/owo/config/annotation/Modmenu.java: -------------------------------------------------------------------------------- 1 | package io.wispforest.owo.config.annotation; 2 | 3 | import java.lang.annotation.ElementType; 4 | import java.lang.annotation.Retention; 5 | import java.lang.annotation.RetentionPolicy; 6 | import java.lang.annotation.Target; 7 | 8 | /** 9 | * Applied to a class also annotated with {@link Config} 10 | * to indicate that a standard owo-config screen should 11 | * automatically be provided to ModMenu. 12 | *

13 | * In case you want more specific control over the generated 14 | * screen, potentially with a special subclass, you should instead 15 | * implement {@link com.terraformersmc.modmenu.api.ModMenuApi} like usual 16 | */ 17 | @Retention(RetentionPolicy.RUNTIME) 18 | @Target(ElementType.TYPE) 19 | public @interface Modmenu { 20 | 21 | /** 22 | * @return The mod ID for which to register 23 | * the config screen factory 24 | */ 25 | String modId(); 26 | 27 | /** 28 | * @return The ID of the UI model to use for the screen. 29 | * You can change this to a model you provide in your 30 | * mod's resources to customize the generated screen 31 | */ 32 | String uiModelId() default "owo:config"; 33 | 34 | } 35 | -------------------------------------------------------------------------------- /src/main/java/io/wispforest/owo/config/annotation/Nest.java: -------------------------------------------------------------------------------- 1 | package io.wispforest.owo.config.annotation; 2 | 3 | import java.lang.annotation.ElementType; 4 | import java.lang.annotation.Retention; 5 | import java.lang.annotation.RetentionPolicy; 6 | import java.lang.annotation.Target; 7 | 8 | /** 9 | * Applied to a class to declare that instances of it 10 | * should be treated as a container for nested options 11 | * within a class annotated with {@link Config} instead of 12 | * as an option in itself 13 | */ 14 | @Retention(RetentionPolicy.RUNTIME) 15 | @Target(ElementType.FIELD) 16 | public @interface Nest {} 17 | -------------------------------------------------------------------------------- /src/main/java/io/wispforest/owo/config/annotation/PredicateConstraint.java: -------------------------------------------------------------------------------- 1 | package io.wispforest.owo.config.annotation; 2 | 3 | import java.lang.annotation.ElementType; 4 | import java.lang.annotation.Retention; 5 | import java.lang.annotation.RetentionPolicy; 6 | import java.lang.annotation.Target; 7 | 8 | /** 9 | * Applied to fields to define the name of a predicate 10 | * method to use for verifying values of said field 11 | */ 12 | @Retention(RetentionPolicy.RUNTIME) 13 | @Target(ElementType.FIELD) 14 | public @interface PredicateConstraint { 15 | String value(); 16 | } 17 | -------------------------------------------------------------------------------- /src/main/java/io/wispforest/owo/config/annotation/RangeConstraint.java: -------------------------------------------------------------------------------- 1 | package io.wispforest.owo.config.annotation; 2 | 3 | import java.lang.annotation.ElementType; 4 | import java.lang.annotation.Retention; 5 | import java.lang.annotation.RetentionPolicy; 6 | import java.lang.annotation.Target; 7 | 8 | /** 9 | * Applied to fields with a numeric value to express 10 | * a range of values which should be accepted 11 | */ 12 | @Retention(RetentionPolicy.RUNTIME) 13 | @Target(ElementType.FIELD) 14 | public @interface RangeConstraint { 15 | double min(); 16 | 17 | double max(); 18 | 19 | /** 20 | * @return How many decimals places to show in the config 21 | * screen, if this is a floating point option 22 | */ 23 | int decimalPlaces() default 2; 24 | } 25 | -------------------------------------------------------------------------------- /src/main/java/io/wispforest/owo/config/annotation/RegexConstraint.java: -------------------------------------------------------------------------------- 1 | package io.wispforest.owo.config.annotation; 2 | 3 | import java.lang.annotation.ElementType; 4 | import java.lang.annotation.Retention; 5 | import java.lang.annotation.RetentionPolicy; 6 | import java.lang.annotation.Target; 7 | 8 | /** 9 | * Applied to fields which can be represented as a {@link CharSequence} 10 | * to define a regular expressions all values need to match 11 | */ 12 | @Retention(RetentionPolicy.RUNTIME) 13 | @Target(ElementType.FIELD) 14 | public @interface RegexConstraint { 15 | 16 | String value(); 17 | 18 | } 19 | -------------------------------------------------------------------------------- /src/main/java/io/wispforest/owo/config/annotation/RestartRequired.java: -------------------------------------------------------------------------------- 1 | package io.wispforest.owo.config.annotation; 2 | 3 | import java.lang.annotation.ElementType; 4 | import java.lang.annotation.Retention; 5 | import java.lang.annotation.RetentionPolicy; 6 | import java.lang.annotation.Target; 7 | 8 | /** 9 | * Applied to a field to indicate 10 | * that changes made to its value will only 11 | * apply after a restart of the game 12 | */ 13 | @Retention(RetentionPolicy.RUNTIME) 14 | @Target(ElementType.FIELD) 15 | public @interface RestartRequired {} 16 | -------------------------------------------------------------------------------- /src/main/java/io/wispforest/owo/config/annotation/SectionHeader.java: -------------------------------------------------------------------------------- 1 | package io.wispforest.owo.config.annotation; 2 | 3 | import java.lang.annotation.ElementType; 4 | import java.lang.annotation.Retention; 5 | import java.lang.annotation.RetentionPolicy; 6 | import java.lang.annotation.Target; 7 | 8 | /** 9 | * Applied to a field to indicate that 10 | * the generated screen should prepend 11 | * a section header to option 12 | */ 13 | @Retention(RetentionPolicy.RUNTIME) 14 | @Target(ElementType.FIELD) 15 | public @interface SectionHeader { 16 | /** 17 | * @return The name of the section describe by this annotation. Used to 18 | * derive a translation key with the pattern {@code text.config..section.} 19 | */ 20 | String value(); 21 | } 22 | -------------------------------------------------------------------------------- /src/main/java/io/wispforest/owo/config/annotation/Sync.java: -------------------------------------------------------------------------------- 1 | package io.wispforest.owo.config.annotation; 2 | 3 | import io.wispforest.owo.config.Option; 4 | 5 | import java.lang.annotation.ElementType; 6 | import java.lang.annotation.Retention; 7 | import java.lang.annotation.RetentionPolicy; 8 | import java.lang.annotation.Target; 9 | 10 | /** 11 | * Applied to a field to indicate that 12 | * its value should be synchronized between server 13 | * and client in some way 14 | */ 15 | @Retention(RetentionPolicy.RUNTIME) 16 | @Target({ElementType.FIELD, ElementType.TYPE}) 17 | public @interface Sync { 18 | Option.SyncMode value(); 19 | } 20 | -------------------------------------------------------------------------------- /src/main/java/io/wispforest/owo/config/annotation/WithAlpha.java: -------------------------------------------------------------------------------- 1 | package io.wispforest.owo.config.annotation; 2 | 3 | import java.lang.annotation.ElementType; 4 | import java.lang.annotation.Retention; 5 | import java.lang.annotation.RetentionPolicy; 6 | import java.lang.annotation.Target; 7 | 8 | /** 9 | * Applied to a config option of type 10 | * {@link io.wispforest.owo.ui.core.Color} to indicate 11 | * that the config screen should expose the alpha 12 | * component 13 | */ 14 | @Retention(RetentionPolicy.RUNTIME) 15 | @Target(ElementType.FIELD) 16 | public @interface WithAlpha {} 17 | -------------------------------------------------------------------------------- /src/main/java/io/wispforest/owo/config/ui/RestartRequiredScreen.java: -------------------------------------------------------------------------------- 1 | package io.wispforest.owo.config.ui; 2 | 3 | import io.wispforest.owo.ui.base.BaseUIModelScreen; 4 | import io.wispforest.owo.ui.component.ButtonComponent; 5 | import io.wispforest.owo.ui.container.FlowLayout; 6 | import io.wispforest.owo.ui.core.Surface; 7 | import net.minecraft.client.MinecraftClient; 8 | import net.minecraft.client.gui.screen.Screen; 9 | import net.minecraft.util.Identifier; 10 | import org.jetbrains.annotations.ApiStatus; 11 | 12 | @ApiStatus.Internal 13 | public class RestartRequiredScreen extends BaseUIModelScreen { 14 | 15 | protected final Screen parent; 16 | 17 | public RestartRequiredScreen(Screen parent) { 18 | super(FlowLayout.class, DataSource.asset(Identifier.of("owo", "restart_required"))); 19 | this.parent = parent; 20 | } 21 | 22 | @Override 23 | public void close() { 24 | this.client.setScreen(parent); 25 | } 26 | 27 | @Override 28 | @SuppressWarnings("ConstantConditions") 29 | protected void build(FlowLayout rootComponent) { 30 | if (this.client.world == null) { 31 | rootComponent.surface(Surface.OPTIONS_BACKGROUND); 32 | } 33 | 34 | rootComponent.childById(ButtonComponent.class, "exit-button") 35 | .onPress(button -> MinecraftClient.getInstance().scheduleStop()); 36 | 37 | rootComponent.childById(ButtonComponent.class, "ignore-button") 38 | .onPress(button -> this.close()); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/main/java/io/wispforest/owo/config/ui/component/ConfigSlider.java: -------------------------------------------------------------------------------- 1 | package io.wispforest.owo.config.ui.component; 2 | 3 | import io.wispforest.owo.ui.component.DiscreteSliderComponent; 4 | import io.wispforest.owo.ui.core.Sizing; 5 | import io.wispforest.owo.util.NumberReflection; 6 | import org.jetbrains.annotations.ApiStatus; 7 | 8 | @ApiStatus.Internal 9 | public class ConfigSlider extends DiscreteSliderComponent implements OptionValueProvider { 10 | 11 | protected Class valueType; 12 | 13 | public ConfigSlider() { 14 | super(Sizing.content(), 0, 1); 15 | } 16 | 17 | public ConfigSlider valueType(Class valueType) { 18 | this.valueType = valueType; 19 | return this; 20 | } 21 | 22 | public ConfigSlider min(double min) { 23 | this.min = min; 24 | return this; 25 | } 26 | 27 | public ConfigSlider max(double max) { 28 | this.max = max; 29 | return this; 30 | } 31 | 32 | @Override 33 | public boolean isValid() { 34 | return true; 35 | } 36 | 37 | @Override 38 | public Object parsedValue() { 39 | double value = this.min + this.value * (this.max - this.min); 40 | if (!NumberReflection.isFloatingPointType(this.valueType)) { 41 | value = Math.round(value); 42 | } 43 | 44 | return NumberReflection.convert(value, this.valueType); 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /src/main/java/io/wispforest/owo/config/ui/component/ConfigToggleButton.java: -------------------------------------------------------------------------------- 1 | package io.wispforest.owo.config.ui.component; 2 | 3 | import io.wispforest.owo.ui.component.ButtonComponent; 4 | import io.wispforest.owo.ui.core.Sizing; 5 | import net.minecraft.text.Text; 6 | import org.jetbrains.annotations.ApiStatus; 7 | 8 | @ApiStatus.Internal 9 | public class ConfigToggleButton extends ButtonComponent implements OptionValueProvider { 10 | 11 | protected static final Text ENABLED_MESSAGE = Text.translatable("text.owo.config.boolean_toggle.enabled"); 12 | protected static final Text DISABLED_MESSAGE = Text.translatable("text.owo.config.boolean_toggle.disabled"); 13 | 14 | protected boolean enabled = false; 15 | 16 | public ConfigToggleButton() { 17 | super(Text.empty(), button -> {}); 18 | this.verticalSizing(Sizing.fixed(20)); 19 | this.updateMessage(); 20 | } 21 | 22 | @Override 23 | public void onPress() { 24 | this.enabled = !this.enabled; 25 | this.updateMessage(); 26 | super.onPress(); 27 | } 28 | 29 | protected void updateMessage() { 30 | this.setMessage(this.enabled ? ENABLED_MESSAGE : DISABLED_MESSAGE); 31 | } 32 | 33 | public ConfigToggleButton enabled(boolean enabled) { 34 | this.enabled = enabled; 35 | this.updateMessage(); 36 | return this; 37 | } 38 | 39 | @Override 40 | public boolean isValid() { 41 | return true; 42 | } 43 | 44 | @Override 45 | public Object parsedValue() { 46 | return this.enabled; 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /src/main/java/io/wispforest/owo/config/ui/component/OptionValueProvider.java: -------------------------------------------------------------------------------- 1 | package io.wispforest.owo.config.ui.component; 2 | 3 | public interface OptionValueProvider { 4 | 5 | /** 6 | * @return {@code true} if the current state of this component 7 | * describes a valid value for the option it is linked to 8 | */ 9 | boolean isValid(); 10 | 11 | /** 12 | * @return The value described by the current state 13 | * of this component 14 | */ 15 | Object parsedValue(); 16 | } 17 | -------------------------------------------------------------------------------- /src/main/java/io/wispforest/owo/ext/OwoItem.java: -------------------------------------------------------------------------------- 1 | package io.wispforest.owo.ext; 2 | 3 | import net.minecraft.component.ComponentChanges; 4 | import net.minecraft.component.ComponentMap; 5 | import org.jetbrains.annotations.ApiStatus; 6 | 7 | public interface OwoItem { 8 | /** 9 | * Generates component-derived-components from the stack's components 10 | * @param source a map containing the item stack's non-derived components 11 | * @param target a builder for the derived component map 12 | */ 13 | @ApiStatus.Experimental 14 | default void deriveStackComponents(ComponentMap source, ComponentChanges.Builder target) { } 15 | } 16 | -------------------------------------------------------------------------------- /src/main/java/io/wispforest/owo/itemgroup/ItemGroupReference.java: -------------------------------------------------------------------------------- 1 | package io.wispforest.owo.itemgroup; 2 | 3 | public record ItemGroupReference(OwoItemGroup group, int tab) {} 4 | -------------------------------------------------------------------------------- /src/main/java/io/wispforest/owo/itemgroup/gui/ItemGroupButtonWidget.java: -------------------------------------------------------------------------------- 1 | package io.wispforest.owo.itemgroup.gui; 2 | 3 | import com.mojang.blaze3d.systems.RenderSystem; 4 | import io.wispforest.owo.itemgroup.OwoItemGroup; 5 | import net.minecraft.client.gui.DrawContext; 6 | import net.minecraft.client.gui.widget.ButtonWidget; 7 | import net.minecraft.client.render.RenderLayer; 8 | import org.jetbrains.annotations.ApiStatus; 9 | 10 | import java.util.function.Consumer; 11 | 12 | @ApiStatus.Internal 13 | public class ItemGroupButtonWidget extends ButtonWidget { 14 | 15 | public boolean isSelected = false; 16 | private final OwoItemGroup.ButtonDefinition definition; 17 | private final int baseU; 18 | 19 | public ItemGroupButtonWidget(int x, int y, int baseU, OwoItemGroup.ButtonDefinition definition, Consumer onPress) { 20 | super(x, y, 24, 24, definition.tooltip(), button -> onPress.accept((ItemGroupButtonWidget) button), ButtonWidget.DEFAULT_NARRATION_SUPPLIER); 21 | this.baseU = baseU; 22 | this.definition = definition; 23 | } 24 | 25 | @Override 26 | public void renderWidget(DrawContext context, int mouseX, int mouseY, float delta) { 27 | context.drawTexture(RenderLayer::getGuiTextured, this.definition.texture(), this.getX(), this.getY(), this.baseU, this.isSelected() || this.isSelected ? this.height : 0, this.width, this.height, 64, 64); 28 | 29 | this.definition.icon().render(context, this.getX() + 4, this.getY() + 4, mouseX, mouseY, delta); 30 | } 31 | 32 | public boolean isTab() { 33 | return this.definition instanceof ItemGroupTab; 34 | } 35 | 36 | public boolean trulyHovered() { 37 | return this.hovered; 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /src/main/java/io/wispforest/owo/itemgroup/gui/ItemGroupTab.java: -------------------------------------------------------------------------------- 1 | package io.wispforest.owo.itemgroup.gui; 2 | 3 | import io.wispforest.owo.itemgroup.Icon; 4 | import io.wispforest.owo.itemgroup.OwoItemGroup; 5 | import io.wispforest.owo.itemgroup.OwoItemSettingsExtension; 6 | import net.minecraft.item.ItemGroup; 7 | import net.minecraft.text.Text; 8 | import net.minecraft.util.Identifier; 9 | 10 | /** 11 | * Represents a tab inside an {@link OwoItemGroup} that contains all items in the 12 | * passed {@code contentTag}. If you want to use {@link OwoItemSettingsExtension#tab(int)} to 13 | * define the contents, use {@code null} as the tag 14 | */ 15 | public record ItemGroupTab( 16 | Icon icon, 17 | Text name, 18 | ContentSupplier contentSupplier, 19 | Identifier texture, 20 | boolean primary 21 | ) implements OwoItemGroup.ButtonDefinition { 22 | 23 | public static final Identifier DEFAULT_TEXTURE = Identifier.of("owo", "textures/gui/tabs.png"); 24 | 25 | @Override 26 | public Text tooltip() { 27 | return this.name; 28 | } 29 | 30 | @FunctionalInterface 31 | public interface ContentSupplier { 32 | void addItems(ItemGroup.DisplayContext context, ItemGroup.Entries entries); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/main/java/io/wispforest/owo/mixin/CachedRegistryInfoGetterAccessor.java: -------------------------------------------------------------------------------- 1 | package io.wispforest.owo.mixin; 2 | 3 | import net.minecraft.registry.RegistryOps; 4 | import net.minecraft.registry.RegistryWrapper; 5 | import org.spongepowered.asm.mixin.Mixin; 6 | import org.spongepowered.asm.mixin.gen.Accessor; 7 | 8 | @Mixin(RegistryOps.CachedRegistryInfoGetter.class) 9 | public interface CachedRegistryInfoGetterAccessor { 10 | @Accessor("registries") RegistryWrapper.WrapperLookup owo$getRegistriesLookup(); 11 | } 12 | -------------------------------------------------------------------------------- /src/main/java/io/wispforest/owo/mixin/ClientCommonNetworkHandlerAccessor.java: -------------------------------------------------------------------------------- 1 | package io.wispforest.owo.mixin; 2 | 3 | import net.minecraft.client.network.ClientCommonNetworkHandler; 4 | import net.minecraft.network.ClientConnection; 5 | import org.spongepowered.asm.mixin.Mixin; 6 | import org.spongepowered.asm.mixin.gen.Accessor; 7 | 8 | @Mixin(ClientCommonNetworkHandler.class) 9 | public interface ClientCommonNetworkHandlerAccessor { 10 | @Accessor 11 | ClientConnection getConnection(); 12 | } 13 | -------------------------------------------------------------------------------- /src/main/java/io/wispforest/owo/mixin/ClientConfigurationNetworkHandlerMixin.java: -------------------------------------------------------------------------------- 1 | package io.wispforest.owo.mixin; 2 | 3 | import io.wispforest.owo.network.OwoClientConnectionExtension; 4 | import io.wispforest.owo.network.QueuedChannelSet; 5 | import net.minecraft.client.network.ClientConfigurationNetworkHandler; 6 | import net.minecraft.network.ClientConnection; 7 | import org.spongepowered.asm.mixin.Mixin; 8 | import org.spongepowered.asm.mixin.injection.At; 9 | import org.spongepowered.asm.mixin.injection.ModifyArg; 10 | 11 | @Mixin(ClientConfigurationNetworkHandler.class) 12 | public class ClientConfigurationNetworkHandlerMixin { 13 | 14 | @ModifyArg(method = "onReady", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/network/ClientPlayNetworkHandler;(Lnet/minecraft/client/MinecraftClient;Lnet/minecraft/network/ClientConnection;Lnet/minecraft/client/network/ClientConnectionState;)V")) 15 | private ClientConnection applyChannelSet(ClientConnection connection) { 16 | ((OwoClientConnectionExtension) connection).owo$setChannelSet(QueuedChannelSet.channels); 17 | QueuedChannelSet.channels = null; 18 | 19 | return connection; 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/main/java/io/wispforest/owo/mixin/ClientConnectionMixin.java: -------------------------------------------------------------------------------- 1 | package io.wispforest.owo.mixin; 2 | 3 | import io.wispforest.owo.network.OwoClientConnectionExtension; 4 | import net.minecraft.network.ClientConnection; 5 | import net.minecraft.util.Identifier; 6 | import org.spongepowered.asm.mixin.Mixin; 7 | 8 | import java.util.Collections; 9 | import java.util.Set; 10 | 11 | @Mixin(ClientConnection.class) 12 | public class ClientConnectionMixin implements OwoClientConnectionExtension { 13 | private Set channels = Collections.emptySet(); 14 | 15 | @Override 16 | public void owo$setChannelSet(Set channels) { 17 | this.channels = channels; 18 | } 19 | 20 | @Override 21 | public Set owo$getChannelSet() { 22 | return this.channels; 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/main/java/io/wispforest/owo/mixin/ClientLoginNetworkHandlerAccessor.java: -------------------------------------------------------------------------------- 1 | package io.wispforest.owo.mixin; 2 | 3 | import net.minecraft.client.network.ClientLoginNetworkHandler; 4 | import net.minecraft.network.ClientConnection; 5 | import org.spongepowered.asm.mixin.Mixin; 6 | import org.spongepowered.asm.mixin.gen.Accessor; 7 | 8 | @Mixin(ClientLoginNetworkHandler.class) 9 | public interface ClientLoginNetworkHandlerAccessor { 10 | 11 | @Accessor("connection") 12 | ClientConnection owo$getConnection(); 13 | 14 | } 15 | -------------------------------------------------------------------------------- /src/main/java/io/wispforest/owo/mixin/ComponentTypeBuilderMixin.java: -------------------------------------------------------------------------------- 1 | package io.wispforest.owo.mixin; 2 | 3 | import io.wispforest.owo.serialization.OwoComponentTypeBuilder; 4 | import net.minecraft.component.ComponentType; 5 | import org.spongepowered.asm.mixin.Mixin; 6 | 7 | @Mixin(ComponentType.Builder.class) 8 | public class ComponentTypeBuilderMixin implements OwoComponentTypeBuilder { 9 | } 10 | -------------------------------------------------------------------------------- /src/main/java/io/wispforest/owo/mixin/DrawContextMixin.java: -------------------------------------------------------------------------------- 1 | package io.wispforest.owo.mixin; 2 | 3 | import io.wispforest.owo.ui.util.MatrixStackTransformer; 4 | import net.minecraft.client.gui.DrawContext; 5 | import net.minecraft.client.util.math.MatrixStack; 6 | import org.spongepowered.asm.mixin.Mixin; 7 | import org.spongepowered.asm.mixin.Shadow; 8 | 9 | @Mixin(DrawContext.class) 10 | public abstract class DrawContextMixin implements MatrixStackTransformer { 11 | 12 | @Shadow public abstract MatrixStack getMatrices(); 13 | 14 | @Override 15 | public MatrixStack getMatrixStack() { 16 | return getMatrices(); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/main/java/io/wispforest/owo/mixin/ForwardingDynamicOpsAccessor.java: -------------------------------------------------------------------------------- 1 | package io.wispforest.owo.mixin; 2 | 3 | import com.mojang.serialization.DynamicOps; 4 | import net.minecraft.util.dynamic.ForwardingDynamicOps; 5 | import org.spongepowered.asm.mixin.Mixin; 6 | import org.spongepowered.asm.mixin.gen.Accessor; 7 | 8 | @Mixin(ForwardingDynamicOps.class) 9 | public interface ForwardingDynamicOpsAccessor { 10 | @Accessor("delegate") 11 | DynamicOps owo$delegate(); 12 | } 13 | -------------------------------------------------------------------------------- /src/main/java/io/wispforest/owo/mixin/GlResourceManagerMixin.java: -------------------------------------------------------------------------------- 1 | package io.wispforest.owo.mixin; 2 | 3 | import io.wispforest.owo.ui.core.OwoUIDrawContext; 4 | import net.minecraft.client.gl.GlResourceManager; 5 | import org.spongepowered.asm.mixin.Mixin; 6 | import org.spongepowered.asm.mixin.injection.At; 7 | import org.spongepowered.asm.mixin.injection.ModifyArgs; 8 | import org.spongepowered.asm.mixin.injection.invoke.arg.Args; 9 | 10 | @Mixin(GlResourceManager.class) 11 | public class GlResourceManagerMixin { 12 | 13 | @ModifyArgs( 14 | method = "createRenderPass(Lcom/mojang/blaze3d/textures/GpuTexture;Ljava/util/OptionalInt;Lcom/mojang/blaze3d/textures/GpuTexture;Ljava/util/OptionalDouble;)Lcom/mojang/blaze3d/systems/RenderPass;", 15 | at = @At(value = "INVOKE", target = "Lcom/mojang/blaze3d/opengl/GlStateManager;_viewport(IIII)V") 16 | ) 17 | public void injectViewportOverride(Args args) { 18 | if (OwoUIDrawContext.viewportOverride == null) return; 19 | args.set(0, OwoUIDrawContext.viewportOverride.position().x()); 20 | args.set(1, OwoUIDrawContext.viewportOverride.position().y()); 21 | args.set(2, OwoUIDrawContext.viewportOverride.width()); 22 | args.set(3, OwoUIDrawContext.viewportOverride.height()); 23 | } 24 | 25 | } 26 | -------------------------------------------------------------------------------- /src/main/java/io/wispforest/owo/mixin/MainMixin.java: -------------------------------------------------------------------------------- 1 | package io.wispforest.owo.mixin; 2 | 3 | import io.wispforest.owo.util.OwoFreezer; 4 | import net.minecraft.server.Main; 5 | import org.spongepowered.asm.mixin.Mixin; 6 | import org.spongepowered.asm.mixin.injection.At; 7 | import org.spongepowered.asm.mixin.injection.Group; 8 | import org.spongepowered.asm.mixin.injection.Inject; 9 | import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; 10 | 11 | @Mixin(value = Main.class, priority = 0) 12 | public class MainMixin { 13 | 14 | @SuppressWarnings({"MixinAnnotationTarget", "UnresolvedMixinReference"}) 15 | @Group(name = "serverFreezeHooks", min = 1, max = 1) 16 | @Inject(method = "main", at = @At(value = "INVOKE", remap = false, 17 | target = "Lnet/fabricmc/loader/impl/game/minecraft/Hooks;startServer(Ljava/io/File;Ljava/lang/Object;)V", shift = At.Shift.AFTER)) 18 | private static void afterFabricHook(CallbackInfo ci) { 19 | OwoFreezer.freeze(); 20 | } 21 | 22 | @SuppressWarnings({"MixinAnnotationTarget", "UnresolvedMixinReference"}) 23 | @Group(name = "serverFreezeHooks", min = 1, max = 1) 24 | @Inject(method = "main", at = @At(value = "INVOKE", remap = false, 25 | target = "Lorg/quiltmc/loader/impl/game/minecraft/Hooks;startServer(Ljava/io/File;Ljava/lang/Object;)V", shift = At.Shift.AFTER)) 26 | private static void afterQuiltHook(CallbackInfo ci) { 27 | OwoFreezer.freeze(); 28 | } 29 | 30 | } 31 | -------------------------------------------------------------------------------- /src/main/java/io/wispforest/owo/mixin/MinecraftClientMixin.java: -------------------------------------------------------------------------------- 1 | package io.wispforest.owo.mixin; 2 | 3 | import io.wispforest.owo.util.OwoFreezer; 4 | import net.minecraft.client.MinecraftClient; 5 | import net.minecraft.client.RunArgs; 6 | import org.spongepowered.asm.mixin.Mixin; 7 | import org.spongepowered.asm.mixin.injection.At; 8 | import org.spongepowered.asm.mixin.injection.Group; 9 | import org.spongepowered.asm.mixin.injection.Inject; 10 | import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; 11 | 12 | @Mixin(value = MinecraftClient.class, priority = 0) 13 | public class MinecraftClientMixin { 14 | 15 | @SuppressWarnings({"MixinAnnotationTarget", "UnresolvedMixinReference"}) 16 | @Group(name = "clientFreezeHooks", min = 1, max = 1) 17 | @Inject(method = "", at = @At(value = "INVOKE", remap = false, 18 | target = "Lnet/fabricmc/loader/impl/game/minecraft/Hooks;startClient(Ljava/io/File;Ljava/lang/Object;)V", shift = At.Shift.AFTER)) 19 | private void afterFabricHook(RunArgs args, CallbackInfo ci) { 20 | OwoFreezer.freeze(); 21 | } 22 | 23 | @SuppressWarnings({"MixinAnnotationTarget", "UnresolvedMixinReference"}) 24 | @Group(name = "clientFreezeHooks", min = 1, max = 1) 25 | @Inject(method = "", at = @At(value = "INVOKE", remap = false, 26 | target = "Lorg/quiltmc/loader/impl/game/minecraft/Hooks;startClient(Ljava/io/File;Ljava/lang/Object;)V", shift = At.Shift.AFTER)) 27 | private void afterQuiltHook(RunArgs args, CallbackInfo ci) { 28 | OwoFreezer.freeze(); 29 | } 30 | 31 | } 32 | 33 | -------------------------------------------------------------------------------- /src/main/java/io/wispforest/owo/mixin/NbtCompoundMixin.java: -------------------------------------------------------------------------------- 1 | package io.wispforest.owo.mixin; 2 | 3 | import io.wispforest.endec.SerializationAttributes; 4 | import io.wispforest.endec.SerializationContext; 5 | import io.wispforest.endec.impl.KeyedEndec; 6 | import io.wispforest.endec.util.MapCarrier; 7 | import io.wispforest.owo.serialization.format.nbt.NbtDeserializer; 8 | import io.wispforest.owo.serialization.format.nbt.NbtSerializer; 9 | import net.minecraft.nbt.NbtCompound; 10 | import net.minecraft.nbt.NbtElement; 11 | import org.jetbrains.annotations.NotNull; 12 | import org.jetbrains.annotations.Nullable; 13 | import org.spongepowered.asm.mixin.Mixin; 14 | import org.spongepowered.asm.mixin.Shadow; 15 | 16 | @SuppressWarnings("AddedMixinMembersNamePattern") 17 | @Mixin(NbtCompound.class) 18 | public abstract class NbtCompoundMixin implements MapCarrier { 19 | 20 | @Shadow 21 | public abstract @Nullable NbtElement get(String key); 22 | @Shadow 23 | public abstract @Nullable NbtElement put(String key, NbtElement element); 24 | @Shadow 25 | public abstract void remove(String key); 26 | @Shadow 27 | public abstract boolean contains(String key); 28 | 29 | @Override 30 | public T getWithErrors(SerializationContext ctx, @NotNull KeyedEndec key) { 31 | if (!this.has(key)) return key.defaultValue(); 32 | return key.endec().decodeFully(ctx.withAttributes(SerializationAttributes.HUMAN_READABLE), NbtDeserializer::of, this.get(key.key())); 33 | } 34 | 35 | @Override 36 | public void put(SerializationContext ctx, @NotNull KeyedEndec key, @NotNull T value) { 37 | this.put(key.key(), key.endec().encodeFully(ctx.withAttributes(SerializationAttributes.HUMAN_READABLE), NbtSerializer::of, value)); 38 | } 39 | 40 | @Override 41 | public void delete(@NotNull KeyedEndec key) { 42 | this.remove(key.key()); 43 | } 44 | 45 | @Override 46 | public boolean has(@NotNull KeyedEndec key) { 47 | return this.contains(key.key()); 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /src/main/java/io/wispforest/owo/mixin/PacketByteBufMixin.java: -------------------------------------------------------------------------------- 1 | package io.wispforest.owo.mixin; 2 | 3 | import io.wispforest.endec.Endec; 4 | import io.wispforest.endec.SerializationContext; 5 | import io.wispforest.endec.format.bytebuf.ByteBufDeserializer; 6 | import io.wispforest.endec.format.bytebuf.ByteBufSerializer; 7 | import io.wispforest.endec.util.EndecBuffer; 8 | import net.minecraft.network.PacketByteBuf; 9 | import org.spongepowered.asm.mixin.Mixin; 10 | 11 | @SuppressWarnings({"DataFlowIssue", "AddedMixinMembersNamePattern"}) 12 | @Mixin(PacketByteBuf.class) 13 | public class PacketByteBufMixin implements EndecBuffer { 14 | @Override 15 | public void write(SerializationContext ctx, Endec endec, T value) { 16 | endec.encodeFully(ctx, () -> ByteBufSerializer.of((PacketByteBuf) (Object) this), value); 17 | } 18 | 19 | @Override 20 | public T read(SerializationContext ctx, Endec endec) { 21 | return endec.decodeFully(ctx, ByteBufDeserializer::of, (PacketByteBuf) (Object) this); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/main/java/io/wispforest/owo/mixin/RegistryOpsAccessor.java: -------------------------------------------------------------------------------- 1 | package io.wispforest.owo.mixin; 2 | 3 | import net.minecraft.registry.RegistryOps; 4 | import org.spongepowered.asm.mixin.Mixin; 5 | import org.spongepowered.asm.mixin.gen.Accessor; 6 | 7 | @Mixin(RegistryOps.class) 8 | public interface RegistryOpsAccessor { 9 | @Accessor("registryInfoGetter") 10 | RegistryOps.RegistryInfoGetter owo$infoGetter(); 11 | } 12 | -------------------------------------------------------------------------------- /src/main/java/io/wispforest/owo/mixin/ScreenAccessor.java: -------------------------------------------------------------------------------- 1 | package io.wispforest.owo.mixin; 2 | 3 | import net.minecraft.client.gui.CubeMapRenderer; 4 | import net.minecraft.client.gui.RotatingCubeMapRenderer; 5 | import net.minecraft.client.gui.screen.Screen; 6 | import org.spongepowered.asm.mixin.Mixin; 7 | import org.spongepowered.asm.mixin.gen.Accessor; 8 | 9 | @Mixin(Screen.class) 10 | public interface ScreenAccessor { 11 | @Accessor("PANORAMA_RENDERER") 12 | static CubeMapRenderer owo$PANORAMA_RENDERER() { 13 | throw new UnsupportedOperationException(); 14 | } 15 | 16 | @Accessor("ROTATING_PANORAMA_RENDERER") 17 | static RotatingCubeMapRenderer owo$ROTATING_PANORAMA_RENDERER() { 18 | throw new UnsupportedOperationException(); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/main/java/io/wispforest/owo/mixin/ScreenHandlerInvoker.java: -------------------------------------------------------------------------------- 1 | package io.wispforest.owo.mixin; 2 | 3 | import net.minecraft.item.ItemStack; 4 | import net.minecraft.screen.ScreenHandler; 5 | import org.spongepowered.asm.mixin.Mixin; 6 | import org.spongepowered.asm.mixin.gen.Invoker; 7 | 8 | @Mixin(ScreenHandler.class) 9 | public interface ScreenHandlerInvoker { 10 | 11 | @Invoker("insertItem") 12 | boolean owo$insertItem(ItemStack stack, int startIndex, int endIndex, boolean fromLast); 13 | 14 | } 15 | -------------------------------------------------------------------------------- /src/main/java/io/wispforest/owo/mixin/ServerCommonNetworkHandlerAccessor.java: -------------------------------------------------------------------------------- 1 | package io.wispforest.owo.mixin; 2 | 3 | import net.minecraft.network.ClientConnection; 4 | import net.minecraft.server.network.ServerCommonNetworkHandler; 5 | import net.minecraft.server.network.ServerPlayNetworkHandler; 6 | import org.spongepowered.asm.mixin.Mixin; 7 | import org.spongepowered.asm.mixin.gen.Accessor; 8 | 9 | @Mixin(ServerCommonNetworkHandler.class) 10 | public interface ServerCommonNetworkHandlerAccessor { 11 | 12 | @Accessor("connection") 13 | ClientConnection owo$getConnection(); 14 | 15 | } 16 | -------------------------------------------------------------------------------- /src/main/java/io/wispforest/owo/mixin/ServerPlayerEntityMixin.java: -------------------------------------------------------------------------------- 1 | package io.wispforest.owo.mixin; 2 | 3 | import io.wispforest.owo.util.pond.OwoScreenHandlerExtension; 4 | import net.minecraft.screen.ScreenHandler; 5 | import net.minecraft.server.network.ServerPlayerEntity; 6 | import org.spongepowered.asm.mixin.Mixin; 7 | import org.spongepowered.asm.mixin.injection.At; 8 | import org.spongepowered.asm.mixin.injection.Inject; 9 | import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; 10 | 11 | @Mixin(ServerPlayerEntity.class) 12 | public class ServerPlayerEntityMixin { 13 | 14 | @SuppressWarnings("ConstantConditions") 15 | @Inject(method = "onScreenHandlerOpened", at = @At("HEAD")) 16 | private void attachScreenHandler(ScreenHandler screenHandler, CallbackInfo ci) { 17 | ((OwoScreenHandlerExtension) screenHandler).owo$attachToPlayer((ServerPlayerEntity) (Object) this); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/main/java/io/wispforest/owo/mixin/ServerPlayerInteractionManagerMixin.java: -------------------------------------------------------------------------------- 1 | package io.wispforest.owo.mixin; 2 | 3 | import io.wispforest.owo.util.pond.OwoItemExtensions; 4 | import net.minecraft.item.ItemStack; 5 | import net.minecraft.server.network.ServerPlayerEntity; 6 | import net.minecraft.server.network.ServerPlayerInteractionManager; 7 | import net.minecraft.stat.Stats; 8 | import net.minecraft.util.ActionResult; 9 | import net.minecraft.util.Hand; 10 | import net.minecraft.world.World; 11 | import org.spongepowered.asm.mixin.Mixin; 12 | import org.spongepowered.asm.mixin.injection.At; 13 | import org.spongepowered.asm.mixin.injection.Inject; 14 | import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; 15 | 16 | @Mixin(ServerPlayerInteractionManager.class) 17 | public class ServerPlayerInteractionManagerMixin { 18 | 19 | @Inject(method = "interactItem", at = @At("RETURN")) 20 | private void incrementUseState(ServerPlayerEntity player, World world, ItemStack stack, Hand hand, CallbackInfoReturnable cir) { 21 | var result = cir.getReturnValue(); 22 | 23 | if(((OwoItemExtensions) stack.getItem()).owo$shouldTrackUsageStat() || (result instanceof ActionResult.Success successResult && successResult.shouldIncrementStat())) { 24 | player.incrementStat(Stats.USED.getOrCreateStat(stack.getItem())); 25 | } 26 | } 27 | 28 | } 29 | -------------------------------------------------------------------------------- /src/main/java/io/wispforest/owo/mixin/SetComponentsLootFunctionAccessor.java: -------------------------------------------------------------------------------- 1 | package io.wispforest.owo.mixin; 2 | 3 | import net.minecraft.component.ComponentChanges; 4 | import net.minecraft.loot.condition.LootCondition; 5 | import net.minecraft.loot.function.SetComponentsLootFunction; 6 | import org.spongepowered.asm.mixin.Mixin; 7 | import org.spongepowered.asm.mixin.gen.Invoker; 8 | 9 | import java.util.List; 10 | 11 | @Mixin(SetComponentsLootFunction.class) 12 | public interface SetComponentsLootFunctionAccessor { 13 | @Invoker("") 14 | static SetComponentsLootFunction createSetComponentsLootFunction(List list, ComponentChanges componentChanges) { 15 | throw new UnsupportedOperationException(); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/main/java/io/wispforest/owo/mixin/TagGroupLoaderMixin.java: -------------------------------------------------------------------------------- 1 | package io.wispforest.owo.mixin; 2 | 3 | import io.wispforest.owo.util.TagInjector; 4 | import net.minecraft.registry.tag.TagGroupLoader; 5 | import net.minecraft.resource.ResourceManager; 6 | import net.minecraft.util.Identifier; 7 | import org.spongepowered.asm.mixin.Final; 8 | import org.spongepowered.asm.mixin.Mixin; 9 | import org.spongepowered.asm.mixin.Shadow; 10 | import org.spongepowered.asm.mixin.injection.At; 11 | import org.spongepowered.asm.mixin.injection.Inject; 12 | import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; 13 | 14 | import java.util.ArrayList; 15 | import java.util.List; 16 | import java.util.Map; 17 | 18 | @Mixin(TagGroupLoader.class) 19 | public class TagGroupLoaderMixin { 20 | 21 | @Shadow 22 | @Final 23 | private String dataType; 24 | 25 | @Inject(method = "loadTags", at = @At("TAIL")) 26 | public void injectValues(ResourceManager manager, CallbackInfoReturnable>> cir) { 27 | var map = cir.getReturnValue(); 28 | 29 | TagInjector.ADDITIONS.forEach((location, entries) -> { 30 | if (!this.dataType.equals(location.type())) return; 31 | 32 | var list = map.computeIfAbsent(location.tagId(), id -> new ArrayList<>()); 33 | entries.forEach(addition -> list.add(new TagGroupLoader.TrackedEntry(addition, "owo"))); 34 | }); 35 | } 36 | 37 | } 38 | -------------------------------------------------------------------------------- /src/main/java/io/wispforest/owo/mixin/ext/ItemMixin.java: -------------------------------------------------------------------------------- 1 | package io.wispforest.owo.mixin.ext; 2 | 3 | import io.wispforest.owo.ext.OwoItem; 4 | import net.minecraft.item.Item; 5 | import org.spongepowered.asm.mixin.Mixin; 6 | 7 | @Mixin(Item.class) 8 | public class ItemMixin implements OwoItem { 9 | } 10 | -------------------------------------------------------------------------------- /src/main/java/io/wispforest/owo/mixin/ext/MergedComponentMapAccessor.java: -------------------------------------------------------------------------------- 1 | package io.wispforest.owo.mixin.ext; 2 | 3 | import net.minecraft.component.ComponentMap; 4 | import net.minecraft.component.MergedComponentMap; 5 | import org.spongepowered.asm.mixin.Mixin; 6 | import org.spongepowered.asm.mixin.Mutable; 7 | import org.spongepowered.asm.mixin.gen.Accessor; 8 | 9 | @Mixin(MergedComponentMap.class) 10 | public interface MergedComponentMapAccessor { 11 | @Accessor("baseComponents") 12 | ComponentMap owo$getBaseComponents(); 13 | 14 | @Accessor("baseComponents") 15 | @Mutable 16 | void owo$setBaseComponents(ComponentMap baseComponents); 17 | } 18 | -------------------------------------------------------------------------------- /src/main/java/io/wispforest/owo/mixin/ext/MergedComponentMapMixin.java: -------------------------------------------------------------------------------- 1 | package io.wispforest.owo.mixin.ext; 2 | 3 | import com.llamalad7.mixinextras.injector.ModifyExpressionValue; 4 | import io.wispforest.owo.ext.DerivedComponentMap; 5 | import net.minecraft.component.ComponentMap; 6 | import net.minecraft.component.MergedComponentMap; 7 | import org.spongepowered.asm.mixin.Mixin; 8 | import org.spongepowered.asm.mixin.injection.At; 9 | 10 | @Mixin(MergedComponentMap.class) 11 | public class MergedComponentMapMixin { 12 | @ModifyExpressionValue(method = "copy", at = @At(value = "FIELD", target = "Lnet/minecraft/component/MergedComponentMap;baseComponents:Lnet/minecraft/component/ComponentMap;")) 13 | private ComponentMap reWrapDerived(ComponentMap original) { 14 | return DerivedComponentMap.reWrapIfNeeded(original); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/main/java/io/wispforest/owo/mixin/itemgroup/CreativeInventoryScreenAccessor.java: -------------------------------------------------------------------------------- 1 | package io.wispforest.owo.mixin.itemgroup; 2 | 3 | import net.minecraft.client.gui.screen.ingame.CreativeInventoryScreen; 4 | import net.minecraft.item.ItemGroup; 5 | import org.spongepowered.asm.mixin.Mixin; 6 | import org.spongepowered.asm.mixin.gen.Accessor; 7 | 8 | @Mixin(CreativeInventoryScreen.class) 9 | public interface CreativeInventoryScreenAccessor { 10 | 11 | @Accessor("selectedTab") 12 | static ItemGroup owo$getSelectedTab() { 13 | throw new IllegalStateException("Mixin stub must not be called"); 14 | } 15 | 16 | } 17 | -------------------------------------------------------------------------------- /src/main/java/io/wispforest/owo/mixin/itemgroup/ItemGroupAccessor.java: -------------------------------------------------------------------------------- 1 | package io.wispforest.owo.mixin.itemgroup; 2 | 3 | import net.minecraft.item.ItemGroup; 4 | import net.minecraft.item.ItemStack; 5 | import net.minecraft.text.Text; 6 | import org.spongepowered.asm.mixin.Mixin; 7 | import org.spongepowered.asm.mixin.Mutable; 8 | import org.spongepowered.asm.mixin.gen.Accessor; 9 | 10 | import java.util.List; 11 | import java.util.Set; 12 | import java.util.function.Consumer; 13 | 14 | @Mixin(ItemGroup.class) 15 | public interface ItemGroupAccessor { 16 | 17 | @Accessor("entryCollector") 18 | ItemGroup.EntryCollector owo$getEntryCollector(); 19 | 20 | @Mutable 21 | @Accessor("entryCollector") 22 | void owo$setEntryCollector(ItemGroup.EntryCollector collector); 23 | 24 | @Accessor("searchTabStacks") 25 | void owo$setSearchTabStacks(Set searchTabStacks); 26 | 27 | @Mutable 28 | @Accessor("displayName") 29 | void owo$setDisplayName(Text displayName); 30 | 31 | @Mutable 32 | @Accessor("column") 33 | void owo$setColumn(int column); 34 | 35 | @Mutable 36 | @Accessor("row") 37 | void owo$setRow(ItemGroup.Row row); 38 | } 39 | -------------------------------------------------------------------------------- /src/main/java/io/wispforest/owo/mixin/itemgroup/StatusEffectsDisplayMixin.java: -------------------------------------------------------------------------------- 1 | package io.wispforest.owo.mixin.itemgroup; 2 | 3 | import io.wispforest.owo.itemgroup.OwoItemGroup; 4 | import net.minecraft.client.gui.screen.ingame.CreativeInventoryScreen; 5 | import net.minecraft.client.gui.screen.ingame.StatusEffectsDisplay; 6 | import org.spongepowered.asm.mixin.Mixin; 7 | import org.spongepowered.asm.mixin.injection.At; 8 | import org.spongepowered.asm.mixin.injection.ModifyVariable; 9 | 10 | @Mixin(StatusEffectsDisplay.class) 11 | public class StatusEffectsDisplayMixin { 12 | 13 | @ModifyVariable(method = "drawStatusEffects(Lnet/minecraft/client/gui/DrawContext;II)V", 14 | at = @At(value = "FIELD", target = "Lnet/minecraft/client/gui/screen/ingame/HandledScreen;width:I", ordinal = 0), 15 | ordinal = 2) 16 | private int shiftStatusEffects(int x) { 17 | if (!((Object) this instanceof CreativeInventoryScreen)) return x; 18 | if (!(CreativeInventoryScreenAccessor.owo$getSelectedTab() instanceof OwoItemGroup group)) return x; 19 | if (group.getButtons().isEmpty()) return x; 20 | 21 | return x + 28; 22 | } 23 | 24 | } 25 | -------------------------------------------------------------------------------- /src/main/java/io/wispforest/owo/mixin/offline/AdvancementProgressAccessor.java: -------------------------------------------------------------------------------- 1 | package io.wispforest.owo.mixin.offline; 2 | 3 | import net.minecraft.advancement.AdvancementProgress; 4 | import net.minecraft.advancement.AdvancementRequirements; 5 | import net.minecraft.advancement.criterion.CriterionProgress; 6 | import org.spongepowered.asm.mixin.Mixin; 7 | import org.spongepowered.asm.mixin.gen.Accessor; 8 | 9 | import java.util.Map; 10 | 11 | @Mixin(AdvancementProgress.class) 12 | public interface AdvancementProgressAccessor { 13 | @Accessor 14 | AdvancementRequirements getRequirements(); 15 | 16 | @Accessor 17 | void setRequirements(AdvancementRequirements requirements); 18 | } -------------------------------------------------------------------------------- /src/main/java/io/wispforest/owo/mixin/offline/PlayerAdvancementTrackerMixin.java: -------------------------------------------------------------------------------- 1 | package io.wispforest.owo.mixin.offline; 2 | 3 | import io.wispforest.owo.offline.DataSavedEvents; 4 | import net.minecraft.advancement.AdvancementProgress; 5 | import net.minecraft.advancement.PlayerAdvancementTracker; 6 | import net.minecraft.server.network.ServerPlayerEntity; 7 | import net.minecraft.util.Identifier; 8 | import org.spongepowered.asm.mixin.Mixin; 9 | import org.spongepowered.asm.mixin.Shadow; 10 | import org.spongepowered.asm.mixin.injection.At; 11 | import org.spongepowered.asm.mixin.injection.ModifyArg; 12 | 13 | import java.util.Map; 14 | 15 | @Mixin(PlayerAdvancementTracker.class) 16 | public class PlayerAdvancementTrackerMixin { 17 | @Shadow 18 | private ServerPlayerEntity owner; 19 | 20 | @SuppressWarnings("unchecked") 21 | @ModifyArg(method = "save", at = @At(value = "INVOKE", target = "Lcom/mojang/serialization/Codec;encodeStart(Lcom/mojang/serialization/DynamicOps;Ljava/lang/Object;)Lcom/mojang/serialization/DataResult;", remap = false), index = 1) 22 | private Object onAdvancementsSaved(Object map) { 23 | DataSavedEvents.ADVANCEMENTS.invoker().onSaved(owner.getUuid(), ((ProgressMapAccessor) map).getMap()); 24 | return map; 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/main/java/io/wispforest/owo/mixin/offline/ProgressMapAccessor.java: -------------------------------------------------------------------------------- 1 | package io.wispforest.owo.mixin.offline; 2 | 3 | import net.minecraft.advancement.AdvancementProgress; 4 | import net.minecraft.util.Identifier; 5 | import org.spongepowered.asm.mixin.Mixin; 6 | import org.spongepowered.asm.mixin.gen.Accessor; 7 | 8 | import java.util.Map; 9 | 10 | @Mixin(targets = "net/minecraft/advancement/PlayerAdvancementTracker$ProgressMap") 11 | public interface ProgressMapAccessor { 12 | @Accessor 13 | Map getMap(); 14 | } 15 | -------------------------------------------------------------------------------- /src/main/java/io/wispforest/owo/mixin/offline/WorldSaveHandlerMixin.java: -------------------------------------------------------------------------------- 1 | package io.wispforest.owo.mixin.offline; 2 | 3 | import io.wispforest.owo.offline.DataSavedEvents; 4 | import net.minecraft.entity.player.PlayerEntity; 5 | import net.minecraft.nbt.NbtCompound; 6 | import net.minecraft.world.PlayerSaveHandler; 7 | import org.spongepowered.asm.mixin.Mixin; 8 | import org.spongepowered.asm.mixin.injection.At; 9 | import org.spongepowered.asm.mixin.injection.Inject; 10 | import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; 11 | import org.spongepowered.asm.mixin.injection.callback.LocalCapture; 12 | 13 | @Mixin(PlayerSaveHandler.class) 14 | public class WorldSaveHandlerMixin { 15 | @Inject(method = "savePlayerData", at = @At(value = "INVOKE", target = "Ljava/nio/file/Files;createTempFile(Ljava/nio/file/Path;Ljava/lang/String;Ljava/lang/String;[Ljava/nio/file/attribute/FileAttribute;)Ljava/nio/file/Path;"), locals = LocalCapture.CAPTURE_FAILHARD) 16 | public void onPlayerDataSaved(PlayerEntity player, CallbackInfo ci, NbtCompound tag) { 17 | DataSavedEvents.PLAYER_DATA.invoker().onSaved(player.getUuid(), tag); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/main/java/io/wispforest/owo/mixin/recipe_remainders/ServerRecipeManagerAccessor.java: -------------------------------------------------------------------------------- 1 | package io.wispforest.owo.mixin.recipe_remainders; 2 | 3 | import net.minecraft.recipe.ServerRecipeManager; 4 | import net.minecraft.resource.ResourceFinder; 5 | import org.spongepowered.asm.mixin.Mixin; 6 | import org.spongepowered.asm.mixin.gen.Accessor; 7 | 8 | @Mixin(ServerRecipeManager.class) 9 | public interface ServerRecipeManagerAccessor { 10 | 11 | @Accessor("FINDER") 12 | static ResourceFinder owo$getFinder() { 13 | throw new UnsupportedOperationException(); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/main/java/io/wispforest/owo/mixin/registry/ReferenceAccessor.java: -------------------------------------------------------------------------------- 1 | package io.wispforest.owo.mixin.registry; 2 | 3 | import net.minecraft.registry.RegistryKey; 4 | import net.minecraft.registry.entry.RegistryEntry; 5 | import org.spongepowered.asm.mixin.Mixin; 6 | import org.spongepowered.asm.mixin.gen.Invoker; 7 | 8 | @Mixin(RegistryEntry.Reference.class) 9 | public interface ReferenceAccessor { 10 | @Invoker("setRegistryKey") 11 | void owo$setRegistryKey(RegistryKey registryKey); 12 | 13 | @Invoker("setValue") 14 | void owo$setValue(T value); 15 | } 16 | -------------------------------------------------------------------------------- /src/main/java/io/wispforest/owo/mixin/shader/ShaderProgramAccessor.java: -------------------------------------------------------------------------------- 1 | package io.wispforest.owo.mixin.shader; 2 | 3 | import net.minecraft.client.gl.GlUniform; 4 | import net.minecraft.client.gl.ShaderProgram; 5 | import org.spongepowered.asm.mixin.Mixin; 6 | import org.spongepowered.asm.mixin.gen.Accessor; 7 | 8 | import java.util.Map; 9 | 10 | @Mixin(ShaderProgram.class) 11 | public interface ShaderProgramAccessor { 12 | 13 | @Accessor("uniformsByName") 14 | Map owo$getUniformsByName(); 15 | 16 | } 17 | -------------------------------------------------------------------------------- /src/main/java/io/wispforest/owo/mixin/text/stapi/SystemDelegatedLanguageFixin.java: -------------------------------------------------------------------------------- 1 | package io.wispforest.owo.mixin.text.stapi; 2 | 3 | import io.wispforest.owo.text.TextLanguage; 4 | import net.minecraft.text.Text; 5 | import net.minecraft.util.Language; 6 | import org.spongepowered.asm.mixin.Final; 7 | import org.spongepowered.asm.mixin.Mixin; 8 | import org.spongepowered.asm.mixin.Pseudo; 9 | import org.spongepowered.asm.mixin.Shadow; 10 | import xyz.nucleoid.server.translations.api.language.ServerLanguage; 11 | import xyz.nucleoid.server.translations.impl.language.SystemDelegatedLanguage; 12 | 13 | @Pseudo 14 | @Mixin(SystemDelegatedLanguage.class) 15 | public abstract class SystemDelegatedLanguageFixin implements TextLanguage { 16 | @Final 17 | @Shadow private Language vanilla; 18 | 19 | @Shadow 20 | protected abstract ServerLanguage getSystemLanguage(); 21 | 22 | @Override 23 | public Text getText(String key) { 24 | if (!(vanilla instanceof TextLanguage lang) || this.getSystemLanguage().serverTranslations().contains(key)) { 25 | return null; 26 | } 27 | 28 | return lang.getText(key); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/main/java/io/wispforest/owo/mixin/tweaks/LevelInfoMixin.java: -------------------------------------------------------------------------------- 1 | package io.wispforest.owo.mixin.tweaks; 2 | 3 | import io.wispforest.owo.Owo; 4 | import net.fabricmc.loader.api.FabricLoader; 5 | import net.minecraft.resource.DataConfiguration; 6 | import net.minecraft.world.Difficulty; 7 | import net.minecraft.world.GameMode; 8 | import net.minecraft.world.GameRules; 9 | import net.minecraft.world.level.LevelInfo; 10 | import org.spongepowered.asm.mixin.Final; 11 | import org.spongepowered.asm.mixin.Mixin; 12 | import org.spongepowered.asm.mixin.Shadow; 13 | import org.spongepowered.asm.mixin.injection.At; 14 | import org.spongepowered.asm.mixin.injection.Inject; 15 | import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; 16 | 17 | @Mixin(LevelInfo.class) 18 | public class LevelInfoMixin { 19 | 20 | @Shadow 21 | @Final 22 | private GameRules gameRules; 23 | 24 | @Inject(method = "", at = @At("TAIL")) 25 | private void simulationIsForNerds(String name, GameMode gameMode, boolean hardcore, Difficulty difficulty, boolean allowCommands, GameRules gameRules, DataConfiguration dataConfiguration, CallbackInfo ci) { 26 | if (!(Owo.DEBUG && FabricLoader.getInstance().isDevelopmentEnvironment())) return; 27 | 28 | this.gameRules.get(GameRules.DO_DAYLIGHT_CYCLE).set(false, null); 29 | this.gameRules.get(GameRules.DO_WEATHER_CYCLE).set(false, null); 30 | } 31 | 32 | } 33 | -------------------------------------------------------------------------------- /src/main/java/io/wispforest/owo/mixin/tweaks/OperatingSystemMixin.java: -------------------------------------------------------------------------------- 1 | package io.wispforest.owo.mixin.tweaks; 2 | 3 | import com.mojang.logging.LogUtils; 4 | import net.minecraft.util.Util; 5 | import org.spongepowered.asm.mixin.Mixin; 6 | import org.spongepowered.asm.mixin.Overwrite; 7 | import org.spongepowered.asm.mixin.Shadow; 8 | 9 | import java.io.IOException; 10 | import java.net.URI; 11 | import java.net.URL; 12 | import java.util.concurrent.CompletableFuture; 13 | 14 | @Mixin(value = Util.OperatingSystem.class) 15 | public abstract class OperatingSystemMixin { 16 | 17 | @Shadow protected abstract String[] getURIOpenCommand(URI uri); 18 | 19 | /** 20 | * @author glisco 21 | * @reason By not properly consuming the stdout stream of the started process, 22 | * Minecraft's implementation of this method causes xdg-open on linux to fail at actually 23 | * opening the target program about 80% of the time. This overwrite uses a more modern approach 24 | * to starting processes and properly voids both stdout and stderr, making xdg-open succeed 25 | * at opening the user's desired application 100% of the time 26 | */ 27 | @Overwrite() 28 | public void open(URI uri) { 29 | CompletableFuture.runAsync(() -> { 30 | try { 31 | final var command = getURIOpenCommand(uri); 32 | new ProcessBuilder(command) 33 | .redirectError(ProcessBuilder.Redirect.DISCARD) 34 | .redirectOutput(ProcessBuilder.Redirect.DISCARD) 35 | .start(); 36 | } catch (IOException e) { 37 | LogUtils.getLogger().error("Couldn't open uri '{}'", uri, e); 38 | } 39 | }, Util.getMainWorkerExecutor()); 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/main/java/io/wispforest/owo/mixin/tweaks/TextFieldWidgetMixin.java: -------------------------------------------------------------------------------- 1 | package io.wispforest.owo.mixin.tweaks; 2 | 3 | import io.wispforest.owo.Owo; 4 | import net.minecraft.client.gui.widget.ClickableWidget; 5 | import net.minecraft.client.gui.widget.TextFieldWidget; 6 | import net.minecraft.text.Text; 7 | import org.spongepowered.asm.mixin.Mixin; 8 | import org.spongepowered.asm.mixin.Shadow; 9 | import org.spongepowered.asm.mixin.Unique; 10 | import org.spongepowered.asm.mixin.injection.At; 11 | import org.spongepowered.asm.mixin.injection.Inject; 12 | import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; 13 | 14 | @Mixin(TextFieldWidget.class) 15 | public abstract class TextFieldWidgetMixin extends ClickableWidget { 16 | 17 | @Shadow 18 | private String text; 19 | 20 | public TextFieldWidgetMixin(int x, int y, int width, int height, Text message) { 21 | super(x, y, width, height, message); 22 | } 23 | 24 | @Inject(method = "getWordSkipPosition(IIZ)I", at = @At("HEAD"), cancellable = true) 25 | private void iProvideUsefulSeparators(int wordOffset, int cursorPosition, boolean skipOverSpaces, CallbackInfoReturnable cir) { 26 | if (!Owo.DEBUG) return; 27 | 28 | int wordsToSkip = Math.abs(wordOffset); 29 | boolean forward = wordOffset > 0; 30 | 31 | for (int i = 0; i < wordsToSkip; i++) { 32 | if (forward) { 33 | cursorPosition++; 34 | while (cursorPosition < this.text.length() && owo$isWordChar(this.text.charAt(cursorPosition))) cursorPosition++; 35 | } else if (cursorPosition > 0) { 36 | cursorPosition--; 37 | while (cursorPosition > 0 && owo$isWordChar(this.text.charAt(cursorPosition - 1))) cursorPosition--; 38 | } 39 | } 40 | 41 | cir.setReturnValue(cursorPosition); 42 | } 43 | 44 | @Unique 45 | private boolean owo$isWordChar(char charAt) { 46 | return charAt == '_' || Character.isAlphabetic(charAt) || Character.isDigit(charAt); 47 | } 48 | 49 | } 50 | -------------------------------------------------------------------------------- /src/main/java/io/wispforest/owo/mixin/ui/ChatScreenMixin.java: -------------------------------------------------------------------------------- 1 | package io.wispforest.owo.mixin.ui; 2 | 3 | import io.wispforest.owo.ui.util.CommandOpenedScreen; 4 | import net.minecraft.client.MinecraftClient; 5 | import net.minecraft.client.gui.screen.ChatScreen; 6 | import org.spongepowered.asm.mixin.Mixin; 7 | import org.spongepowered.asm.mixin.injection.At; 8 | import org.spongepowered.asm.mixin.injection.Inject; 9 | import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; 10 | 11 | @Mixin(ChatScreen.class) 12 | public class ChatScreenMixin { 13 | 14 | @Inject(method = "keyPressed", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/MinecraftClient;setScreen(Lnet/minecraft/client/gui/screen/Screen;)V"), cancellable = true) 15 | private void cancelClose(int keyCode, int scanCode, int modifiers, CallbackInfoReturnable cir) { 16 | if (MinecraftClient.getInstance().currentScreen instanceof CommandOpenedScreen) { 17 | cir.setReturnValue(true); 18 | } 19 | } 20 | 21 | } 22 | -------------------------------------------------------------------------------- /src/main/java/io/wispforest/owo/mixin/ui/DrawContextInvoker.java: -------------------------------------------------------------------------------- 1 | package io.wispforest.owo.mixin.ui; 2 | 3 | import net.minecraft.client.font.TextRenderer; 4 | import net.minecraft.client.gui.DrawContext; 5 | import net.minecraft.client.gui.tooltip.TooltipComponent; 6 | import net.minecraft.client.gui.tooltip.TooltipPositioner; 7 | import net.minecraft.client.render.VertexConsumerProvider; 8 | import net.minecraft.client.util.math.MatrixStack; 9 | import net.minecraft.util.Identifier; 10 | import org.jetbrains.annotations.Nullable; 11 | import org.spongepowered.asm.mixin.Mixin; 12 | import org.spongepowered.asm.mixin.Mutable; 13 | import org.spongepowered.asm.mixin.gen.Accessor; 14 | import org.spongepowered.asm.mixin.gen.Invoker; 15 | 16 | import java.util.List; 17 | 18 | @Mixin(DrawContext.class) 19 | public interface DrawContextInvoker { 20 | 21 | @Invoker("drawTooltip") 22 | void owo$renderTooltipFromComponents(TextRenderer textRenderer, List components, int x, int y, TooltipPositioner positioner, @Nullable Identifier texture); 23 | 24 | @Accessor("vertexConsumers") 25 | VertexConsumerProvider.Immediate owo$vertexConsumers(); 26 | 27 | @Accessor("matrices") 28 | MatrixStack owo$getMatrices(); 29 | 30 | @Mutable 31 | @Accessor("matrices") 32 | void owo$setMatrices(MatrixStack matrices); 33 | 34 | @Accessor("scissorStack") 35 | DrawContext.ScissorStack owo$getScissorStack(); 36 | 37 | @Mutable 38 | @Accessor("scissorStack") 39 | void owo$setScissorStack(DrawContext.ScissorStack scissorStack); 40 | } 41 | -------------------------------------------------------------------------------- /src/main/java/io/wispforest/owo/mixin/ui/EditBoxWidgetMixin.java: -------------------------------------------------------------------------------- 1 | package io.wispforest.owo.mixin.ui; 2 | 3 | import io.wispforest.owo.ui.core.Component; 4 | import io.wispforest.owo.ui.inject.GreedyInputComponent; 5 | import net.minecraft.client.gui.widget.EditBoxWidget; 6 | import net.minecraft.client.gui.widget.ScrollableWidget; 7 | import net.minecraft.text.Text; 8 | import org.spongepowered.asm.mixin.Mixin; 9 | 10 | @Mixin(EditBoxWidget.class) 11 | public abstract class EditBoxWidgetMixin extends ScrollableWidget implements GreedyInputComponent { 12 | 13 | public EditBoxWidgetMixin(int i, int j, int k, int l, Text text) { 14 | super(i, j, k, l, text); 15 | } 16 | 17 | @Override 18 | public void onFocusGained(Component.FocusSource source) { 19 | super.onFocusGained(source); 20 | this.setFocused(true); 21 | } 22 | 23 | } 24 | -------------------------------------------------------------------------------- /src/main/java/io/wispforest/owo/mixin/ui/RenderPhaseMixin.java: -------------------------------------------------------------------------------- 1 | package io.wispforest.owo.mixin.ui; 2 | 3 | import com.llamalad7.mixinextras.injector.ModifyExpressionValue; 4 | import io.wispforest.owo.ui.container.RenderEffectWrapper; 5 | import net.minecraft.client.gl.Framebuffer; 6 | import net.minecraft.client.render.RenderPhase; 7 | import org.spongepowered.asm.mixin.Mixin; 8 | import org.spongepowered.asm.mixin.injection.At; 9 | 10 | @Mixin(RenderPhase.class) 11 | public class RenderPhaseMixin { 12 | 13 | @ModifyExpressionValue(method = "method_68490", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/MinecraftClient;getFramebuffer()Lnet/minecraft/client/gl/Framebuffer;")) 14 | private static Framebuffer injectProperRenderTarget(Framebuffer original) { 15 | if (RenderEffectWrapper.currentFramebuffer() != null) { 16 | return RenderEffectWrapper.currentFramebuffer(); 17 | } 18 | 19 | return original; 20 | } 21 | 22 | } 23 | -------------------------------------------------------------------------------- /src/main/java/io/wispforest/owo/mixin/ui/ScreenMixin.java: -------------------------------------------------------------------------------- 1 | package io.wispforest.owo.mixin.ui; 2 | 3 | import com.llamalad7.mixinextras.injector.ModifyExpressionValue; 4 | import io.wispforest.owo.ui.base.BaseOwoHandledScreen; 5 | import io.wispforest.owo.ui.base.BaseOwoScreen; 6 | import io.wispforest.owo.ui.core.OwoUIDrawContext; 7 | import net.minecraft.client.gui.screen.Screen; 8 | import org.jetbrains.annotations.Nullable; 9 | import org.spongepowered.asm.mixin.Mixin; 10 | import org.spongepowered.asm.mixin.injection.At; 11 | import org.spongepowered.asm.mixin.injection.ModifyArg; 12 | 13 | @Mixin(Screen.class) 14 | public class ScreenMixin { 15 | 16 | @ModifyExpressionValue(method = "keyPressed", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/gui/screen/Screen;shouldCloseOnEsc()Z", ordinal = 0)) 17 | private boolean dontCloseOwoScreens(boolean original) { 18 | //noinspection ConstantValue 19 | if ((Object) this instanceof BaseOwoScreen || (Object) this instanceof BaseOwoHandledScreen) { 20 | return false; 21 | } 22 | 23 | return original; 24 | } 25 | 26 | 27 | 28 | @ModifyArg(method = "method_61039", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/MinecraftClient;setScreen(Lnet/minecraft/client/gui/screen/Screen;)V"), index = 0) 29 | private @Nullable Screen injectProperLinkSource(@Nullable Screen screen) { 30 | if ((Object) this != OwoUIDrawContext.utilityScreen()) return screen; 31 | return OwoUIDrawContext.utilityScreen().getAndClearLinkSource(); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/main/java/io/wispforest/owo/mixin/ui/SlotAccessor.java: -------------------------------------------------------------------------------- 1 | package io.wispforest.owo.mixin.ui; 2 | 3 | import net.minecraft.screen.slot.Slot; 4 | import org.spongepowered.asm.mixin.Mixin; 5 | import org.spongepowered.asm.mixin.Mutable; 6 | import org.spongepowered.asm.mixin.gen.Accessor; 7 | 8 | @Mixin(Slot.class) 9 | public interface SlotAccessor { 10 | @Mutable 11 | @Accessor("x") 12 | void owo$setX(int x); 13 | 14 | @Mutable 15 | @Accessor("y") 16 | void owo$setY(int y); 17 | } 18 | -------------------------------------------------------------------------------- /src/main/java/io/wispforest/owo/mixin/ui/SlotMixin.java: -------------------------------------------------------------------------------- 1 | package io.wispforest.owo.mixin.ui; 2 | 3 | import io.wispforest.owo.ui.core.PositionedRectangle; 4 | import io.wispforest.owo.util.pond.OwoSlotExtension; 5 | import net.minecraft.screen.slot.Slot; 6 | import org.jetbrains.annotations.Nullable; 7 | import org.spongepowered.asm.mixin.Mixin; 8 | import org.spongepowered.asm.mixin.Unique; 9 | import org.spongepowered.asm.mixin.injection.At; 10 | import org.spongepowered.asm.mixin.injection.Inject; 11 | import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; 12 | 13 | @Mixin(Slot.class) 14 | public class SlotMixin implements OwoSlotExtension { 15 | 16 | @Unique 17 | private boolean owo$disabledOverride = false; 18 | 19 | @Unique 20 | private @Nullable PositionedRectangle owo$scissorArea = null; 21 | 22 | @Override 23 | public void owo$setDisabledOverride(boolean disabled) { 24 | this.owo$disabledOverride = disabled; 25 | } 26 | 27 | @Override 28 | public boolean owo$getDisabledOverride() { 29 | return this.owo$disabledOverride; 30 | } 31 | 32 | @Override 33 | public void owo$setScissorArea(@Nullable PositionedRectangle scissor) { 34 | this.owo$scissorArea = scissor; 35 | } 36 | 37 | @Override 38 | public @Nullable PositionedRectangle owo$getScissorArea() { 39 | return this.owo$scissorArea; 40 | } 41 | 42 | @Inject(method = "isEnabled", at = @At("TAIL"), cancellable = true) 43 | private void injectOverride(CallbackInfoReturnable cir) { 44 | if (!this.owo$disabledOverride) return; 45 | cir.setReturnValue(false); 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /src/main/java/io/wispforest/owo/mixin/ui/TextFieldWidgetMixin.java: -------------------------------------------------------------------------------- 1 | package io.wispforest.owo.mixin.ui; 2 | 3 | import io.wispforest.owo.mixin.ui.access.TextBoxComponentAccessor; 4 | import io.wispforest.owo.ui.inject.GreedyInputComponent; 5 | import net.minecraft.client.gui.widget.ClickableWidget; 6 | import net.minecraft.client.gui.widget.TextFieldWidget; 7 | import net.minecraft.text.Text; 8 | import org.spongepowered.asm.mixin.Mixin; 9 | import org.spongepowered.asm.mixin.injection.At; 10 | import org.spongepowered.asm.mixin.injection.Inject; 11 | import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; 12 | 13 | @Mixin(TextFieldWidget.class) 14 | public abstract class TextFieldWidgetMixin extends ClickableWidget implements GreedyInputComponent { 15 | 16 | public TextFieldWidgetMixin(int x, int y, int width, int height, Text message) { 17 | super(x, y, width, height, message); 18 | } 19 | 20 | @Inject(method = "onChanged", at = @At("HEAD")) 21 | private void callOwoListener(String newText, CallbackInfo ci) { 22 | if (!(this instanceof TextBoxComponentAccessor accessor)) return; 23 | accessor.owo$textValue().set(newText); 24 | } 25 | 26 | @Override 27 | public void onFocusGained(FocusSource source) { 28 | super.onFocusGained(source); 29 | this.setFocused(true); 30 | } 31 | 32 | } 33 | -------------------------------------------------------------------------------- /src/main/java/io/wispforest/owo/mixin/ui/access/BaseOwoHandledScreenAccessor.java: -------------------------------------------------------------------------------- 1 | package io.wispforest.owo.mixin.ui.access; 2 | 3 | import io.wispforest.owo.ui.base.BaseOwoHandledScreen; 4 | import io.wispforest.owo.ui.core.OwoUIAdapter; 5 | import org.spongepowered.asm.mixin.Mixin; 6 | import org.spongepowered.asm.mixin.gen.Accessor; 7 | 8 | @Mixin(value = BaseOwoHandledScreen.class, remap = false) 9 | public interface BaseOwoHandledScreenAccessor { 10 | @Accessor("uiAdapter") 11 | OwoUIAdapter owo$getUIAdapter(); 12 | } 13 | -------------------------------------------------------------------------------- /src/main/java/io/wispforest/owo/mixin/ui/access/BlockEntityAccessor.java: -------------------------------------------------------------------------------- 1 | package io.wispforest.owo.mixin.ui.access; 2 | 3 | import net.minecraft.block.BlockState; 4 | import net.minecraft.block.entity.BlockEntity; 5 | import org.spongepowered.asm.mixin.Mixin; 6 | import org.spongepowered.asm.mixin.gen.Accessor; 7 | 8 | @Mixin(BlockEntity.class) 9 | public interface BlockEntityAccessor { 10 | @Accessor("cachedState") 11 | void owo$setCachedState(BlockState state); 12 | } 13 | -------------------------------------------------------------------------------- /src/main/java/io/wispforest/owo/mixin/ui/access/ButtonWidgetAccessor.java: -------------------------------------------------------------------------------- 1 | package io.wispforest.owo.mixin.ui.access; 2 | 3 | import net.minecraft.client.gui.widget.ButtonWidget; 4 | import org.spongepowered.asm.mixin.Mixin; 5 | import org.spongepowered.asm.mixin.Mutable; 6 | import org.spongepowered.asm.mixin.gen.Accessor; 7 | 8 | @Mixin(ButtonWidget.class) 9 | public interface ButtonWidgetAccessor { 10 | 11 | @Mutable 12 | @Accessor("onPress") 13 | void owo$setOnPress(ButtonWidget.PressAction onPress); 14 | 15 | } 16 | -------------------------------------------------------------------------------- /src/main/java/io/wispforest/owo/mixin/ui/access/CheckboxWidgetAccessor.java: -------------------------------------------------------------------------------- 1 | package io.wispforest.owo.mixin.ui.access; 2 | 3 | import net.minecraft.client.gui.widget.CheckboxWidget; 4 | import net.minecraft.client.gui.widget.MultilineTextWidget; 5 | import org.spongepowered.asm.mixin.Mixin; 6 | import org.spongepowered.asm.mixin.gen.Accessor; 7 | 8 | @Mixin(CheckboxWidget.class) 9 | public interface CheckboxWidgetAccessor { 10 | @Accessor("checked") 11 | void owo$setChecked(boolean checked); 12 | 13 | @Accessor("textWidget") 14 | MultilineTextWidget owo$getTextWidget(); 15 | } 16 | -------------------------------------------------------------------------------- /src/main/java/io/wispforest/owo/mixin/ui/access/ClickableWidgetAccessor.java: -------------------------------------------------------------------------------- 1 | package io.wispforest.owo.mixin.ui.access; 2 | 3 | import net.minecraft.client.gui.tooltip.TooltipState; 4 | import net.minecraft.client.gui.widget.ClickableWidget; 5 | import org.spongepowered.asm.mixin.Mixin; 6 | import org.spongepowered.asm.mixin.gen.Accessor; 7 | 8 | @Mixin(ClickableWidget.class) 9 | public interface ClickableWidgetAccessor { 10 | 11 | @Accessor("height") 12 | void owo$setHeight(int height); 13 | 14 | @Accessor("width") 15 | void owo$setWidth(int width); 16 | 17 | @Accessor("x") 18 | void owo$setX(int x); 19 | 20 | @Accessor("y") 21 | void owo$setY(int y); 22 | 23 | @Accessor("tooltip") 24 | TooltipState owo$getTooltip(); 25 | } 26 | -------------------------------------------------------------------------------- /src/main/java/io/wispforest/owo/mixin/ui/access/EditBoxAccessor.java: -------------------------------------------------------------------------------- 1 | package io.wispforest.owo.mixin.ui.access; 2 | 3 | import net.minecraft.client.gui.EditBox; 4 | import org.spongepowered.asm.mixin.Mixin; 5 | import org.spongepowered.asm.mixin.Mutable; 6 | import org.spongepowered.asm.mixin.gen.Accessor; 7 | 8 | @Mixin(EditBox.class) 9 | public interface EditBoxAccessor { 10 | 11 | @Mutable 12 | @Accessor("width") 13 | void owo$setWidth(int width); 14 | 15 | @Accessor("selectionEnd") 16 | void owo$setSelectionEnd(int width); 17 | 18 | @Accessor("selectionEnd") 19 | int owo$getSelectionEnd(); 20 | 21 | } 22 | -------------------------------------------------------------------------------- /src/main/java/io/wispforest/owo/mixin/ui/access/EditBoxWidgetAccessor.java: -------------------------------------------------------------------------------- 1 | package io.wispforest.owo.mixin.ui.access; 2 | 3 | import net.minecraft.client.gui.EditBox; 4 | import net.minecraft.client.gui.widget.EditBoxWidget; 5 | import org.spongepowered.asm.mixin.Mixin; 6 | import org.spongepowered.asm.mixin.gen.Accessor; 7 | 8 | @Mixin(EditBoxWidget.class) 9 | public interface EditBoxWidgetAccessor { 10 | 11 | @Accessor("editBox") 12 | EditBox owo$getEditBox(); 13 | 14 | } 15 | -------------------------------------------------------------------------------- /src/main/java/io/wispforest/owo/mixin/ui/access/TextBoxComponentAccessor.java: -------------------------------------------------------------------------------- 1 | package io.wispforest.owo.mixin.ui.access; 2 | 3 | import io.wispforest.owo.ui.component.TextBoxComponent; 4 | import io.wispforest.owo.util.Observable; 5 | import org.jetbrains.annotations.ApiStatus; 6 | import org.spongepowered.asm.mixin.Mixin; 7 | import org.spongepowered.asm.mixin.gen.Accessor; 8 | 9 | // now you might think that simply AW'ing onChanged in TextFieldWidget 10 | // would be the way to go about this. but you see, tiny remapper (or more specifically how 11 | // loom uses it) begs to differ and simply does not remap your override, causing 12 | // that approach to break in prod. thus we need to mix into TextFieldWidget 13 | // and use this accessor to update it instead 14 | @ApiStatus.Internal 15 | @Mixin(TextBoxComponent.class) 16 | public interface TextBoxComponentAccessor { 17 | 18 | @Accessor("textValue") 19 | Observable owo$textValue(); 20 | 21 | } 22 | -------------------------------------------------------------------------------- /src/main/java/io/wispforest/owo/mixin/ui/access/TextFieldWidgetAccessor.java: -------------------------------------------------------------------------------- 1 | package io.wispforest.owo.mixin.ui.access; 2 | 3 | import net.minecraft.client.gui.widget.TextFieldWidget; 4 | import org.spongepowered.asm.mixin.Mixin; 5 | import org.spongepowered.asm.mixin.gen.Accessor; 6 | 7 | @Mixin(TextFieldWidget.class) 8 | public interface TextFieldWidgetAccessor { 9 | @Accessor("drawsBackground") 10 | boolean owo$drawsBackground(); 11 | } 12 | -------------------------------------------------------------------------------- /src/main/java/io/wispforest/owo/mixin/ui/layers/HandledScreenAccessor.java: -------------------------------------------------------------------------------- 1 | package io.wispforest.owo.mixin.ui.layers; 2 | 3 | import net.minecraft.client.gui.screen.ingame.HandledScreen; 4 | import org.spongepowered.asm.mixin.Mixin; 5 | import org.spongepowered.asm.mixin.gen.Accessor; 6 | 7 | @Mixin(HandledScreen.class) 8 | public interface HandledScreenAccessor { 9 | 10 | @Accessor("x") 11 | int owo$getRootX(); 12 | 13 | @Accessor("y") 14 | int owo$getRootY(); 15 | 16 | } 17 | -------------------------------------------------------------------------------- /src/main/java/io/wispforest/owo/mixin/ui/layers/KeyboardMixin.java: -------------------------------------------------------------------------------- 1 | package io.wispforest.owo.mixin.ui.layers; 2 | 3 | import com.llamalad7.mixinextras.injector.wrapoperation.Operation; 4 | import com.llamalad7.mixinextras.injector.wrapoperation.WrapOperation; 5 | import io.wispforest.owo.ui.layers.Layers; 6 | import net.minecraft.client.Keyboard; 7 | import net.minecraft.client.MinecraftClient; 8 | import net.minecraft.client.gui.Element; 9 | import net.minecraft.client.gui.screen.Screen; 10 | import org.spongepowered.asm.mixin.Mixin; 11 | import org.spongepowered.asm.mixin.injection.At; 12 | import org.spongepowered.asm.mixin.injection.Inject; 13 | import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; 14 | 15 | @Mixin(Keyboard.class) 16 | public class KeyboardMixin { 17 | 18 | @WrapOperation(method = "onChar", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/gui/screen/Screen;charTyped(CI)Z")) 19 | private boolean captureScreenCharTyped(Screen screen, char character, int modifiers, Operation original) { 20 | boolean handled = false; 21 | for (var instance : Layers.getInstances(screen)) { 22 | handled = instance.adapter.charTyped(character, modifiers); 23 | if (handled) break; 24 | } 25 | 26 | return handled || original.call(screen, character, modifiers); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/main/java/io/wispforest/owo/mixin/ui/layers/MouseMixin.java: -------------------------------------------------------------------------------- 1 | package io.wispforest.owo.mixin.ui.layers; 2 | 3 | import com.llamalad7.mixinextras.injector.wrapoperation.Operation; 4 | import com.llamalad7.mixinextras.injector.wrapoperation.WrapOperation; 5 | import io.wispforest.owo.ui.layers.Layers; 6 | import net.minecraft.client.Mouse; 7 | import net.minecraft.client.gui.screen.Screen; 8 | import org.spongepowered.asm.mixin.Mixin; 9 | import org.spongepowered.asm.mixin.Shadow; 10 | import org.spongepowered.asm.mixin.injection.At; 11 | import org.spongepowered.asm.mixin.injection.Inject; 12 | import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; 13 | 14 | @Mixin(Mouse.class) 15 | public class MouseMixin { 16 | 17 | @Shadow private int activeButton; 18 | 19 | @WrapOperation(method = "tick", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/gui/screen/Screen;mouseDragged(DDIDD)Z")) 20 | private boolean captureScreenMouseDrag(Screen screen, double mouseX, double mouseY, int i, double deltaX, double deltaY, Operation original) { 21 | boolean handled = false; 22 | for (var instance : Layers.getInstances(screen)) { 23 | handled = instance.adapter.mouseDragged(mouseX, mouseY, this.activeButton, deltaX, deltaY); 24 | if (handled) break; 25 | } 26 | 27 | return handled || original.call(screen, mouseX, mouseY, i, deltaX, deltaY); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/main/java/io/wispforest/owo/moddata/ModDataConsumer.java: -------------------------------------------------------------------------------- 1 | package io.wispforest.owo.moddata; 2 | 3 | import com.google.gson.JsonObject; 4 | import net.minecraft.util.Identifier; 5 | 6 | /** 7 | * A class that can accept some JSON data loaded from a subdirectory 8 | * of all other mods' {@code data} directories when instructed to using 9 | * {@link ModDataLoader#load(ModDataConsumer)} 10 | */ 11 | public interface ModDataConsumer { 12 | 13 | /** 14 | * The {@code data} subdirectory to search. For example {@code items} would 15 | * mean {@code .../data/{modid}/items/...} 16 | * 17 | * @return The subdirectory to load from 18 | */ 19 | String getDataSubdirectory(); 20 | 21 | /** 22 | * This method should process the loaded data 23 | * 24 | * @param object The .json files parsed into {@code JsonObject}s 25 | */ 26 | void acceptParsedFile(Identifier id, JsonObject object); 27 | 28 | } 29 | -------------------------------------------------------------------------------- /src/main/java/io/wispforest/owo/network/ClientAccess.java: -------------------------------------------------------------------------------- 1 | package io.wispforest.owo.network; 2 | 3 | import net.fabricmc.api.EnvType; 4 | import net.fabricmc.api.Environment; 5 | import net.minecraft.client.MinecraftClient; 6 | import net.minecraft.client.network.ClientPlayNetworkHandler; 7 | import net.minecraft.client.network.ClientPlayerEntity; 8 | 9 | public class ClientAccess implements OwoNetChannel.EnvironmentAccess { 10 | 11 | @Environment(EnvType.CLIENT) private final ClientPlayNetworkHandler netHandler; 12 | @Environment(EnvType.CLIENT) private final MinecraftClient instance = MinecraftClient.getInstance(); 13 | 14 | public ClientAccess(ClientPlayNetworkHandler netHandler) { 15 | this.netHandler = netHandler; 16 | } 17 | 18 | @Override 19 | @Environment(EnvType.CLIENT) 20 | public ClientPlayerEntity player() { 21 | return instance.player; 22 | } 23 | 24 | @Override 25 | @Environment(EnvType.CLIENT) 26 | public MinecraftClient runtime() { 27 | return instance; 28 | } 29 | 30 | @Override 31 | @Environment(EnvType.CLIENT) 32 | public ClientPlayNetworkHandler netHandler() { 33 | return netHandler; 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/main/java/io/wispforest/owo/network/NetworkException.java: -------------------------------------------------------------------------------- 1 | package io.wispforest.owo.network; 2 | 3 | public class NetworkException extends IllegalStateException { 4 | 5 | public NetworkException(String cause) { 6 | super(cause); 7 | } 8 | 9 | } 10 | -------------------------------------------------------------------------------- /src/main/java/io/wispforest/owo/network/OwoClientConnectionExtension.java: -------------------------------------------------------------------------------- 1 | package io.wispforest.owo.network; 2 | 3 | import net.minecraft.util.Identifier; 4 | import org.jetbrains.annotations.ApiStatus; 5 | 6 | import java.util.Set; 7 | 8 | @ApiStatus.Internal 9 | public interface OwoClientConnectionExtension { 10 | void owo$setChannelSet(Set channels); 11 | 12 | Set owo$getChannelSet(); 13 | } 14 | -------------------------------------------------------------------------------- /src/main/java/io/wispforest/owo/network/QueuedChannelSet.java: -------------------------------------------------------------------------------- 1 | package io.wispforest.owo.network; 2 | 3 | import net.fabricmc.api.EnvType; 4 | import net.fabricmc.api.Environment; 5 | import net.minecraft.util.Identifier; 6 | import org.jetbrains.annotations.ApiStatus; 7 | 8 | import java.util.Set; 9 | 10 | @ApiStatus.Internal 11 | @Environment(EnvType.CLIENT) 12 | public class QueuedChannelSet { 13 | public static Set channels; 14 | } 15 | -------------------------------------------------------------------------------- /src/main/java/io/wispforest/owo/network/ServerAccess.java: -------------------------------------------------------------------------------- 1 | package io.wispforest.owo.network; 2 | 3 | import net.minecraft.server.MinecraftServer; 4 | import net.minecraft.server.network.ServerPlayNetworkHandler; 5 | import net.minecraft.server.network.ServerPlayerEntity; 6 | 7 | public record ServerAccess(ServerPlayerEntity player) implements 8 | OwoNetChannel.EnvironmentAccess { 9 | 10 | @Override 11 | public MinecraftServer runtime() { 12 | return player.server; 13 | } 14 | 15 | @Override 16 | public ServerPlayNetworkHandler netHandler() { 17 | return player.networkHandler; 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/main/java/io/wispforest/owo/network/serialization/SealedPolymorphic.java: -------------------------------------------------------------------------------- 1 | package io.wispforest.owo.network.serialization; 2 | 3 | import java.lang.annotation.ElementType; 4 | import java.lang.annotation.Retention; 5 | import java.lang.annotation.RetentionPolicy; 6 | import java.lang.annotation.Target; 7 | 8 | /** 9 | * @deprecated Moved to {@link io.wispforest.owo.serialization.annotations.SealedPolymorphic} for consistency. This 10 | * annotation will keep working for the time being but eventually get removed 11 | */ 12 | @Target(ElementType.TYPE) 13 | @Retention(RetentionPolicy.RUNTIME) 14 | @Deprecated(forRemoval = true) 15 | public @interface SealedPolymorphic {} 16 | -------------------------------------------------------------------------------- /src/main/java/io/wispforest/owo/offline/DataSavedEvents.java: -------------------------------------------------------------------------------- 1 | package io.wispforest.owo.offline; 2 | 3 | import net.fabricmc.fabric.api.event.Event; 4 | import net.fabricmc.fabric.api.event.EventFactory; 5 | import net.minecraft.advancement.AdvancementProgress; 6 | import net.minecraft.nbt.NbtCompound; 7 | import net.minecraft.util.Identifier; 8 | 9 | import java.util.Map; 10 | import java.util.UUID; 11 | 12 | /** 13 | * Events that get fired when the data 14 | * of a player is about to be saved, allowing 15 | * the data to be modified before it's written to disk 16 | * 17 | * @author BasiqueEvangelist 18 | */ 19 | public interface DataSavedEvents { 20 | 21 | /** 22 | * Called when the player data of a given player is about to be saved 23 | */ 24 | Event PLAYER_DATA = EventFactory.createArrayBacked(PlayerData.class, callbacks -> (playerUuid, newTag) -> { 25 | for (PlayerData callback : callbacks) { 26 | callback.onSaved(playerUuid, newTag); 27 | } 28 | }); 29 | 30 | interface PlayerData { 31 | void onSaved(UUID playerUuid, NbtCompound newTag); 32 | } 33 | 34 | /** 35 | * Called when the advancements of a given player are about to be saved 36 | */ 37 | Event ADVANCEMENTS = EventFactory.createArrayBacked(Advancements.class, callbacks -> (playerUuid, newMap) -> { 38 | for (Advancements callback : callbacks) { 39 | callback.onSaved(playerUuid, newMap); 40 | } 41 | }); 42 | 43 | interface Advancements { 44 | void onSaved(UUID playerUuid, Map newMap); 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /src/main/java/io/wispforest/owo/particles/systems/ParticleSystemExecutor.java: -------------------------------------------------------------------------------- 1 | package io.wispforest.owo.particles.systems; 2 | 3 | import net.minecraft.util.math.Vec3d; 4 | import net.minecraft.world.World; 5 | 6 | public interface ParticleSystemExecutor { 7 | /** 8 | * Called when particles should be displayed 9 | * at the given position in the given world, 10 | * with the given data as additional context 11 | * 12 | * @param world The world to display in 13 | * @param pos The position to display at 14 | * @param data The data to display with 15 | */ 16 | void executeParticleSystem(World world, Vec3d pos, T data); 17 | } 18 | -------------------------------------------------------------------------------- /src/main/java/io/wispforest/owo/registration/annotations/AssignedName.java: -------------------------------------------------------------------------------- 1 | package io.wispforest.owo.registration.annotations; 2 | 3 | import io.wispforest.owo.registration.reflect.FieldRegistrationHandler; 4 | 5 | import java.lang.annotation.ElementType; 6 | import java.lang.annotation.Retention; 7 | import java.lang.annotation.RetentionPolicy; 8 | import java.lang.annotation.Target; 9 | 10 | /** 11 | * Declares the name the targeted field should be assigned when processed by 12 | * {@link FieldRegistrationHandler} 13 | */ 14 | @Retention(RetentionPolicy.RUNTIME) 15 | @Target(ElementType.FIELD) 16 | public @interface AssignedName { 17 | String value(); 18 | } 19 | -------------------------------------------------------------------------------- /src/main/java/io/wispforest/owo/registration/annotations/IterationIgnored.java: -------------------------------------------------------------------------------- 1 | package io.wispforest.owo.registration.annotations; 2 | 3 | import io.wispforest.owo.registration.reflect.FieldRegistrationHandler; 4 | 5 | import java.lang.annotation.ElementType; 6 | import java.lang.annotation.Retention; 7 | import java.lang.annotation.RetentionPolicy; 8 | import java.lang.annotation.Target; 9 | 10 | /** 11 | * Specifies that the target field should be ignored by all operations 12 | * of {@link FieldRegistrationHandler} 13 | */ 14 | @Retention(RetentionPolicy.RUNTIME) 15 | @Target(ElementType.FIELD) 16 | public @interface IterationIgnored {} 17 | -------------------------------------------------------------------------------- /src/main/java/io/wispforest/owo/registration/annotations/RegistryNamespace.java: -------------------------------------------------------------------------------- 1 | package io.wispforest.owo.registration.annotations; 2 | 3 | import io.wispforest.owo.registration.reflect.AutoRegistryContainer; 4 | import io.wispforest.owo.registration.reflect.FieldRegistrationHandler; 5 | 6 | import java.lang.annotation.ElementType; 7 | import java.lang.annotation.Retention; 8 | import java.lang.annotation.RetentionPolicy; 9 | import java.lang.annotation.Target; 10 | 11 | /** 12 | * Can be used to override the namespace an implementation of 13 | * {@link AutoRegistryContainer} uses. 14 | *

15 | * This only applies to inner classes, top level classes have their namespace defined 16 | * in the call to {@link FieldRegistrationHandler} 17 | */ 18 | @Retention(RetentionPolicy.RUNTIME) 19 | @Target(ElementType.TYPE) 20 | public @interface RegistryNamespace { 21 | String value(); 22 | } 23 | -------------------------------------------------------------------------------- /src/main/java/io/wispforest/owo/registration/reflect/AutoRegistryContainer.java: -------------------------------------------------------------------------------- 1 | package io.wispforest.owo.registration.reflect; 2 | 3 | import io.wispforest.owo.registration.annotations.AssignedName; 4 | import net.minecraft.registry.Registry; 5 | 6 | import java.lang.reflect.Field; 7 | 8 | /** 9 | * A special version of {@link FieldProcessingSubject} that contains fields which should 10 | * be registered into a {@link Registry} using the field names in lowercase as ID 11 | *

12 | * Use {@link #register(Class, String, boolean)} to automatically register all fields 13 | * of a given implementation into its specified registry 14 | * 15 | * @param The type of objects to register, same as the Registry's type parameter 16 | */ 17 | public interface AutoRegistryContainer extends FieldProcessingSubject { 18 | 19 | /** 20 | * @return The registry the fields of this class should be registered into 21 | */ 22 | Registry getRegistry(); 23 | 24 | /** 25 | * Called after the given field has been registered 26 | * 27 | * @param namespace The namespace that is being used to register this class' fields 28 | * @param value The value that was registered 29 | * @param identifier The identifier the field was assigned, possibly overridden by an {@link AssignedName} 30 | * annotation and always fully lowercase 31 | */ 32 | default void postProcessField(String namespace, T value, String identifier, Field field) {} 33 | 34 | /** 35 | * Convenience-alias for {@link FieldRegistrationHandler#register(Class, String, boolean)} 36 | */ 37 | static void register(Class> container, String namespace, boolean recurse) { 38 | FieldRegistrationHandler.register(container, namespace, recurse); 39 | } 40 | 41 | @SuppressWarnings({"unchecked"}) 42 | static Class conform(Class input) { 43 | return (Class) input; 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /src/main/java/io/wispforest/owo/registration/reflect/BlockEntityRegistryContainer.java: -------------------------------------------------------------------------------- 1 | package io.wispforest.owo.registration.reflect; 2 | 3 | import net.minecraft.block.entity.BlockEntityType; 4 | import net.minecraft.registry.Registries; 5 | import net.minecraft.registry.Registry; 6 | 7 | public interface BlockEntityRegistryContainer extends AutoRegistryContainer> { 8 | 9 | @Override 10 | default Registry> getRegistry() { 11 | return Registries.BLOCK_ENTITY_TYPE; 12 | } 13 | 14 | @Override 15 | default Class> getTargetFieldType() { 16 | return AutoRegistryContainer.conform(BlockEntityType.class); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/main/java/io/wispforest/owo/registration/reflect/EntityRegistryContainer.java: -------------------------------------------------------------------------------- 1 | package io.wispforest.owo.registration.reflect; 2 | 3 | /** 4 | * Due to changes within 1.21.2 makes {@link AutoRegistryContainer} impossible due to requiring 5 | * the {@link net.minecraft.registry.RegistryKey} leading to the need to supplier the key before 6 | * fully creating the desired entry. 7 | *

8 | * Recommend using Minecraft methods for registering such entry 9 | */ 10 | @Deprecated(forRemoval = true) 11 | public interface EntityRegistryContainer /*extends AutoRegistryContainer>*/ { 12 | // @Override 13 | // default Registry> getRegistry() { 14 | // return Registries.ENTITY_TYPE; 15 | // } 16 | // 17 | // @Override 18 | // default Class> getTargetFieldType() { 19 | // return AutoRegistryContainer.conform(EntityType.class); 20 | // } 21 | } 22 | -------------------------------------------------------------------------------- /src/main/java/io/wispforest/owo/registration/reflect/FieldProcessingSubject.java: -------------------------------------------------------------------------------- 1 | package io.wispforest.owo.registration.reflect; 2 | 3 | import io.wispforest.owo.registration.annotations.AssignedName; 4 | 5 | import java.lang.reflect.Field; 6 | 7 | /** 8 | * A class that can have its accessible static fields that match the 9 | * class of T processed by the {@link FieldRegistrationHandler} 10 | *

11 | * All implementations must provide a zero-args constructor 12 | * 13 | * @param The type of field to be processed 14 | */ 15 | public interface FieldProcessingSubject { 16 | 17 | /** 18 | * @return The class of T 19 | */ 20 | Class getTargetFieldType(); 21 | 22 | /** 23 | * Called to check if a given field should be processed 24 | * 25 | * @param value The value the inspected field currently has 26 | * @param identifier The identifier that field was assigned, possibly overridden by an {@link AssignedName} 27 | * annotation and always fully lowercase 28 | * @return {@code true} if the inspected field should be processed 29 | */ 30 | default boolean shouldProcessField(T value, String identifier, Field field) { 31 | return true; 32 | } 33 | 34 | /** 35 | * Called after all applicable fields of this class have been processed 36 | */ 37 | default void afterFieldProcessing() { 38 | 39 | } 40 | 41 | } 42 | -------------------------------------------------------------------------------- /src/main/java/io/wispforest/owo/registration/reflect/ItemRegistryContainer.java: -------------------------------------------------------------------------------- 1 | package io.wispforest.owo.registration.reflect; 2 | 3 | import net.minecraft.item.Item; 4 | import net.minecraft.registry.Registries; 5 | import net.minecraft.registry.Registry; 6 | 7 | /** 8 | * Due to changes within 1.21.2 makes {@link AutoRegistryContainer} impossible due to requiring 9 | * the {@link net.minecraft.registry.RegistryKey} leading to the need to supplier the key before 10 | * fully creating the desired entry. 11 | *

12 | * Recommend using Minecraft methods for registering such entry 13 | */ 14 | @Deprecated(forRemoval = true) 15 | public interface ItemRegistryContainer /*extends AutoRegistryContainer*/ { 16 | // @Override 17 | // default Registry getRegistry() { 18 | // return Registries.ITEM; 19 | // } 20 | // 21 | // @Override 22 | // default Class getTargetFieldType() { 23 | // return Item.class; 24 | // } 25 | } 26 | -------------------------------------------------------------------------------- /src/main/java/io/wispforest/owo/registration/reflect/SimpleFieldProcessingSubject.java: -------------------------------------------------------------------------------- 1 | package io.wispforest.owo.registration.reflect; 2 | 3 | import io.wispforest.owo.registration.annotations.AssignedName; 4 | 5 | import java.lang.reflect.Field; 6 | 7 | /** 8 | * A simpler to use version of {@link FieldProcessingSubject} that 9 | * provides the processor to apply to its fields 10 | * 11 | * @param 12 | */ 13 | public interface SimpleFieldProcessingSubject extends FieldProcessingSubject { 14 | 15 | /** 16 | * Processes the given field 17 | * 18 | * @param value The value of the inspected field at the time this method is called 19 | * @param identifier The identifier that field was assigned, either it's name in lowercase or specified 20 | * by an {@link AssignedName} annotation 21 | */ 22 | void processField(T value, String identifier, Field field); 23 | } 24 | -------------------------------------------------------------------------------- /src/main/java/io/wispforest/owo/serialization/EndecRecipeSerializer.java: -------------------------------------------------------------------------------- 1 | package io.wispforest.owo.serialization; 2 | 3 | import com.mojang.serialization.MapCodec; 4 | import io.wispforest.endec.Endec; 5 | import io.wispforest.endec.SerializationAttributes; 6 | import io.wispforest.endec.SerializationContext; 7 | import io.wispforest.endec.StructEndec; 8 | import net.minecraft.network.PacketByteBuf; 9 | import net.minecraft.network.RegistryByteBuf; 10 | import net.minecraft.network.codec.PacketCodec; 11 | import net.minecraft.recipe.Recipe; 12 | import net.minecraft.recipe.RecipeSerializer; 13 | 14 | public class EndecRecipeSerializer> implements RecipeSerializer { 15 | 16 | private final PacketCodec packetCodec; 17 | private final MapCodec codec; 18 | 19 | public EndecRecipeSerializer(StructEndec endec, Endec networkEndec) { 20 | this.packetCodec = CodecUtils.toPacketCodec(networkEndec); 21 | this.codec = CodecUtils.toMapCodec(endec, SerializationContext.attributes(SerializationAttributes.HUMAN_READABLE)); 22 | } 23 | 24 | public EndecRecipeSerializer(StructEndec endec) { 25 | this(endec, endec); 26 | } 27 | 28 | @Override 29 | public MapCodec codec() { 30 | return this.codec; 31 | } 32 | 33 | @Override 34 | public PacketCodec packetCodec() { 35 | return this.packetCodec.cast(); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/main/java/io/wispforest/owo/serialization/OwoComponentTypeBuilder.java: -------------------------------------------------------------------------------- 1 | package io.wispforest.owo.serialization; 2 | 3 | import io.wispforest.endec.Endec; 4 | import io.wispforest.endec.SerializationContext; 5 | import net.minecraft.component.ComponentType; 6 | 7 | public interface OwoComponentTypeBuilder { 8 | default ComponentType.Builder endec(Endec endec) { 9 | return this.endec(endec, SerializationContext.empty()); 10 | } 11 | 12 | default ComponentType.Builder endec(Endec endec, SerializationContext assumedContext) { 13 | return ((ComponentType.Builder) this) 14 | .codec(CodecUtils.toCodec(endec, assumedContext)) 15 | .packetCodec(CodecUtils.toPacketCodec(endec)); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/main/java/io/wispforest/owo/serialization/endec/DefaultedListEndec.java: -------------------------------------------------------------------------------- 1 | package io.wispforest.owo.serialization.endec; 2 | 3 | import io.wispforest.endec.Endec; 4 | import io.wispforest.endec.impl.StructEndecBuilder; 5 | import net.minecraft.util.collection.DefaultedList; 6 | 7 | import java.util.ArrayList; 8 | import java.util.Objects; 9 | import java.util.function.Predicate; 10 | 11 | public final class DefaultedListEndec { 12 | 13 | private DefaultedListEndec() {} 14 | 15 | public static Endec> forSize(Endec elementEndec, T defaultValue, int size) { 16 | return forSize(elementEndec, defaultValue, element -> Objects.equals(defaultValue, element), size); 17 | } 18 | 19 | public static Endec> forSize(Endec elementEndec, T defaultValue, Predicate skipWhen, int size) { 20 | var entryEndec = StructEndecBuilder.of( 21 | elementEndec.fieldOf("element", s -> s.element), 22 | Endec.VAR_INT.fieldOf("idx", s -> s.idx), 23 | Entry::new 24 | ); 25 | 26 | return entryEndec.listOf().xmap( 27 | entries -> { 28 | var list = DefaultedList.ofSize(size, defaultValue); 29 | entries.forEach(entry -> list.set(entry.idx, entry.element)); 30 | return list; 31 | }, elements -> { 32 | var entries = new ArrayList>(); 33 | for (int i = 0; i < elements.size(); i++) { 34 | if (skipWhen.test(elements.get(i))) continue; 35 | entries.add(new Entry<>(elements.get(i), i)); 36 | } 37 | return entries; 38 | } 39 | ); 40 | } 41 | 42 | private record Entry(T element, int idx) {} 43 | } -------------------------------------------------------------------------------- /src/main/java/io/wispforest/owo/serialization/format/ContextHolder.java: -------------------------------------------------------------------------------- 1 | package io.wispforest.owo.serialization.format; 2 | 3 | import io.wispforest.endec.SerializationContext; 4 | 5 | /** 6 | * A common interface for parts of a serialization infrastructure 7 | * which provide an instance of {@link SerializationContext}. Primarily 8 | * used for attaching context to {@link com.mojang.serialization.DynamicOps} 9 | */ 10 | public interface ContextHolder { 11 | SerializationContext capturedContext(); 12 | } -------------------------------------------------------------------------------- /src/main/java/io/wispforest/owo/serialization/format/DynamicOpsWithContext.java: -------------------------------------------------------------------------------- 1 | package io.wispforest.owo.serialization.format; 2 | 3 | import com.mojang.serialization.DynamicOps; 4 | import io.wispforest.endec.SerializationContext; 5 | import net.minecraft.util.dynamic.ForwardingDynamicOps; 6 | 7 | public class DynamicOpsWithContext extends ForwardingDynamicOps implements ContextHolder { 8 | 9 | private final SerializationContext capturedContext; 10 | 11 | protected DynamicOpsWithContext(SerializationContext capturedContext, DynamicOps delegate) { 12 | super(delegate); 13 | 14 | this.capturedContext = capturedContext; 15 | } 16 | 17 | public static DynamicOpsWithContext of(SerializationContext context, DynamicOps delegate) { 18 | return new DynamicOpsWithContext<>(context, delegate); 19 | } 20 | 21 | public static DynamicOpsWithContext ofEmptyContext(DynamicOps delegate) { 22 | return new DynamicOpsWithContext<>(SerializationContext.empty(), delegate); 23 | } 24 | 25 | @Override 26 | public SerializationContext capturedContext() { 27 | return this.capturedContext; 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/main/java/io/wispforest/owo/serialization/format/nbt/IdentityHolder.java: -------------------------------------------------------------------------------- 1 | package io.wispforest.owo.serialization.format.nbt; 2 | 3 | record IdentityHolder(T t) { 4 | @Override 5 | public boolean equals(Object obj) { 6 | if (obj == null || obj.getClass() != this.getClass()) return false; 7 | return this.t == ((IdentityHolder) obj).t; 8 | } 9 | 10 | @Override 11 | public int hashCode() { 12 | return System.identityHashCode(this.t); 13 | } 14 | 15 | @Override 16 | public String toString() { 17 | return "IdentityHolder[t=" + t + ']'; 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/main/java/io/wispforest/owo/serialization/format/nbt/NbtEndec.java: -------------------------------------------------------------------------------- 1 | package io.wispforest.owo.serialization.format.nbt; 2 | 3 | import com.google.common.io.ByteStreams; 4 | import io.wispforest.endec.*; 5 | import net.minecraft.nbt.NbtCompound; 6 | import net.minecraft.nbt.NbtElement; 7 | import net.minecraft.nbt.NbtIo; 8 | import net.minecraft.nbt.NbtSizeTracker; 9 | 10 | import java.io.IOException; 11 | 12 | public final class NbtEndec implements Endec { 13 | 14 | public static final Endec ELEMENT = new NbtEndec(); 15 | public static final Endec COMPOUND = new NbtEndec().xmap(NbtCompound.class::cast, compound -> compound); 16 | 17 | private NbtEndec() {} 18 | 19 | @Override 20 | public void encode(SerializationContext ctx, Serializer serializer, NbtElement value) { 21 | if (serializer instanceof SelfDescribedSerializer) { 22 | NbtDeserializer.of(value).readAny(ctx, serializer); 23 | return; 24 | } 25 | 26 | try { 27 | var output = ByteStreams.newDataOutput(); 28 | NbtIo.writeForPacket(value, output); 29 | 30 | serializer.writeBytes(ctx, output.toByteArray()); 31 | } catch (IOException e) { 32 | throw new RuntimeException("Failed to encode binary NBT in NbtEndec", e); 33 | } 34 | } 35 | 36 | @Override 37 | public NbtElement decode(SerializationContext ctx, Deserializer deserializer) { 38 | if (deserializer instanceof SelfDescribedDeserializer selfDescribedDeserializer) { 39 | var nbt = NbtSerializer.of(); 40 | selfDescribedDeserializer.readAny(ctx, nbt); 41 | 42 | return nbt.result(); 43 | } 44 | 45 | try { 46 | return NbtIo.read(ByteStreams.newDataInput(deserializer.readBytes(ctx)), NbtSizeTracker.ofUnlimitedBytes()); 47 | } catch (IOException e) { 48 | throw new RuntimeException("Failed to parse binary NBT in NbtEndec", e); 49 | } 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /src/main/java/io/wispforest/owo/text/CustomTextRegistry.java: -------------------------------------------------------------------------------- 1 | package io.wispforest.owo.text; 2 | 3 | import net.minecraft.text.TextContent; 4 | import org.jetbrains.annotations.ApiStatus; 5 | 6 | import java.util.HashMap; 7 | import java.util.Map; 8 | 9 | public final class CustomTextRegistry { 10 | 11 | private static final Map> TYPES = new HashMap<>(); 12 | 13 | private CustomTextRegistry() {} 14 | 15 | public static void register(TextContent.Type type, String triggerField) { 16 | TYPES.put(type.id(), new Entry<>(triggerField, type)); 17 | } 18 | 19 | @ApiStatus.Internal 20 | public static Map> typesMap() { 21 | return TYPES; 22 | } 23 | 24 | public record Entry(String triggerField, TextContent.Type type) {} 25 | } 26 | -------------------------------------------------------------------------------- /src/main/java/io/wispforest/owo/text/InsertingTextContent.java: -------------------------------------------------------------------------------- 1 | package io.wispforest.owo.text; 2 | 3 | import io.wispforest.endec.Endec; 4 | import io.wispforest.endec.impl.StructEndecBuilder; 5 | import io.wispforest.owo.serialization.CodecUtils; 6 | import net.minecraft.text.StringVisitable; 7 | import net.minecraft.text.Style; 8 | import net.minecraft.text.Text; 9 | import net.minecraft.text.TextContent; 10 | 11 | import java.util.Optional; 12 | 13 | public record InsertingTextContent(int index) implements TextContent { 14 | 15 | public static final TextContent.Type TYPE = new Type<>( 16 | CodecUtils.toMapCodec(StructEndecBuilder.of(Endec.INT.fieldOf("index", InsertingTextContent::index), InsertingTextContent::new)), 17 | "owo:insert" 18 | ); 19 | 20 | @Override 21 | public Optional visit(StringVisitable.Visitor visitor) { 22 | var current = TranslationContext.getCurrent(); 23 | 24 | if (current == null || current.getArgs().length <= index) {return visitor.accept("%" + (index + 1) + "$s");} 25 | 26 | Object arg = current.getArgs()[index]; 27 | 28 | if (arg instanceof Text text) { 29 | return text.visit(visitor); 30 | } else { 31 | return visitor.accept(arg.toString()); 32 | } 33 | } 34 | 35 | @Override 36 | public Optional visit(StringVisitable.StyledVisitor visitor, Style style) { 37 | var current = TranslationContext.getCurrent(); 38 | 39 | if (current == null || current.getArgs().length <= index) { 40 | return visitor.accept(style, "%" + (index + 1) + "$s"); 41 | } 42 | 43 | Object arg = current.getArgs()[index]; 44 | 45 | if (arg instanceof Text text) { 46 | return text.visit(visitor, style); 47 | } else { 48 | return visitor.accept(style, arg.toString()); 49 | } 50 | } 51 | 52 | @Override 53 | public Type getType() { 54 | return TYPE; 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /src/main/java/io/wispforest/owo/text/LanguageAccess.java: -------------------------------------------------------------------------------- 1 | package io.wispforest.owo.text; 2 | 3 | import net.minecraft.text.Text; 4 | import org.jetbrains.annotations.ApiStatus; 5 | 6 | import java.util.function.BiConsumer; 7 | 8 | @ApiStatus.Internal 9 | public class LanguageAccess { 10 | public static BiConsumer textConsumer; 11 | } 12 | -------------------------------------------------------------------------------- /src/main/java/io/wispforest/owo/text/TextLanguage.java: -------------------------------------------------------------------------------- 1 | package io.wispforest.owo.text; 2 | 3 | import net.minecraft.text.Text; 4 | 5 | public interface TextLanguage { 6 | Text getText(String key); 7 | } 8 | -------------------------------------------------------------------------------- /src/main/java/io/wispforest/owo/text/TranslationContext.java: -------------------------------------------------------------------------------- 1 | package io.wispforest.owo.text; 2 | 3 | import net.minecraft.text.TranslatableTextContent; 4 | 5 | import java.util.ArrayList; 6 | import java.util.List; 7 | 8 | public class TranslationContext { 9 | private static final ThreadLocal> translationStack = ThreadLocal.withInitial(ArrayList::new); 10 | 11 | public static boolean pushContent(TranslatableTextContent content) { 12 | var stack = translationStack.get(); 13 | 14 | for (int i = 0; i < stack.size(); i++) { 15 | if (stack.get(i) == content) 16 | return false; 17 | } 18 | 19 | stack.add(content); 20 | 21 | return true; 22 | } 23 | 24 | public static void popContent() { 25 | var stack = translationStack.get(); 26 | 27 | stack.remove(stack.size() - 1); 28 | } 29 | 30 | public static TranslatableTextContent getCurrent() { 31 | var stack = translationStack.get(); 32 | 33 | if (stack.isEmpty()) 34 | return null; 35 | else 36 | return stack.get(stack.size() - 1); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/main/java/io/wispforest/owo/ui/base/BaseOwoTooltipComponent.java: -------------------------------------------------------------------------------- 1 | package io.wispforest.owo.ui.base; 2 | 3 | import io.wispforest.owo.ui.core.OwoUIDrawContext; 4 | import io.wispforest.owo.ui.core.ParentComponent; 5 | import io.wispforest.owo.ui.core.Size; 6 | import net.minecraft.client.MinecraftClient; 7 | import net.minecraft.client.font.TextRenderer; 8 | import net.minecraft.client.gui.DrawContext; 9 | import net.minecraft.client.gui.tooltip.TooltipComponent; 10 | import org.jetbrains.annotations.ApiStatus; 11 | 12 | import java.util.function.Supplier; 13 | 14 | @ApiStatus.Experimental 15 | public abstract class BaseOwoTooltipComponent implements TooltipComponent { 16 | 17 | protected final R rootComponent; 18 | protected int virtualWidth = 1000, virtualHeight = 1000; 19 | 20 | protected BaseOwoTooltipComponent(Supplier components) { 21 | this.rootComponent = components.get(); 22 | 23 | this.rootComponent.inflate(Size.of(this.virtualWidth, this.virtualHeight)); 24 | this.rootComponent.mount(null, 0, 0); 25 | } 26 | 27 | @Override 28 | public void drawItems(TextRenderer textRenderer, int x, int y, int width, int height, DrawContext context) { 29 | var tickCounter = MinecraftClient.getInstance().getRenderTickCounter(); 30 | 31 | this.rootComponent.moveTo(x, y); 32 | this.rootComponent.draw(OwoUIDrawContext.of(context), -1000, -1000, tickCounter.getTickProgress(false), tickCounter.getDynamicDeltaTicks()); 33 | } 34 | 35 | @Override 36 | public int getHeight(TextRenderer textRenderer) { 37 | return this.rootComponent.fullSize().height(); 38 | } 39 | 40 | @Override 41 | public int getWidth(TextRenderer textRenderer) { 42 | return this.rootComponent.fullSize().width(); 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /src/main/java/io/wispforest/owo/ui/component/SpacerComponent.java: -------------------------------------------------------------------------------- 1 | package io.wispforest.owo.ui.component; 2 | 3 | import io.wispforest.owo.ui.base.BaseComponent; 4 | import io.wispforest.owo.ui.core.OwoUIDrawContext; 5 | import io.wispforest.owo.ui.core.Sizing; 6 | import io.wispforest.owo.ui.parsing.UIParsing; 7 | import org.w3c.dom.Element; 8 | 9 | public class SpacerComponent extends BaseComponent { 10 | 11 | protected SpacerComponent(int percent) { 12 | this.sizing(Sizing.expand(percent)); 13 | } 14 | 15 | @Override 16 | public void draw(OwoUIDrawContext context, int mouseX, int mouseY, float partialTicks, float delta) {} 17 | 18 | public static SpacerComponent parse(Element element) { 19 | if (!element.hasAttribute("percent")) return Components.spacer(); 20 | return Components.spacer(UIParsing.parseUnsignedInt(element.getAttributeNode("percent"))); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/main/java/io/wispforest/owo/ui/core/Animatable.java: -------------------------------------------------------------------------------- 1 | package io.wispforest.owo.ui.core; 2 | 3 | public interface Animatable> { 4 | 5 | T interpolate(T next, float delta); 6 | 7 | } 8 | -------------------------------------------------------------------------------- /src/main/java/io/wispforest/owo/ui/core/Easing.java: -------------------------------------------------------------------------------- 1 | package io.wispforest.owo.ui.core; 2 | 3 | /** 4 | * An easing function which can smoothly move 5 | * an interpolation value from 0 to 1 6 | */ 7 | public interface Easing { 8 | 9 | Easing LINEAR = x -> x; 10 | 11 | Easing SINE = x -> { 12 | return (float) (Math.sin(x * Math.PI - Math.PI / 2) * 0.5 + 0.5); 13 | }; 14 | 15 | Easing QUADRATIC = x -> { 16 | return x < 0.5 ? 2 * x * x : (float) (1 - Math.pow(-2 * x + 2, 2) / 2); 17 | }; 18 | 19 | Easing CUBIC = x -> { 20 | return x < 0.5 ? 4 * x * x * x : (float) (1 - Math.pow(-2 * x + 2, 3) / 2); 21 | }; 22 | 23 | Easing QUARTIC = x -> { 24 | return x < 0.5 ? 8 * x * x * x * x : (float) (1 - Math.pow(-2 * x + 2, 4) / 2); 25 | }; 26 | 27 | Easing EXPO = x -> { 28 | if (x == 0) return 0; 29 | if (x == 1) return 1; 30 | 31 | return x < 0.5 32 | ? (float) Math.pow(2, 20 * x - 10) / 2 33 | : (2 - (float) Math.pow(2, -20 * x + 10)) / 2; 34 | }; 35 | 36 | float apply(float x); 37 | 38 | } 39 | -------------------------------------------------------------------------------- /src/main/java/io/wispforest/owo/ui/core/HorizontalAlignment.java: -------------------------------------------------------------------------------- 1 | package io.wispforest.owo.ui.core; 2 | 3 | import org.w3c.dom.Element; 4 | 5 | import java.util.Locale; 6 | 7 | public enum HorizontalAlignment { 8 | LEFT, CENTER, RIGHT; 9 | 10 | public int align(int componentWidth, int span) { 11 | return switch (this) { 12 | case LEFT -> 0; 13 | case CENTER -> span / 2 - componentWidth / 2; 14 | case RIGHT -> span - componentWidth; 15 | }; 16 | } 17 | 18 | public static HorizontalAlignment parse(Element element) { 19 | return valueOf(element.getTextContent().strip().toUpperCase(Locale.ROOT)); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/main/java/io/wispforest/owo/ui/core/OwoUIRenderLayers.java: -------------------------------------------------------------------------------- 1 | package io.wispforest.owo.ui.core; 2 | 3 | import net.minecraft.client.render.RenderLayer; 4 | import net.minecraft.client.render.RenderPhase; 5 | import net.minecraft.util.Identifier; 6 | import net.minecraft.util.TriState; 7 | import net.minecraft.util.Util; 8 | 9 | import java.util.function.Function; 10 | 11 | public class OwoUIRenderLayers { 12 | 13 | public static final RenderLayer.MultiPhase GUI_TRIANGLE_FAN = RenderLayer.of( 14 | "owo:gui_triangle_fan_default_blend", 15 | 0xc0000, 16 | OwoUIPipelines.GUI_TRIANGLE_FAN, 17 | RenderLayer.MultiPhaseParameters.builder().build(false) 18 | ); 19 | 20 | public static final RenderLayer.MultiPhase GUI_TRIANGLE_STRIP = RenderLayer.of( 21 | "owo:gui_triangle_strip_default_blend", 22 | 0xc0000, 23 | OwoUIPipelines.GUI_TRIANGLE_STRIP, 24 | RenderLayer.MultiPhaseParameters.builder().build(false) 25 | ); 26 | 27 | public static final RenderLayer.MultiPhase GUI_SPECTRUM = RenderLayer.of( 28 | "owo:gui_spectrum", 29 | 0xc0000, 30 | OwoUIPipelines.GUI_HSV, 31 | RenderLayer.MultiPhaseParameters.builder().build(false) 32 | ); 33 | 34 | //-- 35 | 36 | private static final Function GUI_TEXTURED_NO_BLEND = Util.memoize( 37 | (texture) -> RenderLayer.of( 38 | "gui_textured", 39 | 0xc0000, 40 | OwoUIPipelines.GUI_TEXTURED_NO_BLEND, 41 | RenderLayer.MultiPhaseParameters.builder() 42 | .texture(new RenderPhase.Texture(texture, TriState.FALSE, false)) 43 | .build(false) 44 | ) 45 | ); 46 | 47 | public static RenderLayer getGuiTextured(Identifier texture, boolean blend) { 48 | return blend ? RenderLayer.getGuiTextured(texture) : GUI_TEXTURED_NO_BLEND.apply(texture); 49 | } 50 | 51 | } 52 | -------------------------------------------------------------------------------- /src/main/java/io/wispforest/owo/ui/core/Size.java: -------------------------------------------------------------------------------- 1 | package io.wispforest.owo.ui.core; 2 | 3 | import io.wispforest.endec.Endec; 4 | import io.wispforest.endec.impl.StructEndecBuilder; 5 | import org.jetbrains.annotations.ApiStatus; 6 | 7 | /** 8 | * Represents a two-dimensional value, used for 9 | * describing position-less rectangles in 2D-space 10 | * 11 | * @param width The width of the rectangle 12 | * @param height The height of the rectangle 13 | */ 14 | public record Size(int width, int height) { 15 | 16 | public static final Endec ENDEC =StructEndecBuilder.of( 17 | Endec.INT.fieldOf("width", Size::width), 18 | Endec.INT.fieldOf("height", Size::height), 19 | Size::of 20 | ); 21 | 22 | private static final Size ZERO = new Size(0, 0); 23 | 24 | @ApiStatus.Internal 25 | @Deprecated(forRemoval = true) 26 | public Size {} 27 | 28 | public static Size of(int width, int height) { 29 | return new Size(width, height); 30 | } 31 | 32 | public static Size square(int sideLength) { 33 | return new Size(sideLength, sideLength); 34 | } 35 | 36 | /** 37 | * @return A size with both values equal to 0 38 | */ 39 | public static Size zero() { 40 | return ZERO; 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/main/java/io/wispforest/owo/ui/core/VerticalAlignment.java: -------------------------------------------------------------------------------- 1 | package io.wispforest.owo.ui.core; 2 | 3 | import org.w3c.dom.Element; 4 | 5 | import java.util.Locale; 6 | 7 | public enum VerticalAlignment { 8 | TOP, CENTER, BOTTOM; 9 | 10 | public int align(int componentWidth, int span) { 11 | return switch (this) { 12 | case TOP -> 0; 13 | case CENTER -> span / 2 - componentWidth / 2; 14 | case BOTTOM -> span - componentWidth; 15 | }; 16 | } 17 | 18 | public static VerticalAlignment parse(Element element) { 19 | return valueOf(element.getTextContent().strip().toUpperCase(Locale.ROOT)); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/main/java/io/wispforest/owo/ui/event/CharTyped.java: -------------------------------------------------------------------------------- 1 | package io.wispforest.owo.ui.event; 2 | 3 | import io.wispforest.owo.util.EventStream; 4 | 5 | public interface CharTyped { 6 | boolean onCharTyped(char chr, int modifiers); 7 | 8 | static EventStream newStream() { 9 | return new EventStream<>(subscribers -> (chr, modifiers) -> { 10 | var anyTriggered = false; 11 | for (var subscriber : subscribers) { 12 | anyTriggered |= subscriber.onCharTyped(chr, modifiers); 13 | } 14 | return anyTriggered; 15 | }); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/main/java/io/wispforest/owo/ui/event/ClientRenderCallback.java: -------------------------------------------------------------------------------- 1 | package io.wispforest.owo.ui.event; 2 | 3 | import net.fabricmc.fabric.api.event.Event; 4 | import net.fabricmc.fabric.api.event.EventFactory; 5 | import net.minecraft.client.MinecraftClient; 6 | 7 | public interface ClientRenderCallback { 8 | 9 | /** 10 | * Invoked just before the client's window enters the 'Render' phase, after the client 11 | * has ticked and cleared the render task queue 12 | */ 13 | Event BEFORE = EventFactory.createArrayBacked(ClientRenderCallback.class, callbacks -> (client) -> { 14 | for (var callback : callbacks) { 15 | callback.onRender(client); 16 | } 17 | }); 18 | 19 | /** 20 | * Called just after the client has finished rendering and drawing the 21 | * current frame and swapped buffers 22 | */ 23 | Event AFTER = EventFactory.createArrayBacked(ClientRenderCallback.class, callbacks -> (client) -> { 24 | for (var callback : callbacks) { 25 | callback.onRender(client); 26 | } 27 | }); 28 | 29 | void onRender(MinecraftClient client); 30 | } 31 | -------------------------------------------------------------------------------- /src/main/java/io/wispforest/owo/ui/event/FocusGained.java: -------------------------------------------------------------------------------- 1 | package io.wispforest.owo.ui.event; 2 | 3 | import io.wispforest.owo.ui.core.Component; 4 | import io.wispforest.owo.util.EventStream; 5 | 6 | public interface FocusGained { 7 | void onFocusGained(Component.FocusSource source); 8 | 9 | static EventStream newStream() { 10 | return new EventStream<>(subscribers -> source -> { 11 | for (var subscriber : subscribers) { 12 | subscriber.onFocusGained(source); 13 | } 14 | }); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/main/java/io/wispforest/owo/ui/event/FocusLost.java: -------------------------------------------------------------------------------- 1 | package io.wispforest.owo.ui.event; 2 | 3 | import io.wispforest.owo.util.EventStream; 4 | 5 | public interface FocusLost { 6 | void onFocusLost(); 7 | 8 | static EventStream newStream() { 9 | return new EventStream<>(subscribers -> () -> { 10 | for (var subscriber : subscribers) { 11 | subscriber.onFocusLost(); 12 | } 13 | }); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/main/java/io/wispforest/owo/ui/event/KeyPress.java: -------------------------------------------------------------------------------- 1 | package io.wispforest.owo.ui.event; 2 | 3 | import io.wispforest.owo.util.EventStream; 4 | 5 | public interface KeyPress { 6 | boolean onKeyPress(int keyCode, int scanCode, int modifiers); 7 | 8 | static EventStream newStream() { 9 | return new EventStream<>(subscribers -> (keyCode, scanCode, modifiers) -> { 10 | var anyTriggered = false; 11 | for (var subscriber : subscribers) { 12 | anyTriggered |= subscriber.onKeyPress(keyCode, scanCode, modifiers); 13 | } 14 | return anyTriggered; 15 | }); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/main/java/io/wispforest/owo/ui/event/MouseDown.java: -------------------------------------------------------------------------------- 1 | package io.wispforest.owo.ui.event; 2 | 3 | import io.wispforest.owo.util.EventStream; 4 | 5 | public interface MouseDown { 6 | boolean onMouseDown(double mouseX, double mouseY, int button); 7 | 8 | static EventStream newStream() { 9 | return new EventStream<>(subscribers -> (mouseX, mouseY, button) -> { 10 | var anyTriggered = false; 11 | for (var subscriber : subscribers) { 12 | anyTriggered |= subscriber.onMouseDown(mouseX, mouseY, button); 13 | } 14 | return anyTriggered; 15 | }); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/main/java/io/wispforest/owo/ui/event/MouseDrag.java: -------------------------------------------------------------------------------- 1 | package io.wispforest.owo.ui.event; 2 | 3 | import io.wispforest.owo.util.EventStream; 4 | 5 | public interface MouseDrag { 6 | boolean onMouseDrag(double mouseX, double mouseY, double deltaX, double deltaY, int button); 7 | 8 | static EventStream newStream() { 9 | return new EventStream<>(subscribers -> (mouseX, mouseY, deltaX, deltaY, button) -> { 10 | var anyTriggered = false; 11 | for (var subscriber : subscribers) { 12 | anyTriggered |= subscriber.onMouseDrag(mouseX, mouseY, deltaX, deltaY, button); 13 | } 14 | return anyTriggered; 15 | }); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/main/java/io/wispforest/owo/ui/event/MouseEnter.java: -------------------------------------------------------------------------------- 1 | package io.wispforest.owo.ui.event; 2 | 3 | import io.wispforest.owo.util.EventStream; 4 | 5 | public interface MouseEnter { 6 | void onMouseEnter(); 7 | 8 | static EventStream newStream() { 9 | return new EventStream<>(subscribers -> () -> { 10 | for (var subscriber : subscribers) { 11 | subscriber.onMouseEnter(); 12 | } 13 | }); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/main/java/io/wispforest/owo/ui/event/MouseLeave.java: -------------------------------------------------------------------------------- 1 | package io.wispforest.owo.ui.event; 2 | 3 | import io.wispforest.owo.util.EventStream; 4 | 5 | public interface MouseLeave { 6 | void onMouseLeave(); 7 | 8 | static EventStream newStream() { 9 | return new EventStream<>(subscribers -> () -> { 10 | for (var subscriber : subscribers) { 11 | subscriber.onMouseLeave(); 12 | } 13 | }); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/main/java/io/wispforest/owo/ui/event/MouseScroll.java: -------------------------------------------------------------------------------- 1 | package io.wispforest.owo.ui.event; 2 | 3 | import io.wispforest.owo.util.EventStream; 4 | 5 | public interface MouseScroll { 6 | boolean onMouseScroll(double mouseX, double mouseY, double amount); 7 | 8 | static EventStream newStream() { 9 | return new EventStream<>(subscribers -> (mouseX, mouseY, amount) -> { 10 | var anyTriggered = false; 11 | for (var subscriber : subscribers) { 12 | anyTriggered |= subscriber.onMouseScroll(mouseX, mouseY, amount); 13 | } 14 | return anyTriggered; 15 | }); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/main/java/io/wispforest/owo/ui/event/MouseUp.java: -------------------------------------------------------------------------------- 1 | package io.wispforest.owo.ui.event; 2 | 3 | import io.wispforest.owo.util.EventStream; 4 | 5 | public interface MouseUp { 6 | boolean onMouseUp(double mouseX, double mouseY, int button); 7 | 8 | static EventStream newStream() { 9 | return new EventStream<>(subscribers -> (mouseX, mouseY, button) -> { 10 | var anyTriggered = false; 11 | for (var subscriber : subscribers) { 12 | anyTriggered |= subscriber.onMouseUp(mouseX, mouseY, button); 13 | } 14 | return anyTriggered; 15 | }); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/main/java/io/wispforest/owo/ui/event/WindowResizeCallback.java: -------------------------------------------------------------------------------- 1 | package io.wispforest.owo.ui.event; 2 | 3 | import net.fabricmc.fabric.api.event.Event; 4 | import net.fabricmc.fabric.api.event.EventFactory; 5 | import net.minecraft.client.MinecraftClient; 6 | import net.minecraft.client.util.Window; 7 | 8 | public interface WindowResizeCallback { 9 | 10 | Event EVENT = EventFactory.createArrayBacked(WindowResizeCallback.class, callbacks -> (client, window) -> { 11 | for (var callback : callbacks) { 12 | callback.onResized(client, window); 13 | } 14 | }); 15 | 16 | /** 17 | * Called after the client's window has been resized 18 | * 19 | * @param client The currently active client 20 | * @param window The window which was resized 21 | */ 22 | void onResized(MinecraftClient client, Window window); 23 | 24 | } 25 | -------------------------------------------------------------------------------- /src/main/java/io/wispforest/owo/ui/hud/HudContainer.java: -------------------------------------------------------------------------------- 1 | package io.wispforest.owo.ui.hud; 2 | 3 | import io.wispforest.owo.ui.container.FlowLayout; 4 | import io.wispforest.owo.ui.core.Component; 5 | import io.wispforest.owo.ui.core.Positioning; 6 | import io.wispforest.owo.ui.core.Sizing; 7 | import org.jetbrains.annotations.Nullable; 8 | 9 | import java.util.function.Consumer; 10 | 11 | /** 12 | * Very simple extension of {@link io.wispforest.owo.ui.container.FlowLayout} that 13 | * does not allow children to be layout-positioned, used by {@link Hud} 14 | */ 15 | public class HudContainer extends FlowLayout { 16 | 17 | protected HudContainer(Sizing horizontalSizing, Sizing verticalSizing) { 18 | super(horizontalSizing, verticalSizing, Algorithm.VERTICAL); 19 | } 20 | 21 | @Override 22 | protected void mountChild(@Nullable Component child, Consumer layoutFunc) { 23 | if (child == null) return; 24 | 25 | if (child.positioning().get().type == Positioning.Type.LAYOUT) { 26 | throw new IllegalStateException("owo-ui HUD components must be explicitly positioned"); 27 | } else { 28 | super.mountChild(child, layoutFunc); 29 | } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/main/java/io/wispforest/owo/ui/hud/HudInspectorScreen.java: -------------------------------------------------------------------------------- 1 | package io.wispforest.owo.ui.hud; 2 | 3 | import io.wispforest.owo.ui.util.CommandOpenedScreen; 4 | import net.minecraft.client.gui.DrawContext; 5 | import net.minecraft.client.gui.screen.Screen; 6 | import net.minecraft.text.Text; 7 | 8 | public class HudInspectorScreen extends Screen implements CommandOpenedScreen { 9 | 10 | public HudInspectorScreen() { 11 | super(Text.empty()); 12 | if (Hud.adapter != null) { 13 | Hud.suppress = true; 14 | Hud.adapter.enableInspector = true; 15 | } 16 | } 17 | 18 | @Override 19 | public void render(DrawContext context, int mouseX, int mouseY, float delta) { 20 | super.render(context, mouseX, mouseY, delta); 21 | 22 | if (Hud.adapter == null) return; 23 | Hud.adapter.render(context, mouseX, mouseY, delta); 24 | } 25 | 26 | @Override 27 | public void removed() { 28 | if (Hud.adapter != null) { 29 | Hud.suppress = false; 30 | Hud.adapter.enableInspector = false; 31 | } 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/main/java/io/wispforest/owo/ui/inject/GreedyInputComponent.java: -------------------------------------------------------------------------------- 1 | package io.wispforest.owo.ui.inject; 2 | 3 | import io.wispforest.owo.ui.core.Component; 4 | 5 | /** 6 | * A marker interface for components which consume 7 | * text input when focused - this is used to prevent handled 8 | * screens from closing when said component is focused and the 9 | * inventory key is pressed 10 | */ 11 | public interface GreedyInputComponent extends Component {} -------------------------------------------------------------------------------- /src/main/java/io/wispforest/owo/ui/parsing/IncompatibleUIModelException.java: -------------------------------------------------------------------------------- 1 | package io.wispforest.owo.ui.parsing; 2 | 3 | /** 4 | * Describes an error that occurred because the UIModel provided 5 | * to a method did not match the expectations set by said method. 6 | * These expectations are most often expressed in terms of component 7 | * classes, a violation of which will throw this exception 8 | */ 9 | public class IncompatibleUIModelException extends RuntimeException { 10 | 11 | public IncompatibleUIModelException(String message) { 12 | super(message); 13 | } 14 | 15 | public IncompatibleUIModelException(String message, Throwable cause) { 16 | super(message, cause); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/main/java/io/wispforest/owo/ui/parsing/UIModelParsingException.java: -------------------------------------------------------------------------------- 1 | package io.wispforest.owo.ui.parsing; 2 | 3 | /** 4 | * Describes an error that happened during instantiation 5 | * of a UIModel, most commonly due to improperly formatted XML 6 | * or XML which describes invalid values 7 | */ 8 | public class UIModelParsingException extends RuntimeException { 9 | 10 | public UIModelParsingException(String message) { 11 | super(message); 12 | } 13 | 14 | public UIModelParsingException(String message, Throwable cause) { 15 | super(message, cause); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/main/java/io/wispforest/owo/ui/util/CommandOpenedScreen.java: -------------------------------------------------------------------------------- 1 | package io.wispforest.owo.ui.util; 2 | 3 | /** 4 | * A marker interface for screens that are opened by client-sided commands 5 | * which prevents the chat screen from closing the newly opened screen 6 | */ 7 | public interface CommandOpenedScreen {} 8 | -------------------------------------------------------------------------------- /src/main/java/io/wispforest/owo/ui/util/Delta.java: -------------------------------------------------------------------------------- 1 | package io.wispforest.owo.ui.util; 2 | 3 | /** 4 | * Trying to give this utility class a 5 | * sensible name makes me mald 6 | */ 7 | public final class Delta { 8 | 9 | private Delta() {} 10 | 11 | /** 12 | * Compute an additive interpolator for smoothly approaching the 13 | * target value given the current value and some interpolation 14 | * delta 15 | * 16 | * @param current The current value 17 | * @param target The target value to approach 18 | * @param delta The interpolation delta - this is usually the frame delta, 19 | * optionally multiplied by some factor 20 | * @return The computed interpolator, to be added to the current value 21 | */ 22 | public static float compute(float current, float target, float delta) { 23 | float diff = target - current; 24 | delta = diff * delta; 25 | 26 | return Math.abs(delta) > Math.abs(diff) ? diff : delta; 27 | } 28 | 29 | /** 30 | * Compute an additive interpolator for smoothly approaching the 31 | * target value given the current value and some interpolation 32 | * delta 33 | * 34 | * @param current The current value 35 | * @param target The target value to approach 36 | * @param delta The interpolation delta - this is usually the frame delta, 37 | * optionally multiplied by some factor 38 | * @return The computed interpolator, to be added to the current value 39 | */ 40 | public static double compute(double current, double target, double delta) { 41 | double diff = target - current; 42 | delta = diff * delta; 43 | 44 | return Math.abs(delta) > Math.abs(diff) ? diff : delta; 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /src/main/java/io/wispforest/owo/ui/util/DisposableScreen.java: -------------------------------------------------------------------------------- 1 | package io.wispforest.owo.ui.util; 2 | 3 | import net.minecraft.client.gui.screen.Screen; 4 | 5 | /** 6 | * Screens that wish to be notified when the players navigates back to 7 | * the game instead of to another screen may implement this interface 8 | * for a more reliable alternative to {@link Screen#removed()} 9 | */ 10 | public interface DisposableScreen { 11 | 12 | /** 13 | * Invoked when a best-effort algorithm has determined 14 | * that the player is navigating to return to the game instead of opening 15 | * another screen - ensured to be called too often than too rarely 16 | */ 17 | void dispose(); 18 | 19 | } 20 | -------------------------------------------------------------------------------- /src/main/java/io/wispforest/owo/ui/util/MatrixStackTransformer.java: -------------------------------------------------------------------------------- 1 | package io.wispforest.owo.ui.util; 2 | 3 | import net.minecraft.client.gui.DrawContext; 4 | import net.minecraft.client.util.math.MatrixStack; 5 | import org.joml.Matrix4f; 6 | import org.joml.Quaternionf; 7 | 8 | /** 9 | * Helper interface implemented on top of the {@link DrawContext} to allow for easier matrix stack transformations 10 | */ 11 | public interface MatrixStackTransformer { 12 | 13 | default MatrixStackTransformer translate(double x, double y, double z) { 14 | this.getMatrixStack().translate(x, y, z); 15 | return this; 16 | } 17 | 18 | default MatrixStackTransformer translate(float x, float y, float z) { 19 | this.getMatrixStack().translate(x, y, z); 20 | return this; 21 | } 22 | 23 | default MatrixStackTransformer scale(float x, float y, float z) { 24 | this.getMatrixStack().scale(x, y, z); 25 | return this; 26 | } 27 | 28 | default MatrixStackTransformer multiply(Quaternionf quaternion) { 29 | this.getMatrixStack().multiply(quaternion); 30 | return this; 31 | } 32 | 33 | default MatrixStackTransformer multiply(Quaternionf quaternion, float originX, float originY, float originZ) { 34 | this.getMatrixStack().multiply(quaternion, originX, originY, originZ); 35 | return this; 36 | } 37 | 38 | default MatrixStackTransformer push() { 39 | this.getMatrixStack().push(); 40 | return this; 41 | } 42 | 43 | default MatrixStackTransformer pop() { 44 | this.getMatrixStack().pop(); 45 | return this; 46 | } 47 | 48 | default MatrixStackTransformer multiplyPositionMatrix(Matrix4f matrix) { 49 | this.getMatrixStack().multiplyPositionMatrix(matrix); 50 | return this; 51 | } 52 | 53 | default MatrixStack getMatrixStack(){ 54 | throw new IllegalStateException("getMatrices() method hasn't been override leading to exception!"); 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /src/main/java/io/wispforest/owo/ui/util/SpriteUtilInvoker.java: -------------------------------------------------------------------------------- 1 | package io.wispforest.owo.ui.util; 2 | 3 | import io.wispforest.owo.Owo; 4 | import net.fabricmc.loader.api.FabricLoader; 5 | import net.minecraft.client.texture.Sprite; 6 | 7 | import java.lang.invoke.MethodHandle; 8 | import java.lang.invoke.MethodHandles; 9 | import java.lang.invoke.MethodType; 10 | 11 | public class SpriteUtilInvoker { 12 | private static final MethodHandle MARK_SPRITE_ACTIVE = getMarkSpriteActive(); 13 | 14 | public static void markSpriteActive(Sprite sprite) { 15 | try { 16 | MARK_SPRITE_ACTIVE.invoke((Sprite) sprite); 17 | } catch (Throwable e) { 18 | throw new RuntimeException(e); 19 | } 20 | } 21 | 22 | private static MethodHandle getMarkSpriteActive() { 23 | if (FabricLoader.getInstance().isModLoaded("sodium")) { 24 | try { 25 | Class spriteUtil = Class.forName("me.jellysquid.mods.sodium.client.render.texture.SpriteUtil"); 26 | var m = spriteUtil.getMethod("markSpriteActive", Sprite.class); 27 | m.setAccessible(true); 28 | return MethodHandles.lookup().unreflect(m); 29 | } catch (Exception e) { 30 | Owo.LOGGER.error("Couldn't get SpriteUtil.markSpriteActive from Sodium", e); 31 | } 32 | } 33 | 34 | return MethodHandles.empty(MethodType.methodType(void.class, Sprite.class)); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/main/java/io/wispforest/owo/ui/util/UISounds.java: -------------------------------------------------------------------------------- 1 | package io.wispforest.owo.ui.util; 2 | 3 | import net.fabricmc.api.EnvType; 4 | import net.fabricmc.api.Environment; 5 | import net.minecraft.client.MinecraftClient; 6 | import net.minecraft.client.sound.PositionedSoundInstance; 7 | import net.minecraft.sound.SoundEvent; 8 | import net.minecraft.sound.SoundEvents; 9 | import net.minecraft.util.Identifier; 10 | 11 | public final class UISounds { 12 | 13 | public static final SoundEvent UI_INTERACTION = SoundEvent.of(Identifier.of("owo", "ui.owo.interaction")); 14 | 15 | private UISounds() {} 16 | 17 | @Environment(EnvType.CLIENT) 18 | public static void playButtonSound() { 19 | MinecraftClient.getInstance().getSoundManager().play(PositionedSoundInstance.master(SoundEvents.UI_BUTTON_CLICK, 1)); 20 | } 21 | 22 | @Environment(EnvType.CLIENT) 23 | public static void playInteractionSound() { 24 | MinecraftClient.getInstance().getSoundManager().play(PositionedSoundInstance.master(UI_INTERACTION, 1)); 25 | } 26 | 27 | } 28 | -------------------------------------------------------------------------------- /src/main/java/io/wispforest/owo/util/EventSource.java: -------------------------------------------------------------------------------- 1 | package io.wispforest.owo.util; 2 | 3 | public class EventSource { 4 | 5 | private final EventStream stream; 6 | 7 | protected EventSource(EventStream stream) { 8 | this.stream = stream; 9 | } 10 | 11 | public Subscription subscribe(T subscriber) { 12 | this.stream.addSubscriber(subscriber); 13 | return new Subscription(subscriber); 14 | } 15 | 16 | public class Subscription { 17 | protected final T subscriber; 18 | 19 | public Subscription(T subscriber) { 20 | this.subscriber = subscriber; 21 | } 22 | 23 | public void cancel() { 24 | EventSource.this.stream.removeSubscriber(this.subscriber); 25 | } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/main/java/io/wispforest/owo/util/EventStream.java: -------------------------------------------------------------------------------- 1 | package io.wispforest.owo.util; 2 | 3 | import java.util.ArrayList; 4 | import java.util.List; 5 | import java.util.function.Function; 6 | 7 | public class EventStream { 8 | 9 | protected final Function, T> sinkFactory; 10 | protected final List subscribers = new ArrayList<>(); 11 | protected final EventSource source = new EventSource<>(this); 12 | protected T sink; 13 | 14 | public EventStream(Function, T> sinkFactory) { 15 | this.sinkFactory = sinkFactory; 16 | this.regenerateSink(); 17 | } 18 | 19 | public T sink() { 20 | return this.sink; 21 | } 22 | 23 | public EventSource source() { 24 | return this.source; 25 | } 26 | 27 | protected void addSubscriber(T subscriber) { 28 | this.subscribers.add(subscriber); 29 | this.regenerateSink(); 30 | } 31 | 32 | protected void removeSubscriber(T subscriber) { 33 | this.subscribers.remove(subscriber); 34 | this.regenerateSink(); 35 | } 36 | 37 | protected void regenerateSink() { 38 | this.sink = this.sinkFactory.apply(this.subscribers); 39 | } 40 | 41 | } 42 | -------------------------------------------------------------------------------- /src/main/java/io/wispforest/owo/util/Maldenhagen.java: -------------------------------------------------------------------------------- 1 | package io.wispforest.owo.util; 2 | 3 | import net.minecraft.block.Block; 4 | 5 | import java.util.HashSet; 6 | import java.util.Set; 7 | 8 | /** 9 | * A simple utility class for making ore blocks update after they are generated. 10 | * This is especially useful for ores that are supposed to glow, as with the normal 11 | * ore feature they won't do that since lighting is never calculated for them 12 | */ 13 | public final class Maldenhagen { 14 | 15 | private Maldenhagen() {} 16 | 17 | private static final Set COPIUM_INJECTED = new HashSet<>(); 18 | 19 | /** 20 | * Marks a block for update after generation 21 | * 22 | * @param block The block to update 23 | */ 24 | public static void injectCopium(Block block) { 25 | COPIUM_INJECTED.add(block); 26 | } 27 | 28 | /** 29 | * @param block The block to test 30 | * @return {@code true} if the block should update after generation 31 | */ 32 | public static boolean isOnCopium(Block block) { 33 | return COPIUM_INJECTED.contains(block); 34 | } 35 | 36 | } 37 | -------------------------------------------------------------------------------- /src/main/java/io/wispforest/owo/util/RecipeRemainderStorage.java: -------------------------------------------------------------------------------- 1 | package io.wispforest.owo.util; 2 | 3 | import net.fabricmc.fabric.api.event.lifecycle.v1.ServerLifecycleEvents; 4 | import net.minecraft.item.Item; 5 | import net.minecraft.item.ItemStack; 6 | import net.minecraft.util.Identifier; 7 | import org.jetbrains.annotations.ApiStatus; 8 | 9 | import java.util.HashMap; 10 | import java.util.Map; 11 | 12 | @ApiStatus.Internal 13 | public final class RecipeRemainderStorage { 14 | 15 | private RecipeRemainderStorage() {} 16 | 17 | private static final Map> REMAINDERS = new HashMap<>(); 18 | 19 | public static void store(Identifier recipe, Map remainders) { 20 | REMAINDERS.put(recipe, remainders); 21 | } 22 | 23 | public static boolean has(Identifier recipe) { 24 | return REMAINDERS.containsKey(recipe); 25 | } 26 | 27 | public static Map get(Identifier recipe) { 28 | return REMAINDERS.get(recipe); 29 | } 30 | 31 | static { 32 | ServerLifecycleEvents.START_DATA_PACK_RELOAD.register((server, resourceManager) -> REMAINDERS.clear()); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/main/java/io/wispforest/owo/util/Scary.java: -------------------------------------------------------------------------------- 1 | package io.wispforest.owo.util; 2 | 3 | /** 4 | * Annotations used to indicate that a given whatever is design in some manor that may or may not 5 | * cause you pain. Combined it might scare your skeleton out of your body having it run down the block. 6 | */ 7 | public @interface Scary { 8 | } 9 | -------------------------------------------------------------------------------- /src/main/java/io/wispforest/owo/util/ServicesFrozenException.java: -------------------------------------------------------------------------------- 1 | package io.wispforest.owo.util; 2 | 3 | public class ServicesFrozenException extends IllegalStateException { 4 | public ServicesFrozenException(String message) { 5 | super(message); 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /src/main/java/io/wispforest/owo/util/StackTraceSupplier.java: -------------------------------------------------------------------------------- 1 | package io.wispforest.owo.util; 2 | 3 | import org.jetbrains.annotations.Nullable; 4 | 5 | import java.util.function.Supplier; 6 | 7 | public final class StackTraceSupplier implements Supplier { 8 | private final Throwable throwable; 9 | private final @Nullable Supplier message; 10 | 11 | private StackTraceSupplier(@Nullable Throwable throwable, @Nullable Supplier message) { 12 | this.throwable = throwable; 13 | this.message = message; 14 | } 15 | 16 | public static StackTraceSupplier of(Throwable throwable) { 17 | return new StackTraceSupplier(throwable, null); 18 | } 19 | 20 | public static StackTraceSupplier of(Throwable throwable, Supplier supplier) { 21 | return new StackTraceSupplier(throwable, supplier); 22 | } 23 | 24 | public static StackTraceSupplier of(String message) { 25 | return new StackTraceSupplier(new IllegalStateException(message), null); 26 | } 27 | 28 | @Override 29 | public String get() { 30 | return message != null ? message.get() : throwable.getMessage(); 31 | } 32 | 33 | public StackTraceElement[] getFullStackTrace() { 34 | var innerThrowable = throwable(); 35 | while (innerThrowable.getCause() != null) { 36 | innerThrowable = throwable().getCause(); 37 | } 38 | return innerThrowable.getStackTrace(); 39 | } 40 | 41 | public Throwable throwable() { 42 | return throwable; 43 | } 44 | 45 | @Override 46 | public String toString() { 47 | return "StackTraceSupplier[" + 48 | "throwable=" + throwable + ", " + 49 | "message=" + message + ']'; 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /src/main/java/io/wispforest/owo/util/pond/OwoCreativeInventoryScreenExtensions.java: -------------------------------------------------------------------------------- 1 | package io.wispforest.owo.util.pond; 2 | 3 | public interface OwoCreativeInventoryScreenExtensions { 4 | int owo$getRootX(); 5 | 6 | int owo$getRootY(); 7 | } 8 | -------------------------------------------------------------------------------- /src/main/java/io/wispforest/owo/util/pond/OwoEntityRenderDispatcherExtension.java: -------------------------------------------------------------------------------- 1 | package io.wispforest.owo.util.pond; 2 | 3 | public interface OwoEntityRenderDispatcherExtension { 4 | void owo$setShowNametag(boolean showNametag); 5 | boolean owo$showNametag(); 6 | 7 | void owo$setCounterRotate(boolean counterRotation); 8 | boolean owo$counterRotate(); 9 | } 10 | -------------------------------------------------------------------------------- /src/main/java/io/wispforest/owo/util/pond/OwoItemExtensions.java: -------------------------------------------------------------------------------- 1 | package io.wispforest.owo.util.pond; 2 | 3 | import io.wispforest.owo.itemgroup.OwoItemGroup; 4 | import io.wispforest.owo.itemgroup.json.OwoItemGroupLoader; 5 | import net.minecraft.item.Item; 6 | import net.minecraft.item.ItemGroup; 7 | import org.jetbrains.annotations.Nullable; 8 | 9 | import java.util.function.BiConsumer; 10 | 11 | public interface OwoItemExtensions { 12 | 13 | /** 14 | * @return The 0-indexed tab id this item resides in, {@code -1} if none is defined 15 | */ 16 | int owo$tab(); 17 | 18 | /** 19 | * @return The function used for adding stacks of 20 | * this item to an {@link OwoItemGroup} it resides in 21 | */ 22 | BiConsumer owo$stackGenerator(); 23 | 24 | /** 25 | * Sets the group of this item, used by {@link OwoItemGroupLoader} to ensure 26 | * all {@code ItemGroup} references in items are correct for data-driven owo groups 27 | * 28 | * @param group The group to replace the current on with 29 | */ 30 | void owo$setGroup(ItemGroup group); 31 | 32 | /** 33 | * @return The item group this item should reside in 34 | */ 35 | @Nullable ItemGroup owo$group(); 36 | 37 | /** 38 | * @return {@code true} if this item should automatically 39 | * have its usage stat incremented 40 | */ 41 | boolean owo$shouldTrackUsageStat(); 42 | } 43 | -------------------------------------------------------------------------------- /src/main/java/io/wispforest/owo/util/pond/OwoScreenExtension.java: -------------------------------------------------------------------------------- 1 | package io.wispforest.owo.util.pond; 2 | 3 | import io.wispforest.owo.ui.core.ParentComponent; 4 | import io.wispforest.owo.ui.layers.Layer; 5 | import net.minecraft.client.gui.screen.Screen; 6 | 7 | import java.util.List; 8 | 9 | public interface OwoScreenExtension { 10 | List.Instance> owo$getInstancesView(); 11 | Layer.Instance owo$getInstance(Layer layer); 12 | 13 | void owo$updateLayers(); 14 | } 15 | -------------------------------------------------------------------------------- /src/main/java/io/wispforest/owo/util/pond/OwoScreenHandlerExtension.java: -------------------------------------------------------------------------------- 1 | package io.wispforest.owo.util.pond; 2 | 3 | import io.wispforest.owo.client.screens.ScreenInternals; 4 | import net.minecraft.entity.player.PlayerEntity; 5 | 6 | public interface OwoScreenHandlerExtension { 7 | void owo$attachToPlayer(PlayerEntity player); 8 | 9 | void owo$readPropertySync(ScreenInternals.SyncPropertiesPacket packet); 10 | 11 | void owo$handlePacket(ScreenInternals.LocalPacket packet, boolean clientbound); 12 | } 13 | -------------------------------------------------------------------------------- /src/main/java/io/wispforest/owo/util/pond/OwoSimpleRegistryExtensions.java: -------------------------------------------------------------------------------- 1 | package io.wispforest.owo.util.pond; 2 | 3 | import net.minecraft.registry.RegistryKey; 4 | import net.minecraft.registry.entry.RegistryEntry; 5 | import net.minecraft.registry.entry.RegistryEntryInfo; 6 | import org.jetbrains.annotations.ApiStatus; 7 | 8 | public interface OwoSimpleRegistryExtensions { 9 | 10 | @ApiStatus.Internal 11 | RegistryEntry.Reference owo$set(int id, RegistryKey arg, T object, RegistryEntryInfo arg2); 12 | } 13 | -------------------------------------------------------------------------------- /src/main/java/io/wispforest/owo/util/pond/OwoSlotExtension.java: -------------------------------------------------------------------------------- 1 | package io.wispforest.owo.util.pond; 2 | 3 | import io.wispforest.owo.ui.core.PositionedRectangle; 4 | import org.jetbrains.annotations.Nullable; 5 | 6 | public interface OwoSlotExtension { 7 | 8 | void owo$setDisabledOverride(boolean disabled); 9 | 10 | boolean owo$getDisabledOverride(); 11 | 12 | void owo$setScissorArea(@Nullable PositionedRectangle scissor); 13 | 14 | @Nullable PositionedRectangle owo$getScissorArea(); 15 | } 16 | -------------------------------------------------------------------------------- /src/main/java/io/wispforest/owo/util/pond/OwoTessellatorExtension.java: -------------------------------------------------------------------------------- 1 | package io.wispforest.owo.util.pond; 2 | 3 | import net.minecraft.client.render.BufferBuilder; 4 | 5 | public interface OwoTessellatorExtension { 6 | void owo$skipNextBegin(); 7 | 8 | void owo$setStoredBuilder(BufferBuilder builder); 9 | 10 | BufferBuilder owo$getStoredBuilder(); 11 | } 12 | -------------------------------------------------------------------------------- /src/main/java/io/wispforest/owo/util/pond/OwoTextRendererExtension.java: -------------------------------------------------------------------------------- 1 | package io.wispforest.owo.util.pond; 2 | 3 | public interface OwoTextRendererExtension { 4 | void owo$beginCache(); 5 | void owo$submitCache(); 6 | } 7 | -------------------------------------------------------------------------------- /src/main/java/io/wispforest/owo/util/pond/package-info.java: -------------------------------------------------------------------------------- 1 | @ApiStatus.Internal 2 | package io.wispforest.owo.util.pond; 3 | 4 | import org.jetbrains.annotations.ApiStatus; -------------------------------------------------------------------------------- /src/main/resources/META-INF/services/javax.annotation.processing.Processor: -------------------------------------------------------------------------------- 1 | io.wispforest.owo.config.ConfigAP -------------------------------------------------------------------------------- /src/main/resources/architectury.common.json: -------------------------------------------------------------------------------- 1 | { 2 | "accessWidener": "owo.accesswidener", 3 | "injected_interfaces": { 4 | "net/minecraft/class_2487": [ 5 | "io/wispforest/endec/util/MapCarrier" 6 | ], 7 | "net/minecraft/class_2540": [ 8 | "io/wispforest/endec/util/EndecBuffer" 9 | ], 10 | "net/minecraft/class_339": [ 11 | "io/wispforest/owo/ui/inject/ComponentStub" 12 | ], 13 | "net/minecraft/class_342": [ 14 | "io/wispforest/owo/ui/inject/GreedyInputComponent" 15 | ], 16 | "net/minecraft/class_1703": [ 17 | "io/wispforest/owo/client/screens/OwoScreenHandler" 18 | ], 19 | "net/minecraft/class_332": [ 20 | "io/wispforest/owo/ui/util/MatrixStackTransformer" 21 | ], 22 | "net/minecraft/class_9331\u0024class_9332": [ 23 | "io/wispforest/owo/serialization/OwoComponentTypeBuilder" 24 | ], 25 | "net/minecraft/class_1792\u0024class_1793": [ 26 | "io/wispforest/owo/itemgroup/OwoItemSettingsExtension" 27 | ], 28 | "net/minecraft/class_1792": [ 29 | "io/wispforest/owo/ext/OwoItem" 30 | ] 31 | } 32 | } -------------------------------------------------------------------------------- /src/main/resources/assets/owo/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wisp-forest/owo-lib/ef83709dae78614695e2842cd792990e3db28900/src/main/resources/assets/owo/icon.png -------------------------------------------------------------------------------- /src/main/resources/assets/owo/nine_patch_textures/button/active.json: -------------------------------------------------------------------------------- 1 | { 2 | "texture": "owo:textures/gui/buttons.png", 3 | "texture_width": 64, 4 | "texture_height": 192, 5 | "repeat": true, 6 | "corner_patch_size": { 7 | "width": 3, 8 | "height": 3 9 | }, 10 | "center_patch_size": { 11 | "width": 58, 12 | "height": 58 13 | } 14 | } -------------------------------------------------------------------------------- /src/main/resources/assets/owo/nine_patch_textures/button/disabled.json: -------------------------------------------------------------------------------- 1 | { 2 | "texture": "owo:textures/gui/buttons.png", 3 | "texture_width": 64, 4 | "texture_height": 192, 5 | "v": 128, 6 | "repeat": true, 7 | "corner_patch_size": { 8 | "width": 3, 9 | "height": 3 10 | }, 11 | "center_patch_size": { 12 | "width": 58, 13 | "height": 58 14 | } 15 | } -------------------------------------------------------------------------------- /src/main/resources/assets/owo/nine_patch_textures/button/hovered.json: -------------------------------------------------------------------------------- 1 | { 2 | "texture": "owo:textures/gui/buttons.png", 3 | "texture_width": 64, 4 | "texture_height": 192, 5 | "v": 64, 6 | "repeat": true, 7 | "corner_patch_size": { 8 | "width": 3, 9 | "height": 3 10 | }, 11 | "center_patch_size": { 12 | "width": 58, 13 | "height": 58 14 | } 15 | } -------------------------------------------------------------------------------- /src/main/resources/assets/owo/nine_patch_textures/panel/dark.json: -------------------------------------------------------------------------------- 1 | { 2 | "texture": "owo:textures/gui/dark_panel.png", 3 | "texture_width": 16, 4 | "texture_height": 16, 5 | "repeat": false, 6 | "patch_size": { 7 | "width": 5, 8 | "height": 5 9 | } 10 | } -------------------------------------------------------------------------------- /src/main/resources/assets/owo/nine_patch_textures/panel/default.json: -------------------------------------------------------------------------------- 1 | { 2 | "texture": "owo:textures/gui/panel.png", 3 | "texture_width": 16, 4 | "texture_height": 16, 5 | "repeat": false, 6 | "patch_size": { 7 | "width": 5, 8 | "height": 5 9 | } 10 | } -------------------------------------------------------------------------------- /src/main/resources/assets/owo/nine_patch_textures/panel/inset.json: -------------------------------------------------------------------------------- 1 | { 2 | "texture": "owo:textures/gui/panel_inset.png", 3 | "texture_width": 16, 4 | "texture_height": 16, 5 | "repeat": false, 6 | "patch_size": { 7 | "width": 5, 8 | "height": 5 9 | } 10 | } -------------------------------------------------------------------------------- /src/main/resources/assets/owo/nine_patch_textures/scrollbar/track.json: -------------------------------------------------------------------------------- 1 | { 2 | "texture": "owo:textures/gui/scrollbar.png", 3 | "texture_width": 64, 4 | "texture_height": 32, 5 | "u": 26, 6 | "repeat": false, 7 | "corner_patch_size": { 8 | "width": 1, 9 | "height": 1 10 | }, 11 | "center_patch_size": { 12 | "width": 4, 13 | "height": 30 14 | } 15 | } -------------------------------------------------------------------------------- /src/main/resources/assets/owo/nine_patch_textures/scrollbar/vanilla_flat.json: -------------------------------------------------------------------------------- 1 | { 2 | "texture": "owo:textures/gui/scrollbar.png", 3 | "texture_width": 64, 4 | "texture_height": 32, 5 | "u": 33, 6 | "repeat": false, 7 | "corner_patch_size": { 8 | "width": 1, 9 | "height": 1 10 | }, 11 | "center_patch_size": { 12 | "width": 14, 13 | "height": 30 14 | } 15 | } -------------------------------------------------------------------------------- /src/main/resources/assets/owo/nine_patch_textures/scrollbar/vanilla_horizontal.json: -------------------------------------------------------------------------------- 1 | { 2 | "texture": "owo:textures/gui/scrollbar.png", 3 | "texture_width": 64, 4 | "texture_height": 32, 5 | "v": 16, 6 | "repeat": true, 7 | "corner_patch_size": { 8 | "width": 2, 9 | "height": 2 10 | }, 11 | "center_patch_size": { 12 | "width": 8, 13 | "height": 12 14 | } 15 | } -------------------------------------------------------------------------------- /src/main/resources/assets/owo/nine_patch_textures/scrollbar/vanilla_horizontal_disabled.json: -------------------------------------------------------------------------------- 1 | { 2 | "texture": "owo:textures/gui/scrollbar.png", 3 | "texture_width": 64, 4 | "texture_height": 32, 5 | "u": 13, 6 | "v": 16, 7 | "repeat": true, 8 | "corner_patch_size": { 9 | "width": 2, 10 | "height": 2 11 | }, 12 | "center_patch_size": { 13 | "width": 8, 14 | "height": 12 15 | } 16 | } -------------------------------------------------------------------------------- /src/main/resources/assets/owo/nine_patch_textures/scrollbar/vanilla_vertical.json: -------------------------------------------------------------------------------- 1 | { 2 | "texture": "owo:textures/gui/scrollbar.png", 3 | "texture_width": 64, 4 | "texture_height": 32, 5 | "repeat": true, 6 | "corner_patch_size": { 7 | "width": 2, 8 | "height": 2 9 | }, 10 | "center_patch_size": { 11 | "width": 8, 12 | "height": 12 13 | } 14 | } -------------------------------------------------------------------------------- /src/main/resources/assets/owo/nine_patch_textures/scrollbar/vanilla_vertical_disabled.json: -------------------------------------------------------------------------------- 1 | { 2 | "texture": "owo:textures/gui/scrollbar.png", 3 | "texture_width": 64, 4 | "texture_height": 32, 5 | "u": 13, 6 | "repeat": true, 7 | "corner_patch_size": { 8 | "width": 2, 9 | "height": 2 10 | }, 11 | "center_patch_size": { 12 | "width": 8, 13 | "height": 12 14 | } 15 | } -------------------------------------------------------------------------------- /src/main/resources/assets/owo/nine_patch_textures/slim_slider_track.json: -------------------------------------------------------------------------------- 1 | { 2 | "texture": "owo:textures/gui/slim_slider.png", 3 | "texture_width": 16, 4 | "texture_height": 16, 5 | "repeat": false, 6 | "corner_patch_size": { 7 | "width": 1, 8 | "height": 1 9 | }, 10 | "center_patch_size": { 11 | "width": 14, 12 | "height": 1 13 | } 14 | } -------------------------------------------------------------------------------- /src/main/resources/assets/owo/owo_ui/restart_required.xml: -------------------------------------------------------------------------------- 1 | 3 | 4 | 5 | 6 | 10 | 11 | 12 | 13 | 17 | 18 | 22 | 23 | 24 | 25 | 25 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | center 36 | center 37 | 38 | 39 | 40 | 41 | 42 | 53 | 54 | 55 | -------------------------------------------------------------------------------- /src/main/resources/assets/owo/shaders/core/blur.fsh: -------------------------------------------------------------------------------- 1 | #version 150 2 | 3 | uniform sampler2D InputSampler; 4 | uniform vec2 InputResolution; 5 | uniform vec4 ColorModulator; 6 | 7 | uniform float Directions; 8 | uniform float Quality; 9 | uniform float Size; 10 | 11 | out vec4 fragColor; 12 | 13 | // shader adapted from https://www.shadertoy.com/view/Xltfzj 14 | 15 | void main() { 16 | #define TAU 6.28318530718 17 | 18 | vec2 Radius = Size / InputResolution.xy; 19 | 20 | // Normalized pixel coordinates (from 0 to 1) 21 | vec2 uv = gl_FragCoord.xy / InputResolution.xy; 22 | // Pixel colour 23 | vec4 Color = texture(InputSampler, uv); 24 | 25 | // Blur calculations 26 | for (float d = 0.0; d < TAU; d += TAU / Directions) { 27 | for (float i = 1.0 / Quality; i <= 1.0; i += 1.0 / Quality) { 28 | Color += texture(InputSampler, uv + vec2(cos(d), sin(d)) * Radius * i); 29 | } 30 | } 31 | 32 | // Output to screen 33 | Color /= Quality * Directions; 34 | fragColor = Color * ColorModulator; 35 | } 36 | -------------------------------------------------------------------------------- /src/main/resources/assets/owo/shaders/core/blur.vsh: -------------------------------------------------------------------------------- 1 | #version 150 2 | 3 | in vec3 Position; 4 | 5 | uniform mat4 ProjMat; 6 | uniform mat4 ModelViewMat; 7 | 8 | void main() { 9 | gl_Position = ProjMat * ModelViewMat * vec4(Position, 1.0); 10 | } 11 | -------------------------------------------------------------------------------- /src/main/resources/assets/owo/shaders/core/spectrum.fsh: -------------------------------------------------------------------------------- 1 | #version 150 2 | 3 | uniform vec4 ColorModulator; 4 | 5 | in vec4 vertexColor; 6 | out vec4 fragColor; 7 | 8 | vec3 hsv2rgb(vec3 hsv) { 9 | vec4 K = vec4(1.0, 2.0 / 3.0, 1.0 / 3.0, 3.0); 10 | vec3 p = abs(fract(hsv.xxx + K.xyz) * 6.0 - K.www); 11 | return hsv.z * mix(K.xxx, clamp(p - K.xxx, 0.0, 1.0), hsv.y); 12 | } 13 | 14 | void main() { 15 | fragColor = vec4( 16 | hsv2rgb(vertexColor.xyz).xyz, 17 | vertexColor.w 18 | ) * ColorModulator; 19 | } 20 | -------------------------------------------------------------------------------- /src/main/resources/assets/owo/sounds.json: -------------------------------------------------------------------------------- 1 | { 2 | "ui.owo.interaction": { 3 | "sounds": [ 4 | "owo:ui_interaction" 5 | ] 6 | } 7 | } -------------------------------------------------------------------------------- /src/main/resources/assets/owo/sounds/ui_interaction.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wisp-forest/owo-lib/ef83709dae78614695e2842cd792990e3db28900/src/main/resources/assets/owo/sounds/ui_interaction.ogg -------------------------------------------------------------------------------- /src/main/resources/assets/owo/textures/gui/buttons.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wisp-forest/owo-lib/ef83709dae78614695e2842cd792990e3db28900/src/main/resources/assets/owo/textures/gui/buttons.png -------------------------------------------------------------------------------- /src/main/resources/assets/owo/textures/gui/config_search.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wisp-forest/owo-lib/ef83709dae78614695e2842cd792990e3db28900/src/main/resources/assets/owo/textures/gui/config_search.png -------------------------------------------------------------------------------- /src/main/resources/assets/owo/textures/gui/dark_panel.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wisp-forest/owo-lib/ef83709dae78614695e2842cd792990e3db28900/src/main/resources/assets/owo/textures/gui/dark_panel.png -------------------------------------------------------------------------------- /src/main/resources/assets/owo/textures/gui/dropdown_icons.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wisp-forest/owo-lib/ef83709dae78614695e2842cd792990e3db28900/src/main/resources/assets/owo/textures/gui/dropdown_icons.png -------------------------------------------------------------------------------- /src/main/resources/assets/owo/textures/gui/icons.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wisp-forest/owo-lib/ef83709dae78614695e2842cd792990e3db28900/src/main/resources/assets/owo/textures/gui/icons.png -------------------------------------------------------------------------------- /src/main/resources/assets/owo/textures/gui/panel.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wisp-forest/owo-lib/ef83709dae78614695e2842cd792990e3db28900/src/main/resources/assets/owo/textures/gui/panel.png -------------------------------------------------------------------------------- /src/main/resources/assets/owo/textures/gui/panel_inset.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wisp-forest/owo-lib/ef83709dae78614695e2842cd792990e3db28900/src/main/resources/assets/owo/textures/gui/panel_inset.png -------------------------------------------------------------------------------- /src/main/resources/assets/owo/textures/gui/scrollbar.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wisp-forest/owo-lib/ef83709dae78614695e2842cd792990e3db28900/src/main/resources/assets/owo/textures/gui/scrollbar.png -------------------------------------------------------------------------------- /src/main/resources/assets/owo/textures/gui/slim_slider.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wisp-forest/owo-lib/ef83709dae78614695e2842cd792990e3db28900/src/main/resources/assets/owo/textures/gui/slim_slider.png -------------------------------------------------------------------------------- /src/main/resources/assets/owo/textures/gui/smol_checkbox.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wisp-forest/owo-lib/ef83709dae78614695e2842cd792990e3db28900/src/main/resources/assets/owo/textures/gui/smol_checkbox.png -------------------------------------------------------------------------------- /src/main/resources/assets/owo/textures/gui/tabs.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wisp-forest/owo-lib/ef83709dae78614695e2842cd792990e3db28900/src/main/resources/assets/owo/textures/gui/tabs.png -------------------------------------------------------------------------------- /src/main/resources/owo.accesswidener: -------------------------------------------------------------------------------- 1 | accessWidener v2 named 2 | 3 | accessible method net/minecraft/client/gui/screen/Screen addDrawableChild (Lnet/minecraft/client/gui/Element;)Lnet/minecraft/client/gui/Element; 4 | 5 | extendable method net/minecraft/item/ItemGroup (Lnet/minecraft/item/ItemGroup$Row;ILnet/minecraft/item/ItemGroup$Type;Lnet/minecraft/text/Text;Ljava/util/function/Supplier;Lnet/minecraft/item/ItemGroup$EntryCollector;)V 6 | transitive-accessible class net/minecraft/item/ItemGroup$Entries 7 | transitive-accessible class net/minecraft/item/ItemGroup$EntriesImpl 8 | 9 | transitive-accessible class net/minecraft/client/gui/DrawContext$ScissorStack 10 | transitive-extendable method net/minecraft/client/gui/widget/CheckboxWidget (IIILnet/minecraft/text/Text;Lnet/minecraft/client/font/TextRenderer;ZLnet/minecraft/client/gui/widget/CheckboxWidget$Callback;)V 11 | 12 | accessible class net/minecraft/registry/RegistryOps$CachedRegistryInfoGetter 13 | 14 | extendable method net/minecraft/client/gui/screen/ingame/HandledScreen drawSlotHighlightBack (Lnet/minecraft/client/gui/DrawContext;)V 15 | extendable method net/minecraft/client/gui/screen/ingame/HandledScreen drawSlotHighlightFront (Lnet/minecraft/client/gui/DrawContext;)V 16 | extendable method net/minecraft/client/gui/screen/ingame/HandledScreen drawItem (Lnet/minecraft/client/gui/DrawContext;Lnet/minecraft/item/ItemStack;IILjava/lang/String;)V -------------------------------------------------------------------------------- /src/testmod/java/io/wispforest/uwu/blockentity/ProcessBlockEntity.java: -------------------------------------------------------------------------------- 1 | package io.wispforest.uwu.blockentity; 2 | 3 | import io.wispforest.owo.blockentity.LinearProcess; 4 | import io.wispforest.owo.blockentity.LinearProcessExecutor; 5 | import net.minecraft.block.BlockState; 6 | import net.minecraft.block.entity.BlockEntity; 7 | import net.minecraft.block.entity.BlockEntityType; 8 | import net.minecraft.util.math.BlockPos; 9 | import net.minecraft.world.World; 10 | 11 | public class ProcessBlockEntity extends BlockEntity { 12 | 13 | public static final LinearProcess PROCESS = new LinearProcess<>(30); 14 | 15 | private final LinearProcessExecutor executor; 16 | 17 | public ProcessBlockEntity(BlockEntityType type, BlockPos pos, BlockState state) { 18 | super(type, pos, state); 19 | this.executor = PROCESS.createExecutor(this); 20 | } 21 | 22 | @Override 23 | public void setWorld(World world) { 24 | super.setWorld(world); 25 | PROCESS.configureExecutor(this.executor, world.isClient); 26 | } 27 | 28 | public void tick() { 29 | this.executor.tick(); 30 | } 31 | 32 | static { 33 | 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/testmod/java/io/wispforest/uwu/client/EpicHandledModelScreen.java: -------------------------------------------------------------------------------- 1 | package io.wispforest.uwu.client; 2 | 3 | import io.wispforest.owo.ui.base.BaseUIModelHandledScreen; 4 | import io.wispforest.owo.ui.base.BaseUIModelScreen; 5 | import io.wispforest.owo.ui.component.ButtonComponent; 6 | import io.wispforest.owo.ui.container.FlowLayout; 7 | import io.wispforest.uwu.EpicScreenHandler; 8 | import net.minecraft.client.gui.widget.TextFieldWidget; 9 | import net.minecraft.entity.player.PlayerInventory; 10 | import net.minecraft.text.Text; 11 | 12 | public class EpicHandledModelScreen extends BaseUIModelHandledScreen { 13 | 14 | public EpicHandledModelScreen(EpicScreenHandler handler, PlayerInventory inventory, Text title) { 15 | super(handler, inventory, title, FlowLayout.class, BaseUIModelScreen.DataSource.file("epic_handled_screen.xml")); 16 | } 17 | 18 | @Override 19 | protected void build(FlowLayout rootComponent) { 20 | var indexField = rootComponent.childById(TextFieldWidget.class, "index-field"); 21 | indexField.setTextPredicate(s -> s.matches("\\d*")); 22 | 23 | rootComponent.childById(ButtonComponent.class, "enable-button").onPress(button -> this.enableSlot(Integer.parseInt(indexField.getText()))); 24 | rootComponent.childById(ButtonComponent.class, "disable-button").onPress(button -> this.disableSlot(Integer.parseInt(indexField.getText()))); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/testmod/java/io/wispforest/uwu/client/ParseFailScreen.java: -------------------------------------------------------------------------------- 1 | package io.wispforest.uwu.client; 2 | 3 | import io.wispforest.owo.ui.base.BaseUIModelScreen; 4 | import io.wispforest.owo.ui.container.FlowLayout; 5 | import net.minecraft.util.Identifier; 6 | 7 | public class ParseFailScreen extends BaseUIModelScreen { 8 | 9 | public ParseFailScreen() { 10 | super(FlowLayout.class, DataSource.asset(Identifier.of("uwu", "parse_fail"))); 11 | } 12 | 13 | @Override 14 | protected void build(FlowLayout rootComponent) { 15 | 16 | } 17 | } -------------------------------------------------------------------------------- /src/testmod/java/io/wispforest/uwu/client/ScissorTestScreen.java: -------------------------------------------------------------------------------- 1 | package io.wispforest.uwu.client; 2 | 3 | import io.wispforest.owo.ui.base.BaseOwoScreen; 4 | import io.wispforest.owo.ui.component.Components; 5 | import io.wispforest.owo.ui.container.Containers; 6 | import io.wispforest.owo.ui.container.FlowLayout; 7 | import io.wispforest.owo.ui.container.StackLayout; 8 | import io.wispforest.owo.ui.core.*; 9 | import org.jetbrains.annotations.NotNull; 10 | 11 | import java.util.List; 12 | 13 | public class ScissorTestScreen extends BaseOwoScreen { 14 | @Override 15 | protected @NotNull OwoUIAdapter createAdapter() { 16 | return OwoUIAdapter.create(this, Containers::stack); 17 | } 18 | 19 | @Override 20 | protected void build(StackLayout rootComponent) { 21 | rootComponent.alignment(HorizontalAlignment.CENTER, VerticalAlignment.CENTER); 22 | rootComponent.child(Containers.verticalScroll( 23 | Sizing.fixed(100), Sizing.fixed(35), 24 | Containers.verticalFlow(Sizing.content(), Sizing.content()).children(List.of( 25 | Components.box(Sizing.fixed(75), Sizing.fixed(25)), 26 | Components.textBox(Sizing.fill(100)), 27 | Components.box(Sizing.fixed(75), Sizing.fixed(25)) 28 | // Components.textBox(Sizing.fill(100)), 29 | // Components.box(Sizing.fixed(75), Sizing.fixed(25)) 30 | )) 31 | ).surface(Surface.VANILLA_TRANSLUCENT)); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/testmod/java/io/wispforest/uwu/client/SizingTestScreen.java: -------------------------------------------------------------------------------- 1 | package io.wispforest.uwu.client; 2 | 3 | import io.wispforest.owo.ui.base.BaseOwoScreen; 4 | import io.wispforest.owo.ui.component.Components; 5 | import io.wispforest.owo.ui.container.Containers; 6 | import io.wispforest.owo.ui.container.FlowLayout; 7 | import io.wispforest.owo.ui.container.StackLayout; 8 | import io.wispforest.owo.ui.core.*; 9 | import net.minecraft.text.ClickEvent; 10 | import net.minecraft.text.Style; 11 | import net.minecraft.text.Text; 12 | import org.jetbrains.annotations.NotNull; 13 | 14 | import java.net.URI; 15 | 16 | public class SizingTestScreen extends BaseOwoScreen { 17 | @Override 18 | protected @NotNull OwoUIAdapter createAdapter() { 19 | return OwoUIAdapter.create(this, Containers::verticalFlow); 20 | } 21 | 22 | @Override 23 | protected void build(FlowLayout rootComponent) { 24 | rootComponent.horizontalAlignment(HorizontalAlignment.CENTER).verticalAlignment(VerticalAlignment.CENTER); 25 | rootComponent.child(Containers.stack(Sizing.content(), Sizing.content()).configure(container -> { 26 | container.horizontalAlignment(HorizontalAlignment.CENTER).surface(Surface.panelWithInset(6)).padding(Insets.of(15)); 27 | 28 | var animation = container.horizontalSizing().animate(500, Easing.CUBIC, Sizing.fill(75)); 29 | container.child(Components.button(Text.literal("initialize sizenite"), button -> { 30 | animation.reverse(); 31 | }).horizontalSizing(Sizing.fill(50))); 32 | })); 33 | 34 | rootComponent.child(Components.label(Text.literal("bruh").setStyle(Style.EMPTY.withClickEvent(new ClickEvent.OpenUrl(URI.create("https://wispforest.io")))))); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/testmod/java/io/wispforest/uwu/client/TooManyComponentsScreen.java: -------------------------------------------------------------------------------- 1 | package io.wispforest.uwu.client; 2 | 3 | import io.wispforest.owo.ui.base.BaseOwoScreen; 4 | import io.wispforest.owo.ui.component.Components; 5 | import io.wispforest.owo.ui.container.Containers; 6 | import io.wispforest.owo.ui.container.FlowLayout; 7 | import io.wispforest.owo.ui.core.*; 8 | import io.wispforest.uwu.items.UwuItems; 9 | import net.minecraft.text.Text; 10 | import org.jetbrains.annotations.NotNull; 11 | 12 | import java.util.concurrent.ThreadLocalRandom; 13 | 14 | public class TooManyComponentsScreen extends BaseOwoScreen { 15 | @Override 16 | protected @NotNull OwoUIAdapter createAdapter() { 17 | return OwoUIAdapter.create(this, Containers::verticalFlow); 18 | } 19 | 20 | @Override 21 | protected void build(FlowLayout rootComponent) { 22 | rootComponent.child( 23 | Containers.verticalScroll( 24 | Sizing.fill(45), Sizing.fill(45), 25 | Containers.verticalFlow(Sizing.content(), Sizing.content()).configure(flowLayout -> { 26 | for (int i = 0; i < 50000; i++) { 27 | flowLayout.child( 28 | Containers.collapsible(Sizing.content(), Sizing.content(), Text.of(String.valueOf(ThreadLocalRandom.current().nextInt(100000))), false) 29 | .child( 30 | Components.item(UwuItems.SCREEN_SHARD.getDefaultStack()).sizing(Sizing.fixed(100)) 31 | ) 32 | ); 33 | } 34 | }) 35 | ).surface(Surface.DARK_PANEL).padding(Insets.of(5)) 36 | ).verticalAlignment(VerticalAlignment.CENTER).horizontalAlignment(HorizontalAlignment.CENTER); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/testmod/java/io/wispforest/uwu/config/UowouConfigModel.java: -------------------------------------------------------------------------------- 1 | package io.wispforest.uwu.config; 2 | 3 | import io.wispforest.owo.config.Option; 4 | import io.wispforest.owo.config.annotation.*; 5 | import io.wispforest.owo.ui.core.Color; 6 | import net.minecraft.util.Identifier; 7 | 8 | import java.util.Set; 9 | 10 | @Modmenu(modId = "fabric") 11 | @Config(name = "uowou", wrapperName = "BruhConfig") 12 | @Sync(Option.SyncMode.OVERRIDE_CLIENT) 13 | public class UowouConfigModel { 14 | 15 | @RestartRequired 16 | public boolean thisIsNotSyncable = false; 17 | 18 | @Hook 19 | public Identifier idPlease = Identifier.of("uowou", "bruh"); 20 | 21 | @Sync(Option.SyncMode.NONE) 22 | public Set setPlease = Set.of("that's a value"); 23 | 24 | public Color bruhve = Color.BLACK; 25 | } 26 | 27 | 28 | -------------------------------------------------------------------------------- /src/testmod/java/io/wispforest/uwu/items/UwuScreenShardItem.java: -------------------------------------------------------------------------------- 1 | package io.wispforest.uwu.items; 2 | 3 | import io.wispforest.uwu.EpicScreenHandler; 4 | import io.wispforest.uwu.client.SelectUwuScreenScreen; 5 | import net.fabricmc.api.EnvType; 6 | import net.fabricmc.api.Environment; 7 | import net.minecraft.client.MinecraftClient; 8 | import net.minecraft.entity.player.PlayerEntity; 9 | import net.minecraft.entity.player.PlayerInventory; 10 | import net.minecraft.item.Item; 11 | import net.minecraft.screen.NamedScreenHandlerFactory; 12 | import net.minecraft.screen.ScreenHandler; 13 | import net.minecraft.screen.ScreenHandlerContext; 14 | import net.minecraft.text.Text; 15 | import net.minecraft.util.ActionResult; 16 | import net.minecraft.util.Hand; 17 | import net.minecraft.util.Rarity; 18 | import net.minecraft.world.World; 19 | import org.jetbrains.annotations.NotNull; 20 | 21 | public class UwuScreenShardItem extends Item { 22 | 23 | public UwuScreenShardItem(Item.Settings settings) { 24 | super(settings.rarity(Rarity.UNCOMMON)); 25 | } 26 | 27 | @Override 28 | @Environment(EnvType.CLIENT) 29 | public ActionResult use(World world, PlayerEntity user, Hand hand) { 30 | if (user.isSneaking()) { 31 | if (world.isClient) MinecraftClient.getInstance().setScreen(new SelectUwuScreenScreen()); 32 | } else if (!world.isClient) { 33 | user.openHandledScreen(new NamedScreenHandlerFactory() { 34 | @Override 35 | public Text getDisplayName() { 36 | return Text.literal("bruh momento"); 37 | } 38 | 39 | @Override 40 | public @NotNull ScreenHandler createMenu(int syncId, PlayerInventory inv, PlayerEntity player) { 41 | return new EpicScreenHandler(syncId, inv, ScreenHandlerContext.create(world, player.getBlockPos())); 42 | } 43 | }); 44 | } 45 | return ActionResult.PASS; 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /src/testmod/java/io/wispforest/uwu/mixin/TitleScreenMixin.java: -------------------------------------------------------------------------------- 1 | package io.wispforest.uwu.mixin; 2 | 3 | import io.wispforest.owo.config.ui.ConfigScreen; 4 | import io.wispforest.uwu.Uwu; 5 | import net.minecraft.client.MinecraftClient; 6 | import net.minecraft.client.gui.screen.Screen; 7 | import net.minecraft.client.gui.screen.TitleScreen; 8 | import net.minecraft.client.gui.widget.ButtonWidget; 9 | import net.minecraft.text.Text; 10 | import org.spongepowered.asm.mixin.Mixin; 11 | import org.spongepowered.asm.mixin.injection.At; 12 | import org.spongepowered.asm.mixin.injection.Inject; 13 | import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; 14 | 15 | @Mixin(TitleScreen.class) 16 | public abstract class TitleScreenMixin extends Screen { 17 | 18 | protected TitleScreenMixin(Text title) { 19 | super(title); 20 | } 21 | 22 | @Inject(method = "method_41198", at = @At("HEAD"), cancellable = true) 23 | private void injectUwuConfigScreen(ButtonWidget button, CallbackInfo ci) { 24 | if (!Screen.hasAltDown()) return; 25 | 26 | MinecraftClient.getInstance().setScreen(ConfigScreen.create(Uwu.BRUHHHHH, this)); 27 | ci.cancel(); 28 | } 29 | 30 | } 31 | -------------------------------------------------------------------------------- /src/testmod/java/io/wispforest/uwu/network/DispatchedInterface.java: -------------------------------------------------------------------------------- 1 | package io.wispforest.uwu.network; 2 | 3 | public interface DispatchedInterface { 4 | String getName(); 5 | } 6 | -------------------------------------------------------------------------------- /src/testmod/java/io/wispforest/uwu/network/DispatchedSubclassOne.java: -------------------------------------------------------------------------------- 1 | package io.wispforest.uwu.network; 2 | 3 | public record DispatchedSubclassOne(String a) implements DispatchedInterface { 4 | @Override 5 | public String getName() { 6 | return "one"; 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /src/testmod/java/io/wispforest/uwu/network/DispatchedSubclassTwo.java: -------------------------------------------------------------------------------- 1 | package io.wispforest.uwu.network; 2 | 3 | public record DispatchedSubclassTwo(int a) implements DispatchedInterface { 4 | @Override 5 | public String getName() { 6 | return "two"; 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /src/testmod/java/io/wispforest/uwu/network/KeycodePacket.java: -------------------------------------------------------------------------------- 1 | package io.wispforest.uwu.network; 2 | 3 | public record KeycodePacket(Integer key) {} 4 | -------------------------------------------------------------------------------- /src/testmod/java/io/wispforest/uwu/network/MaldingPacket.java: -------------------------------------------------------------------------------- 1 | package io.wispforest.uwu.network; 2 | 3 | public record MaldingPacket(DispatchedInterface value) { 4 | } 5 | -------------------------------------------------------------------------------- /src/testmod/java/io/wispforest/uwu/network/NullablePacket.java: -------------------------------------------------------------------------------- 1 | package io.wispforest.uwu.network; 2 | 3 | import io.wispforest.endec.annotations.NullableComponent; 4 | 5 | import java.util.List; 6 | 7 | public record NullablePacket(@NullableComponent String name, @NullableComponent List names) {} 8 | -------------------------------------------------------------------------------- /src/testmod/java/io/wispforest/uwu/network/SealedSubclassOne.java: -------------------------------------------------------------------------------- 1 | package io.wispforest.uwu.network; 2 | 3 | public record SealedSubclassOne(String a, int b) implements SealedTestClass { 4 | } 5 | -------------------------------------------------------------------------------- /src/testmod/java/io/wispforest/uwu/network/SealedSubclassTwo.java: -------------------------------------------------------------------------------- 1 | package io.wispforest.uwu.network; 2 | 3 | public record SealedSubclassTwo(long a, Void b) implements SealedTestClass { 4 | } 5 | -------------------------------------------------------------------------------- /src/testmod/java/io/wispforest/uwu/network/SealedTestClass.java: -------------------------------------------------------------------------------- 1 | package io.wispforest.uwu.network; 2 | 3 | import io.wispforest.endec.annotations.SealedPolymorphic; 4 | 5 | @SealedPolymorphic 6 | public sealed interface SealedTestClass permits SealedSubclassOne, SealedSubclassTwo { 7 | } -------------------------------------------------------------------------------- /src/testmod/java/io/wispforest/uwu/network/StringPacket.java: -------------------------------------------------------------------------------- 1 | package io.wispforest.uwu.network; 2 | 3 | public record StringPacket(String value) {} 4 | -------------------------------------------------------------------------------- /src/testmod/java/io/wispforest/uwu/rei/UwuReiPlugin.java: -------------------------------------------------------------------------------- 1 | package io.wispforest.uwu.rei; 2 | 3 | import me.shedaniel.rei.api.client.plugins.REIClientPlugin; 4 | import me.shedaniel.rei.api.client.registry.category.CategoryRegistry; 5 | import me.shedaniel.rei.api.client.registry.display.DisplayRegistry; 6 | 7 | public class UwuReiPlugin implements REIClientPlugin { 8 | 9 | @Override 10 | public void registerCategories(CategoryRegistry registry) { 11 | registry.add(new UiCategory()); 12 | } 13 | 14 | @Override 15 | public void registerDisplays(DisplayRegistry registry) { 16 | registry.add(new UiCategory.UiDisplay()); 17 | } 18 | 19 | } 20 | -------------------------------------------------------------------------------- /src/testmod/java/io/wispforest/uwu/text/BasedTextContent.java: -------------------------------------------------------------------------------- 1 | package io.wispforest.uwu.text; 2 | 3 | import io.wispforest.endec.Endec; 4 | import io.wispforest.endec.impl.StructEndecBuilder; 5 | import io.wispforest.owo.serialization.CodecUtils; 6 | import net.minecraft.text.StringVisitable; 7 | import net.minecraft.text.Style; 8 | import net.minecraft.text.TextContent; 9 | 10 | import java.util.Optional; 11 | 12 | public class BasedTextContent implements TextContent { 13 | 14 | public static final Type TYPE = new Type<>( 15 | CodecUtils.toMapCodec(StructEndecBuilder.of(Endec.STRING.fieldOf("based", o -> o.basedText), BasedTextContent::new)), 16 | "uwu:based"); 17 | 18 | private final String basedText; 19 | 20 | public BasedTextContent(String basedText) { 21 | this.basedText = basedText; 22 | } 23 | 24 | @Override 25 | public Optional visit(StringVisitable.Visitor visitor) { 26 | return visitor.accept("I am extremely based: " + basedText); 27 | } 28 | 29 | @Override 30 | public Optional visit(StringVisitable.StyledVisitor visitor, Style style) { 31 | return visitor.accept(style, "I am extremely based: " + basedText); 32 | } 33 | 34 | @Override 35 | public Type getType() { 36 | return TYPE; 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/testmod/resources/assets/uowou/models/item/owo_ingot.json: -------------------------------------------------------------------------------- 1 | { 2 | "parent": "item/generated", 3 | "textures": { 4 | "layer0": "uowou:item/owo_ingot" 5 | } 6 | } -------------------------------------------------------------------------------- /src/testmod/resources/assets/uowou/textures/item/owo_ingot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wisp-forest/owo-lib/ef83709dae78614695e2842cd792990e3db28900/src/testmod/resources/assets/uowou/textures/item/owo_ingot.png -------------------------------------------------------------------------------- /src/testmod/resources/assets/uwu/models/item/counter.json: -------------------------------------------------------------------------------- 1 | { 2 | "parent": "item/generated", 3 | "textures": { 4 | "layer0": "item/compass_16" 5 | } 6 | } -------------------------------------------------------------------------------- /src/testmod/resources/assets/uwu/models/item/screen_shard.json: -------------------------------------------------------------------------------- 1 | { 2 | "parent": "item/generated", 3 | "textures": { 4 | "layer0": "item/echo_shard" 5 | } 6 | } -------------------------------------------------------------------------------- /src/testmod/resources/assets/uwu/models/item/test_stick.json: -------------------------------------------------------------------------------- 1 | { 2 | "parent": "item/generated", 3 | "textures": { 4 | "layer0": "item/stick" 5 | } 6 | } -------------------------------------------------------------------------------- /src/testmod/resources/assets/uwu/owo_ui/parse_fail.xml: -------------------------------------------------------------------------------- 1 | 3 | 4 | 5 | 6 | 12 | 13 | 14 | center 15 | center 16 | 17 | 18 | 19 | 20 | 21 | 23 | -------------------------------------------------------------------------------- /src/testmod/resources/assets/uwu/owo_ui/test_element_one.xml: -------------------------------------------------------------------------------- 1 | 3 | 4 | 9 | 10 | -------------------------------------------------------------------------------- /src/testmod/resources/assets/uwu/owo_ui/test_element_two.xml: -------------------------------------------------------------------------------- 1 | 3 | 4 |