├── .github └── FUNDING.yml ├── .gitignore ├── README.md ├── build.gradle ├── gradle.properties ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── lib └── standardsettings-1.2.2+1.16.1.jar ├── scripts └── convert.py ├── settings.gradle └── src └── main ├── java └── me │ └── falu │ └── peepopractice │ ├── PeepoPractice.java │ ├── core │ ├── CustomPortalForcer.java │ ├── ExecuteAtIndexArrayList.java │ ├── category │ │ ├── CustomCategoryResourceManager.java │ │ ├── PracticeCategoriesAA.java │ │ ├── PracticeCategoriesAny.java │ │ ├── PracticeCategory.java │ │ ├── preferences │ │ │ ├── CategoryPreference.java │ │ │ ├── CategoryPreferences.java │ │ │ └── PreferenceTypes.java │ │ ├── presets │ │ │ ├── MiscPresets.java │ │ │ └── TaskPresets.java │ │ ├── properties │ │ │ ├── BaseProperties.java │ │ │ ├── PlayerProperties.java │ │ │ ├── StructureProperties.java │ │ │ ├── WorldProperties.java │ │ │ └── event │ │ │ │ ├── ChangeDimensionSplitEvent.java │ │ │ │ ├── EnterVehicleSplitEvent.java │ │ │ │ ├── GetAdvancementSplitEvent.java │ │ │ │ ├── InteractLootChestSplitEvent.java │ │ │ │ ├── SplitEvent.java │ │ │ │ └── ThrowEntitySplitEvent.java │ │ └── utils │ │ │ ├── InventoryUtils.java │ │ │ ├── KeyBindingUtils.java │ │ │ ├── PracticeCategoryUtils.java │ │ │ └── StandardSettingsUtils.java │ ├── exception │ │ ├── InvalidCategorySyntaxException.java │ │ └── NotInitializedException.java │ ├── global │ │ ├── GlobalConfig.java │ │ └── GlobalOptions.java │ ├── item │ │ └── RandomToolItem.java │ ├── loot │ │ └── PiglinBarterState.java │ ├── playerless │ │ ├── PlayerlessHandledScreen.java │ │ ├── PlayerlessInventory.java │ │ ├── PlayerlessPlayerScreenHandler.java │ │ ├── PlayerlessScreenHandler.java │ │ └── PlayerlessShulkerBoxScreenHandler.java │ └── writer │ │ ├── DefaultFileWriter.java │ │ └── PracticeWriter.java │ ├── gui │ ├── hud │ │ └── PeepoPauseManHud.java │ ├── screen │ │ ├── CategoryPreferencesScreen.java │ │ ├── CategorySelectionScreen.java │ │ ├── EditInventoryScreen.java │ │ ├── EditShulkerBoxScreen.java │ │ ├── GlobalConfigScreen.java │ │ ├── InventoryOptionsScreen.java │ │ ├── SettingsTypeSelectionScreen.java │ │ ├── SplitSettingsScreen.java │ │ └── StandardSettingsScreen.java │ └── widget │ │ ├── LimitlessButtonWidget.java │ │ ├── LimitlessDoubleOptionSliderWidget.java │ │ └── ThumbnailButtonWidget.java │ ├── mixin │ ├── ClientAdvancementManagerMixin.java │ ├── ClientPlayNetworkHandlerMixin.java │ ├── ClientPlayerInteractionManagerMixin.java │ ├── GameOptionsMixin.java │ ├── MinecraftClientMixin.java │ ├── ServerPlayNetworkHandlerMixin.java │ ├── compat │ │ ├── LocateCommandMixin.java │ │ ├── StandardSettingsMixin.java │ │ └── timer │ │ │ ├── InGameTimerUtilsMixin.java │ │ │ └── PracticeTimerManagerMixin.java │ ├── gui │ │ ├── MoreOptionsDialogMixin.java │ │ ├── RecipeBookMixin.java │ │ ├── hud │ │ │ └── InGameHudMixin.java │ │ ├── renderer │ │ │ └── ItemRendererMixin.java │ │ ├── screen │ │ │ ├── CreateWorldScreenMixin.java │ │ │ ├── GameMenuScreenMixin.java │ │ │ ├── LevelLoadingScreenMixin.java │ │ │ ├── OpenToLanScreenMixin.java │ │ │ ├── ScreenMixin.java │ │ │ └── TitleScreenMixin.java │ │ └── widget │ │ │ └── SliderWidgetMixin.java │ ├── storage │ │ ├── FileResourcePackProviderMixin.java │ │ ├── StorageIoWorkerMixin.java │ │ └── ThreadedAnvilChunkStorageMixin.java │ ├── structure │ │ ├── ChunkGeneratorMixin.java │ │ ├── StructureFeatureMixin.java │ │ ├── StructurePoolBasedGeneratorMixin.java │ │ ├── StructureStartMixin.java │ │ └── category │ │ │ ├── BastionRemnantFeatureConfigMixin.java │ │ │ ├── DungeonFeatureMixin.java │ │ │ └── PortalRoomMixin.java │ └── world │ │ ├── ChestBlockEntityMixin.java │ │ ├── LootableContainerBlockEntityMixin.java │ │ ├── MinecraftServerMixin.java │ │ ├── PlayerManagerMixin.java │ │ ├── ServerWorldMixin.java │ │ ├── SpikeCacheMixin.java │ │ ├── UnderwaterCaveCarverMixin.java │ │ ├── biome │ │ ├── MultiNoiseBiomeSourceMixin.java │ │ └── VanillaLayeredBiomeSourceMixin.java │ │ ├── category │ │ ├── CarverMixin.java │ │ ├── IglooGeneratorMixin.java │ │ └── RuleStructureProcessorMixin.java │ │ ├── entity │ │ ├── ClientPlayerEntityMixin.java │ │ ├── EyeOfEnderEntityMixin.java │ │ ├── MobEntityMixin.java │ │ ├── ServerPlayerEntityMixin.java │ │ ├── ThrownItemEntityMixin.java │ │ ├── ai │ │ │ ├── HoldingPatternPhaseMixin.java │ │ │ └── PiglinBrainMixin.java │ │ └── category │ │ │ ├── BlazeEntityMixin.java │ │ │ ├── MobEntityMixin.java │ │ │ └── ZombifiedPiglinEntityMixin.java │ │ └── item │ │ ├── BoatItemMixin.java │ │ └── MinecartItemMixin.java │ └── owner │ └── GenerationShutdownOwner.java └── resources ├── assets └── peepopractice │ ├── icon.png │ ├── lang │ ├── de_de.json │ ├── en_us.json │ ├── es_ec.json │ ├── fr_fr.json │ ├── hr_hr.json │ ├── is_is.json │ ├── it_it.json │ ├── nl_nl.json │ ├── no_no.json │ ├── ru_ru.json │ └── uk_ua.json │ ├── models │ └── item │ │ ├── random_axe.json │ │ ├── random_hoe.json │ │ ├── random_pickaxe.json │ │ ├── random_shovel.json │ │ └── random_sword.json │ └── textures │ ├── category │ ├── bastion_split.png │ ├── end_game_split.png │ ├── end_split.png │ ├── fortress_split.png │ ├── igloo_split.png │ ├── island_leave_split.png │ ├── mapless_split.png │ ├── nether_split.png │ ├── post_blind_split.png │ ├── raid_split.png │ ├── ravine_enter_split.png │ ├── stronghold_split.png │ └── uneasy_alliance_split.png │ ├── gear.png │ ├── item │ ├── random_axe.png │ ├── random_hoe.png │ ├── random_pickaxe.png │ ├── random_shovel.png │ └── random_sword.png │ ├── peepopagman.png │ ├── peepopauseman.png │ └── widepeepohappy.png ├── data └── minecraft │ └── tags │ └── blocks │ └── valid_spawn.json ├── datapacks └── aa_endgame.zip ├── fabric.mod.json ├── peepopractice.accesswidener ├── peepopractice.mixins.json └── writer ├── inventories.json └── seed_lists ├── end_game.json └── raid.json /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # These are supported funding model platforms 2 | 3 | github: # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2] 4 | patreon: # Replace with a single Patreon username 5 | open_collective: # Replace with a single Open Collective username 6 | ko_fi: quesia 7 | tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel 8 | community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry 9 | liberapay: # Replace with a single Liberapay username 10 | issuehunt: # Replace with a single IssueHunt username 11 | otechie: # Replace with a single Otechie username 12 | lfx_crowdfunding: # Replace with a single LFX Crowdfunding project-name e.g., cloud-foundry 13 | custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2'] 14 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # gradle 2 | 3 | .gradle/ 4 | build/ 5 | out/ 6 | classes/ 7 | 8 | # eclipse 9 | 10 | *.launch 11 | 12 | # idea 13 | 14 | .idea/ 15 | *.iml 16 | *.ipr 17 | *.iws 18 | 19 | # vscode 20 | 21 | .settings/ 22 | .vscode/ 23 | bin/ 24 | .classpath 25 | .project 26 | 27 | # macos 28 | 29 | *.DS_Store 30 | 31 | # fabric 32 | 33 | run/ 34 | 35 | # node 36 | 37 | node_modules/ 38 | package-lock.json 39 | 40 | # other 41 | 42 | TODO.txt 43 | -------------------------------------------------------------------------------- /build.gradle: -------------------------------------------------------------------------------- 1 | plugins { 2 | id "fabric-loom" version "1.10-SNAPSHOT" 3 | id "io.freefair.lombok" version "8.6" 4 | } 5 | 6 | sourceCompatibility = JavaVersion.VERSION_1_8 7 | targetCompatibility = JavaVersion.VERSION_1_8 8 | 9 | archivesBaseName = project.archives_base_name 10 | version = project.mod_version 11 | group = project.maven_group 12 | 13 | repositories { 14 | maven { 15 | url "https://jitpack.io" 16 | } 17 | } 18 | 19 | loom { 20 | accessWidenerPath = file("src/main/resources/peepopractice.accesswidener") 21 | } 22 | 23 | configurations { 24 | modIncludeImplementation 25 | includeImplementation 26 | include.extendsFrom(modIncludeImplementation, includeImplementation) 27 | modImplementation.extendsFrom(modIncludeImplementation, includeImplementation) 28 | } 29 | 30 | dependencies { 31 | minecraft "com.mojang:minecraft:${project.minecraft_version}" 32 | mappings "com.github.RedLime:yarn:${project.yarn_mappings}:v2" 33 | modImplementation "net.fabricmc:fabric-loader:${project.loader_version}" 34 | 35 | modImplementation "com.github.RedLime:SpeedRunIGT:${project.minecraft_version}-SNAPSHOT" 36 | includeImplementation "org.reflections:reflections:0.9.10" 37 | 38 | modIncludeImplementation(fabricApi.module("fabric-api-base", project.fabric_version)) 39 | modIncludeImplementation(fabricApi.module("fabric-resource-loader-v0", project.fabric_version)) 40 | modIncludeImplementation(fabricApi.module("fabric-item-api-v1", project.fabric_version)) 41 | 42 | modCompileOnlyApi files("lib/standardsettings-1.2.2+1.16.1.jar") 43 | } 44 | 45 | processResources { 46 | inputs.property "version", project.version 47 | 48 | filesMatching("fabric.mod.json") { 49 | expand "version": project.version 50 | } 51 | } 52 | 53 | java { 54 | withSourcesJar() 55 | } 56 | -------------------------------------------------------------------------------- /gradle.properties: -------------------------------------------------------------------------------- 1 | # Done to increase the memory available to gradle. 2 | org.gradle.jvmargs=-Xmx2G 3 | 4 | # Fabric Properties 5 | # Check these on https://fabricmc.net/develop, https://github.com/RedLime/yarn/tags 6 | minecraft_version=1.16.1 7 | yarn_mappings=1.16.1.build.19 8 | loader_version=0.16.13 9 | 10 | # Mod Properties 11 | mod_version=2.2.1 12 | maven_group=me.falu 13 | archives_base_name=peepopractice 14 | 15 | # API Properties 16 | fabric_version=0.18.0+build.387-1.16.1 17 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/faluhub/peepoPractice/2b1e3095a43f04551710fd3094829a83b9d2c6da/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 | networkTimeout=10000 5 | validateDistributionUrl=true 6 | zipStoreBase=GRADLE_USER_HOME 7 | zipStorePath=wrapper/dists 8 | -------------------------------------------------------------------------------- /gradlew.bat: -------------------------------------------------------------------------------- 1 | @rem 2 | @rem Copyright 2015 the original author or authors. 3 | @rem 4 | @rem Licensed under the Apache License, Version 2.0 (the "License"); 5 | @rem you may not use this file except in compliance with the License. 6 | @rem You may obtain a copy of the License at 7 | @rem 8 | @rem https://www.apache.org/licenses/LICENSE-2.0 9 | @rem 10 | @rem Unless required by applicable law or agreed to in writing, software 11 | @rem distributed under the License is distributed on an "AS IS" BASIS, 12 | @rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | @rem See the License for the specific language governing permissions and 14 | @rem limitations under the License. 15 | @rem 16 | 17 | @if "%DEBUG%"=="" @echo off 18 | @rem ########################################################################## 19 | @rem 20 | @rem Gradle startup script for Windows 21 | @rem 22 | @rem ########################################################################## 23 | 24 | @rem Set local scope for the variables with windows NT shell 25 | if "%OS%"=="Windows_NT" setlocal 26 | 27 | set DIRNAME=%~dp0 28 | if "%DIRNAME%"=="" set DIRNAME=. 29 | @rem This is normally unused 30 | set APP_BASE_NAME=%~n0 31 | set APP_HOME=%DIRNAME% 32 | 33 | @rem Resolve any "." and ".." in APP_HOME to make it shorter. 34 | for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi 35 | 36 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 37 | set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" 38 | 39 | @rem Find java.exe 40 | if defined JAVA_HOME goto findJavaFromJavaHome 41 | 42 | set JAVA_EXE=java.exe 43 | %JAVA_EXE% -version >NUL 2>&1 44 | if %ERRORLEVEL% equ 0 goto execute 45 | 46 | echo. 47 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 48 | echo. 49 | echo Please set the JAVA_HOME variable in your environment to match the 50 | echo location of your Java installation. 51 | 52 | goto fail 53 | 54 | :findJavaFromJavaHome 55 | set JAVA_HOME=%JAVA_HOME:"=% 56 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 57 | 58 | if exist "%JAVA_EXE%" goto execute 59 | 60 | echo. 61 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 62 | echo. 63 | echo Please set the JAVA_HOME variable in your environment to match the 64 | echo location of your Java installation. 65 | 66 | goto fail 67 | 68 | :execute 69 | @rem Setup the command line 70 | 71 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 72 | 73 | 74 | @rem Execute Gradle 75 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* 76 | 77 | :end 78 | @rem End local scope for the variables with windows NT shell 79 | if %ERRORLEVEL% equ 0 goto mainEnd 80 | 81 | :fail 82 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 83 | rem the _cmd.exe /c_ return code! 84 | set EXIT_CODE=%ERRORLEVEL% 85 | if %EXIT_CODE% equ 0 set EXIT_CODE=1 86 | if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE% 87 | exit /b %EXIT_CODE% 88 | 89 | :mainEnd 90 | if "%OS%"=="Windows_NT" endlocal 91 | 92 | :omega 93 | -------------------------------------------------------------------------------- /lib/standardsettings-1.2.2+1.16.1.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/faluhub/peepoPractice/2b1e3095a43f04551710fd3094829a83b9d2c6da/lib/standardsettings-1.2.2+1.16.1.jar -------------------------------------------------------------------------------- /scripts/convert.py: -------------------------------------------------------------------------------- 1 | import json 2 | 3 | with open("./seeds.txt", "r") as f: 4 | content = f.read().splitlines() 5 | seeds = [] 6 | for i in content: 7 | seeds.append(int(i)) 8 | with open("./seeds.json", "w") as w: 9 | json.dump(seeds, w, indent=4) 10 | -------------------------------------------------------------------------------- /settings.gradle: -------------------------------------------------------------------------------- 1 | pluginManagement { 2 | repositories { 3 | maven { 4 | name = 'Fabric' 5 | url = 'https://maven.fabricmc.net/' 6 | } 7 | mavenCentral() 8 | gradlePluginPortal() 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /src/main/java/me/falu/peepopractice/core/ExecuteAtIndexArrayList.java: -------------------------------------------------------------------------------- 1 | package me.falu.peepopractice.core; 2 | 3 | import java.util.ArrayList; 4 | 5 | public class ExecuteAtIndexArrayList extends ArrayList { 6 | private final IndexExecutionTask task; 7 | private final int index; 8 | 9 | public ExecuteAtIndexArrayList(IndexExecutionTask task, int index) { 10 | this.task = task; 11 | this.index = index; 12 | } 13 | 14 | @Override 15 | public boolean add(E e) { 16 | if (this.size() == this.index) { 17 | this.task.execute(e); 18 | } 19 | return super.add(e); 20 | } 21 | 22 | @Override 23 | public void add(int index, E element) { 24 | if (index == this.index) { 25 | this.task.execute(element); 26 | } 27 | super.add(index, element); 28 | } 29 | 30 | public interface IndexExecutionTask { 31 | void execute(E element); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/main/java/me/falu/peepopractice/core/category/PracticeCategoriesAA.java: -------------------------------------------------------------------------------- 1 | package me.falu.peepopractice.core.category; 2 | 3 | import me.falu.peepopractice.core.CustomPortalForcer; 4 | import me.falu.peepopractice.core.category.properties.PlayerProperties; 5 | import me.falu.peepopractice.core.category.properties.StructureProperties; 6 | import me.falu.peepopractice.core.category.properties.WorldProperties; 7 | import me.falu.peepopractice.core.category.properties.event.GetAdvancementSplitEvent; 8 | import net.minecraft.entity.effect.StatusEffects; 9 | import net.minecraft.util.Identifier; 10 | import net.minecraft.util.math.BlockPos; 11 | import net.minecraft.util.math.ChunkPos; 12 | import net.minecraft.world.Difficulty; 13 | import net.minecraft.world.World; 14 | import net.minecraft.world.biome.Biomes; 15 | import net.minecraft.world.gen.feature.DefaultBiomeFeatures; 16 | 17 | import java.util.ArrayList; 18 | import java.util.List; 19 | 20 | @SuppressWarnings("UnusedDeclaration") 21 | public class PracticeCategoriesAA { 22 | public static final List ALL = new ArrayList<>(); 23 | public static final PracticeCategory UNEASY_ALLIANCE_SPLIT = new PracticeCategory(true) 24 | .setId("uneasy_alliance_split") 25 | .setPlayerProperties( 26 | new PlayerProperties() 27 | .setSpawnPos((category, random, world) -> CustomPortalForcer.getPortalPosition(new BlockPos(random.nextInt(50, 250) * (random.nextBoolean() ? -1 : 1), 64, random.nextInt(50, 250) * (random.nextBoolean() ? -1 : 1)), world)) 28 | ) 29 | .setWorldProperties( 30 | new WorldProperties() 31 | .setWorldRegistryKey(World.NETHER) 32 | .setSpawnChunksDisabled(true) 33 | .addProBiome(new WorldProperties.BiomeModification() 34 | .setBiome(Biomes.SOUL_SAND_VALLEY) 35 | .setRange(new WorldProperties.Range() 36 | .setRange(null) 37 | .addValidDimension(World.NETHER) 38 | ) 39 | ) 40 | ) 41 | .setSplitEvent( 42 | new GetAdvancementSplitEvent() 43 | .setAdvancement(new Identifier("nether/uneasy_alliance")) 44 | ) 45 | .register(); 46 | public static final PracticeCategory IGLOO_SPLIT = new PracticeCategory(true) 47 | .setId("igloo_split") 48 | .putPermaValue("guaranteeIglooBasement", true) 49 | .setWorldProperties( 50 | new WorldProperties() 51 | .setWorldRegistryKey(World.OVERWORLD) 52 | .setSpawnChunksDisabled(true) 53 | .addProBiome( 54 | new WorldProperties.BiomeModification() 55 | .setBiome(Biomes.SNOWY_TUNDRA) 56 | .setRange(new WorldProperties.Range() 57 | .setRange(120) 58 | .addValidDimension(World.OVERWORLD) 59 | ) 60 | ) 61 | .setStartDifficulty(Difficulty.HARD) 62 | ) 63 | .addStructureProperties( 64 | new StructureProperties() 65 | .setStructure(DefaultBiomeFeatures.IGLOO) 66 | .setChunkPos(new ChunkPos(0, 0)) 67 | ) 68 | .setSplitEvent( 69 | new GetAdvancementSplitEvent() 70 | .setAdvancement(new Identifier("story/cure_zombie_villager")) 71 | ) 72 | .register(); 73 | public static final PracticeCategory RAID_SPLIT = new PracticeCategory(true) 74 | .setId("raid_split") 75 | .setFillerCategory(true) 76 | .setPlayerProperties( 77 | new PlayerProperties() 78 | .addPotionEffect( 79 | new PlayerProperties.PotionEffect() 80 | .setEffect(StatusEffects.BAD_OMEN) 81 | ) 82 | ) 83 | .setWorldProperties( 84 | new WorldProperties() 85 | .setWorldRegistryKey(World.OVERWORLD) 86 | .setSpawnChunksDisabled(true) 87 | .setSeedList("raid") 88 | .setStartDifficulty(Difficulty.NORMAL) 89 | ) 90 | .setSplitEvent( 91 | new GetAdvancementSplitEvent() 92 | .setAdvancement(new Identifier("adventure/hero_of_the_village")) 93 | ) 94 | .register(); 95 | public static final PracticeCategory END_GAME_SPLIT = new PracticeCategory(true) 96 | .setId("end_game_split") 97 | .setWorldProperties( 98 | new WorldProperties() 99 | .setWorldRegistryKey(World.OVERWORLD) 100 | .setSeedList("end_game") 101 | .useDatapack("aa_endgame") 102 | ) 103 | .setSplitEvent( 104 | new GetAdvancementSplitEvent() 105 | .setAdvancement(null) 106 | ) 107 | .register(); 108 | } 109 | -------------------------------------------------------------------------------- /src/main/java/me/falu/peepopractice/core/category/preferences/CategoryPreference.java: -------------------------------------------------------------------------------- 1 | package me.falu.peepopractice.core.category.preferences; 2 | 3 | import com.google.gson.JsonObject; 4 | import lombok.Getter; 5 | import me.falu.peepopractice.PeepoPractice; 6 | import me.falu.peepopractice.core.category.PracticeCategory; 7 | import me.falu.peepopractice.core.writer.PracticeWriter; 8 | import net.minecraft.text.LiteralText; 9 | import net.minecraft.text.Text; 10 | import net.minecraft.text.TranslatableText; 11 | import net.minecraft.util.Formatting; 12 | import net.minecraft.util.Identifier; 13 | 14 | @Getter 15 | public class CategoryPreference> { 16 | private String id; 17 | private T defaultValue; 18 | private Identifier icon; 19 | 20 | public boolean getBoolValue() { 21 | return this.getBoolValue(PeepoPractice.CATEGORY); 22 | } 23 | 24 | public boolean getBoolValue(PracticeCategory category) { 25 | T value = this.getValue(category); 26 | return value.name().equals("ENABLED"); 27 | } 28 | 29 | public T getValue() { 30 | return this.getValue(PeepoPractice.CATEGORY); 31 | } 32 | 33 | @SuppressWarnings("unchecked") 34 | public T getValue(PracticeCategory category) { 35 | JsonObject config = PracticeWriter.PREFERENCES_WRITER.get(); 36 | JsonObject categoryObj = config.has(category.getId()) ? config.getAsJsonObject(category.getId()) : new JsonObject(); 37 | if (categoryObj.has(this.id)) { 38 | String value = categoryObj.get(this.id).getAsString(); 39 | for (Enum v : this.defaultValue.getDeclaringClass().getEnumConstants()) { 40 | if (v.name().equals(value)) { 41 | return (T) v; 42 | } 43 | } 44 | } 45 | this.setValue(category, this.defaultValue); 46 | return this.defaultValue; 47 | } 48 | 49 | @SuppressWarnings("unchecked") 50 | public T getNextValue(PracticeCategory category) { 51 | T value = this.getValue(category); 52 | int index = value.ordinal(); 53 | if (index + 1 == value.getDeclaringClass().getEnumConstants().length) { 54 | index = 0; 55 | } else { 56 | index++; 57 | } 58 | return (T) value.getDeclaringClass().getEnumConstants()[index]; 59 | } 60 | 61 | public void setValue(PracticeCategory category, T value) { 62 | JsonObject config = PracticeWriter.PREFERENCES_WRITER.get(); 63 | JsonObject categoryObj = config.has(category.getId()) ? config.getAsJsonObject(category.getId()) : new JsonObject(); 64 | categoryObj.addProperty(this.id, value.name()); 65 | PracticeWriter.PREFERENCES_WRITER.put(category.getId(), categoryObj); 66 | } 67 | 68 | public void advanceValue(PracticeCategory category) { 69 | this.setValue(category, this.getNextValue(category)); 70 | } 71 | 72 | public CategoryPreference setId(String id) { 73 | this.id = id; 74 | return this; 75 | } 76 | 77 | public Text getValueLabel(PracticeCategory category) { 78 | return this.getValueLabel(category, false); 79 | } 80 | 81 | public Text getValueLabel(PracticeCategory category, boolean styled) { 82 | T value = this.getValue(category); 83 | String style; 84 | switch (value.name()) { 85 | case "RANDOM": 86 | style = styled ? "" + Formatting.YELLOW : ""; 87 | return new LiteralText(style).append(new TranslatableText("peepopractice.text.random")); 88 | case "ENABLED": 89 | style = styled ? "" + Formatting.GREEN : ""; 90 | return new LiteralText(style).append(new TranslatableText("peepopractice.text.enabled")); 91 | case "DISABLED": 92 | style = styled ? "" + Formatting.RED : ""; 93 | return new LiteralText(style).append(new TranslatableText("peepopractice.text.disabled")); 94 | default: 95 | String identifier = ""; 96 | if (value instanceof PreferenceTypes.ConfigTypeBase) { 97 | identifier = ((PreferenceTypes.ConfigTypeBase) value).getIdentifier() + "."; 98 | } 99 | return new TranslatableText("peepopractice.types." + identifier + value.name().toLowerCase()); 100 | } 101 | } 102 | 103 | public Text getLabel() { 104 | return new TranslatableText("peepopractice.preferences." + this.id); 105 | } 106 | 107 | public Text getDescription() { 108 | return new TranslatableText("peepopractice.preferences." + this.id + ".info"); 109 | } 110 | 111 | public CategoryPreference setDefaultValue(T defaultValue) { 112 | this.defaultValue = defaultValue; 113 | return this; 114 | } 115 | 116 | public CategoryPreference setIcon(Identifier icon) { 117 | this.icon = icon; 118 | return this; 119 | } 120 | } 121 | -------------------------------------------------------------------------------- /src/main/java/me/falu/peepopractice/core/category/preferences/PreferenceTypes.java: -------------------------------------------------------------------------------- 1 | package me.falu.peepopractice.core.category.preferences; 2 | 3 | import lombok.Getter; 4 | import net.minecraft.util.math.Vec3i; 5 | import net.minecraft.util.registry.RegistryKey; 6 | import net.minecraft.world.World; 7 | 8 | public class PreferenceTypes { 9 | public enum BooleanType { 10 | ENABLED, 11 | DISABLED 12 | } 13 | 14 | public enum BooleanRandomType { 15 | ENABLED, 16 | DISABLED, 17 | RANDOM 18 | } 19 | 20 | public enum BastionType implements ConfigTypeBase { 21 | HOUSING(0, new Vec3i(-9, 83, 27), -90.0F), 22 | STABLES(1, new Vec3i(3, 54, 30), 90.0F), 23 | TREASURE(2, new Vec3i(16, 75, -1), 180.0F), 24 | BRIDGE(3, new Vec3i(-26, 67, 10), -90.0F), 25 | RANDOM(4, new Vec3i(0, 0, 0), 0.0F); 26 | public final int id; 27 | public final Vec3i pos; 28 | public final float angle; 29 | 30 | BastionType(int id, Vec3i pos, float angle) { 31 | this.id = id; 32 | this.pos = pos; 33 | this.angle = angle; 34 | } 35 | 36 | public static BastionType fromId(int id) { 37 | for (BastionType type : BastionType.values()) { 38 | if (type.id == id) { 39 | return type; 40 | } 41 | } 42 | return null; 43 | } 44 | 45 | @Override 46 | public String getIdentifier() { 47 | return "bastion"; 48 | } 49 | } 50 | 51 | public enum CompareType implements ConfigTypeBase { 52 | PB, 53 | AVERAGE; 54 | 55 | @Override 56 | public String getIdentifier() { 57 | return "compare"; 58 | } 59 | } 60 | 61 | public enum PaceTimerShowType implements ConfigTypeBase { 62 | ALWAYS, 63 | END, 64 | NEVER; 65 | 66 | @Override 67 | public String getIdentifier() { 68 | return "pace_timer_show"; 69 | } 70 | } 71 | 72 | public enum EyeCountType implements ConfigTypeBase { 73 | ALL, 74 | RANDOM, 75 | NONE; 76 | 77 | @Override 78 | public String getIdentifier() { 79 | return "eye_count"; 80 | } 81 | } 82 | 83 | @Getter 84 | public enum StrongholdDistanceType implements ConfigTypeBase { 85 | CLOSE(200, 500), 86 | AVERAGE(700, 1000), 87 | FAR(1200, 1600), 88 | RANDOM(0, 2300); 89 | private final int min; 90 | private final int max; 91 | 92 | StrongholdDistanceType(int min, int max) { 93 | this.min = min; 94 | this.max = max; 95 | } 96 | 97 | @Override 98 | public String getIdentifier() { 99 | return "stronghold_distance"; 100 | } 101 | } 102 | 103 | public enum StartNodeType implements ConfigTypeBase { 104 | RANDOM, 105 | FRONT, 106 | BACK; 107 | 108 | @Override 109 | public String getIdentifier() { 110 | return "start_node"; 111 | } 112 | } 113 | 114 | public enum SpawnLocationType implements ConfigTypeBase { 115 | STRUCTURE, 116 | TERRAIN; 117 | 118 | @Override 119 | public String getIdentifier() { 120 | return "spawn_location"; 121 | } 122 | } 123 | 124 | public enum EndTowerHeightType implements ConfigTypeBase { 125 | RANDOM(), 126 | SMALL_BOY_76(76), 127 | SMALL_CAGE_79(79, true), 128 | TALL_CAGE_82(82, true), 129 | M85(85), 130 | M88(88), 131 | M91(91), 132 | T94(94), 133 | T97(97), 134 | T100(100), 135 | TALL_BOY_103(103); 136 | public final int height; 137 | public final boolean caged; 138 | 139 | EndTowerHeightType(int height, boolean caged) { 140 | this.height = height; 141 | this.caged = caged; 142 | } 143 | 144 | EndTowerHeightType(int height) { 145 | this(height, false); 146 | } 147 | 148 | EndTowerHeightType() { 149 | this.height = -1; 150 | this.caged = false; 151 | } 152 | 153 | @Override 154 | public String getIdentifier() { 155 | return "end_tower_height"; 156 | } 157 | } 158 | 159 | public enum SelectedInventoryType { 160 | ONE, 161 | TWO, 162 | THREE 163 | } 164 | 165 | public enum PostBlindSpawnDimensionType implements ConfigTypeBase { 166 | OVERWORLD(World.OVERWORLD), 167 | NETHER(World.NETHER); 168 | public final RegistryKey key; 169 | 170 | PostBlindSpawnDimensionType(RegistryKey key) { 171 | this.key = key; 172 | } 173 | 174 | @Override 175 | public String getIdentifier() { 176 | return "post_blind_spawn_dimension"; 177 | } 178 | } 179 | 180 | public interface ConfigTypeBase { 181 | String getIdentifier(); 182 | } 183 | } 184 | -------------------------------------------------------------------------------- /src/main/java/me/falu/peepopractice/core/category/presets/MiscPresets.java: -------------------------------------------------------------------------------- 1 | package me.falu.peepopractice.core.category.presets; 2 | 3 | import me.falu.peepopractice.core.category.preferences.CategoryPreferences; 4 | import me.falu.peepopractice.core.category.properties.PlayerProperties; 5 | import net.minecraft.entity.effect.StatusEffects; 6 | 7 | public class MiscPresets { 8 | public static final PlayerProperties.PotionEffect FIRE_RESISTANCE_EFFECT = new PlayerProperties.PotionEffect() 9 | .setEffect(StatusEffects.FIRE_RESISTANCE) 10 | .setDuration(3600) 11 | .setCondition((category, random, world) -> CategoryPreferences.FIRE_RESISTANCE.getBoolValue()); 12 | } 13 | -------------------------------------------------------------------------------- /src/main/java/me/falu/peepopractice/core/category/presets/TaskPresets.java: -------------------------------------------------------------------------------- 1 | package me.falu.peepopractice.core.category.presets; 2 | 3 | import me.falu.peepopractice.core.CustomPortalForcer; 4 | import me.falu.peepopractice.core.category.preferences.CategoryPreferences; 5 | import me.falu.peepopractice.core.category.preferences.PreferenceTypes; 6 | import me.falu.peepopractice.core.category.PracticeCategory; 7 | import me.falu.peepopractice.core.category.properties.StructureProperties; 8 | import me.falu.peepopractice.core.exception.NotInitializedException; 9 | import net.minecraft.block.Blocks; 10 | import net.minecraft.util.math.BlockPos; 11 | import net.minecraft.util.math.Vec2f; 12 | import net.minecraft.world.gen.feature.StructureFeature; 13 | import net.minecraft.world.level.ServerWorldProperties; 14 | 15 | public class TaskPresets { 16 | public static final PracticeCategory.ExecuteReturnTask BASTION_SPAWN_POS = (category, random, world) -> { 17 | if (category.hasCustomValue("bastionType")) { 18 | PreferenceTypes.SpawnLocationType spawnLocation = CategoryPreferences.SPAWN_LOCATION.getValue(); 19 | if (spawnLocation.equals(PreferenceTypes.SpawnLocationType.STRUCTURE)) { 20 | PreferenceTypes.BastionType type = PreferenceTypes.BastionType.fromId((int) category.getCustomValue("bastionType")); 21 | if (type != null) { 22 | StructureProperties properties = category.findStructureProperties(StructureFeature.BASTION_REMNANT); 23 | if (properties != null && properties.getChunkPos() != null) { 24 | BlockPos pos = properties.getChunkPos().getCenterBlockPos().add(type.pos); 25 | world.getServer().execute(() -> { 26 | world.setBlockState(pos, Blocks.AIR.getDefaultState()); 27 | world.setBlockState(pos.up(), Blocks.AIR.getDefaultState()); 28 | }); 29 | return pos; 30 | } 31 | } 32 | } else { 33 | ServerWorldProperties props = world.getServer().getSaveProperties().getMainWorldProperties(); 34 | BlockPos pos = new BlockPos(props.getSpawnX(), props.getSpawnY(), props.getSpawnZ()); 35 | pos = CustomPortalForcer.createPortal(pos, world).down(2); 36 | return pos; 37 | } 38 | } 39 | throw new NotInitializedException(); 40 | }; 41 | public static final PracticeCategory.ExecuteReturnTask BASTION_SPAWN_ANGLE = (category, random, world) -> { 42 | if (category.hasCustomValue("bastionType")) { 43 | PreferenceTypes.SpawnLocationType spawnLocation = CategoryPreferences.SPAWN_LOCATION.getValue(); 44 | if (spawnLocation.equals(PreferenceTypes.SpawnLocationType.STRUCTURE)) { 45 | PreferenceTypes.BastionType type = PreferenceTypes.BastionType.fromId((int) category.getCustomValue("bastionType")); 46 | if (type != null) { 47 | return new Vec2f(type.angle, 0.0F); 48 | } 49 | } else { 50 | return new Vec2f(random.nextFloat(360.0F) - 180.0F, 0.0F); 51 | } 52 | } 53 | throw new NotInitializedException(); 54 | }; 55 | } 56 | -------------------------------------------------------------------------------- /src/main/java/me/falu/peepopractice/core/category/properties/BaseProperties.java: -------------------------------------------------------------------------------- 1 | package me.falu.peepopractice.core.category.properties; 2 | 3 | import lombok.Getter; 4 | import me.falu.peepopractice.core.category.PracticeCategory; 5 | 6 | @Getter 7 | public class BaseProperties { 8 | private PracticeCategory category; 9 | 10 | public BaseProperties setCategory(PracticeCategory category) { 11 | if (this.category == null) { 12 | this.category = category; 13 | } 14 | return this; 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/main/java/me/falu/peepopractice/core/category/properties/PlayerProperties.java: -------------------------------------------------------------------------------- 1 | package me.falu.peepopractice.core.category.properties; 2 | 3 | import com.google.common.collect.Lists; 4 | import lombok.Getter; 5 | import me.falu.peepopractice.core.category.PracticeCategory; 6 | import me.falu.peepopractice.core.exception.NotInitializedException; 7 | import net.minecraft.entity.Entity; 8 | import net.minecraft.entity.EntityType; 9 | import net.minecraft.entity.effect.StatusEffect; 10 | import net.minecraft.server.world.ServerWorld; 11 | import net.minecraft.util.math.BlockPos; 12 | import net.minecraft.util.math.Vec2f; 13 | 14 | import java.util.ArrayList; 15 | import java.util.List; 16 | import java.util.Random; 17 | 18 | @SuppressWarnings("UnusedDeclaration") 19 | public class PlayerProperties extends BaseProperties { 20 | @Getter private final List commands = new ArrayList<>(); 21 | @Getter private final List potionEffects = new ArrayList<>(); 22 | private BlockPos spawnPos; 23 | private PracticeCategory.ExecuteReturnTask spawnPosTask; 24 | private Vec2f spawnAngle; 25 | private PracticeCategory.ExecuteReturnTask spawnAngleTask; 26 | @Getter private EntityType vehicle; 27 | 28 | public BlockPos getSpawnPos() { 29 | return this.spawnPos != null ? this.spawnPos : new BlockPos(0, 62, 0); 30 | } 31 | 32 | public PlayerProperties setSpawnPos(BlockPos spawnPos) { 33 | this.spawnPos = spawnPos; 34 | return this; 35 | } 36 | 37 | public PlayerProperties setSpawnPos(PracticeCategory.ExecuteReturnTask spawnPosTask) { 38 | this.spawnPosTask = spawnPosTask; 39 | return this; 40 | } 41 | 42 | public boolean hasSpawnPos() { 43 | return this.spawnPos != null; 44 | } 45 | 46 | public Vec2f getSpawnAngle() { 47 | return this.spawnAngle != null ? this.spawnAngle : new Vec2f(0.0F, 0.0F); 48 | } 49 | 50 | public PlayerProperties setSpawnAngle(PracticeCategory.ExecuteReturnTask spawnAngleTask) { 51 | this.spawnAngleTask = spawnAngleTask; 52 | return this; 53 | } 54 | 55 | public boolean hasSpawnAngle() { 56 | return this.spawnAngle != null; 57 | } 58 | 59 | public PlayerProperties setSpawnAngle(float yaw, float pitch) { 60 | this.spawnAngle = new Vec2f(yaw, pitch); 61 | return this; 62 | } 63 | 64 | public PlayerProperties setVehicle(EntityType vehicle) { 65 | this.vehicle = vehicle; 66 | return this; 67 | } 68 | 69 | public boolean hasVehicle() { 70 | return this.vehicle != null; 71 | } 72 | 73 | public PlayerProperties runCommands(String[] commands) { 74 | this.commands.addAll(Lists.newArrayList(commands)); 75 | return this; 76 | } 77 | 78 | public PlayerProperties runCommand(String command) { 79 | this.commands.add(command); 80 | return this; 81 | } 82 | 83 | public PlayerProperties addPotionEffect(PotionEffect potionEffect) { 84 | this.potionEffects.add(potionEffect); 85 | return this; 86 | } 87 | 88 | public void reset(Random random, ServerWorld world) throws NotInitializedException { 89 | if (this.spawnPosTask != null) { 90 | this.setSpawnPos(this.spawnPosTask.execute(this.getCategory(), random, world)); 91 | } 92 | if (this.spawnAngleTask != null) { 93 | Vec2f vec = this.spawnAngleTask.execute(this.getCategory(), random, world); 94 | if (vec != null) { 95 | this.setSpawnAngle(vec.x, vec.y); 96 | } 97 | } 98 | } 99 | 100 | @Getter 101 | public static class PotionEffect { 102 | private StatusEffect effect; 103 | private int amplifier = 0; 104 | private int duration = Integer.MAX_VALUE; 105 | private PracticeCategory.ExecuteReturnTask condition; 106 | 107 | public PotionEffect setEffect(StatusEffect effect) { 108 | this.effect = effect; 109 | return this; 110 | } 111 | 112 | public boolean hasEffect() { 113 | return this.effect != null; 114 | } 115 | 116 | public PotionEffect setAmplifier(int amplifier) { 117 | this.amplifier = amplifier; 118 | return this; 119 | } 120 | 121 | public PotionEffect setDuration(int duration) { 122 | this.duration = duration; 123 | return this; 124 | } 125 | 126 | public PotionEffect setCondition(PracticeCategory.ExecuteReturnTask condition) { 127 | this.condition = condition; 128 | return this; 129 | } 130 | 131 | public boolean hasCondition() { 132 | return this.condition != null; 133 | } 134 | } 135 | } 136 | -------------------------------------------------------------------------------- /src/main/java/me/falu/peepopractice/core/category/properties/StructureProperties.java: -------------------------------------------------------------------------------- 1 | package me.falu.peepopractice.core.category.properties; 2 | 3 | import lombok.Getter; 4 | import me.falu.peepopractice.core.category.PracticeCategory; 5 | import me.falu.peepopractice.core.exception.NotInitializedException; 6 | import net.minecraft.server.world.ServerWorld; 7 | import net.minecraft.util.BlockRotation; 8 | import net.minecraft.util.math.ChunkPos; 9 | import net.minecraft.util.math.Direction; 10 | import net.minecraft.world.gen.feature.ConfiguredStructureFeature; 11 | import net.minecraft.world.gen.feature.StructureFeature; 12 | 13 | import java.util.Random; 14 | 15 | @SuppressWarnings("UnusedDeclaration") 16 | public class StructureProperties extends BaseProperties { 17 | @Getter private ConfiguredStructureFeature structure; 18 | @Getter private ChunkPos chunkPos; 19 | private PracticeCategory.ExecuteReturnTask chunkPosTask; 20 | @Getter private Direction orientation; 21 | @Getter private BlockRotation rotation; 22 | @Getter private Integer structureTopY; 23 | private PracticeCategory.ExecuteReturnTask structureTopYTask; 24 | @Getter private boolean generatable = false; 25 | private PracticeCategory.ExecuteReturnTask generatableTask; 26 | private boolean generated = false; 27 | 28 | public StructureProperties setStructure(ConfiguredStructureFeature structure) { 29 | this.structure = structure; 30 | return this; 31 | } 32 | 33 | public boolean isSameStructure(ConfiguredStructureFeature feature) { 34 | return this.hasStructure() && feature.structureFeature.getName().equals(this.structure.structureFeature.getName()); 35 | } 36 | 37 | public boolean isSameStructure(StructureFeature feature) { 38 | return this.hasStructure() && feature.getName().equals(this.structure.structureFeature.getName()); 39 | } 40 | 41 | public boolean hasStructure() { 42 | return this.structure != null; 43 | } 44 | 45 | public StructureProperties setChunkPos(ChunkPos structureChunkPos) { 46 | this.chunkPos = structureChunkPos; 47 | return this; 48 | } 49 | 50 | public StructureProperties setChunkPos(PracticeCategory.ExecuteReturnTask task) { 51 | this.chunkPosTask = task; 52 | return this; 53 | } 54 | 55 | public boolean hasChunkPos() { 56 | return this.chunkPos != null; 57 | } 58 | 59 | public StructureProperties setOrientation(Direction orientation) { 60 | this.orientation = orientation; 61 | return this; 62 | } 63 | 64 | public boolean hasOrientation() { 65 | return this.orientation != null; 66 | } 67 | 68 | public StructureProperties setRotation(BlockRotation rotation) { 69 | this.rotation = rotation; 70 | return this; 71 | } 72 | 73 | public boolean hasRotation() { 74 | return this.rotation != null; 75 | } 76 | 77 | public StructureProperties setStructureTopY(int structureTopY) { 78 | this.structureTopY = structureTopY; 79 | return this; 80 | } 81 | 82 | public StructureProperties setStructureTopY(PracticeCategory.ExecuteReturnTask task) { 83 | this.structureTopYTask = task; 84 | return this; 85 | } 86 | 87 | public boolean hasStructureTopY() { 88 | return this.structureTopY != null; 89 | } 90 | 91 | public StructureProperties setGeneratable(boolean generatable) { 92 | this.generatable = generatable; 93 | return this; 94 | } 95 | 96 | public StructureProperties setGeneratable(PracticeCategory.ExecuteReturnTask task) { 97 | this.generatableTask = task; 98 | return this; 99 | } 100 | 101 | public boolean hasGenerated() { 102 | return this.generated; 103 | } 104 | 105 | public void setGenerated() { 106 | this.generated = true; 107 | } 108 | 109 | public void reset(Random random, ServerWorld world) throws NotInitializedException { 110 | this.generated = false; 111 | if (this.chunkPosTask != null) { 112 | this.setChunkPos(this.chunkPosTask.execute(this.getCategory(), random, world)); 113 | } 114 | if (this.structureTopYTask != null) { 115 | Integer value = this.structureTopYTask.execute(this.getCategory(), random, world); 116 | if (value != null) { 117 | this.setStructureTopY(value); 118 | } 119 | } 120 | if (this.generatableTask != null) { 121 | Boolean value = this.generatableTask.execute(this.getCategory(), random, world); 122 | if (value != null) { 123 | this.setGeneratable(value); 124 | } 125 | } 126 | } 127 | } 128 | -------------------------------------------------------------------------------- /src/main/java/me/falu/peepopractice/core/category/properties/WorldProperties.java: -------------------------------------------------------------------------------- 1 | package me.falu.peepopractice.core.category.properties; 2 | 3 | import lombok.Getter; 4 | import me.falu.peepopractice.core.category.PracticeCategory; 5 | import me.falu.peepopractice.core.exception.NotInitializedException; 6 | import net.minecraft.server.world.ServerWorld; 7 | import net.minecraft.util.registry.RegistryKey; 8 | import net.minecraft.world.Difficulty; 9 | import net.minecraft.world.World; 10 | import net.minecraft.world.biome.Biome; 11 | import org.jetbrains.annotations.Nullable; 12 | 13 | import java.util.ArrayList; 14 | import java.util.List; 15 | import java.util.Random; 16 | 17 | @Getter 18 | @SuppressWarnings("UnusedDeclaration") 19 | public class WorldProperties extends BaseProperties { 20 | private final List proBiomes = new ArrayList<>(); 21 | private final List antiBiomes = new ArrayList<>(); 22 | private final List dataPacks = new ArrayList<>(); 23 | private RegistryKey worldRegistryKey = World.OVERWORLD; 24 | private PracticeCategory.ExecuteReturnTask> worldRegistryKeyTask; 25 | private boolean spawnChunksDisabled = false; 26 | private String seedListPath; 27 | private Difficulty startDifficulty = Difficulty.EASY; 28 | 29 | public WorldProperties setWorldRegistryKey(RegistryKey worldRegistryKey) { 30 | this.worldRegistryKey = worldRegistryKey; 31 | return this; 32 | } 33 | 34 | public WorldProperties setWorldRegistryKey(PracticeCategory.ExecuteReturnTask> task) { 35 | this.worldRegistryKeyTask = task; 36 | return this; 37 | } 38 | 39 | public boolean hasWorldRegistryKey() { 40 | return this.worldRegistryKey != null; 41 | } 42 | 43 | public WorldProperties setSpawnChunksDisabled(boolean spawnChunksDisabled) { 44 | this.spawnChunksDisabled = spawnChunksDisabled; 45 | return this; 46 | } 47 | 48 | public WorldProperties addAntiBiome(BiomeModification antiBiome) { 49 | this.antiBiomes.add(antiBiome); 50 | return this; 51 | } 52 | 53 | public WorldProperties addProBiome(BiomeModification proBiome) { 54 | this.proBiomes.add(proBiome); 55 | return this; 56 | } 57 | 58 | public boolean hasSeedListPath() { 59 | return this.seedListPath != null; 60 | } 61 | 62 | public WorldProperties setSeedList(String seedListPath) { 63 | this.seedListPath = seedListPath; 64 | return this; 65 | } 66 | 67 | public WorldProperties useDatapack(String dataPack) { 68 | this.dataPacks.add(dataPack); 69 | return this; 70 | } 71 | 72 | public WorldProperties setStartDifficulty(Difficulty startDifficulty) { 73 | this.startDifficulty = startDifficulty; 74 | return this; 75 | } 76 | 77 | public void reset(Random random, ServerWorld world) throws NotInitializedException { 78 | if (this.worldRegistryKeyTask != null) { 79 | this.worldRegistryKey = this.worldRegistryKeyTask.execute(this.getCategory(), random, world); 80 | } 81 | } 82 | 83 | public interface ConditionTask { 84 | boolean execute(); 85 | } 86 | 87 | public static class Range { 88 | private final List> validDimensions = new ArrayList<>(); 89 | @Getter private Integer range; 90 | private ConditionTask condition = () -> true; 91 | 92 | public Range setRange(Integer range) { 93 | this.range = range; 94 | return this; 95 | } 96 | 97 | public boolean shouldPlace() { 98 | return this.condition.execute(); 99 | } 100 | 101 | public Range setCondition(ConditionTask condition) { 102 | this.condition = condition; 103 | return this; 104 | } 105 | 106 | public boolean isValidDimension(RegistryKey dimension) { 107 | return this.validDimensions.contains(dimension); 108 | } 109 | 110 | public Range addValidDimension(RegistryKey dimension) { 111 | this.validDimensions.add(dimension); 112 | return this; 113 | } 114 | 115 | public Range addValidDimensions(List> dimensions) { 116 | this.validDimensions.addAll(dimensions); 117 | return this; 118 | } 119 | } 120 | 121 | @Getter 122 | public static class BiomeModification { 123 | private Biome biome; 124 | private Range range; 125 | private Biome replacement; 126 | 127 | public BiomeModification setBiome(Biome biome) { 128 | this.biome = biome; 129 | return this; 130 | } 131 | 132 | public boolean hasBiome() { 133 | return this.biome != null; 134 | } 135 | 136 | public BiomeModification setRange(@Nullable Range range) { 137 | this.range = range == null ? new Range().setRange(null) : range; 138 | return this; 139 | } 140 | 141 | public boolean isInfinite() { 142 | return this.range.getRange() == null; 143 | } 144 | 145 | public BiomeModification setReplacement(Biome replacement) { 146 | this.replacement = replacement; 147 | return this; 148 | } 149 | 150 | public boolean hasReplacement() { 151 | return this.replacement != null; 152 | } 153 | } 154 | } 155 | -------------------------------------------------------------------------------- /src/main/java/me/falu/peepopractice/core/category/properties/event/ChangeDimensionSplitEvent.java: -------------------------------------------------------------------------------- 1 | package me.falu.peepopractice.core.category.properties.event; 2 | 3 | import lombok.Getter; 4 | import net.minecraft.util.registry.RegistryKey; 5 | import net.minecraft.world.World; 6 | 7 | @Getter 8 | public class ChangeDimensionSplitEvent extends SplitEvent { 9 | private RegistryKey toDimension; 10 | private RegistryKey fromDimension; 11 | 12 | public ChangeDimensionSplitEvent setToDimension(RegistryKey toDimension) { 13 | this.toDimension = toDimension; 14 | return this; 15 | } 16 | 17 | public boolean hasToDimension() { 18 | return this.toDimension != null; 19 | } 20 | 21 | public ChangeDimensionSplitEvent setFromDimension(RegistryKey fromDimension) { 22 | this.fromDimension = fromDimension; 23 | return this; 24 | } 25 | 26 | public boolean hasFromDimension() { 27 | return this.fromDimension != null; 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/main/java/me/falu/peepopractice/core/category/properties/event/EnterVehicleSplitEvent.java: -------------------------------------------------------------------------------- 1 | package me.falu.peepopractice.core.category.properties.event; 2 | 3 | import lombok.Getter; 4 | import net.minecraft.entity.EntityType; 5 | 6 | public class EnterVehicleSplitEvent extends SplitEvent { 7 | @Getter private EntityType vehicle; 8 | private boolean keepItem; 9 | 10 | public EnterVehicleSplitEvent setVehicle(EntityType vehicle) { 11 | this.vehicle = vehicle; 12 | return this; 13 | } 14 | 15 | public boolean hasVehicle() { 16 | return this.vehicle != null; 17 | } 18 | 19 | public boolean shouldKeepItem() { 20 | return this.keepItem; 21 | } 22 | 23 | public EnterVehicleSplitEvent setKeepItem(boolean keepItem) { 24 | this.keepItem = keepItem; 25 | return this; 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/main/java/me/falu/peepopractice/core/category/properties/event/GetAdvancementSplitEvent.java: -------------------------------------------------------------------------------- 1 | package me.falu.peepopractice.core.category.properties.event; 2 | 3 | import lombok.Getter; 4 | import net.minecraft.util.Identifier; 5 | 6 | @Getter 7 | public class GetAdvancementSplitEvent extends SplitEvent { 8 | private Identifier advancement; 9 | 10 | public GetAdvancementSplitEvent setAdvancement(Identifier advancement) { 11 | this.advancement = advancement; 12 | return this; 13 | } 14 | 15 | public boolean allAdvancements() { 16 | return this.advancement == null; 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/main/java/me/falu/peepopractice/core/category/properties/event/InteractLootChestSplitEvent.java: -------------------------------------------------------------------------------- 1 | package me.falu.peepopractice.core.category.properties.event; 2 | 3 | import lombok.Getter; 4 | import net.minecraft.util.Identifier; 5 | 6 | @Getter 7 | public class InteractLootChestSplitEvent extends SplitEvent { 8 | private Identifier lootTable; 9 | private boolean onClose = false; 10 | 11 | public InteractLootChestSplitEvent setLootTable(Identifier lootTable) { 12 | this.lootTable = lootTable; 13 | return this; 14 | } 15 | 16 | public boolean hasLootTable() { 17 | return this.lootTable != null; 18 | } 19 | 20 | public InteractLootChestSplitEvent setOnClose(boolean onClose) { 21 | this.onClose = onClose; 22 | return this; 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/main/java/me/falu/peepopractice/core/category/properties/event/ThrowEntitySplitEvent.java: -------------------------------------------------------------------------------- 1 | package me.falu.peepopractice.core.category.properties.event; 2 | 3 | import lombok.Getter; 4 | import net.minecraft.item.Item; 5 | 6 | @Getter 7 | public class ThrowEntitySplitEvent extends SplitEvent { 8 | private Item item; 9 | 10 | public ThrowEntitySplitEvent setItem(Item item) { 11 | this.item = item; 12 | return this; 13 | } 14 | 15 | public boolean hasItem() { 16 | return this.item != null; 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/main/java/me/falu/peepopractice/core/category/utils/KeyBindingUtils.java: -------------------------------------------------------------------------------- 1 | package me.falu.peepopractice.core.category.utils; 2 | 3 | import com.google.common.collect.Lists; 4 | import net.minecraft.client.options.KeyBinding; 5 | import net.minecraft.text.Text; 6 | import net.minecraft.text.TranslatableText; 7 | import net.minecraft.util.Language; 8 | 9 | import java.util.List; 10 | import java.util.Optional; 11 | 12 | public class KeyBindingUtils { 13 | public static final List KEY_BINDINGS = Lists.newArrayList(); 14 | 15 | public static Text getTranslation(String path, String text) { 16 | return (Language.getInstance().get(path).equals(path) ? new TranslatableText(text) : new TranslatableText(path)); 17 | } 18 | 19 | private static boolean noCategory(String key) { 20 | return !KeyBinding.categoryOrderMap.containsKey(key); 21 | } 22 | 23 | public static void addCategory(String key) { 24 | if (noCategory(key)) { 25 | Optional largest = KeyBinding.categoryOrderMap.values().stream().max(Integer::compare); 26 | int largestInt = largest.orElse(0); 27 | KeyBinding.categoryOrderMap.put(key, largestInt + 1); 28 | } 29 | } 30 | 31 | public static KeyBinding registerKeyBinding(KeyBinding keyBinding) { 32 | if (noCategory(keyBinding.getCategory())) { 33 | addCategory(keyBinding.getCategory()); 34 | } 35 | 36 | KEY_BINDINGS.add(keyBinding); 37 | return keyBinding; 38 | } 39 | 40 | public static KeyBinding[] process(KeyBinding[] all) { 41 | List keyBindings = Lists.newArrayList(all); 42 | keyBindings.removeAll(KEY_BINDINGS); 43 | keyBindings.addAll(KEY_BINDINGS); 44 | return keyBindings.toArray(new KeyBinding[0]); 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /src/main/java/me/falu/peepopractice/core/category/utils/PracticeCategoryUtils.java: -------------------------------------------------------------------------------- 1 | package me.falu.peepopractice.core.category.utils; 2 | 3 | import net.minecraft.block.BlockState; 4 | import net.minecraft.client.MinecraftClient; 5 | import net.minecraft.client.gui.screen.SaveLevelScreen; 6 | import net.minecraft.client.gui.screen.TitleScreen; 7 | import net.minecraft.client.gui.screen.multiplayer.MultiplayerScreen; 8 | import net.minecraft.realms.RealmsBridge; 9 | import net.minecraft.server.world.ServerWorld; 10 | import net.minecraft.text.TranslatableText; 11 | import net.minecraft.util.math.BlockPos; 12 | import net.minecraft.world.Heightmap; 13 | import net.minecraft.world.biome.Biome; 14 | import net.minecraft.world.chunk.WorldChunk; 15 | 16 | import java.util.Random; 17 | 18 | public class PracticeCategoryUtils { 19 | public static void quit(boolean close) { 20 | MinecraftClient client = MinecraftClient.getInstance(); 21 | 22 | boolean bl = client.isInSingleplayer(); 23 | boolean bl2 = client.isConnectedToRealms(); 24 | 25 | if (client.world != null) { 26 | client.world.disconnect(); 27 | } 28 | 29 | if (bl) { 30 | client.disconnect(new SaveLevelScreen(new TranslatableText("menu.savingLevel"))); 31 | } else { 32 | client.disconnect(); 33 | } 34 | 35 | if (bl && close) { 36 | client.openScreen(new TitleScreen()); 37 | } else if (bl2) { 38 | RealmsBridge realmsBridge = new RealmsBridge(); 39 | realmsBridge.switchToRealms(new TitleScreen()); 40 | } else { 41 | client.openScreen(new MultiplayerScreen(new TitleScreen())); 42 | } 43 | } 44 | 45 | public static int findTopPos(ServerWorld world, BlockPos blockPos) { 46 | int x = blockPos.getX(); 47 | int z = blockPos.getZ(); 48 | int i; 49 | BlockPos.Mutable mutable = new BlockPos.Mutable(x, 0, z); 50 | Biome biome = world.getBiome(mutable); 51 | boolean bl = world.getDimension().hasCeiling(); 52 | BlockState blockState = biome.getSurfaceConfig().getTopMaterial(); 53 | WorldChunk worldChunk = world.getChunk(x >> 4, z >> 4); 54 | i = bl ? world.getChunkManager().getChunkGenerator().getSpawnHeight() : worldChunk.sampleHeightmap(Heightmap.Type.MOTION_BLOCKING, x & 0xF, z & 0xF); 55 | if (bl) { 56 | for (int k = i + 1; k >= 0; --k) { 57 | mutable.set(x, k, z); 58 | BlockState blockState2 = world.getBlockState(mutable); 59 | if (!blockState2.getFluidState().isEmpty()) { 60 | break; 61 | } 62 | if (!blockState2.equals(blockState)) { 63 | continue; 64 | } 65 | return mutable.up().toImmutable().getY(); 66 | } 67 | } else { 68 | mutable.setY(i); 69 | } 70 | return mutable.getY(); 71 | } 72 | 73 | public static BlockPos getRandomBlockInRadius(int radius, BlockPos blockPos, Random random) { 74 | return getRandomBlockInRadius(radius, 0, blockPos, random); 75 | } 76 | 77 | public static BlockPos getRandomBlockInRadius(int radius, int min, BlockPos blockPos, Random random) { 78 | BlockPos newPos; 79 | do { 80 | double ang = random.nextDouble() * 2 * Math.PI; 81 | double hyp = Math.sqrt(random.nextDouble()) * radius; 82 | double adj = Math.cos(ang) * hyp; 83 | double opp = Math.sin(ang) * hyp; 84 | newPos = new BlockPos(blockPos.getX() + adj, blockPos.getY(), blockPos.getZ() + opp); 85 | } while (newPos.isWithinDistance(blockPos, min)); 86 | return newPos; 87 | } 88 | } 89 | -------------------------------------------------------------------------------- /src/main/java/me/falu/peepopractice/core/exception/InvalidCategorySyntaxException.java: -------------------------------------------------------------------------------- 1 | package me.falu.peepopractice.core.exception; 2 | 3 | public class InvalidCategorySyntaxException extends Exception { 4 | public InvalidCategorySyntaxException() { 5 | super(); 6 | } 7 | 8 | public InvalidCategorySyntaxException(String categoryId) { 9 | super("Invalid category: " + categoryId); 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /src/main/java/me/falu/peepopractice/core/exception/NotInitializedException.java: -------------------------------------------------------------------------------- 1 | package me.falu.peepopractice.core.exception; 2 | 3 | public class NotInitializedException extends Exception { } 4 | -------------------------------------------------------------------------------- /src/main/java/me/falu/peepopractice/core/global/GlobalConfig.java: -------------------------------------------------------------------------------- 1 | package me.falu.peepopractice.core.global; 2 | 3 | import com.google.gson.JsonObject; 4 | import com.google.gson.JsonPrimitive; 5 | import me.falu.peepopractice.core.writer.PracticeWriter; 6 | 7 | public class GlobalConfig { 8 | public static double getDoubleValue(String key, double def) { 9 | JsonObject object = PracticeWriter.GLOBAL_CONFIG.get(); 10 | if (!object.has(key)) { 11 | PracticeWriter.GLOBAL_CONFIG.put(key, new JsonPrimitive(def)); 12 | return def; 13 | } 14 | return object.get(key).getAsDouble(); 15 | } 16 | 17 | public static boolean getBoolValue(String key, boolean def) { 18 | JsonObject object = PracticeWriter.GLOBAL_CONFIG.get(); 19 | if (!object.has(key)) { 20 | PracticeWriter.GLOBAL_CONFIG.put(key, new JsonPrimitive(def)); 21 | return def; 22 | } 23 | return object.get(key).getAsBoolean(); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/main/java/me/falu/peepopractice/core/global/GlobalOptions.java: -------------------------------------------------------------------------------- 1 | package me.falu.peepopractice.core.global; 2 | 3 | import com.google.gson.JsonPrimitive; 4 | import me.falu.peepopractice.core.writer.PracticeWriter; 5 | import net.minecraft.client.MinecraftClient; 6 | import net.minecraft.client.options.BooleanOption; 7 | import net.minecraft.client.options.DoubleOption; 8 | 9 | public class GlobalOptions { 10 | public static final DoubleOption BACKGROUND_RED = new DoubleOption("BG Red", 0.0D, 255.0D, 1.0F, gameOptions -> GlobalConfig.getDoubleValue("background_red", 68.0D), (gameOptions, aDouble) -> PracticeWriter.GLOBAL_CONFIG.put("background_red", new JsonPrimitive(aDouble)), (gameOptions, doubleOption) -> { 11 | double value = doubleOption.get(gameOptions); 12 | return doubleOption.getDisplayPrefix().append(Integer.toString((int) value)); 13 | }); 14 | public static final DoubleOption BACKGROUND_GREEN = new DoubleOption("BG Green", 0.0D, 255.0D, 1.0F, gameOptions -> GlobalConfig.getDoubleValue("background_green", 112.0D), (gameOptions, aDouble) -> PracticeWriter.GLOBAL_CONFIG.put("background_green", new JsonPrimitive(aDouble)), (gameOptions, doubleOption) -> { 15 | double value = doubleOption.get(gameOptions); 16 | return doubleOption.getDisplayPrefix().append(Integer.toString((int) value)); 17 | }); 18 | public static final DoubleOption BACKGROUND_BLUE = new DoubleOption("BG Blue", 0.0D, 255.0D, 1.0F, gameOptions -> GlobalConfig.getDoubleValue("background_blue", 106.0D), (gameOptions, aDouble) -> PracticeWriter.GLOBAL_CONFIG.put("background_blue", new JsonPrimitive(aDouble)), (gameOptions, doubleOption) -> { 19 | double value = doubleOption.get(gameOptions); 20 | return doubleOption.getDisplayPrefix().append(Integer.toString((int) value)); 21 | }); 22 | public static final DoubleOption BACKGROUND_RED_2 = new DoubleOption("BG Red 2", 0.0D, 255.0D, 1.0F, gameOptions -> GlobalConfig.getDoubleValue("background_red_2", 51.0D), (gameOptions, aDouble) -> PracticeWriter.GLOBAL_CONFIG.put("background_red_2", new JsonPrimitive(aDouble)), (gameOptions, doubleOption) -> { 23 | double value = doubleOption.get(gameOptions); 24 | return doubleOption.getDisplayPrefix().append(Integer.toString((int) value)); 25 | }); 26 | public static final DoubleOption BACKGROUND_GREEN_2 = new DoubleOption("BG Green 2", 0.0D, 255.0D, 1.0F, gameOptions -> GlobalConfig.getDoubleValue("background_green_2", 73.0D), (gameOptions, aDouble) -> PracticeWriter.GLOBAL_CONFIG.put("background_green_2", new JsonPrimitive(aDouble)), (gameOptions, doubleOption) -> { 27 | double value = doubleOption.get(gameOptions); 28 | return doubleOption.getDisplayPrefix().append(Integer.toString((int) value)); 29 | }); 30 | public static final DoubleOption BACKGROUND_BLUE_2 = new DoubleOption("BG Blue 2", 0.0D, 255.0D, 1.0F, gameOptions -> GlobalConfig.getDoubleValue("background_blue_2", 70.0D), (gameOptions, aDouble) -> PracticeWriter.GLOBAL_CONFIG.put("background_blue_2", new JsonPrimitive(aDouble)), (gameOptions, doubleOption) -> { 31 | double value = doubleOption.get(gameOptions); 32 | return doubleOption.getDisplayPrefix().append(Integer.toString((int) value)); 33 | }); 34 | public static final BooleanOption SAME_INVENTORY = new BooleanOption("Use Same Inventory", gameOptions -> GlobalConfig.getBoolValue("same_inventory", true), (gameOptions, aBoolean) -> PracticeWriter.GLOBAL_CONFIG.put("same_inventory", new JsonPrimitive(aBoolean))); 35 | public static final BooleanOption CHANGE_WINDOW_TITLE = new BooleanOption("Change Window Title", gameOptions -> GlobalConfig.getBoolValue("change_window_title", true), (gameOptions, aBoolean) -> { 36 | PracticeWriter.GLOBAL_CONFIG.put("change_window_title", new JsonPrimitive(aBoolean)); 37 | MinecraftClient.getInstance().updateWindowTitle(); 38 | }); 39 | public static final BooleanOption GIVE_SATURATION = new BooleanOption("Give Saturation", gameOptions -> GlobalConfig.getBoolValue("give_saturation", true), (gameOptions, aBoolean) -> PracticeWriter.GLOBAL_CONFIG.put("give_saturation", new JsonPrimitive(aBoolean))); 40 | } 41 | -------------------------------------------------------------------------------- /src/main/java/me/falu/peepopractice/core/item/RandomToolItem.java: -------------------------------------------------------------------------------- 1 | package me.falu.peepopractice.core.item; 2 | 3 | import net.minecraft.item.*; 4 | import net.minecraft.util.Identifier; 5 | import net.minecraft.util.registry.Registry; 6 | 7 | import java.util.Locale; 8 | import java.util.Random; 9 | 10 | public class RandomToolItem extends ToolItem { 11 | private final ToolType toolType; 12 | private final String[] materials; 13 | 14 | public RandomToolItem(ToolType toolType, String... materials) { 15 | super(ToolMaterials.DIAMOND, new Settings().group(ItemGroup.TOOLS)); 16 | this.toolType = toolType; 17 | this.materials = materials; 18 | } 19 | 20 | public ItemStack convert() { 21 | String material = this.materials[new Random().nextInt(this.materials.length)]; 22 | Item item = Registry.ITEM.get(new Identifier(material + "_" + this.toolType.getRegistrySuffix())); 23 | return new ItemStack(item); 24 | } 25 | 26 | @Override 27 | public String getTranslationKey() { 28 | return "Random " + this.toolType.getDisplayName(); 29 | } 30 | 31 | public enum ToolType { 32 | PICKAXE, 33 | AXE, 34 | SHOVEL, 35 | SWORD, 36 | HOE; 37 | 38 | public String getRegistrySuffix() { 39 | return this.name().toLowerCase(Locale.ROOT); 40 | } 41 | 42 | public String getDisplayName() { 43 | return this.name().toLowerCase(Locale.ROOT).replaceFirst(Character.toString(Character.toLowerCase(this.name().charAt(0))), Character.toString(Character.toUpperCase(this.name().charAt(0)))); 44 | } 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /src/main/java/me/falu/peepopractice/core/loot/PiglinBarterState.java: -------------------------------------------------------------------------------- 1 | package me.falu.peepopractice.core.loot; 2 | 3 | import net.minecraft.entity.mob.PiglinBrain; 4 | import net.minecraft.entity.mob.PiglinEntity; 5 | import net.minecraft.item.ItemStack; 6 | import net.minecraft.item.Items; 7 | import net.minecraft.nbt.CompoundTag; 8 | import net.minecraft.world.PersistentState; 9 | 10 | import java.util.List; 11 | import java.util.Random; 12 | 13 | public class PiglinBarterState extends PersistentState { 14 | private int currentGuarantee; 15 | private int obsidianCount; 16 | private int pearlCount; 17 | private int pearl; 18 | private int obsidian; 19 | 20 | public PiglinBarterState() { 21 | super("piglin_barters"); 22 | this.currentGuarantee = 0; 23 | this.pearlCount = 3; 24 | this.pearl = 0; 25 | this.obsidianCount = 6; 26 | this.obsidian = 0; 27 | } 28 | 29 | public ItemStack guaranteeItem(PiglinEntity piglin, ItemStack itemStack, Random random) { 30 | ItemStack newItem = this.guaranteeItem2(piglin, itemStack, random); 31 | this.currentGuarantee++; 32 | return newItem; 33 | } 34 | 35 | private ItemStack guaranteeItem2(PiglinEntity piglin, ItemStack itemStack, Random random) { 36 | if (this.currentGuarantee == 72) { 37 | this.currentGuarantee = 0; 38 | this.pearl = 0; 39 | this.obsidian = 0; 40 | this.pearlCount = 3; 41 | this.obsidianCount = 6; 42 | } 43 | if (this.pearl == 0 && this.pearlCount > 0) { 44 | this.rollPearlIndex(random); 45 | } 46 | if (this.obsidian == 0 && this.obsidianCount > 0) { 47 | this.rollObsidianIndex(random); 48 | } 49 | if (itemStack.getItem() == Items.ENDER_PEARL) { 50 | if (this.pearlCount < 0) { 51 | List newBarterItem = PiglinBrain.getBarteredItem(piglin); 52 | return this.guaranteeItem2(piglin, newBarterItem.get(0), random); 53 | } 54 | this.rollPearlIndex(random); 55 | return itemStack; 56 | } 57 | if (itemStack.getItem() == Items.OBSIDIAN) { 58 | this.rollObsidianIndex(random); 59 | return itemStack; 60 | } 61 | if (this.pearl <= this.currentGuarantee && this.pearlCount >= 0) { 62 | this.rollPearlIndex(random); 63 | return new ItemStack(Items.ENDER_PEARL, random.nextInt(5) + 4); 64 | } 65 | if (this.obsidian <= this.currentGuarantee && this.obsidianCount >= 0) { 66 | this.rollObsidianIndex(random); 67 | return new ItemStack(Items.OBSIDIAN); 68 | } 69 | return itemStack; 70 | } 71 | 72 | public void rollPearlIndex(Random random) { 73 | this.pearl = random.nextInt(Math.max(1, 72 - this.currentGuarantee - this.pearlCount)) + this.currentGuarantee; 74 | this.pearlCount--; 75 | } 76 | 77 | public void rollObsidianIndex(Random random) { 78 | this.obsidian = random.nextInt(Math.max(1, 72 - this.currentGuarantee - this.obsidianCount)) + this.currentGuarantee; 79 | this.obsidianCount--; 80 | } 81 | 82 | public void fromTag(CompoundTag tag) { 83 | this.obsidian = tag.getInt("obsidian"); 84 | this.obsidianCount = tag.getInt("obsidianCount"); 85 | this.pearl = tag.getInt("pearl"); 86 | this.pearlCount = tag.getInt("pearlCount"); 87 | this.currentGuarantee = tag.getInt("currentGuarantee"); 88 | } 89 | 90 | public CompoundTag toTag(CompoundTag tag) { 91 | tag.putInt("obsidian", this.obsidian); 92 | tag.putInt("obsidianCount", this.obsidianCount); 93 | tag.putInt("pearl", this.pearl); 94 | tag.putInt("pearlCount", this.pearlCount); 95 | tag.putInt("currentGuarantee", this.currentGuarantee); 96 | return tag; 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /src/main/java/me/falu/peepopractice/core/playerless/PlayerlessShulkerBoxScreenHandler.java: -------------------------------------------------------------------------------- 1 | package me.falu.peepopractice.core.playerless; 2 | 3 | import net.minecraft.inventory.Inventories; 4 | import net.minecraft.inventory.Inventory; 5 | import net.minecraft.inventory.SimpleInventory; 6 | import net.minecraft.item.ItemStack; 7 | import net.minecraft.nbt.CompoundTag; 8 | import net.minecraft.nbt.ListTag; 9 | import net.minecraft.screen.slot.ShulkerBoxSlot; 10 | import net.minecraft.screen.slot.Slot; 11 | import net.minecraft.util.collection.DefaultedList; 12 | 13 | public class PlayerlessShulkerBoxScreenHandler extends PlayerlessScreenHandler { 14 | private final Inventory inventory; 15 | 16 | public PlayerlessShulkerBoxScreenHandler(PlayerlessInventory playerInventory) { 17 | this(playerInventory, new SimpleInventory(27)); 18 | } 19 | 20 | private PlayerlessShulkerBoxScreenHandler(PlayerlessInventory playerInventory, Inventory inventory) { 21 | super(); 22 | checkSize(inventory, 27); 23 | this.inventory = inventory; 24 | for (int k = 0; k < 3; ++k) { 25 | for (int l = 0; l < 9; ++l) { 26 | this.addSlot(new ShulkerBoxSlot(inventory, l + k * 9, 8 + l * 18, 18 + k * 18)); 27 | } 28 | } 29 | for (int k = 0; k < 3; ++k) { 30 | for (int l = 0; l < 9; ++l) { 31 | this.addSlot(new Slot(playerInventory, l + k * 9 + 9, 8 + l * 18, 84 + k * 18)); 32 | } 33 | } 34 | for (int k = 0; k < 9; ++k) { 35 | this.addSlot(new Slot(playerInventory, k, 8 + k * 18, 142)); 36 | } 37 | } 38 | 39 | @SuppressWarnings("SameParameterValue") 40 | protected static void checkSize(Inventory inventory, int expectedSize) { 41 | int i = inventory.size(); 42 | if (i < expectedSize) { 43 | throw new IllegalArgumentException("Container size " + i + " is smaller than expected " + expectedSize); 44 | } 45 | } 46 | 47 | public ListTag getItemsTag() { 48 | DefaultedList list = DefaultedList.ofSize(this.inventory.size(), ItemStack.EMPTY); 49 | for (int i = 0; i < this.inventory.size(); i++) { 50 | list.set(i, this.inventory.getStack(i)); 51 | } 52 | CompoundTag tag = new CompoundTag(); 53 | Inventories.toTag(tag, list); 54 | return tag.getList("Items", 10); 55 | } 56 | 57 | public void initializeItems(ItemStack itemStack) { 58 | DefaultedList list = DefaultedList.ofSize(this.inventory.size(), ItemStack.EMPTY); 59 | CompoundTag tag = itemStack.getOrCreateSubTag("BlockEntityTag"); 60 | Inventories.fromTag(tag, list); 61 | for (int i = 0; i < list.size(); i++) { 62 | this.inventory.setStack(i, list.get(i)); 63 | } 64 | } 65 | 66 | public int size() { 67 | return this.inventory.size(); 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /src/main/java/me/falu/peepopractice/core/writer/DefaultFileWriter.java: -------------------------------------------------------------------------------- 1 | package me.falu.peepopractice.core.writer; 2 | 3 | import com.google.common.collect.Lists; 4 | import com.google.gson.JsonElement; 5 | import com.google.gson.JsonObject; 6 | import com.google.gson.JsonParser; 7 | import me.falu.peepopractice.PeepoPractice; 8 | import net.fabricmc.loader.api.FabricLoader; 9 | import org.apache.commons.io.FileUtils; 10 | import org.reflections.Reflections; 11 | import org.reflections.scanners.ResourcesScanner; 12 | 13 | import java.io.*; 14 | import java.nio.charset.StandardCharsets; 15 | import java.nio.file.Path; 16 | import java.util.List; 17 | import java.util.Map; 18 | import java.util.Set; 19 | import java.util.regex.Pattern; 20 | 21 | public class DefaultFileWriter { 22 | public static final Path FOLDER = FabricLoader.getInstance().getConfigDir().resolve(PeepoPractice.MOD_NAME); 23 | public static final DefaultFileWriter INSTANCE = new DefaultFileWriter(); 24 | 25 | @SuppressWarnings("ResultOfMethodCallIgnored") 26 | public void writeDefaultFiles() { 27 | try { 28 | FOLDER.toFile().mkdirs(); 29 | List resources = this.getResourceFiles(); 30 | for (String resource : resources) { 31 | File destination = FOLDER.resolve(resource.replace("writer/", "")).toFile(); 32 | InputStream stream = this.getClass().getClassLoader().getResourceAsStream(resource); 33 | if (stream != null) { 34 | JsonParser parser = new JsonParser(); 35 | JsonElement defaultElement = parser.parse(new InputStreamReader(stream, StandardCharsets.UTF_8)); 36 | if (!destination.exists() || destination.length() == 0) { 37 | destination.getParentFile().mkdirs(); 38 | FileUtils.writeStringToFile(destination, PracticeWriter.GSON.toJson(defaultElement), StandardCharsets.UTF_8); 39 | } else if (defaultElement.isJsonObject()) { 40 | JsonElement element = parser.parse(FileUtils.readFileToString(destination, StandardCharsets.UTF_8)); 41 | if (element.isJsonObject()) { 42 | JsonObject defaultObject = defaultElement.getAsJsonObject(); 43 | JsonObject object = element.getAsJsonObject(); 44 | boolean hasChanged = false; 45 | for (Map.Entry entry : defaultObject.entrySet()) { 46 | if (!object.has(entry.getKey())) { 47 | object.add(entry.getKey(), entry.getValue()); 48 | hasChanged = true; 49 | } 50 | } 51 | if (hasChanged) { 52 | destination.getParentFile().mkdirs(); 53 | FileUtils.writeStringToFile(destination, PracticeWriter.GSON.toJson(object), StandardCharsets.UTF_8); 54 | } 55 | } 56 | } 57 | stream.close(); 58 | } 59 | } 60 | } catch (Exception e) { 61 | throw new RuntimeException(e); 62 | } 63 | } 64 | 65 | private List getResourceFiles() { 66 | Reflections reflections = new Reflections("writer", new ResourcesScanner()); 67 | Set resources = reflections.getResources(Pattern.compile(".*\\.json")); 68 | return Lists.newArrayList(resources.toArray(new String[0])); 69 | } 70 | 71 | public JsonElement getResourceJson(String resourceName) { 72 | JsonParser parser = new JsonParser(); 73 | InputStream stream = this.getClass().getClassLoader().getResourceAsStream(resourceName); 74 | if (stream != null) { 75 | JsonElement element = parser.parse(new InputStreamReader(stream, StandardCharsets.UTF_8)); 76 | try { 77 | stream.close(); 78 | } catch (IOException ignored) { 79 | } 80 | return element; 81 | } 82 | return null; 83 | } 84 | 85 | public File getResourceAsFile(String resourcePath, String name) { 86 | try { 87 | File tempFile = new File(System.getProperty("java.io.tmpdir") + "/" + name); 88 | if (tempFile.exists()) { 89 | return tempFile; 90 | } else { 91 | boolean ignored = tempFile.createNewFile(); 92 | } 93 | InputStream in = this.getClass().getClassLoader().getResourceAsStream(resourcePath); 94 | if (in == null) { 95 | PeepoPractice.LOGGER.error("Couldn't open input stream for datapack '{}'.", name); 96 | return null; 97 | } 98 | try (FileOutputStream out = new FileOutputStream(tempFile)) { 99 | byte[] buffer = new byte[1024]; 100 | int bytesRead; 101 | while ((bytesRead = in.read(buffer)) != -1) { 102 | out.write(buffer, 0, bytesRead); 103 | } 104 | } 105 | in.close(); 106 | return tempFile; 107 | } catch (IOException e) { 108 | PeepoPractice.LOGGER.error("Couldn't get resource '{}' as file:", resourcePath, e); 109 | } 110 | return null; 111 | } 112 | } 113 | -------------------------------------------------------------------------------- /src/main/java/me/falu/peepopractice/core/writer/PracticeWriter.java: -------------------------------------------------------------------------------- 1 | package me.falu.peepopractice.core.writer; 2 | 3 | import com.google.gson.*; 4 | import me.falu.peepopractice.PeepoPractice; 5 | import net.fabricmc.loader.api.FabricLoader; 6 | 7 | import java.io.File; 8 | import java.io.FileReader; 9 | import java.io.FileWriter; 10 | import java.io.IOException; 11 | 12 | public class PracticeWriter { 13 | public static final Gson GSON = new GsonBuilder().setPrettyPrinting().create(); 14 | 15 | static { 16 | DefaultFileWriter.INSTANCE.writeDefaultFiles(); 17 | } 18 | 19 | public static final PracticeWriter PREFERENCES_WRITER = new PracticeWriter("preferences.json"); 20 | public static final PracticeWriter INVENTORY_WRITER = new PracticeWriter("inventories.json"); 21 | public static final PracticeWriter COMPLETIONS_WRITER = new PracticeWriter("completions.json"); 22 | public static final PracticeWriter STANDARD_SETTINGS_WRITER = new PracticeWriter("standard_settings.json"); 23 | public static final PracticeWriter GLOBAL_CONFIG = new PracticeWriter("global_config.json"); 24 | private final File file; 25 | private JsonObject local; 26 | 27 | public PracticeWriter(String fileName) { 28 | this.file = this.create(fileName); 29 | this.update(); 30 | } 31 | 32 | @SuppressWarnings({ "ResultOfMethodCallIgnored" }) 33 | private File create(String fileName) { 34 | File folder = FabricLoader.getInstance().getConfigDir().resolve(PeepoPractice.MOD_NAME).toFile(); 35 | if (!folder.exists()) { 36 | folder.mkdirs(); 37 | } 38 | File file = folder.toPath().resolve(fileName).toFile(); 39 | if (!file.exists()) { 40 | try { 41 | file.createNewFile(); 42 | } catch (IOException e) { 43 | throw new RuntimeException(e); 44 | } 45 | } 46 | return file; 47 | } 48 | 49 | public void write() { 50 | this.create(this.file.getName()); 51 | try { 52 | FileWriter writer = new FileWriter(this.file); 53 | 54 | writer.write(GSON.toJson(this.local)); 55 | writer.flush(); 56 | writer.close(); 57 | 58 | PeepoPractice.log("Wrote to '" + this.file.getName() + "'."); 59 | } catch (IOException ignored) { 60 | } 61 | this.update(); 62 | } 63 | 64 | public void update() { 65 | this.local = null; 66 | this.local = this.get(); 67 | } 68 | 69 | public JsonObject get() { 70 | if (this.local != null) { 71 | return this.local; 72 | } 73 | 74 | this.create(this.file.getName()); 75 | 76 | try { 77 | FileReader reader = new FileReader(this.file); 78 | JsonParser parser = new JsonParser(); 79 | 80 | Object obj = parser.parse(reader); 81 | reader.close(); 82 | 83 | return obj == null || obj.equals(JsonNull.INSTANCE) ? new JsonObject() : (JsonObject) obj; 84 | } catch (IOException ignored) { 85 | } 86 | 87 | return null; 88 | } 89 | 90 | public void put(String element, JsonElement obj) { 91 | if (this.local == null) { 92 | this.update(); 93 | } 94 | if (this.local.has(element)) { 95 | this.local.remove(element); 96 | } 97 | this.local.add(element, obj); 98 | } 99 | } 100 | -------------------------------------------------------------------------------- /src/main/java/me/falu/peepopractice/gui/hud/PeepoPauseManHud.java: -------------------------------------------------------------------------------- 1 | package me.falu.peepopractice.gui.hud; 2 | 3 | import com.mojang.blaze3d.systems.RenderSystem; 4 | import me.falu.peepopractice.PeepoPractice; 5 | import net.minecraft.client.MinecraftClient; 6 | import net.minecraft.client.gui.DrawableHelper; 7 | import net.minecraft.client.util.math.MatrixStack; 8 | import net.minecraft.util.Identifier; 9 | import net.minecraft.util.math.MathHelper; 10 | 11 | public class PeepoPauseManHud extends DrawableHelper { 12 | private static final Identifier PEEPO_PAUSE_MAN = new Identifier(PeepoPractice.MOD_ID, "textures/peepopauseman.png"); 13 | private static final Identifier PEEPO_PAG_MAN = new Identifier(PeepoPractice.MOD_ID, "textures/peepopagman.png"); 14 | private final MinecraftClient client; 15 | private Long startTime; 16 | 17 | public PeepoPauseManHud(MinecraftClient client) { 18 | this.client = client; 19 | } 20 | 21 | @SuppressWarnings("deprecation") 22 | public void render(MatrixStack matrices) { 23 | if (PeepoPractice.CATEGORY.hasCustomValue("showPauseBoy") && (boolean) PeepoPractice.CATEGORY.getCustomValue("showPauseBoy")) { 24 | if (this.startTime == null) { 25 | this.startTime = System.currentTimeMillis(); 26 | } 27 | 28 | int seconds = 2; 29 | long time = System.currentTimeMillis(); 30 | long difference = time - this.startTime; 31 | float div = (float) difference / (seconds * 1000); 32 | float percentage = (float) MathHelper.clamp(1 - Math.pow(1 - div, 5), 0.0F, 1.0F); 33 | RenderSystem.pushMatrix(); 34 | RenderSystem.enableBlend(); 35 | Identifier texture = PEEPO_PAUSE_MAN; 36 | if (PeepoPractice.CATEGORY.hasCustomValue("isCompletion")) { 37 | if ((boolean) PeepoPractice.CATEGORY.getCustomValue("isCompletion")) { 38 | texture = PEEPO_PAG_MAN; 39 | } else { 40 | texture = null; 41 | } 42 | } 43 | if (texture != null) { 44 | this.client.getTextureManager().bindTexture(texture); 45 | this.setZOffset(100); 46 | RenderSystem.translatef(0, 0, 100.0F); 47 | int spriteDiv = 6; 48 | int spriteWidth = 112 / spriteDiv; 49 | int spriteHeight = 84 / spriteDiv; 50 | drawTexture(matrices, 0, (int) (this.client.getWindow().getScaledHeight() - spriteHeight * percentage), 0.0F, 0.0F, spriteWidth, spriteHeight, spriteWidth, spriteHeight); 51 | } 52 | 53 | RenderSystem.disableBlend(); 54 | RenderSystem.popMatrix(); 55 | } else { 56 | this.startTime = null; 57 | } 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /src/main/java/me/falu/peepopractice/gui/screen/CategoryPreferencesScreen.java: -------------------------------------------------------------------------------- 1 | package me.falu.peepopractice.gui.screen; 2 | 3 | import me.falu.peepopractice.PeepoPractice; 4 | import me.falu.peepopractice.core.category.preferences.CategoryPreference; 5 | import me.falu.peepopractice.core.category.PracticeCategory; 6 | import me.falu.peepopractice.core.writer.PracticeWriter; 7 | import me.falu.peepopractice.gui.widget.LimitlessButtonWidget; 8 | import net.minecraft.client.gui.screen.Screen; 9 | import net.minecraft.client.util.math.MatrixStack; 10 | import net.minecraft.text.LiteralText; 11 | import net.minecraft.text.Text; 12 | import net.minecraft.text.TranslatableText; 13 | import net.minecraft.util.Formatting; 14 | 15 | public class CategoryPreferencesScreen extends Screen { 16 | private final Screen parent; 17 | private final PracticeCategory category; 18 | 19 | public CategoryPreferencesScreen(Screen parent, PracticeCategory category) { 20 | super(new TranslatableText("peepopractice.title.category_preferences", category.getSimpleName())); 21 | this.parent = parent; 22 | this.category = category; 23 | } 24 | 25 | public static Text getFormattedText(PracticeCategory category, CategoryPreference preference) { 26 | return new LiteralText(Formatting.BOLD.toString()) 27 | .append(preference.getLabel().copy().append(":\n").formatted(Formatting.BOLD)) 28 | .append(Formatting.RESET.toString()) 29 | .append(preference.getValueLabel(category, true)); 30 | } 31 | 32 | @Override 33 | protected void init() { 34 | if (this.client == null) { 35 | return; 36 | } 37 | 38 | int offset = this.width / 16; 39 | int size = this.width / 6 + offset; 40 | int column = 0; 41 | int row = 0; 42 | int values = Math.max(this.category.getPreferences().size(), 3); 43 | int maxColumns = Math.round(this.width / (float) (size * values)) + 2; 44 | 45 | for (CategoryPreference preference : this.category.getPreferences()) { 46 | this.addButton( 47 | new LimitlessButtonWidget( 48 | null, 49 | preference.getIcon(), 50 | (int) (32 * ((size - offset) / 110.0F)), 51 | this.width * (column + 1) / (Math.max(maxColumns, 3) + 1) - size / 2, 52 | 32 + size * row, 53 | size, 54 | size - offset, 55 | getFormattedText(this.category, preference), 56 | b -> { 57 | preference.advanceValue(this.category); 58 | b.setMessage(getFormattedText(this.category, preference)); 59 | }, 60 | (button, matrices, mouseX, mouseY) -> this.renderTooltip(matrices, preference.getDescription(), mouseX, mouseY) 61 | ) 62 | ); 63 | 64 | column++; 65 | if (column >= maxColumns) { 66 | row++; 67 | column = 0; 68 | } 69 | } 70 | } 71 | 72 | @Override 73 | public void render(MatrixStack matrices, int mouseX, int mouseY, float delta) { 74 | PeepoPractice.drawBackground(matrices, this); 75 | this.drawCenteredText(matrices, this.textRenderer, this.title, this.width / 2, 13, 16777215); 76 | super.render(matrices, mouseX, mouseY, delta); 77 | } 78 | 79 | @Override 80 | public void onClose() { 81 | PracticeWriter.PREFERENCES_WRITER.write(); 82 | if (this.client != null) { 83 | this.client.openScreen(this.parent); 84 | } 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /src/main/java/me/falu/peepopractice/gui/screen/EditShulkerBoxScreen.java: -------------------------------------------------------------------------------- 1 | package me.falu.peepopractice.gui.screen; 2 | 3 | import com.mojang.blaze3d.systems.RenderSystem; 4 | import me.falu.peepopractice.PeepoPractice; 5 | import me.falu.peepopractice.core.playerless.PlayerlessHandledScreen; 6 | import me.falu.peepopractice.core.playerless.PlayerlessInventory; 7 | import me.falu.peepopractice.core.playerless.PlayerlessShulkerBoxScreenHandler; 8 | import me.falu.peepopractice.gui.widget.LimitlessButtonWidget; 9 | import net.minecraft.client.gui.screen.ScreenTexts; 10 | import net.minecraft.client.util.math.MatrixStack; 11 | import net.minecraft.item.ItemStack; 12 | import net.minecraft.screen.slot.Slot; 13 | import net.minecraft.screen.slot.SlotActionType; 14 | import net.minecraft.text.Text; 15 | import net.minecraft.util.Identifier; 16 | 17 | public class EditShulkerBoxScreen extends PlayerlessHandledScreen { 18 | private static final Identifier TEXTURE = new Identifier("textures/gui/container/shulker_box.png"); 19 | private final EditInventoryScreen parent; 20 | private final Slot slot; 21 | private final PlayerlessShulkerBoxScreenHandler shulkerHandler; 22 | 23 | public EditShulkerBoxScreen(EditInventoryScreen parent, Slot slot, PlayerlessInventory inventory, Text title) { 24 | super(new PlayerlessShulkerBoxScreenHandler(inventory), inventory, title); 25 | this.backgroundHeight++; 26 | this.parent = parent; 27 | this.slot = slot; 28 | this.shulkerHandler = (PlayerlessShulkerBoxScreenHandler) this.getScreenHandler(); 29 | this.shulkerHandler.initializeItems(slot.getStack()); 30 | } 31 | 32 | @Override 33 | protected void init() { 34 | super.init(); 35 | this.addButton( 36 | new LimitlessButtonWidget( 37 | null, 38 | new Identifier("textures/item/barrier.png"), 39 | null, 40 | this.x - this.x / 2 - (this.width / 8) / 2, 41 | this.y, 42 | this.width / 8, 43 | this.backgroundHeight, 44 | ScreenTexts.DONE, 45 | b -> this.onClose() 46 | ) 47 | ); 48 | } 49 | 50 | @Override 51 | public void render(MatrixStack matrices, int mouseX, int mouseY, float delta) { 52 | PeepoPractice.drawBackground(matrices, this); 53 | super.render(matrices, mouseX, mouseY, delta); 54 | this.drawMouseoverTooltip(matrices, mouseX, mouseY); 55 | } 56 | 57 | @Override 58 | @SuppressWarnings("deprecation") 59 | protected void drawBackground(MatrixStack matrices, float delta, int mouseX, int mouseY) { 60 | if (this.client == null) { 61 | return; 62 | } 63 | RenderSystem.color4f(1.0f, 1.0f, 1.0f, 1.0f); 64 | this.client.getTextureManager().bindTexture(TEXTURE); 65 | int i = (this.width - this.backgroundWidth) / 2; 66 | int j = (this.height - this.backgroundHeight) / 2; 67 | this.drawTexture(matrices, i, j, 0, 0, this.backgroundWidth, this.backgroundHeight); 68 | } 69 | 70 | @Override 71 | public void onClose() { 72 | if (this.client == null) { 73 | return; 74 | } 75 | ItemStack stack = this.slot.getStack(); 76 | stack.getOrCreateSubTag("BlockEntityTag").put("Items", this.shulkerHandler.getItemsTag()); 77 | this.slot.setStack(stack); 78 | this.slot.markDirty(); 79 | this.client.openScreen(this.parent); 80 | } 81 | 82 | @Override 83 | protected void onMouseClick(Slot slot, int invSlot, int clickData, SlotActionType actionType) { 84 | if (slot == null || slot.id - this.shulkerHandler.size() + 9 != this.slot.id) { 85 | super.onMouseClick(slot, invSlot, clickData, actionType); 86 | } 87 | } 88 | } 89 | -------------------------------------------------------------------------------- /src/main/java/me/falu/peepopractice/gui/screen/GlobalConfigScreen.java: -------------------------------------------------------------------------------- 1 | package me.falu.peepopractice.gui.screen; 2 | 3 | import me.falu.peepopractice.PeepoPractice; 4 | import me.falu.peepopractice.core.global.GlobalOptions; 5 | import me.falu.peepopractice.core.writer.PracticeWriter; 6 | import me.falu.peepopractice.gui.widget.LimitlessButtonWidget; 7 | import me.falu.peepopractice.gui.widget.LimitlessDoubleOptionSliderWidget; 8 | import net.minecraft.client.gui.screen.Screen; 9 | import net.minecraft.client.gui.screen.ScreenTexts; 10 | import net.minecraft.client.gui.widget.AbstractButtonWidget; 11 | import net.minecraft.client.gui.widget.ButtonWidget; 12 | import net.minecraft.client.options.BooleanOption; 13 | import net.minecraft.client.options.DoubleOption; 14 | import net.minecraft.client.util.math.MatrixStack; 15 | import net.minecraft.text.TranslatableText; 16 | 17 | public class GlobalConfigScreen extends Screen { 18 | private static final DoubleOption[] BACKGROUND_OPTIONS = { 19 | GlobalOptions.BACKGROUND_RED, 20 | GlobalOptions.BACKGROUND_GREEN, 21 | GlobalOptions.BACKGROUND_BLUE, 22 | GlobalOptions.BACKGROUND_RED_2, 23 | GlobalOptions.BACKGROUND_GREEN_2, 24 | GlobalOptions.BACKGROUND_BLUE_2 25 | }; 26 | private static final BooleanOption[] OPTIONS = { 27 | GlobalOptions.SAME_INVENTORY, 28 | GlobalOptions.CHANGE_WINDOW_TITLE, 29 | GlobalOptions.GIVE_SATURATION 30 | }; 31 | private final Screen parent; 32 | 33 | public GlobalConfigScreen(Screen parent) { 34 | super(new TranslatableText("peepopractice.title.global_config")); 35 | this.parent = parent; 36 | } 37 | 38 | @Override 39 | protected void init() { 40 | if (this.client == null) { 41 | return; 42 | } 43 | 44 | GlobalOptions.SAME_INVENTORY.setTooltip(this.client.textRenderer.wrapLines(new TranslatableText("peepopractice.global_options.same_inventory.info"), 200)); 45 | GlobalOptions.CHANGE_WINDOW_TITLE.setTooltip(this.client.textRenderer.wrapLines(new TranslatableText("peepopractice.global_options.change_window_title.info"), 200)); 46 | GlobalOptions.GIVE_SATURATION.setTooltip(this.client.textRenderer.wrapLines(new TranslatableText("peepopractice.global_options.give_saturation.info"), 200)); 47 | 48 | int btnWidth = this.width / 2; 49 | int btnHeight = this.height / 8; 50 | int yOffset = btnHeight / 8; 51 | int actualWidth = btnWidth / (BACKGROUND_OPTIONS.length / 2) + 1; 52 | 53 | for (int i = -1; i <= 1; i++) { 54 | DoubleOption option = BACKGROUND_OPTIONS[i + 1]; 55 | this.addButton(new LimitlessDoubleOptionSliderWidget(this.client.options, this.width / 2 + actualWidth * i - actualWidth / 2, 16 + btnHeight / 2, actualWidth, btnHeight / 2, option)); 56 | } 57 | for (int i = -1; i <= 1; i++) { 58 | DoubleOption option = BACKGROUND_OPTIONS[i + 4]; 59 | this.addButton(new LimitlessDoubleOptionSliderWidget(this.client.options, this.width / 2 + actualWidth * i - actualWidth / 2, 16 + btnHeight, actualWidth, btnHeight / 2, option)); 60 | } 61 | 62 | int index = 0; 63 | for (BooleanOption option : OPTIONS) { 64 | index++; 65 | int x = this.width / 2 - btnWidth / 2; 66 | int y = 8 + btnHeight + yOffset * index + btnHeight * index; 67 | AbstractButtonWidget button = option.createButton(this.client.options, x, y, btnWidth); 68 | ButtonWidget.PressAction action = ((ButtonWidget) button).onPress; 69 | this.addButton(new LimitlessButtonWidget(x, y, btnWidth, btnHeight, button.getMessage(), action, option.getTooltip().isPresent() ? (button1, matrices, mouseX, mouseY) -> this.renderTooltip(matrices, option.getTooltip().get(), mouseX, mouseY) : null)); 70 | } 71 | 72 | this.addButton(new LimitlessButtonWidget(this.width / 2 - btnWidth / 2, this.height - btnHeight - yOffset * 4, btnWidth, btnHeight, ScreenTexts.DONE, b -> this.onClose())); 73 | } 74 | 75 | @Override 76 | public void render(MatrixStack matrices, int mouseX, int mouseY, float delta) { 77 | PeepoPractice.drawBackground(matrices, this); 78 | super.render(matrices, mouseX, mouseY, delta); 79 | this.drawCenteredText(matrices, this.textRenderer, this.title, this.width / 2, 13, 16777215); 80 | } 81 | 82 | @Override 83 | public void tick() { 84 | PeepoPractice.BACKGROUND_COLOR = PeepoPractice.updateBackgroundColor(); 85 | super.tick(); 86 | } 87 | 88 | @Override 89 | public void onClose() { 90 | PracticeWriter.GLOBAL_CONFIG.write(); 91 | if (this.client != null) { 92 | this.client.openScreen(this.parent); 93 | } 94 | } 95 | } 96 | -------------------------------------------------------------------------------- /src/main/java/me/falu/peepopractice/gui/screen/SettingsTypeSelectionScreen.java: -------------------------------------------------------------------------------- 1 | package me.falu.peepopractice.gui.screen; 2 | 3 | import me.falu.peepopractice.PeepoPractice; 4 | import me.falu.peepopractice.core.category.PracticeCategory; 5 | import me.falu.peepopractice.gui.widget.LimitlessButtonWidget; 6 | import net.minecraft.client.gui.screen.Screen; 7 | import net.minecraft.client.gui.widget.ButtonWidget; 8 | import net.minecraft.client.util.math.MatrixStack; 9 | import net.minecraft.text.Text; 10 | import net.minecraft.text.TranslatableText; 11 | import net.minecraft.util.Formatting; 12 | import net.minecraft.util.Identifier; 13 | 14 | public class SettingsTypeSelectionScreen extends Screen { 15 | private final ButtonChoice[] buttons = new ButtonChoice[] { 16 | new ButtonChoice("inventory", new Identifier("textures/item/chest_minecart.png"), category -> new InventoryOptionsScreen(this, category)), 17 | new ButtonChoice("preferences", new Identifier("textures/item/heart_of_the_sea.png"), category -> new CategoryPreferencesScreen(this, category), new ButtonDisabledInfo() { 18 | @Override 19 | public boolean isDisabled(PracticeCategory category) { 20 | return !category.hasPreferences(); 21 | } 22 | 23 | @Override 24 | public String getReason() { 25 | return "preferences"; 26 | } 27 | }), 28 | new ButtonChoice("standard_settings", new Identifier(PeepoPractice.MOD_ID, "textures/gear.png"), category -> new StandardSettingsScreen(this, category)), 29 | new ButtonChoice("split", new Identifier("textures/item/clock_00.png"), category -> new SplitSettingsScreen(this, category), new ButtonDisabledInfo() { 30 | @Override 31 | public boolean isDisabled(PracticeCategory category) { 32 | return !category.hasSplitEvent(); 33 | } 34 | 35 | @Override 36 | public String getReason() { 37 | return "split"; 38 | } 39 | }) 40 | }; 41 | private final Screen parent; 42 | private final PracticeCategory category; 43 | 44 | public SettingsTypeSelectionScreen(Screen parent, PracticeCategory category) { 45 | super(new TranslatableText("peepopractice.title.settings_type_selection", category.getSimpleName())); 46 | this.parent = parent; 47 | this.category = category; 48 | } 49 | 50 | @Override 51 | public void onClose() { 52 | if (this.client != null) { 53 | this.client.openScreen(this.parent); 54 | } 55 | } 56 | 57 | @Override 58 | protected void init() { 59 | int index = 0; 60 | int width = (int) (this.width / 2 * 0.8F); 61 | int height = this.height / 5; 62 | for (ButtonChoice buttonChoice : this.buttons) { 63 | ButtonWidget bw = this.addButton(new LimitlessButtonWidget(index % 2 == 0, buttonChoice.icon, null, this.width / 2 - width / 2, this.height * (index + 1) / (this.buttons.length + 1) - height / 2, width, height, buttonChoice.text, b -> { 64 | if (this.client != null) { 65 | Screen screen = buttonChoice.screenTask.execute(this.category); 66 | this.client.openScreen(screen); 67 | } 68 | }, (button, matrices, mouseX, mouseY) -> { 69 | if (buttonChoice.disabledTask != null && buttonChoice.disabledTask.isDisabled(this.category)) { 70 | this.renderTooltip(matrices, new TranslatableText("peepopractice.text.disabled." + buttonChoice.disabledTask.getReason()).formatted(Formatting.YELLOW), mouseX, mouseY); 71 | } 72 | })); 73 | if (buttonChoice.disabledTask != null) { 74 | bw.active = !buttonChoice.disabledTask.isDisabled(this.category); 75 | } 76 | index++; 77 | } 78 | } 79 | 80 | @Override 81 | public void render(MatrixStack matrices, int mouseX, int mouseY, float delta) { 82 | PeepoPractice.drawBackground(matrices, this); 83 | super.render(matrices, mouseX, mouseY, delta); 84 | this.drawCenteredText(matrices, this.textRenderer, this.title, this.width / 2, 13, 16777215); 85 | } 86 | 87 | public interface ButtonScreenTask { 88 | Screen execute(PracticeCategory category); 89 | } 90 | 91 | private static class ButtonChoice { 92 | public final Text text; 93 | public final Identifier icon; 94 | public final ButtonScreenTask screenTask; 95 | public ButtonDisabledInfo disabledTask; 96 | 97 | public ButtonChoice(String text, Identifier icon, ButtonScreenTask screenTask) { 98 | this.text = new TranslatableText("peepopractice.button.settings_type." + text); 99 | this.icon = icon; 100 | this.screenTask = screenTask; 101 | } 102 | 103 | public ButtonChoice(String text, Identifier icon, ButtonScreenTask screenTask, ButtonDisabledInfo disabledTask) { 104 | this(text, icon, screenTask); 105 | this.disabledTask = disabledTask; 106 | } 107 | } 108 | 109 | public abstract static class ButtonDisabledInfo { 110 | public abstract boolean isDisabled(PracticeCategory category); 111 | public abstract String getReason(); 112 | } 113 | } 114 | -------------------------------------------------------------------------------- /src/main/java/me/falu/peepopractice/gui/screen/SplitSettingsScreen.java: -------------------------------------------------------------------------------- 1 | package me.falu.peepopractice.gui.screen; 2 | 3 | import me.falu.peepopractice.PeepoPractice; 4 | import me.falu.peepopractice.core.category.PracticeCategory; 5 | import me.falu.peepopractice.core.category.preferences.CategoryPreference; 6 | import me.falu.peepopractice.core.category.preferences.CategoryPreferences; 7 | import me.falu.peepopractice.core.writer.PracticeWriter; 8 | import me.falu.peepopractice.gui.widget.LimitlessButtonWidget; 9 | import net.minecraft.client.gui.screen.Screen; 10 | import net.minecraft.client.gui.widget.ButtonWidget; 11 | import net.minecraft.client.util.math.MatrixStack; 12 | import net.minecraft.text.TranslatableText; 13 | import net.minecraft.util.Identifier; 14 | 15 | public class SplitSettingsScreen extends Screen { 16 | private static final CategoryPreference[] PREFERENCES = new CategoryPreference[] { 17 | CategoryPreferences.COMPARE_TYPE, 18 | CategoryPreferences.PACE_TIMER_SHOW_TYPE 19 | }; 20 | private final Screen parent; 21 | private final PracticeCategory category; 22 | 23 | public SplitSettingsScreen(Screen parent, PracticeCategory category) { 24 | super(new TranslatableText("peepopractice.title.split_settings", category.getSimpleName())); 25 | this.parent = parent; 26 | this.category = category; 27 | } 28 | 29 | @Override 30 | protected void init() { 31 | int index = 0; 32 | int width = this.width / 2; 33 | int height = this.height / 5; 34 | int amount = PREFERENCES.length + 1; 35 | for (CategoryPreference preference : PREFERENCES) { 36 | this.addButton( 37 | new LimitlessButtonWidget( 38 | false, 39 | preference.getIcon(), 40 | null, 41 | this.width / 2 - width / 2, 42 | this.height * (index + 1) / (amount + 1) - height / 2, 43 | width, 44 | height, 45 | preference.getLabel().copy().append(": ").append(preference.getValueLabel(this.category)), 46 | b -> { 47 | preference.advanceValue(this.category); 48 | b.setMessage(preference.getLabel().copy().append(": ").append(preference.getValueLabel(this.category))); 49 | } 50 | ) 51 | ); 52 | index++; 53 | } 54 | ButtonWidget bw = this.addButton(new LimitlessButtonWidget(false, new Identifier("textures/item/barrier.png"), null, this.width / 2 - width / 2, this.height * (index + 1) / (amount + 1) - height / 2, width, height, new TranslatableText("peepopractice.button.clear_saved_times"), b -> { 55 | this.category.getSplitEvent().clearTimes(); 56 | b.active = false; 57 | })); 58 | bw.active = this.category.getSplitEvent().hasCompletedTimes(); 59 | } 60 | 61 | @Override 62 | public void render(MatrixStack matrices, int mouseX, int mouseY, float delta) { 63 | PeepoPractice.drawBackground(matrices, this); 64 | super.render(matrices, mouseX, mouseY, delta); 65 | this.drawCenteredText(matrices, this.textRenderer, this.title, this.width / 2, 13, 16777215); 66 | } 67 | 68 | @Override 69 | public void onClose() { 70 | PracticeWriter.PREFERENCES_WRITER.write(); 71 | if (this.client != null) { 72 | this.client.openScreen(this.parent); 73 | return; 74 | } 75 | super.onClose(); 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /src/main/java/me/falu/peepopractice/gui/screen/StandardSettingsScreen.java: -------------------------------------------------------------------------------- 1 | package me.falu.peepopractice.gui.screen; 2 | 3 | import me.falu.peepopractice.PeepoPractice; 4 | import me.falu.peepopractice.core.category.PracticeCategoriesAny; 5 | import me.falu.peepopractice.core.category.PracticeCategory; 6 | import me.falu.peepopractice.core.category.utils.StandardSettingsUtils; 7 | import me.falu.peepopractice.core.writer.PracticeWriter; 8 | import me.falu.peepopractice.gui.widget.LimitlessButtonWidget; 9 | import me.falu.peepopractice.gui.widget.LimitlessDoubleOptionSliderWidget; 10 | import net.minecraft.client.gui.screen.Screen; 11 | import net.minecraft.client.gui.screen.ScreenTexts; 12 | import net.minecraft.client.gui.widget.AbstractButtonWidget; 13 | import net.minecraft.client.gui.widget.ButtonWidget; 14 | import net.minecraft.client.gui.widget.DoubleOptionSliderWidget; 15 | import net.minecraft.client.gui.widget.TextFieldWidget; 16 | import net.minecraft.client.options.DoubleOption; 17 | import net.minecraft.client.options.Option; 18 | import net.minecraft.client.util.math.MatrixStack; 19 | import net.minecraft.text.LiteralText; 20 | import net.minecraft.text.TranslatableText; 21 | 22 | public class StandardSettingsScreen extends Screen { 23 | private static final Option[] OPTIONS = new Option[] { 24 | StandardSettingsUtils.FOV, 25 | StandardSettingsUtils.RENDER_DISTANCE, 26 | StandardSettingsUtils.ENTITY_DISTANCE_SCALING, 27 | StandardSettingsUtils.CHUNK_BORDERS, 28 | StandardSettingsUtils.HITBOXES, 29 | StandardSettingsUtils.PERSPECTIVE 30 | }; 31 | private final Screen parent; 32 | private TextFieldWidget pieChartDirectoryField; 33 | 34 | public StandardSettingsScreen(Screen parent, PracticeCategory category) { 35 | super(new TranslatableText("peepopractice.title.standard_settings", category.getSimpleName())); 36 | this.parent = parent; 37 | PeepoPractice.CONFIGURING_CATEGORY = category; 38 | } 39 | 40 | @Override 41 | protected void init() { 42 | if (this.client == null) { 43 | return; 44 | } 45 | this.client.keyboard.enableRepeatEvents(true); 46 | 47 | int index = 0; 48 | int btnIndex = 0; 49 | int btnWidth = this.width / 4; 50 | int btnHeight = this.height / 8; 51 | int yOffset = btnHeight / 8; 52 | boolean otherSide; 53 | for (Option option : OPTIONS) { 54 | otherSide = index + 1 > OPTIONS.length / 2; 55 | if (otherSide && index == OPTIONS.length / 2) { 56 | btnIndex = 0; 57 | } 58 | int x = this.width / 2 - btnWidth / 2 + (otherSide ? btnWidth / 2 : -btnWidth / 2); 59 | int y = 30 + (btnHeight + yOffset) * btnIndex; 60 | AbstractButtonWidget button = option.createButton(this.client.options, x, y, btnWidth); 61 | if (button instanceof ButtonWidget) { 62 | ButtonWidget.PressAction action = ((ButtonWidget) button).onPress; 63 | button = new LimitlessButtonWidget(x, y, btnWidth, btnHeight, button.getMessage(), action); 64 | } else if (button instanceof DoubleOptionSliderWidget) { 65 | button = new LimitlessDoubleOptionSliderWidget(this.client.options, x, y, btnWidth, btnHeight, (DoubleOption) option); 66 | } 67 | this.addButton(button); 68 | btnIndex++; 69 | index++; 70 | } 71 | btnIndex++; 72 | this.pieChartDirectoryField = new TextFieldWidget(this.textRenderer, this.width / 2 - btnWidth, 30 + (btnHeight + yOffset) * (btnIndex - 1) + (20 + yOffset), btnWidth * 2, 20, new LiteralText("")); 73 | this.pieChartDirectoryField.setMaxLength(99); 74 | this.pieChartDirectoryField.setText(StandardSettingsUtils.getSettingForCategory(PeepoPractice.CONFIGURING_CATEGORY, "piechart", "root.gameRenderer.level.entities")); 75 | this.children.add(this.pieChartDirectoryField); 76 | 77 | this.addButton(new LimitlessButtonWidget(this.width / 2 - btnWidth, this.height - btnHeight - yOffset * 4, btnWidth * 2, btnHeight, ScreenTexts.DONE, b -> this.onClose())); 78 | } 79 | 80 | @Override 81 | public void render(MatrixStack matrices, int mouseX, int mouseY, float delta) { 82 | PeepoPractice.drawBackground(matrices, this); 83 | if (this.pieChartDirectoryField != null) { 84 | this.pieChartDirectoryField.render(matrices, mouseX, mouseY, delta); 85 | this.drawTextWithShadow(matrices, this.textRenderer, new TranslatableText("peepopractice.text.pie_dir"), this.pieChartDirectoryField.x, this.pieChartDirectoryField.y - 10, 16777215); 86 | } 87 | this.drawCenteredText(matrices, this.textRenderer, this.title, this.width / 2, 13, 16777215); 88 | super.render(matrices, mouseX, mouseY, delta); 89 | } 90 | 91 | @Override 92 | public void removed() { 93 | if (this.client == null) { 94 | return; 95 | } 96 | this.client.keyboard.enableRepeatEvents(false); 97 | } 98 | 99 | @Override 100 | public void onClose() { 101 | StandardSettingsUtils.setSettingForCategory(PeepoPractice.CONFIGURING_CATEGORY, "piechart", this.pieChartDirectoryField.getText()); 102 | PracticeWriter.STANDARD_SETTINGS_WRITER.write(); 103 | PeepoPractice.CONFIGURING_CATEGORY = PracticeCategoriesAny.EMPTY; 104 | if (this.client != null) { 105 | this.client.openScreen(this.parent); 106 | return; 107 | } 108 | super.onClose(); 109 | } 110 | } 111 | -------------------------------------------------------------------------------- /src/main/java/me/falu/peepopractice/gui/widget/LimitlessDoubleOptionSliderWidget.java: -------------------------------------------------------------------------------- 1 | package me.falu.peepopractice.gui.widget; 2 | 3 | import com.mojang.blaze3d.systems.RenderSystem; 4 | import me.falu.peepopractice.PeepoPractice; 5 | import net.minecraft.client.MinecraftClient; 6 | import net.minecraft.client.font.TextRenderer; 7 | import net.minecraft.client.gui.widget.DoubleOptionSliderWidget; 8 | import net.minecraft.client.options.DoubleOption; 9 | import net.minecraft.client.options.GameOptions; 10 | import net.minecraft.client.util.math.MatrixStack; 11 | import net.minecraft.util.math.MathHelper; 12 | 13 | public class LimitlessDoubleOptionSliderWidget extends DoubleOptionSliderWidget { 14 | public LimitlessDoubleOptionSliderWidget(GameOptions gameOptions, int x, int y, int width, int height, DoubleOption option) { 15 | super(gameOptions, x, y, width, height, option); 16 | } 17 | 18 | @Override 19 | @SuppressWarnings({ "deprecation", "DuplicatedCode" }) 20 | public void renderButton(MatrixStack matrices, int mouseX, int mouseY, float delta) { 21 | MinecraftClient minecraftClient = MinecraftClient.getInstance(); 22 | TextRenderer textRenderer = minecraftClient.textRenderer; 23 | minecraftClient.getTextureManager().bindTexture(WIDGETS_LOCATION); 24 | RenderSystem.color4f(1.0f, 1.0f, 1.0f, this.alpha); 25 | int i = this.getYImage(this.isHovered()); 26 | RenderSystem.enableBlend(); 27 | RenderSystem.defaultBlendFunc(); 28 | RenderSystem.enableDepthTest(); 29 | this.drawTexture(matrices, this.x, this.y, 0, 46 + i * 20, 3, 3); 30 | drawTexture(matrices, this.x + 3, this.y, this.width - 6, 3, 3, 46 + i * 20, 1, 3, 256, 256); 31 | this.drawTexture(matrices, this.x + this.width - 3, this.y, 197, 46 + i * 20, 3, 3); 32 | drawTexture(matrices, this.x, this.y + 3, 3, this.height - 6, 0, 49 + i * 20, 3, 1, 256, 256); 33 | this.drawTexture(matrices, this.x, this.y + this.height - 3, 0, 46 + 17 + i * 20, 3, 3); 34 | drawTexture(matrices, this.x + 3, this.y + this.height - 3, this.width - 6, 3, 3, 46 + 17 + i * 20, 1, 3, 256, 256); 35 | this.drawTexture(matrices, this.x + this.width - 3, this.y + this.height - 3, 197, 46 + 17 + i * 20, 3, 3); 36 | drawTexture(matrices, this.x + this.width - 3, this.y + 3, 3, this.height - 6, 197, 49 + i * 20, 3, 1, 256, 256); 37 | fill(matrices, this.x + 3, this.y + 3, this.x + this.width - 3, this.y + this.height - 3, PeepoPractice.BACKGROUND_OVERLAY_COLOR); 38 | this.renderBg(matrices, minecraftClient, mouseX, mouseY); 39 | int j = this.active ? 0xFFFFFF : 0xA0A0A0; 40 | this.drawCenteredText(matrices, textRenderer, this.getMessage(), this.x + this.width / 2, this.y + (this.height - 8) / 2, j | MathHelper.ceil(this.alpha * 255.0f) << 24); 41 | } 42 | 43 | @Override 44 | @SuppressWarnings("deprecation") 45 | protected void renderBg(MatrixStack matrices, MinecraftClient client, int mouseX, int mouseY) { 46 | client.getTextureManager().bindTexture(WIDGETS_LOCATION); 47 | RenderSystem.color4f(1.0f, 1.0f, 1.0f, 1.0f); 48 | int i = (this.isHovered() ? 2 : 1) * 20; 49 | int dx = this.x + (int) (this.value * (double) (this.width - 8)); 50 | drawTexture(matrices, dx, this.y, 4, 4, 0, 46 + i, 4, 4, 256, 256); 51 | drawTexture(matrices, dx + 4, this.y, 4, 4, 196, 46 + i, 4, 4, 256, 256); 52 | drawTexture(matrices, dx, this.y + this.height - 4, 4, 4, 0, 46 + i + 16, 4, 4, 256, 256); 53 | drawTexture(matrices, dx + 4, this.y + this.height - 4, 4, 4, 196, 46 + i + 16, 4, 4, 256, 256); 54 | drawTexture(matrices, dx, this.y + 4, 4, this.height - 8, 0, 46 + i + 4, 4, 1, 256, 256); 55 | drawTexture(matrices, dx + 4, this.y + 4, 4, this.height - 8, 196, 46 + i + 4, 4, 1, 256, 256); 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /src/main/java/me/falu/peepopractice/gui/widget/ThumbnailButtonWidget.java: -------------------------------------------------------------------------------- 1 | package me.falu.peepopractice.gui.widget; 2 | 3 | import com.mojang.blaze3d.systems.RenderSystem; 4 | import me.falu.peepopractice.core.category.PracticeCategory; 5 | import net.minecraft.client.MinecraftClient; 6 | import net.minecraft.client.gui.hud.BackgroundHelper; 7 | import net.minecraft.client.gui.widget.ButtonWidget; 8 | import net.minecraft.client.util.math.MatrixStack; 9 | import net.minecraft.text.LiteralText; 10 | import net.minecraft.util.Identifier; 11 | 12 | public class ThumbnailButtonWidget extends ButtonWidget { 13 | private static final Identifier GEAR = new Identifier("peepopractice", "textures/gear.png"); 14 | private final PracticeCategory category; 15 | private final ButtonWidget configButton; 16 | 17 | public ThumbnailButtonWidget(int x, int y, int width, int height, PracticeCategory category, ButtonWidget configButton, PressAction pressAction) { 18 | super(x, y, width, height, new LiteralText(""), pressAction); 19 | this.category = category; 20 | this.configButton = configButton; 21 | } 22 | 23 | @Override 24 | public boolean isHovered() { 25 | return super.isHovered() && !this.configButton.isHovered(); 26 | } 27 | 28 | @Override 29 | @SuppressWarnings({ "deprecation", "DuplicatedCode" }) 30 | public void renderButton(MatrixStack matrices, int mouseX, int mouseY, float delta) { 31 | MinecraftClient client = MinecraftClient.getInstance(); 32 | int yOffset = this.getYImage(this.isHovered()); 33 | 34 | client.getTextureManager().bindTexture(WIDGETS_LOCATION); 35 | 36 | RenderSystem.pushMatrix(); 37 | RenderSystem.color4f(1.0F, 1.0F, 1.0F, this.alpha); 38 | RenderSystem.enableBlend(); 39 | RenderSystem.defaultBlendFunc(); 40 | RenderSystem.enableDepthTest(); 41 | 42 | this.drawTexture(matrices, this.x, this.y, 0, 46 + yOffset * 20, 3, 3); 43 | drawTexture(matrices, this.x + 3, this.y, this.width - 6, 3, 3, 46 + yOffset * 20, 1, 3, 256, 256); 44 | this.drawTexture(matrices, this.x + this.width - 3, this.y, 197, 46 + yOffset * 20, 3, 3); 45 | drawTexture(matrices, this.x, this.y + 3, 3, this.height - 6, 0, 49 + yOffset * 20, 3, 1, 256, 256); 46 | this.drawTexture(matrices, this.x, this.y + this.height - 3, 0, 46 + 17 + yOffset * 20, 3, 3); 47 | drawTexture(matrices, this.x + 3, this.y + this.height - 3, this.width - 6, 3, 3, 46 + 17 + yOffset * 20, 1, 3, 256, 256); 48 | this.drawTexture(matrices, this.x + this.width - 3, this.y + this.height - 3, 197, 46 + 17 + yOffset * 20, 3, 3); 49 | drawTexture(matrices, this.x + this.width - 3, this.y + 3, 3, this.height - 6, 197, 49 + yOffset * 20, 3, 1, 256, 256); 50 | 51 | client.getTextureManager().bindTexture(new Identifier("peepopractice", "textures/category/" + this.category.getId() + ".png")); 52 | drawTexture(matrices, this.x + 3, this.y + 3, 0.0F, 0.0F, this.width - 6, this.height - 6, this.width - 6, this.height - 6); 53 | 54 | if (this.isHovered()) { 55 | this.renderToolTip(matrices, mouseX, mouseY); 56 | } 57 | RenderSystem.popMatrix(); 58 | 59 | this.configButton.render(matrices, mouseX, mouseY, delta); 60 | client.getTextureManager().bindTexture(GEAR); 61 | drawTexture(matrices, this.configButton.x + 2, this.configButton.y + 2, 0.0F, 0.0F, 16, 16, 16, 16); 62 | 63 | fill(matrices, this.x, this.y + this.height, this.x + this.width, this.y + this.height - this.height / 2 + 5, BackgroundHelper.ColorMixer.getArgb(150, 0, 0, 0)); 64 | this.drawCenteredText( 65 | matrices, 66 | client.textRenderer, 67 | this.category.getName(false), 68 | this.x + this.width / 2, 69 | this.y + this.height - this.height / 4 - client.textRenderer.fontHeight - 2 + 4, 70 | 0xFFFFFF 71 | ); 72 | this.drawCenteredText( 73 | matrices, 74 | client.textRenderer, 75 | this.isHovered() ? this.category.getStatsText() : this.category.getPbText(), 76 | this.x + this.width / 2, 77 | this.y + this.height - this.height / 4 + 4, 78 | 0xFFFFFF 79 | ); 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /src/main/java/me/falu/peepopractice/mixin/ClientPlayNetworkHandlerMixin.java: -------------------------------------------------------------------------------- 1 | package me.falu.peepopractice.mixin; 2 | 3 | import com.llamalad7.mixinextras.injector.v2.WrapWithCondition; 4 | import me.falu.peepopractice.PeepoPractice; 5 | import me.falu.peepopractice.core.category.PracticeCategoriesAny; 6 | import net.minecraft.client.network.ClientPlayNetworkHandler; 7 | import org.apache.logging.log4j.Logger; 8 | import org.spongepowered.asm.mixin.Mixin; 9 | import org.spongepowered.asm.mixin.injection.At; 10 | 11 | @Mixin(ClientPlayNetworkHandler.class) 12 | public abstract class ClientPlayNetworkHandlerMixin { 13 | @WrapWithCondition(method = "onEntityPassengersSet", at = @At(value = "INVOKE", target = "Lorg/apache/logging/log4j/Logger;warn(Ljava/lang/String;)V", remap = false)) 14 | private boolean peepoPractice$ignoreWarning(Logger instance, String s) { 15 | return PeepoPractice.CATEGORY.equals(PracticeCategoriesAny.EMPTY); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/main/java/me/falu/peepopractice/mixin/ClientPlayerInteractionManagerMixin.java: -------------------------------------------------------------------------------- 1 | package me.falu.peepopractice.mixin; 2 | 3 | import me.falu.peepopractice.core.category.preferences.CategoryPreferences; 4 | import net.minecraft.client.network.ClientPlayNetworkHandler; 5 | import net.minecraft.client.network.ClientPlayerInteractionManager; 6 | import net.minecraft.entity.player.PlayerEntity; 7 | import net.minecraft.network.packet.c2s.play.PlayerMoveC2SPacket; 8 | import net.minecraft.util.ActionResult; 9 | import net.minecraft.util.Hand; 10 | import net.minecraft.world.World; 11 | import org.spongepowered.asm.mixin.Final; 12 | import org.spongepowered.asm.mixin.Mixin; 13 | import org.spongepowered.asm.mixin.Shadow; 14 | import org.spongepowered.asm.mixin.injection.At; 15 | import org.spongepowered.asm.mixin.injection.Inject; 16 | import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; 17 | 18 | @Mixin(ClientPlayerInteractionManager.class) 19 | public class ClientPlayerInteractionManagerMixin { 20 | @Shadow @Final private ClientPlayNetworkHandler networkHandler; 21 | 22 | @Inject(method = "interactItem", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/network/ClientPlayerInteractionManager;syncSelectedSlot()V", shift = At.Shift.AFTER)) 23 | public void onInteractItem(PlayerEntity player, World world, Hand hand, CallbackInfoReturnable cir) { 24 | if (CategoryPreferences.FIX_GHOST_BUCKETS.getBoolValue()) { 25 | this.networkHandler.sendPacket(new PlayerMoveC2SPacket.Both(player.getX(), player.getY(), player.getZ(), player.yaw, player.pitch, player.isOnGround())); 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/main/java/me/falu/peepopractice/mixin/GameOptionsMixin.java: -------------------------------------------------------------------------------- 1 | package me.falu.peepopractice.mixin; 2 | 3 | import me.falu.peepopractice.core.category.utils.KeyBindingUtils; 4 | import me.falu.peepopractice.core.category.utils.StandardSettingsUtils; 5 | import net.minecraft.client.options.GameOptions; 6 | import net.minecraft.client.options.KeyBinding; 7 | import net.minecraft.client.options.Option; 8 | import org.spongepowered.asm.mixin.Final; 9 | import org.spongepowered.asm.mixin.Mixin; 10 | import org.spongepowered.asm.mixin.Mutable; 11 | import org.spongepowered.asm.mixin.Shadow; 12 | import org.spongepowered.asm.mixin.injection.At; 13 | import org.spongepowered.asm.mixin.injection.Inject; 14 | import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; 15 | 16 | @Mixin(GameOptions.class) 17 | public abstract class GameOptionsMixin { 18 | @Mutable @Shadow @Final public KeyBinding[] keysAll; 19 | 20 | @Inject(method = "", at = @At("TAIL")) 21 | private void peepoPractice$setRenderDistanceMax(CallbackInfo ci) { 22 | StandardSettingsUtils.RENDER_DISTANCE.setMax((float) Option.RENDER_DISTANCE.getMax()); 23 | } 24 | 25 | @Inject(method = "load", at = @At("HEAD")) 26 | private void peepoPractice$registerKeyBindings(CallbackInfo ci) { 27 | this.keysAll = KeyBindingUtils.process(this.keysAll); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/main/java/me/falu/peepopractice/mixin/ServerPlayNetworkHandlerMixin.java: -------------------------------------------------------------------------------- 1 | package me.falu.peepopractice.mixin; 2 | 3 | import com.llamalad7.mixinextras.injector.ModifyReturnValue; 4 | import me.falu.peepopractice.PeepoPractice; 5 | import me.falu.peepopractice.core.category.PracticeCategoriesAny; 6 | import net.minecraft.server.network.ServerPlayNetworkHandler; 7 | import org.spongepowered.asm.mixin.Mixin; 8 | import org.spongepowered.asm.mixin.injection.At; 9 | 10 | @Mixin(ServerPlayNetworkHandler.class) 11 | public abstract class ServerPlayNetworkHandlerMixin { 12 | @ModifyReturnValue(method = { "validatePlayerMove", "validateVehicleMove" }, at = @At("RETURN"), require = 2) 13 | private static boolean peepoPractice$alwaysValidMovement(boolean valid) { 14 | return valid && PeepoPractice.CATEGORY.equals(PracticeCategoriesAny.EMPTY); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/main/java/me/falu/peepopractice/mixin/compat/LocateCommandMixin.java: -------------------------------------------------------------------------------- 1 | package me.falu.peepopractice.mixin.compat; 2 | 3 | import com.mojang.brigadier.exceptions.CommandSyntaxException; 4 | import com.mojang.brigadier.exceptions.SimpleCommandExceptionType; 5 | import me.falu.peepopractice.PeepoPractice; 6 | import net.minecraft.server.command.LocateCommand; 7 | import net.minecraft.server.command.ServerCommandSource; 8 | import net.minecraft.text.TranslatableText; 9 | import net.minecraft.world.gen.feature.StructureFeature; 10 | import org.spongepowered.asm.mixin.Mixin; 11 | import org.spongepowered.asm.mixin.Unique; 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(LocateCommand.class) 17 | public abstract class LocateCommandMixin { 18 | @Unique private static final SimpleCommandExceptionType AFFECTED_EXCEPTION = new SimpleCommandExceptionType(new TranslatableText("peepopractice.locate_error")); 19 | 20 | @Inject(method = "execute", at = @At("HEAD")) 21 | private static void peepoPractice$cancelAffectedLocate(ServerCommandSource source, StructureFeature structureFeature, CallbackInfoReturnable cir) throws CommandSyntaxException { 22 | if (PeepoPractice.CATEGORY.hasStructureProperties() && PeepoPractice.CATEGORY.findStructureProperties(structureFeature) != null) { 23 | throw AFFECTED_EXCEPTION.create(); 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/main/java/me/falu/peepopractice/mixin/compat/StandardSettingsMixin.java: -------------------------------------------------------------------------------- 1 | package me.falu.peepopractice.mixin.compat; 2 | 3 | import com.kingcontaria.standardsettings.StandardSettings; 4 | import me.falu.peepopractice.PeepoPractice; 5 | import me.falu.peepopractice.core.category.utils.StandardSettingsUtils; 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(value = StandardSettings.class, remap = false) 12 | public abstract class StandardSettingsMixin { 13 | @Inject(method = { "load([Ljava/lang/String;)V", "changeSettingsOnJoin" }, at = @At("TAIL"), require = 2) 14 | private static void peepoPractice$customSettings1(CallbackInfo ci) { 15 | PeepoPractice.log("Triggered second standard settings call for " + PeepoPractice.CATEGORY.getId()); 16 | StandardSettingsUtils.triggerStandardSettings(PeepoPractice.CATEGORY); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/main/java/me/falu/peepopractice/mixin/compat/timer/InGameTimerUtilsMixin.java: -------------------------------------------------------------------------------- 1 | package me.falu.peepopractice.mixin.compat.timer; 2 | 3 | import com.llamalad7.mixinextras.injector.ModifyExpressionValue; 4 | import com.redlimerl.speedrunigt.timer.InGameTimerUtils; 5 | import me.falu.peepopractice.PeepoPractice; 6 | import me.falu.peepopractice.core.category.PracticeCategoriesAny; 7 | import org.spongepowered.asm.mixin.Mixin; 8 | import org.spongepowered.asm.mixin.injection.At; 9 | import org.spongepowered.asm.mixin.injection.Slice; 10 | 11 | import java.nio.file.Path; 12 | 13 | @Mixin(value = InGameTimerUtils.class, remap = false) 14 | public abstract class InGameTimerUtilsMixin { 15 | @ModifyExpressionValue( 16 | method = "getTimerLogDir", 17 | at = @At( 18 | value = "INVOKE", 19 | target = "Ljava/nio/file/Path;resolve(Ljava/lang/String;)Ljava/nio/file/Path;", 20 | ordinal = 0 21 | ), 22 | slice = @Slice( 23 | from = @At( 24 | value = "CONSTANT", 25 | args = "stringValue=saves" 26 | ) 27 | ) 28 | ) 29 | private static Path peepoPractice$customSavesDirectory(Path path) { 30 | if (!PeepoPractice.CATEGORY.equals(PracticeCategoriesAny.EMPTY) && PeepoPractice.PRACTICE_LEVEL_STORAGE != null) { 31 | return PeepoPractice.PRACTICE_LEVEL_STORAGE.getSavesDirectory(); 32 | } 33 | return path; 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/main/java/me/falu/peepopractice/mixin/compat/timer/PracticeTimerManagerMixin.java: -------------------------------------------------------------------------------- 1 | package me.falu.peepopractice.mixin.compat.timer; 2 | 3 | import com.redlimerl.speedrunigt.timer.PracticeTimerManager; 4 | import me.falu.peepopractice.PeepoPractice; 5 | import me.falu.peepopractice.core.category.PracticeCategoriesAny; 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(value = PracticeTimerManager.class, remap = false) 12 | public abstract class PracticeTimerManagerMixin { 13 | @Inject(method = "startPractice", at = @At("HEAD"), cancellable = true) 14 | private static void peepoPractice$disablePracticeTimer(CallbackInfo ci) { 15 | if (!PeepoPractice.CATEGORY.equals(PracticeCategoriesAny.EMPTY)) { 16 | ci.cancel(); 17 | } 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/main/java/me/falu/peepopractice/mixin/gui/MoreOptionsDialogMixin.java: -------------------------------------------------------------------------------- 1 | package me.falu.peepopractice.mixin.gui; 2 | 3 | import com.google.gson.JsonArray; 4 | import com.google.gson.JsonParser; 5 | import me.falu.peepopractice.PeepoPractice; 6 | import net.fabricmc.loader.api.FabricLoader; 7 | import net.minecraft.client.gui.screen.world.MoreOptionsDialog; 8 | import org.apache.commons.io.FileUtils; 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 | import java.io.IOException; 14 | import java.nio.charset.Charset; 15 | import java.util.OptionalLong; 16 | import java.util.Random; 17 | 18 | @Mixin(MoreOptionsDialog.class) 19 | public class MoreOptionsDialogMixin { 20 | @SuppressWarnings("OptionalUsedAsFieldOrParameterType") 21 | @ModifyArg( 22 | method = "getGeneratorOptions", 23 | at = @At( 24 | value = "INVOKE", 25 | target = "Lnet/minecraft/world/gen/GeneratorOptions;withHardcore(ZLjava/util/OptionalLong;)Lnet/minecraft/world/gen/GeneratorOptions;", 26 | ordinal = 0 27 | ), 28 | index = 1 29 | ) 30 | private OptionalLong peepoPractice$insertSeedList(OptionalLong original) { 31 | if (PeepoPractice.CATEGORY.hasWorldProperties()) { 32 | if (PeepoPractice.CATEGORY.getWorldProperties().hasSeedListPath()) { 33 | String seedListPath = PeepoPractice.CATEGORY.getWorldProperties().getSeedListPath(); 34 | try { 35 | String content = FileUtils.readFileToString(FabricLoader.getInstance().getConfigDir().resolve(PeepoPractice.MOD_NAME + "/seed_lists/" + seedListPath + ".json").toFile(), Charset.defaultCharset()); 36 | JsonArray seeds = new JsonParser().parse(content).getAsJsonArray(); 37 | long seed = seeds.get(new Random().nextInt(seeds.size() - 1)).getAsLong(); 38 | return OptionalLong.of(seed); 39 | } catch (IOException e) { 40 | PeepoPractice.LOGGER.error("Couldn't load seed list", e); 41 | } 42 | } 43 | } 44 | return original; 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /src/main/java/me/falu/peepopractice/mixin/gui/RecipeBookMixin.java: -------------------------------------------------------------------------------- 1 | package me.falu.peepopractice.mixin.gui; 2 | 3 | import me.falu.peepopractice.core.category.utils.InventoryUtils; 4 | import net.minecraft.recipe.book.RecipeBook; 5 | import org.spongepowered.asm.mixin.Mixin; 6 | import org.spongepowered.asm.mixin.injection.At; 7 | import org.spongepowered.asm.mixin.injection.Inject; 8 | import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; 9 | 10 | @Mixin(RecipeBook.class) 11 | public class RecipeBookMixin { 12 | @Inject(method = "setGuiOpen", at = @At("HEAD")) 13 | private void saveBookState(boolean guiOpen, CallbackInfo ci) { 14 | InventoryUtils.BOOK_OPEN = guiOpen; 15 | } 16 | 17 | @Inject(method = "setFilteringCraftable", at = @At("HEAD")) 18 | private void saveFilterState(boolean filteringCraftable, CallbackInfo ci) { 19 | InventoryUtils.FILTERING_CRAFTABLE = filteringCraftable; 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/main/java/me/falu/peepopractice/mixin/gui/hud/InGameHudMixin.java: -------------------------------------------------------------------------------- 1 | package me.falu.peepopractice.mixin.gui.hud; 2 | 3 | import me.falu.peepopractice.gui.hud.PeepoPauseManHud; 4 | import net.minecraft.client.MinecraftClient; 5 | import net.minecraft.client.gui.hud.InGameHud; 6 | import net.minecraft.client.util.math.MatrixStack; 7 | import net.minecraft.text.Text; 8 | import org.jetbrains.annotations.Nullable; 9 | import org.spongepowered.asm.mixin.Final; 10 | import org.spongepowered.asm.mixin.Mixin; 11 | import org.spongepowered.asm.mixin.Shadow; 12 | import org.spongepowered.asm.mixin.Unique; 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(InGameHud.class) 18 | public abstract class InGameHudMixin { 19 | @Shadow private @Nullable Text title; 20 | @Shadow private @Nullable Text subtitle; 21 | @Shadow private int titleTotalTicks; 22 | @Shadow private int titleFadeInTicks; 23 | @Shadow private int titleRemainTicks; 24 | @Shadow private int titleFadeOutTicks; 25 | @Shadow @Final private MinecraftClient client; 26 | @Unique private PeepoPauseManHud peepoPauseManHud; 27 | 28 | // TODO: this is extremely concerning 29 | @Inject(method = "setTitles", at = @At("HEAD"), cancellable = true) 30 | private void peepoPractice$cancelDefaultFunc(Text text, Text text2, int i, int j, int k, CallbackInfo ci) { 31 | ci.cancel(); 32 | 33 | if (text == null && text2 == null && i < 0 && j < 0 && k < 0) { 34 | this.title = null; 35 | this.subtitle = null; 36 | this.titleTotalTicks = 0; 37 | return; 38 | } 39 | if (text != null) { 40 | this.title = text; 41 | this.titleTotalTicks = this.titleFadeInTicks + this.titleRemainTicks + this.titleFadeOutTicks; 42 | } 43 | if (text2 != null) { 44 | this.subtitle = text2; 45 | } 46 | if (i >= 0) { 47 | this.titleFadeInTicks = i; 48 | } 49 | if (j >= 0) { 50 | this.titleRemainTicks = j; 51 | } 52 | if (k >= 0) { 53 | this.titleFadeOutTicks = k; 54 | } 55 | if (this.titleTotalTicks > 0) { 56 | this.titleTotalTicks = this.titleFadeInTicks + this.titleRemainTicks + this.titleFadeOutTicks; 57 | } 58 | } 59 | 60 | @Inject(method = "render", at = @At("TAIL")) 61 | private void peepoPractice$renderPeepoPauseMan(MatrixStack matrixStack, float f, CallbackInfo ci) { 62 | if (this.peepoPauseManHud == null) { 63 | this.peepoPauseManHud = new PeepoPauseManHud(this.client); 64 | } 65 | this.peepoPauseManHud.render(matrixStack); 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /src/main/java/me/falu/peepopractice/mixin/gui/renderer/ItemRendererMixin.java: -------------------------------------------------------------------------------- 1 | package me.falu.peepopractice.mixin.gui.renderer; 2 | 3 | import com.llamalad7.mixinextras.sugar.Local; 4 | import net.minecraft.client.render.item.ItemRenderer; 5 | import net.minecraft.item.ItemStack; 6 | import org.spongepowered.asm.mixin.Mixin; 7 | import org.spongepowered.asm.mixin.Shadow; 8 | import org.spongepowered.asm.mixin.Unique; 9 | import org.spongepowered.asm.mixin.injection.At; 10 | import org.spongepowered.asm.mixin.injection.ModifyArg; 11 | 12 | @Mixin(ItemRenderer.class) 13 | public abstract class ItemRendererMixin { 14 | @Shadow public float zOffset; 15 | 16 | @Unique 17 | private static boolean shouldScale(ItemStack stack) { 18 | return stack.getTag() != null && (stack.getTag().contains("MaxCount") || stack.getTag().contains("MinCount")); 19 | } 20 | 21 | @ModifyArg(method = "renderGuiItemModel", at = @At(value = "INVOKE", target = "Lcom/mojang/blaze3d/systems/RenderSystem;translatef(FFF)V", ordinal = 0), index = 2) 22 | private float peepoPractice$bringForward(float z, @Local(argsOnly = true) ItemStack stack) { 23 | if (shouldScale(stack)) { 24 | return this.zOffset; 25 | } 26 | return z; 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/main/java/me/falu/peepopractice/mixin/gui/screen/CreateWorldScreenMixin.java: -------------------------------------------------------------------------------- 1 | package me.falu.peepopractice.mixin.gui.screen; 2 | 3 | import me.falu.peepopractice.PeepoPractice; 4 | import me.falu.peepopractice.core.category.PracticeCategoriesAny; 5 | import me.falu.peepopractice.core.category.PracticeCategory; 6 | import me.falu.peepopractice.core.category.properties.WorldProperties; 7 | import me.falu.peepopractice.core.category.utils.PracticeCategoryUtils; 8 | import net.minecraft.client.gui.Element; 9 | import net.minecraft.client.gui.screen.GameMenuScreen; 10 | import net.minecraft.client.gui.screen.Screen; 11 | import net.minecraft.client.gui.screen.world.CreateWorldScreen; 12 | import net.minecraft.client.gui.screen.world.MoreOptionsDialog; 13 | import net.minecraft.client.gui.widget.ButtonWidget; 14 | import net.minecraft.client.world.GeneratorType; 15 | import net.minecraft.text.Text; 16 | import net.minecraft.util.registry.Registry; 17 | import net.minecraft.world.Difficulty; 18 | import net.minecraft.world.biome.layer.BiomeLayers; 19 | import org.spongepowered.asm.mixin.Final; 20 | import org.spongepowered.asm.mixin.Mixin; 21 | import org.spongepowered.asm.mixin.Shadow; 22 | import org.spongepowered.asm.mixin.injection.At; 23 | import org.spongepowered.asm.mixin.injection.Inject; 24 | import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; 25 | 26 | @Mixin(CreateWorldScreen.class) 27 | public abstract class CreateWorldScreenMixin extends Screen { 28 | @Shadow @Final public MoreOptionsDialog moreOptionsDialog; 29 | @Shadow public boolean hardcore; 30 | @Shadow private Difficulty difficulty; 31 | @Shadow private Difficulty safeDifficulty; 32 | @Shadow private boolean cheatsEnabled; 33 | @Shadow private boolean tweakedCheats; 34 | @Shadow private String levelName; 35 | 36 | protected CreateWorldScreenMixin(Text title) { 37 | super(title); 38 | } 39 | 40 | @Shadow 41 | protected abstract void createLevel(); 42 | 43 | @Inject(method = "(Lnet/minecraft/client/gui/screen/Screen;Lnet/minecraft/client/gui/screen/world/MoreOptionsDialog;)V", at = @At("TAIL")) 44 | private void peepoPractice$setWorldProperties(CallbackInfo ci) { 45 | PracticeCategory category = PeepoPractice.CATEGORY; 46 | if (!category.equals(PracticeCategoriesAny.EMPTY)) { 47 | this.difficulty = this.safeDifficulty = category.hasWorldProperties() ? category.getWorldProperties().getStartDifficulty() : Difficulty.EASY; 48 | this.cheatsEnabled = this.tweakedCheats = true; 49 | this.levelName = category.getTranslatedName().getString(); 50 | } 51 | } 52 | 53 | @Inject(method = "init", at = @At("TAIL")) 54 | private void peepoPractice$startCreateLevel(CallbackInfo ci) { 55 | if (PeepoPractice.CATEGORY.hasWorldProperties()) { 56 | for (WorldProperties.BiomeModification entry : PeepoPractice.CATEGORY.getWorldProperties().getProBiomes()) { 57 | if (entry.isInfinite() && entry.getRange().shouldPlace() && BiomeLayers.isOcean(Registry.BIOME.getRawId(entry.getBiome()))) { 58 | this.moreOptionsDialog.generatorType = java.util.Optional.of(GeneratorType.SINGLE_BIOME_SURFACE); 59 | this.moreOptionsDialog.setGeneratorOptions(GeneratorType.createFixedBiomeOptions(this.moreOptionsDialog.getGeneratorOptions(this.hardcore), GeneratorType.SINGLE_BIOME_SURFACE, entry.getBiome())); 60 | break; 61 | } 62 | } 63 | } 64 | if (!PeepoPractice.CATEGORY.equals(PracticeCategoriesAny.EMPTY)) { 65 | this.createLevel(); 66 | } 67 | } 68 | 69 | @Inject(method = "createLevel", at = @At("HEAD")) 70 | private void peepoPractice$checkDisconnected(CallbackInfo ci) { 71 | if (!PeepoPractice.CATEGORY.equals(PracticeCategoriesAny.EMPTY)) { 72 | if (PeepoPractice.CATEGORY.hasSplitEvent()) { 73 | PeepoPractice.CATEGORY.getSplitEvent().incrementAttempts(); 74 | } 75 | if (this.client != null && this.client.isIntegratedServerRunning()) { 76 | if (PeepoPractice.HAS_FAST_RESET) { 77 | GameMenuScreen screen = new GameMenuScreen(true); 78 | screen.init(this.client, this.width, this.height); 79 | for (Element element : screen.children()) { 80 | if (element instanceof ButtonWidget) { 81 | ButtonWidget button = (ButtonWidget) element; 82 | if (button.getMessage().getString().equals("Save & Quit")) { 83 | PeepoPractice.RESET_CATEGORY = false; 84 | button.onPress(); 85 | return; 86 | } 87 | } 88 | } 89 | } 90 | PracticeCategoryUtils.quit(false); 91 | } 92 | } 93 | } 94 | } 95 | -------------------------------------------------------------------------------- /src/main/java/me/falu/peepopractice/mixin/gui/screen/OpenToLanScreenMixin.java: -------------------------------------------------------------------------------- 1 | package me.falu.peepopractice.mixin.gui.screen; 2 | 3 | import com.llamalad7.mixinextras.injector.wrapoperation.Operation; 4 | import com.llamalad7.mixinextras.injector.wrapoperation.WrapOperation; 5 | import me.falu.peepopractice.PeepoPractice; 6 | import me.falu.peepopractice.core.category.PracticeCategoriesAny; 7 | import net.minecraft.client.gui.screen.OpenToLanScreen; 8 | import net.minecraft.server.integrated.IntegratedServer; 9 | import net.minecraft.world.GameMode; 10 | import org.spongepowered.asm.mixin.Mixin; 11 | import org.spongepowered.asm.mixin.injection.At; 12 | 13 | @Mixin(OpenToLanScreen.class) 14 | public abstract class OpenToLanScreenMixin { 15 | @SuppressWarnings("DefaultAnnotationParam") 16 | @WrapOperation(method = "method_19851", at = @At(value = "INVOKE", target = "Lnet/minecraft/server/integrated/IntegratedServer;openToLan(Lnet/minecraft/world/GameMode;ZI)Z", remap = true), remap = false) 17 | private boolean peepoPractice$disableLan(IntegratedServer server, GameMode gameMode, boolean cheatsAllowed, int port, Operation original) { 18 | if (!PeepoPractice.CATEGORY.equals(PracticeCategoriesAny.EMPTY)) { 19 | return true; 20 | } 21 | return original.call(server, gameMode, cheatsAllowed, port); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/main/java/me/falu/peepopractice/mixin/gui/screen/ScreenMixin.java: -------------------------------------------------------------------------------- 1 | package me.falu.peepopractice.mixin.gui.screen; 2 | 3 | import net.minecraft.client.MinecraftClient; 4 | import net.minecraft.client.font.TextRenderer; 5 | import net.minecraft.client.gui.AbstractParentElement; 6 | import net.minecraft.client.gui.screen.Screen; 7 | import net.minecraft.client.gui.widget.AbstractButtonWidget; 8 | import org.jetbrains.annotations.Nullable; 9 | import org.spongepowered.asm.mixin.Mixin; 10 | import org.spongepowered.asm.mixin.Shadow; 11 | import org.spongepowered.asm.mixin.injection.At; 12 | import org.spongepowered.asm.mixin.injection.Inject; 13 | import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; 14 | 15 | @Mixin(Screen.class) 16 | public abstract class ScreenMixin extends AbstractParentElement { 17 | @Shadow public int width; 18 | @Shadow public int height; 19 | @Shadow @Nullable protected MinecraftClient client; 20 | @Shadow protected TextRenderer textRenderer; 21 | 22 | @Inject(method = "addButton", at = @At("HEAD")) 23 | protected void peepoPractice$onButtonAdded(T button, CallbackInfoReturnable cir) { } 24 | } 25 | -------------------------------------------------------------------------------- /src/main/java/me/falu/peepopractice/mixin/gui/screen/TitleScreenMixin.java: -------------------------------------------------------------------------------- 1 | package me.falu.peepopractice.mixin.gui.screen; 2 | 3 | import me.falu.peepopractice.gui.screen.CategorySelectionScreen; 4 | import net.minecraft.client.gui.screen.Screen; 5 | import net.minecraft.client.gui.screen.TitleScreen; 6 | import net.minecraft.client.gui.widget.ButtonWidget; 7 | import net.minecraft.text.LiteralText; 8 | import net.minecraft.text.Text; 9 | import org.spongepowered.asm.mixin.Mixin; 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(TitleScreen.class) 15 | public abstract class TitleScreenMixin extends Screen { 16 | protected TitleScreenMixin(Text title) { 17 | super(title); 18 | } 19 | 20 | @Inject(method = "initWidgetsNormal", at = @At("TAIL")) 21 | private void peepoPractice$addPracticeButton(int y, int spacingY, CallbackInfo ci) { 22 | this.addButton( 23 | new ButtonWidget( 24 | this.width / 2 - 100, 25 | y - spacingY, 26 | 200, 27 | 20, 28 | new LiteralText("PeepoPractice"), 29 | b -> { 30 | if (this.client != null) { 31 | this.client.openScreen(new CategorySelectionScreen(this)); 32 | } 33 | } 34 | ) 35 | ); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/main/java/me/falu/peepopractice/mixin/gui/widget/SliderWidgetMixin.java: -------------------------------------------------------------------------------- 1 | package me.falu.peepopractice.mixin.gui.widget; 2 | 3 | import net.minecraft.client.gui.widget.AbstractButtonWidget; 4 | import net.minecraft.client.gui.widget.SliderWidget; 5 | import net.minecraft.text.Text; 6 | import org.spongepowered.asm.mixin.Mixin; 7 | import org.spongepowered.asm.mixin.injection.At; 8 | import org.spongepowered.asm.mixin.injection.ModifyArg; 9 | 10 | @Mixin(SliderWidget.class) 11 | public abstract class SliderWidgetMixin extends AbstractButtonWidget { 12 | public SliderWidgetMixin(int x, int y, int width, int height, Text message) { 13 | super(x, y, width, height, message); 14 | } 15 | 16 | @ModifyArg(method = "renderBg", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/gui/widget/SliderWidget;drawTexture(Lnet/minecraft/client/util/math/MatrixStack;IIIIII)V", ordinal = 0), index = 6) 17 | private int dynamicHeight1(int par2) { 18 | return this.height; 19 | } 20 | 21 | @ModifyArg(method = "renderBg", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/gui/widget/SliderWidget;drawTexture(Lnet/minecraft/client/util/math/MatrixStack;IIIIII)V", ordinal = 1), index = 6) 22 | private int dynamicHeight2(int par2) { 23 | return this.height; 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/main/java/me/falu/peepopractice/mixin/storage/FileResourcePackProviderMixin.java: -------------------------------------------------------------------------------- 1 | package me.falu.peepopractice.mixin.storage; 2 | 3 | import me.falu.peepopractice.PeepoPractice; 4 | import me.falu.peepopractice.core.category.properties.WorldProperties; 5 | import me.falu.peepopractice.core.writer.DefaultFileWriter; 6 | import net.minecraft.resource.FileResourcePackProvider; 7 | import net.minecraft.resource.ResourcePack; 8 | import net.minecraft.resource.ResourcePackProfile; 9 | import net.minecraft.resource.ResourcePackSource; 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 | import java.io.File; 18 | import java.util.function.Consumer; 19 | import java.util.function.Supplier; 20 | 21 | @Mixin(FileResourcePackProvider.class) 22 | public abstract class FileResourcePackProviderMixin { 23 | @Shadow @Final private ResourcePackSource source; 24 | 25 | @Shadow 26 | protected abstract Supplier createResourcePack(File file); 27 | 28 | @Inject(method = "register", at = @At("TAIL")) 29 | private void peepoPractice$addCustomDataPacks(Consumer consumer, ResourcePackProfile.Factory factory, CallbackInfo ci) { 30 | if (PeepoPractice.CATEGORY.hasWorldProperties()) { 31 | WorldProperties props = PeepoPractice.CATEGORY.getWorldProperties(); 32 | for (String dataPack : props.getDataPacks()) { 33 | String string = "peepoPractice/" + dataPack; 34 | String suffix = ".zip"; 35 | File file = DefaultFileWriter.INSTANCE.getResourceAsFile("datapacks/" + dataPack + suffix, dataPack + suffix); 36 | T profile = ResourcePackProfile.of(string, true, this.createResourcePack(file), factory, ResourcePackProfile.InsertionPosition.TOP, this.source); 37 | if (profile == null) { 38 | continue; 39 | } 40 | consumer.accept(profile); 41 | } 42 | } 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /src/main/java/me/falu/peepopractice/mixin/storage/StorageIoWorkerMixin.java: -------------------------------------------------------------------------------- 1 | package me.falu.peepopractice.mixin.storage; 2 | 3 | import me.falu.peepopractice.owner.GenerationShutdownOwner; 4 | import net.minecraft.util.thread.TaskExecutor; 5 | import net.minecraft.util.thread.TaskQueue; 6 | import net.minecraft.world.storage.RegionBasedStorage; 7 | import net.minecraft.world.storage.StorageIoWorker; 8 | import org.apache.logging.log4j.Logger; 9 | import org.spongepowered.asm.mixin.Final; 10 | import org.spongepowered.asm.mixin.Mixin; 11 | import org.spongepowered.asm.mixin.Shadow; 12 | 13 | @Mixin(StorageIoWorker.class) 14 | public class StorageIoWorkerMixin implements GenerationShutdownOwner { 15 | @Shadow @Final private static Logger LOGGER; 16 | @Shadow @Final private TaskExecutor executor; 17 | @Shadow @Final private RegionBasedStorage storage; 18 | 19 | @Override 20 | public void peepoPractice$shutdown() { 21 | this.executor.close(); 22 | try { 23 | this.storage.close(); 24 | } catch (Exception e) { 25 | LOGGER.error("Failed to close storage:", e); 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/main/java/me/falu/peepopractice/mixin/storage/ThreadedAnvilChunkStorageMixin.java: -------------------------------------------------------------------------------- 1 | package me.falu.peepopractice.mixin.storage; 2 | 3 | import com.mojang.datafixers.DataFixer; 4 | import me.falu.peepopractice.owner.GenerationShutdownOwner; 5 | import net.minecraft.server.world.ChunkTaskPrioritySystem; 6 | import net.minecraft.server.world.ThreadedAnvilChunkStorage; 7 | import net.minecraft.world.poi.PointOfInterestStorage; 8 | import net.minecraft.world.storage.VersionedChunkStorage; 9 | import org.spongepowered.asm.mixin.Final; 10 | import org.spongepowered.asm.mixin.Mixin; 11 | import org.spongepowered.asm.mixin.Shadow; 12 | 13 | import java.io.File; 14 | 15 | @Mixin(ThreadedAnvilChunkStorage.class) 16 | public abstract class ThreadedAnvilChunkStorageMixin extends VersionedChunkStorage implements GenerationShutdownOwner { 17 | @Shadow @Final private ChunkTaskPrioritySystem chunkTaskPrioritySystem; 18 | 19 | public ThreadedAnvilChunkStorageMixin(File file, DataFixer dataFixer, boolean bl) { 20 | super(file, dataFixer, bl); 21 | } 22 | 23 | @Shadow 24 | protected abstract PointOfInterestStorage getPointOfInterestStorage(); 25 | 26 | @Override 27 | public void peepoPractice$shutdown() { 28 | try { 29 | this.chunkTaskPrioritySystem.close(); 30 | ((GenerationShutdownOwner) this.getPointOfInterestStorage().worker).peepoPractice$shutdown(); 31 | } finally { 32 | ((GenerationShutdownOwner) this.worker).peepoPractice$shutdown(); 33 | } 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/main/java/me/falu/peepopractice/mixin/structure/ChunkGeneratorMixin.java: -------------------------------------------------------------------------------- 1 | package me.falu.peepopractice.mixin.structure; 2 | 3 | import me.falu.peepopractice.PeepoPractice; 4 | import me.falu.peepopractice.core.category.properties.StructureProperties; 5 | import net.minecraft.client.MinecraftClient; 6 | import net.minecraft.structure.StructureManager; 7 | import net.minecraft.util.math.ChunkPos; 8 | import net.minecraft.world.biome.Biome; 9 | import net.minecraft.world.biome.source.BiomeSource; 10 | import net.minecraft.world.chunk.Chunk; 11 | import net.minecraft.world.chunk.ChunkStatus; 12 | import net.minecraft.world.gen.StructureAccessor; 13 | import net.minecraft.world.gen.chunk.ChunkGenerator; 14 | import net.minecraft.world.gen.feature.ConfiguredStructureFeature; 15 | import net.minecraft.world.gen.feature.StructureFeature; 16 | import org.spongepowered.asm.mixin.Final; 17 | import org.spongepowered.asm.mixin.Mixin; 18 | import org.spongepowered.asm.mixin.Shadow; 19 | import org.spongepowered.asm.mixin.Unique; 20 | import org.spongepowered.asm.mixin.injection.At; 21 | import org.spongepowered.asm.mixin.injection.Inject; 22 | import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; 23 | 24 | import java.util.List; 25 | 26 | @Mixin(ChunkGenerator.class) 27 | public abstract class ChunkGeneratorMixin { 28 | @Shadow @Final public List strongholdPositions; 29 | @Shadow @Final protected BiomeSource biomeSource; 30 | 31 | @Shadow 32 | protected abstract void setStructureStart(ConfiguredStructureFeature configuredStructureFeature, StructureAccessor structureAccessor, Chunk chunk, StructureManager structureManager, long l, ChunkPos chunkPos, Biome biome); 33 | 34 | @Unique 35 | private StructureProperties getUniqueStronghold() { 36 | for (StructureProperties properties : PeepoPractice.CATEGORY.getStructureProperties()) { 37 | if (properties.isSameStructure(StructureFeature.STRONGHOLD) && !properties.isGeneratable()) { 38 | return properties; 39 | } 40 | } 41 | return null; 42 | } 43 | 44 | @Inject(method = "setStructureStarts", at = @At("HEAD")) 45 | private void peepoPractice$customStructureStarts(StructureAccessor structureAccessor, Chunk chunk, StructureManager structureManager, long l, CallbackInfo ci) { 46 | for (StructureProperties properties : PeepoPractice.CATEGORY.getStructureProperties()) { 47 | if (!properties.isSameStructure(StructureFeature.STRONGHOLD)) { 48 | MinecraftClient client = MinecraftClient.getInstance(); 49 | if (client.world != null && properties.getChunkPos() != null) { 50 | ChunkPos chunkPos = client.world.getChunk(properties.getChunkPos().x, properties.getChunkPos().z, ChunkStatus.BIOMES).getPos(); 51 | Biome biome = this.biomeSource.getBiomeForNoiseGen((chunkPos.x << 2) + 2, 0, (chunkPos.z << 2) + 2); 52 | this.setStructureStart(properties.getStructure(), structureAccessor, chunk, structureManager, l, chunkPos, biome); 53 | } 54 | } 55 | } 56 | } 57 | 58 | @Inject(method = "generateStrongholdPositions", at = @At("HEAD"), cancellable = true) 59 | private void peepoPractice$cancelStrongholdLocations(CallbackInfo ci) { 60 | StructureProperties properties = this.getUniqueStronghold(); 61 | if (properties != null) { 62 | if (properties.hasChunkPos()) { 63 | this.strongholdPositions.add(properties.getChunkPos()); 64 | } 65 | ci.cancel(); 66 | } 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /src/main/java/me/falu/peepopractice/mixin/structure/StructureFeatureMixin.java: -------------------------------------------------------------------------------- 1 | package me.falu.peepopractice.mixin.structure; 2 | 3 | import me.falu.peepopractice.PeepoPractice; 4 | import me.falu.peepopractice.core.category.properties.StructureProperties; 5 | import net.minecraft.structure.StructureManager; 6 | import net.minecraft.structure.StructurePiece; 7 | import net.minecraft.structure.StructureStart; 8 | import net.minecraft.util.math.BlockBox; 9 | import net.minecraft.util.math.ChunkPos; 10 | import net.minecraft.world.biome.Biome; 11 | import net.minecraft.world.biome.source.BiomeSource; 12 | import net.minecraft.world.gen.ChunkRandom; 13 | import net.minecraft.world.gen.chunk.ChunkGenerator; 14 | import net.minecraft.world.gen.chunk.StructureConfig; 15 | import net.minecraft.world.gen.feature.FeatureConfig; 16 | import net.minecraft.world.gen.feature.StructureFeature; 17 | import org.spongepowered.asm.mixin.Mixin; 18 | import org.spongepowered.asm.mixin.Overwrite; 19 | import org.spongepowered.asm.mixin.Shadow; 20 | import org.spongepowered.asm.mixin.Unique; 21 | 22 | @Mixin(StructureFeature.class) 23 | public abstract class StructureFeatureMixin { 24 | @Shadow 25 | protected abstract boolean shouldStartAt(ChunkGenerator chunkGenerator, BiomeSource biomeSource, long l, ChunkRandom chunkRandom, int i, int j, Biome biome, ChunkPos chunkPos, C featureConfig); 26 | @Shadow 27 | public abstract ChunkPos getStartChunk(StructureConfig structureConfig, long l, ChunkRandom chunkRandom, int i, int j); 28 | @Shadow 29 | protected abstract StructureStart createStart(int i, int j, BlockBox blockBox, int k, long l); 30 | @Shadow 31 | public abstract String getName(); 32 | 33 | @Unique 34 | @SuppressWarnings("UnreachableCode") 35 | private boolean checkArtificialStructure(ChunkPos chunkPos) { 36 | for (StructureProperties properties : PeepoPractice.CATEGORY.getStructureProperties()) { 37 | if (properties.isSameStructure((StructureFeature) (Object) this)) { 38 | if (properties.hasChunkPos() && properties.getChunkPos().equals(chunkPos)) { 39 | if (!properties.isGeneratable() && !properties.hasGenerated()) { 40 | properties.setGenerated(); 41 | } 42 | return true; 43 | } 44 | } 45 | } 46 | return false; 47 | } 48 | 49 | /** 50 | * @author falu 51 | * @reason Custom structure starts 52 | * NOTE: Don't hate, it's copied from the source. 53 | */ 54 | @Overwrite 55 | @SuppressWarnings("UnreachableCode") 56 | public StructureStart tryPlaceStart(ChunkGenerator chunkGenerator, BiomeSource biomeSource, StructureManager structureManager, long l, ChunkPos chunkPos, Biome biome, int i, ChunkRandom chunkRandom, StructureConfig structureConfig, C featureConfig) { 57 | ChunkPos chunkPos2 = this.getStartChunk(structureConfig, l, chunkRandom, chunkPos.x, chunkPos.z); 58 | StructureProperties props = PeepoPractice.CATEGORY.findStructureProperties((StructureFeature) (Object) this); 59 | boolean bl1 = this.checkArtificialStructure(chunkPos); 60 | boolean bl2 = !bl1 && (props == null || props.isGeneratable()) && chunkPos.x == chunkPos2.x && chunkPos.z == chunkPos2.z && this.shouldStartAt(chunkGenerator, biomeSource, l, chunkRandom, chunkPos.x, chunkPos.z, biome, chunkPos2, featureConfig); 61 | if (bl1 || bl2) { 62 | StructureStart structureStart = this.createStart(chunkPos.x, chunkPos.z, BlockBox.empty(), i, l); 63 | structureStart.init(chunkGenerator, structureManager, chunkPos.x, chunkPos.z, biome, featureConfig); 64 | if (structureStart.hasChildren()) { 65 | for (StructureProperties properties : PeepoPractice.CATEGORY.getStructureProperties()) { 66 | if (properties.hasStructureTopY()) { 67 | if (properties.isSameStructure((StructureFeature) (Object) this)) { 68 | int difference = properties.getStructureTopY() - structureStart.getChildren().get(0).getBoundingBox().maxY; 69 | for (StructurePiece piece : structureStart.getChildren()) { 70 | piece.translate(0, difference, 0); 71 | } 72 | break; 73 | } 74 | } 75 | } 76 | return structureStart; 77 | } 78 | } 79 | return StructureStart.DEFAULT; 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /src/main/java/me/falu/peepopractice/mixin/structure/StructurePoolBasedGeneratorMixin.java: -------------------------------------------------------------------------------- 1 | package me.falu.peepopractice.mixin.structure; 2 | 3 | import com.llamalad7.mixinextras.injector.wrapoperation.Operation; 4 | import com.llamalad7.mixinextras.injector.wrapoperation.WrapOperation; 5 | import me.falu.peepopractice.PeepoPractice; 6 | import me.falu.peepopractice.core.category.properties.StructureProperties; 7 | import net.minecraft.structure.PoolStructurePiece; 8 | import net.minecraft.structure.StructureManager; 9 | import net.minecraft.structure.StructurePieceType; 10 | import net.minecraft.structure.pool.StructurePool; 11 | import net.minecraft.structure.pool.StructurePoolBasedGenerator; 12 | import net.minecraft.structure.pool.StructurePoolElement; 13 | import net.minecraft.structure.pool.StructurePoolRegistry; 14 | import net.minecraft.util.BlockRotation; 15 | import net.minecraft.util.Identifier; 16 | import net.minecraft.util.math.BlockPos; 17 | import net.minecraft.world.gen.chunk.ChunkGenerator; 18 | import net.minecraft.world.gen.feature.StructureFeature; 19 | import org.spongepowered.asm.mixin.Final; 20 | import org.spongepowered.asm.mixin.Mixin; 21 | import org.spongepowered.asm.mixin.Shadow; 22 | import org.spongepowered.asm.mixin.Unique; 23 | import org.spongepowered.asm.mixin.injection.At; 24 | import org.spongepowered.asm.mixin.injection.Inject; 25 | import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; 26 | 27 | import java.util.List; 28 | import java.util.Random; 29 | 30 | @Mixin(StructurePoolBasedGenerator.class) 31 | public abstract class StructurePoolBasedGeneratorMixin { 32 | @Shadow @Final public static StructurePoolRegistry REGISTRY; 33 | @Unique private static StructurePieceType TYPE; 34 | 35 | @Inject(method = "addPieces", at = @At("HEAD")) 36 | private static void peepoPractice$capturePieceType(Identifier startPoolId, int size, StructurePoolBasedGenerator.PieceFactory pieceFactory, ChunkGenerator chunkGenerator, StructureManager structureManager, BlockPos blockPos, List list, Random random, boolean bl, boolean bl2, CallbackInfo ci) { 37 | StructurePool structurePool = REGISTRY.get(startPoolId); 38 | StructurePoolElement structurePoolElement = structurePool.getRandomElement(random); 39 | PoolStructurePiece poolStructurePiece = pieceFactory.create(structureManager, structurePoolElement, blockPos, structurePoolElement.getGroundLevelDelta(), BlockRotation.NONE, structurePoolElement.getBoundingBox(structureManager, blockPos, BlockRotation.NONE)); 40 | TYPE = poolStructurePiece.getType(); 41 | } 42 | 43 | @WrapOperation(method = "addPieces", at = @At(value = "INVOKE", target = "Lnet/minecraft/util/BlockRotation;random(Ljava/util/Random;)Lnet/minecraft/util/BlockRotation;")) 44 | private static BlockRotation peepoPractice$setOrientation(Random random, Operation original) { 45 | for (StructureProperties properties : PeepoPractice.CATEGORY.getStructureProperties()) { 46 | if (properties.hasRotation()) { 47 | if (TYPE != null) { 48 | boolean valid = false; 49 | 50 | // trust me it's not hardcoded COPIUM it's just the 3 things that StructurePoolBasedGenerator mentions in its class soooo... 51 | if (TYPE.equals(StructurePieceType.BASTION_REMNANT) && properties.isSameStructure(StructureFeature.BASTION_REMNANT)) { 52 | valid = true; 53 | } else if (TYPE.equals(StructurePieceType.VILLAGE) && properties.isSameStructure(StructureFeature.VILLAGE)) { 54 | valid = true; 55 | } else if (TYPE.equals(StructurePieceType.PILLAGER_OUTPOST) && properties.isSameStructure(StructureFeature.PILLAGER_OUTPOST)) { 56 | valid = true; 57 | } 58 | 59 | if (valid) { 60 | TYPE = null; 61 | return properties.getRotation(); 62 | } 63 | } 64 | } 65 | } 66 | return original.call(random); 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /src/main/java/me/falu/peepopractice/mixin/structure/StructureStartMixin.java: -------------------------------------------------------------------------------- 1 | package me.falu.peepopractice.mixin.structure; 2 | 3 | import me.falu.peepopractice.PeepoPractice; 4 | import me.falu.peepopractice.core.ExecuteAtIndexArrayList; 5 | import me.falu.peepopractice.core.category.properties.StructureProperties; 6 | import net.minecraft.structure.StructurePiece; 7 | import net.minecraft.structure.StructureStart; 8 | import net.minecraft.util.math.BlockBox; 9 | import net.minecraft.world.gen.feature.StructureFeature; 10 | import org.spongepowered.asm.mixin.Final; 11 | import org.spongepowered.asm.mixin.Mixin; 12 | import org.spongepowered.asm.mixin.Mutable; 13 | import org.spongepowered.asm.mixin.Shadow; 14 | import org.spongepowered.asm.mixin.injection.At; 15 | import org.spongepowered.asm.mixin.injection.Inject; 16 | import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; 17 | 18 | import java.util.List; 19 | 20 | @Mixin(StructureStart.class) 21 | public abstract class StructureStartMixin { 22 | @Mutable @Shadow @Final protected List children; 23 | 24 | @Inject(method = "", at = @At("TAIL")) 25 | private void peepoPractice$customChildrenListType(StructureFeature feature, int chunkX, int chunkZ, BlockBox box, int references, long seed, CallbackInfo ci) { 26 | this.children = new ExecuteAtIndexArrayList<>(piece -> { 27 | for (StructureProperties properties : PeepoPractice.CATEGORY.getStructureProperties()) { 28 | if (properties.isSameStructure(feature) && properties.hasOrientation()) { 29 | piece.setOrientation(properties.getOrientation()); 30 | } 31 | } 32 | }, 0); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/main/java/me/falu/peepopractice/mixin/structure/category/BastionRemnantFeatureConfigMixin.java: -------------------------------------------------------------------------------- 1 | package me.falu.peepopractice.mixin.structure.category; 2 | 3 | import com.llamalad7.mixinextras.injector.ModifyReturnValue; 4 | import me.falu.peepopractice.PeepoPractice; 5 | import me.falu.peepopractice.core.category.preferences.CategoryPreferences; 6 | import me.falu.peepopractice.core.category.preferences.PreferenceTypes; 7 | import net.minecraft.world.gen.feature.BastionRemnantFeatureConfig; 8 | import net.minecraft.world.gen.feature.StructureFeature; 9 | import net.minecraft.world.gen.feature.StructurePoolFeatureConfig; 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 | 15 | import java.util.List; 16 | 17 | @Mixin(BastionRemnantFeatureConfig.class) 18 | public abstract class BastionRemnantFeatureConfigMixin { 19 | @Shadow @Final private List possibleConfigs; 20 | 21 | @ModifyReturnValue(method = "getRandom", at = @At("RETURN")) 22 | private StructurePoolFeatureConfig peepoPractice$bastionType(StructurePoolFeatureConfig config) { 23 | if (PeepoPractice.CATEGORY.findStructureProperties(StructureFeature.BASTION_REMNANT) != null) { 24 | PreferenceTypes.BastionType bastionType = CategoryPreferences.BASTION_TYPE.getValue(); 25 | int index = bastionType.equals(PreferenceTypes.BastionType.RANDOM) ? this.possibleConfigs.indexOf(config) : bastionType.id; 26 | PeepoPractice.CATEGORY.putCustomValue("bastionType", index); 27 | return this.possibleConfigs.get(index); 28 | } 29 | PeepoPractice.CATEGORY.putCustomValue("bastionType", this.possibleConfigs.indexOf(config)); 30 | return config; 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/main/java/me/falu/peepopractice/mixin/structure/category/DungeonFeatureMixin.java: -------------------------------------------------------------------------------- 1 | package me.falu.peepopractice.mixin.structure.category; 2 | 3 | import me.falu.peepopractice.core.category.preferences.CategoryPreferences; 4 | import net.minecraft.world.gen.feature.DungeonFeature; 5 | import org.spongepowered.asm.mixin.Mixin; 6 | import org.spongepowered.asm.mixin.injection.At; 7 | import org.spongepowered.asm.mixin.injection.Inject; 8 | import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; 9 | 10 | @Mixin(DungeonFeature.class) 11 | public abstract class DungeonFeatureMixin { 12 | @Inject(method = "generate(Lnet/minecraft/world/ServerWorldAccess;Lnet/minecraft/world/gen/StructureAccessor;Lnet/minecraft/world/gen/chunk/ChunkGenerator;Ljava/util/Random;Lnet/minecraft/util/math/BlockPos;Lnet/minecraft/world/gen/feature/DefaultFeatureConfig;)Z", at = @At("HEAD"), cancellable = true) 13 | private void peepoPractice$cancelDungeonGen(CallbackInfoReturnable cir) { 14 | if (CategoryPreferences.DISABLE_DUNGEONS.getBoolValue()) { 15 | cir.setReturnValue(true); 16 | } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/main/java/me/falu/peepopractice/mixin/structure/category/PortalRoomMixin.java: -------------------------------------------------------------------------------- 1 | package me.falu.peepopractice.mixin.structure.category; 2 | 3 | import me.falu.peepopractice.core.category.preferences.CategoryPreferences; 4 | import me.falu.peepopractice.core.category.preferences.PreferenceTypes; 5 | import net.minecraft.structure.StrongholdGenerator; 6 | import org.spongepowered.asm.mixin.Mixin; 7 | import org.spongepowered.asm.mixin.injection.Constant; 8 | import org.spongepowered.asm.mixin.injection.ModifyConstant; 9 | 10 | @Mixin(StrongholdGenerator.PortalRoom.class) 11 | public abstract class PortalRoomMixin { 12 | @ModifyConstant(method = "generate(Lnet/minecraft/world/ServerWorldAccess;Lnet/minecraft/world/gen/StructureAccessor;Lnet/minecraft/world/gen/chunk/ChunkGenerator;Ljava/util/Random;Lnet/minecraft/util/math/BlockBox;Lnet/minecraft/util/math/ChunkPos;Lnet/minecraft/util/math/BlockPos;)Z", constant = @Constant(floatValue = 0.9F)) 13 | private float peepoPractice$setEyeCount(float constant) { 14 | PreferenceTypes.EyeCountType eyeCountType = CategoryPreferences.EYE_COUNT.getValue(); 15 | switch (eyeCountType) { 16 | case ALL: 17 | return 0.0F; 18 | case RANDOM: 19 | return constant; 20 | case NONE: 21 | return 1.1F; 22 | } 23 | return constant; 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/main/java/me/falu/peepopractice/mixin/world/ChestBlockEntityMixin.java: -------------------------------------------------------------------------------- 1 | package me.falu.peepopractice.mixin.world; 2 | 3 | import me.falu.peepopractice.PeepoPractice; 4 | import me.falu.peepopractice.core.category.properties.event.InteractLootChestSplitEvent; 5 | import net.minecraft.block.entity.ChestBlockEntity; 6 | import net.minecraft.entity.player.PlayerEntity; 7 | import net.minecraft.screen.ScreenHandler; 8 | import net.minecraft.util.Identifier; 9 | import org.spongepowered.asm.mixin.Mixin; 10 | import org.spongepowered.asm.mixin.Unique; 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 | import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; 15 | 16 | @Mixin(ChestBlockEntity.class) 17 | public abstract class ChestBlockEntityMixin extends LootableContainerBlockEntityMixin { 18 | @Unique private Identifier localLootTableId = this.lootTableId; 19 | @Unique private boolean valid = false; 20 | 21 | @Unique 22 | private InteractLootChestSplitEvent peepoPractice$getCorrespondingEvent(boolean close) { 23 | if (PeepoPractice.CATEGORY.hasSplitEvent()) { 24 | if (PeepoPractice.CATEGORY.getSplitEvent() instanceof InteractLootChestSplitEvent) { 25 | InteractLootChestSplitEvent event = (InteractLootChestSplitEvent) PeepoPractice.CATEGORY.getSplitEvent(); 26 | if (event.hasLootTable() && event.getLootTable().equals(this.localLootTableId)) { 27 | if ((event.isOnClose() && close) || (!event.isOnClose() && !close)) { 28 | return event; 29 | } 30 | } 31 | } 32 | } 33 | return null; 34 | } 35 | 36 | @Inject(method = "onOpen", at = @At("HEAD")) 37 | private void peepoPractice$openChest(PlayerEntity player, CallbackInfo ci) { 38 | InteractLootChestSplitEvent event = this.peepoPractice$getCorrespondingEvent(false); 39 | if (event != null) { 40 | event.complete(!player.isDead()); 41 | this.valid = true; 42 | } 43 | } 44 | 45 | @Inject(method = "onClose", at = @At("HEAD")) 46 | private void peepoPractice$closeChest(PlayerEntity player, CallbackInfo ci) { 47 | InteractLootChestSplitEvent event = this.peepoPractice$getCorrespondingEvent(true); 48 | if (event != null) { 49 | event.complete(!player.isDead()); 50 | this.valid = true; 51 | } 52 | } 53 | 54 | @Override 55 | protected void peepoPractice$onCreateMenu(CallbackInfoReturnable cir) { 56 | if (this.valid) { 57 | cir.setReturnValue(null); 58 | } 59 | } 60 | 61 | @Override 62 | protected void peepoPractice$onCheckLootInteraction(CallbackInfo ci) { 63 | if (this.lootTableId != null) { 64 | this.localLootTableId = this.lootTableId; 65 | } 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /src/main/java/me/falu/peepopractice/mixin/world/LootableContainerBlockEntityMixin.java: -------------------------------------------------------------------------------- 1 | package me.falu.peepopractice.mixin.world; 2 | 3 | import net.minecraft.block.entity.LootableContainerBlockEntity; 4 | import net.minecraft.screen.ScreenHandler; 5 | import net.minecraft.util.Identifier; 6 | import org.jetbrains.annotations.Nullable; 7 | import org.spongepowered.asm.mixin.Mixin; 8 | import org.spongepowered.asm.mixin.Shadow; 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 | import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; 13 | 14 | @Mixin(LootableContainerBlockEntity.class) 15 | public abstract class LootableContainerBlockEntityMixin { 16 | @Shadow @Nullable protected Identifier lootTableId; 17 | 18 | @SuppressWarnings("CancellableInjectionUsage") 19 | @Inject(method = "createMenu", at = @At("HEAD"), cancellable = true) 20 | protected void peepoPractice$onCreateMenu(CallbackInfoReturnable cir) { } 21 | 22 | @Inject(method = "checkLootInteraction", at = @At("HEAD")) 23 | protected void peepoPractice$onCheckLootInteraction(CallbackInfo ci) { } 24 | } 25 | -------------------------------------------------------------------------------- /src/main/java/me/falu/peepopractice/mixin/world/ServerWorldMixin.java: -------------------------------------------------------------------------------- 1 | package me.falu.peepopractice.mixin.world; 2 | 3 | import com.llamalad7.mixinextras.injector.ModifyReturnValue; 4 | import com.llamalad7.mixinextras.injector.v2.WrapWithCondition; 5 | import me.falu.peepopractice.PeepoPractice; 6 | import me.falu.peepopractice.core.category.preferences.CategoryPreferences; 7 | import net.minecraft.block.Blocks; 8 | import net.minecraft.server.world.ChunkTicketType; 9 | import net.minecraft.server.world.ServerChunkManager; 10 | import net.minecraft.server.world.ServerWorld; 11 | import net.minecraft.util.math.BlockPos; 12 | import net.minecraft.util.math.ChunkPos; 13 | import org.spongepowered.asm.mixin.Final; 14 | import org.spongepowered.asm.mixin.Mixin; 15 | import org.spongepowered.asm.mixin.Shadow; 16 | import org.spongepowered.asm.mixin.injection.At; 17 | import org.spongepowered.asm.mixin.injection.Inject; 18 | import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; 19 | 20 | @Mixin(ServerWorld.class) 21 | public abstract class ServerWorldMixin { 22 | @Shadow @Final public static BlockPos END_SPAWN_POS; 23 | 24 | @Inject(method = "createEndSpawnPlatform", at = @At("TAIL")) 25 | private static void peepoPractice$noCage(ServerWorld world, CallbackInfo ci) { 26 | if (CategoryPreferences.NO_CAGE_SPAWN.getBoolValue()) { 27 | BlockPos blockPos = END_SPAWN_POS; 28 | int i = blockPos.getX(); 29 | int j = blockPos.getY() - 2; 30 | int k = blockPos.getZ(); 31 | int xOffset = 6; 32 | int yOffset = 15; 33 | BlockPos.iterate(i - xOffset, j + 1, k - xOffset, i + xOffset, j + yOffset, k + xOffset).forEach(x -> world.setBlockState(x, Blocks.AIR.getDefaultState())); 34 | } 35 | } 36 | 37 | @WrapWithCondition(method = "setSpawnPos", at = @At(value = "INVOKE", target = "Lnet/minecraft/server/world/ServerChunkManager;addTicket(Lnet/minecraft/server/world/ChunkTicketType;Lnet/minecraft/util/math/ChunkPos;ILjava/lang/Object;)V")) 38 | private boolean peepoPractice$removeTicket(ServerChunkManager chunkManager, ChunkTicketType ticketType, ChunkPos pos, int radius, Object argument) { 39 | return !(PeepoPractice.CATEGORY.hasWorldProperties() && PeepoPractice.CATEGORY.getWorldProperties().isSpawnChunksDisabled()); 40 | } 41 | 42 | @ModifyReturnValue(method = "getSpawnPos", at = @At("RETURN")) 43 | private BlockPos peepoPractice$getCustomSpawnPos(BlockPos pos) { 44 | if (PeepoPractice.CATEGORY.hasPlayerProperties() && PeepoPractice.CATEGORY.getPlayerProperties().hasSpawnPos()) { 45 | return PeepoPractice.CATEGORY.getPlayerProperties().getSpawnPos(); 46 | } 47 | return pos; 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /src/main/java/me/falu/peepopractice/mixin/world/SpikeCacheMixin.java: -------------------------------------------------------------------------------- 1 | package me.falu.peepopractice.mixin.world; 2 | 3 | import com.llamalad7.mixinextras.injector.wrapoperation.Operation; 4 | import com.llamalad7.mixinextras.injector.wrapoperation.WrapOperation; 5 | import me.falu.peepopractice.core.category.preferences.CategoryPreferences; 6 | import me.falu.peepopractice.core.category.preferences.PreferenceTypes; 7 | import org.spongepowered.asm.mixin.Mixin; 8 | import org.spongepowered.asm.mixin.Unique; 9 | import org.spongepowered.asm.mixin.injection.At; 10 | 11 | import java.util.List; 12 | import java.util.Random; 13 | 14 | @Mixin(targets = "net/minecraft/world/gen/feature/EndSpikeFeature$SpikeCache") 15 | public abstract class SpikeCacheMixin { 16 | @WrapOperation(method = "load(Ljava/lang/Long;)Ljava/util/List;", at = @At(value = "INVOKE", target = "Ljava/util/Collections;shuffle(Ljava/util/List;Ljava/util/Random;)V")) 17 | private void peepoPractice$modifyTowerOrder(List towers, Random random, Operation original) { 18 | original.call(towers, random); 19 | PreferenceTypes.StartNodeType startNode = CategoryPreferences.START_NODE.getValue(); 20 | startNode = startNode.equals(PreferenceTypes.StartNodeType.RANDOM) ? this.getDragonType(towers) : startNode; 21 | if (startNode != this.getDragonType(towers)) { 22 | int temp = towers.get(0); 23 | towers.set(0, towers.get(5)); 24 | towers.set(5, temp); 25 | } 26 | PreferenceTypes.EndTowerHeightType towerHeight = CategoryPreferences.TOWER_HEIGHT.getValue(); 27 | if (towerHeight.equals(PreferenceTypes.EndTowerHeightType.RANDOM)) { 28 | return; 29 | } 30 | towers.set(startNode.equals(PreferenceTypes.StartNodeType.FRONT) ? 9 : 4, (towerHeight.height - 76) / 3); 31 | } 32 | 33 | @Unique 34 | private PreferenceTypes.StartNodeType getDragonType(List towers) { 35 | return towers.get(0) > towers.get(5) ? PreferenceTypes.StartNodeType.FRONT : PreferenceTypes.StartNodeType.BACK; 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/main/java/me/falu/peepopractice/mixin/world/UnderwaterCaveCarverMixin.java: -------------------------------------------------------------------------------- 1 | package me.falu.peepopractice.mixin.world; 2 | 3 | import me.falu.peepopractice.PeepoPractice; 4 | import me.falu.peepopractice.core.category.PracticeCategoriesAny; 5 | import net.minecraft.util.math.BlockPos; 6 | import net.minecraft.world.chunk.Chunk; 7 | import net.minecraft.world.gen.carver.Carver; 8 | import net.minecraft.world.gen.carver.UnderwaterCaveCarver; 9 | import net.minecraft.world.gen.carver.UnderwaterRavineCarver; 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.CallbackInfoReturnable; 14 | 15 | import java.util.BitSet; 16 | import java.util.Random; 17 | 18 | @Mixin(UnderwaterCaveCarver.class) 19 | public abstract class UnderwaterCaveCarverMixin { 20 | @Inject(method = "carveAtPoint(Lnet/minecraft/world/gen/carver/Carver;Lnet/minecraft/world/chunk/Chunk;Ljava/util/BitSet;Ljava/util/Random;Lnet/minecraft/util/math/BlockPos$Mutable;IIIIIIII)Z", at = @At("HEAD")) 21 | private static void peepoPractice$captureCarverLocation(Carver carver, Chunk chunk, BitSet mask, Random random, BlockPos.Mutable pos, int seaLevel, int mainChunkX, int mainChunkZ, int x, int z, int relativeX, int y, int relativeZ, CallbackInfoReturnable cir) { 22 | if (y < 10 && carver instanceof UnderwaterRavineCarver) { 23 | if (!PeepoPractice.CATEGORY.equals(PracticeCategoriesAny.EMPTY)) { 24 | if (!PeepoPractice.CATEGORY.hasCustomValue("ravinePosition")) { 25 | PeepoPractice.CATEGORY.putCustomValue("ravinePosition", new BlockPos(x, y, z)); 26 | } 27 | } 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/main/java/me/falu/peepopractice/mixin/world/biome/MultiNoiseBiomeSourceMixin.java: -------------------------------------------------------------------------------- 1 | package me.falu.peepopractice.mixin.world.biome; 2 | 3 | import com.llamalad7.mixinextras.injector.ModifyReturnValue; 4 | import me.falu.peepopractice.PeepoPractice; 5 | import me.falu.peepopractice.core.category.properties.WorldProperties; 6 | import net.minecraft.util.math.BlockPos; 7 | import net.minecraft.util.math.Vec3i; 8 | import net.minecraft.world.World; 9 | import net.minecraft.world.biome.Biome; 10 | import net.minecraft.world.biome.Biomes; 11 | import net.minecraft.world.biome.source.MultiNoiseBiomeSource; 12 | import org.spongepowered.asm.mixin.Mixin; 13 | import org.spongepowered.asm.mixin.injection.At; 14 | 15 | @Mixin(MultiNoiseBiomeSource.class) 16 | public abstract class MultiNoiseBiomeSourceMixin { 17 | @ModifyReturnValue(method = "getBiomeForNoiseGen", at = @At("RETURN")) 18 | private Biome peepoPractice$antiBiomeLogic(Biome biome, int biomeX, int biomeY, int biomeZ) { 19 | if (PeepoPractice.CATEGORY.hasWorldProperties()) { 20 | for (WorldProperties.BiomeModification entry : PeepoPractice.CATEGORY.getWorldProperties().getProBiomes()) { 21 | if (entry.getRange().isValidDimension(World.NETHER)) { 22 | if (entry.isInfinite() || new BlockPos(biomeX, biomeY, biomeZ).isWithinDistance(new Vec3i(0, 62, 0), entry.getRange().getRange())) { 23 | if (entry.getRange().shouldPlace()) { 24 | return entry.getBiome(); 25 | } 26 | } 27 | } 28 | } 29 | for (WorldProperties.BiomeModification entry : PeepoPractice.CATEGORY.getWorldProperties().getAntiBiomes()) { 30 | if (entry.getRange().isValidDimension(World.NETHER)) { 31 | if (entry.isInfinite() || new BlockPos(biomeX, biomeY, biomeZ).isWithinDistance(new Vec3i(0, 62, 0), entry.getRange().getRange())) { 32 | if (biome.equals(entry.getBiome())) { 33 | return entry.hasReplacement() ? entry.getReplacement() : Biomes.NETHER_WASTES; 34 | } 35 | } 36 | } 37 | } 38 | } 39 | return biome; 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/main/java/me/falu/peepopractice/mixin/world/biome/VanillaLayeredBiomeSourceMixin.java: -------------------------------------------------------------------------------- 1 | package me.falu.peepopractice.mixin.world.biome; 2 | 3 | import com.llamalad7.mixinextras.injector.ModifyReturnValue; 4 | import me.falu.peepopractice.PeepoPractice; 5 | import me.falu.peepopractice.core.category.properties.WorldProperties; 6 | import net.minecraft.util.math.BlockPos; 7 | import net.minecraft.util.math.Vec3i; 8 | import net.minecraft.world.World; 9 | import net.minecraft.world.biome.Biome; 10 | import net.minecraft.world.biome.Biomes; 11 | import net.minecraft.world.biome.source.VanillaLayeredBiomeSource; 12 | import org.spongepowered.asm.mixin.Mixin; 13 | import org.spongepowered.asm.mixin.injection.At; 14 | 15 | @Mixin(VanillaLayeredBiomeSource.class) 16 | public abstract class VanillaLayeredBiomeSourceMixin { 17 | @ModifyReturnValue(method = "getBiomeForNoiseGen", at = @At("RETURN")) 18 | private Biome peepoPractice$antiBiomeLogic(Biome biome, int biomeX, int biomeY, int biomeZ) { 19 | if (PeepoPractice.CATEGORY.hasWorldProperties()) { 20 | for (WorldProperties.BiomeModification entry : PeepoPractice.CATEGORY.getWorldProperties().getProBiomes()) { 21 | if (entry.getRange().isValidDimension(World.OVERWORLD)) { 22 | if (new BlockPos(biomeX, biomeY, biomeZ).isWithinDistance(new Vec3i(0, 62, 0), entry.getRange().getRange())) { 23 | if (entry.getRange().shouldPlace()) { 24 | return entry.getBiome(); 25 | } 26 | } 27 | } 28 | } 29 | for (WorldProperties.BiomeModification entry : PeepoPractice.CATEGORY.getWorldProperties().getAntiBiomes()) { 30 | if (entry.getRange().isValidDimension(World.OVERWORLD)) { 31 | if (entry.isInfinite() || new BlockPos(biomeX, biomeY, biomeZ).isWithinDistance(new Vec3i(0, 62, 0), entry.getRange().getRange())) { 32 | if (biome.equals(entry.getBiome())) { 33 | return entry.hasReplacement() ? entry.getReplacement() : Biomes.PLAINS; 34 | } 35 | } 36 | } 37 | } 38 | } 39 | return biome; 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/main/java/me/falu/peepopractice/mixin/world/category/CarverMixin.java: -------------------------------------------------------------------------------- 1 | package me.falu.peepopractice.mixin.world.category; 2 | 3 | import com.llamalad7.mixinextras.injector.wrapoperation.Operation; 4 | import com.llamalad7.mixinextras.injector.wrapoperation.WrapOperation; 5 | import com.mojang.serialization.Codec; 6 | import me.falu.peepopractice.PeepoPractice; 7 | import me.falu.peepopractice.core.category.PracticeCategoriesAny; 8 | import net.minecraft.world.gen.ProbabilityConfig; 9 | import net.minecraft.world.gen.carver.Carver; 10 | import net.minecraft.world.gen.carver.ConfiguredCarver; 11 | import org.spongepowered.asm.mixin.Final; 12 | import org.spongepowered.asm.mixin.Mixin; 13 | import org.spongepowered.asm.mixin.Shadow; 14 | import org.spongepowered.asm.mixin.injection.At; 15 | 16 | @Mixin(Carver.class) 17 | public abstract class CarverMixin { 18 | @Shadow @Final public static Carver UNDERWATER_CANYON; 19 | 20 | @Shadow 21 | public abstract Codec> getCodec(); 22 | 23 | @WrapOperation(method = "carveRegion", at = @At(value = "INVOKE", target = "Ljava/lang/Math;min(II)I", ordinal = 1)) 24 | private int peepoPractice$alwaysExposed(int a, int b, Operation original) { 25 | if (PeepoPractice.CATEGORY.equals(PracticeCategoriesAny.RAVINE_ENTER_SPLIT) && this.getCodec() == UNDERWATER_CANYON.getCodec()) { 26 | return b; 27 | } 28 | return original.call(a, b); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/main/java/me/falu/peepopractice/mixin/world/category/IglooGeneratorMixin.java: -------------------------------------------------------------------------------- 1 | package me.falu.peepopractice.mixin.world.category; 2 | 3 | import me.falu.peepopractice.PeepoPractice; 4 | import net.minecraft.structure.IglooGenerator; 5 | import org.spongepowered.asm.mixin.Mixin; 6 | import org.spongepowered.asm.mixin.injection.Constant; 7 | import org.spongepowered.asm.mixin.injection.ModifyConstant; 8 | 9 | @Mixin(IglooGenerator.class) 10 | public class IglooGeneratorMixin { 11 | @ModifyConstant(method = "addPieces", constant = @Constant(doubleValue = 0.5D)) 12 | private static double peepoPractice$guaranteeIglooBasement(double constant) { 13 | if (PeepoPractice.CATEGORY.hasPermaValue("guaranteeIglooBasement")) { 14 | if ((Boolean) PeepoPractice.CATEGORY.getPermaValue("guaranteeIglooBasement")) { 15 | return 1.0D; 16 | } 17 | } 18 | return constant; 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/main/java/me/falu/peepopractice/mixin/world/category/RuleStructureProcessorMixin.java: -------------------------------------------------------------------------------- 1 | package me.falu.peepopractice.mixin.world.category; 2 | 3 | import com.llamalad7.mixinextras.sugar.Local; 4 | import me.falu.peepopractice.PeepoPractice; 5 | import me.falu.peepopractice.core.category.PracticeCategoriesAny; 6 | import net.minecraft.structure.processor.RuleStructureProcessor; 7 | import net.minecraft.util.math.BlockPos; 8 | import org.spongepowered.asm.mixin.Mixin; 9 | import org.spongepowered.asm.mixin.injection.At; 10 | import org.spongepowered.asm.mixin.injection.Redirect; 11 | 12 | import java.util.Random; 13 | 14 | @Mixin(RuleStructureProcessor.class) 15 | public class RuleStructureProcessorMixin { 16 | @Redirect(method = "process", at = @At(value = "NEW", target = "(J)Ljava/util/Random;")) 17 | private Random fixSameBastion(long seed, @Local(ordinal = 0, argsOnly = true) BlockPos p1, @Local(ordinal = 1, argsOnly = true) BlockPos p2) { 18 | return PeepoPractice.CATEGORY.equals(PracticeCategoriesAny.BASTION_SPLIT) ? new Random() : new Random(seed); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/main/java/me/falu/peepopractice/mixin/world/entity/ClientPlayerEntityMixin.java: -------------------------------------------------------------------------------- 1 | package me.falu.peepopractice.mixin.world.entity; 2 | 3 | import com.llamalad7.mixinextras.injector.ModifyReturnValue; 4 | import me.falu.peepopractice.PeepoPractice; 5 | import net.minecraft.client.network.ClientPlayerEntity; 6 | import org.spongepowered.asm.mixin.Mixin; 7 | import org.spongepowered.asm.mixin.injection.At; 8 | 9 | @Mixin(ClientPlayerEntity.class) 10 | public abstract class ClientPlayerEntityMixin { 11 | @ModifyReturnValue(method = "showsDeathScreen", at = @At("RETURN")) 12 | private boolean peepoPractice$noDeathScreen(boolean showsDeathScreen) { 13 | return showsDeathScreen && !PeepoPractice.CATEGORY.hasSplitEvent(); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/main/java/me/falu/peepopractice/mixin/world/entity/EyeOfEnderEntityMixin.java: -------------------------------------------------------------------------------- 1 | package me.falu.peepopractice.mixin.world.entity; 2 | 3 | import me.falu.peepopractice.core.category.preferences.CategoryPreferences; 4 | import me.falu.peepopractice.core.category.preferences.PreferenceTypes; 5 | import net.minecraft.entity.EyeOfEnderEntity; 6 | import net.minecraft.util.math.BlockPos; 7 | import org.spongepowered.asm.mixin.Mixin; 8 | import org.spongepowered.asm.mixin.Shadow; 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(EyeOfEnderEntity.class) 14 | public class EyeOfEnderEntityMixin { 15 | @Shadow private boolean dropsItem; 16 | 17 | @Inject(method = "moveTowards", at = @At("TAIL")) 18 | private void peepoPractice$eyeBreakPreference(BlockPos pos, CallbackInfo ci) { 19 | PreferenceTypes.BooleanRandomType value = CategoryPreferences.EYE_BREAKS.getValue(); 20 | if (!value.equals(PreferenceTypes.BooleanRandomType.RANDOM)) { 21 | this.dropsItem = !value.equals(PreferenceTypes.BooleanRandomType.ENABLED); 22 | } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/main/java/me/falu/peepopractice/mixin/world/entity/MobEntityMixin.java: -------------------------------------------------------------------------------- 1 | package me.falu.peepopractice.mixin.world.entity; 2 | 3 | import com.redlimerl.speedrunigt.option.SpeedRunOption; 4 | import com.redlimerl.speedrunigt.option.SpeedRunOptions; 5 | import com.redlimerl.speedrunigt.timer.InGameTimer; 6 | import net.minecraft.entity.EntityType; 7 | import net.minecraft.entity.LivingEntity; 8 | import net.minecraft.entity.mob.MobEntity; 9 | import net.minecraft.world.World; 10 | import org.spongepowered.asm.mixin.Mixin; 11 | import org.spongepowered.asm.mixin.Unique; 12 | import org.spongepowered.asm.mixin.injection.At; 13 | import org.spongepowered.asm.mixin.injection.Inject; 14 | import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; 15 | import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; 16 | 17 | @Mixin(MobEntity.class) 18 | public abstract class MobEntityMixin extends LivingEntity { 19 | protected MobEntityMixin(EntityType entityType, World world) { 20 | super(entityType, world); 21 | } 22 | 23 | @Unique 24 | private boolean isInactive() { 25 | if (SpeedRunOption.getOption(SpeedRunOptions.WAITING_FIRST_INPUT).isFirstInput(InGameTimer.getInstance())) { 26 | return !InGameTimer.getInstance().isStarted(); 27 | } 28 | return false; 29 | } 30 | 31 | @Inject(method = "canMoveVoluntarily", at = @At("RETURN"), cancellable = true) 32 | private void peepoPractice$cancelMovement(CallbackInfoReturnable cir) { 33 | if (this.isInactive()) { 34 | cir.setReturnValue(false); 35 | } 36 | } 37 | 38 | @Inject(method = "playAmbientSound", at = @At("HEAD"), cancellable = true) 39 | private void peepoPractice$cancelSounds(CallbackInfo ci) { 40 | if (this.isInactive()) { 41 | ci.cancel(); 42 | } 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /src/main/java/me/falu/peepopractice/mixin/world/entity/ThrownItemEntityMixin.java: -------------------------------------------------------------------------------- 1 | package me.falu.peepopractice.mixin.world.entity; 2 | 3 | import me.falu.peepopractice.PeepoPractice; 4 | import me.falu.peepopractice.core.category.properties.event.ThrowEntitySplitEvent; 5 | import net.minecraft.entity.EntityType; 6 | import net.minecraft.entity.projectile.thrown.ThrownEntity; 7 | import net.minecraft.entity.projectile.thrown.ThrownItemEntity; 8 | import net.minecraft.item.ItemStack; 9 | import net.minecraft.world.World; 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(ThrownItemEntity.class) 16 | public abstract class ThrownItemEntityMixin extends ThrownEntity { 17 | protected ThrownItemEntityMixin(EntityType entityType, World world) { 18 | super(entityType, world); 19 | } 20 | 21 | @Inject(method = "setItem", at = @At("HEAD")) 22 | private void peepoPractice$onItemThrown(ItemStack item, CallbackInfo ci) { 23 | if (PeepoPractice.CATEGORY.hasSplitEvent()) { 24 | if (PeepoPractice.CATEGORY.getSplitEvent() instanceof ThrowEntitySplitEvent) { 25 | ThrowEntitySplitEvent event = (ThrowEntitySplitEvent) PeepoPractice.CATEGORY.getSplitEvent(); 26 | if (event.hasItem() && event.getItem().equals(item.getItem())) { 27 | event.complete(this.getOwner() != null && this.getOwner().isAlive()); 28 | this.remove(); 29 | } 30 | } 31 | } 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/main/java/me/falu/peepopractice/mixin/world/entity/ai/HoldingPatternPhaseMixin.java: -------------------------------------------------------------------------------- 1 | package me.falu.peepopractice.mixin.world.entity.ai; 2 | 3 | import com.llamalad7.mixinextras.injector.ModifyExpressionValue; 4 | import me.falu.peepopractice.core.category.preferences.CategoryPreferences; 5 | import me.falu.peepopractice.core.category.preferences.PreferenceTypes; 6 | import net.minecraft.entity.boss.dragon.phase.HoldingPatternPhase; 7 | import org.spongepowered.asm.mixin.Mixin; 8 | import org.spongepowered.asm.mixin.injection.At; 9 | import org.spongepowered.asm.mixin.injection.Slice; 10 | 11 | @Mixin(HoldingPatternPhase.class) 12 | public abstract class HoldingPatternPhaseMixin { 13 | @ModifyExpressionValue( 14 | method = "method_6841", 15 | at = @At( 16 | value = "INVOKE", 17 | target = "Ljava/util/Random;nextInt(I)I", 18 | ordinal = 0 19 | ), 20 | slice = @Slice( 21 | from = @At( 22 | value = "INVOKE", 23 | target = "Lnet/minecraft/entity/boss/dragon/EnderDragonEntity;getNearestPathNodeIndex()I" 24 | ) 25 | ) 26 | ) 27 | private int peepoPractice$oneInEight(int randomInt) { 28 | PreferenceTypes.BooleanRandomType value = CategoryPreferences.ONE_IN_EIGHT.getValue(); 29 | if (!value.equals(PreferenceTypes.BooleanRandomType.RANDOM)) { 30 | return value.equals(PreferenceTypes.BooleanRandomType.ENABLED) ? 0 : 1; 31 | } 32 | return randomInt; 33 | } 34 | 35 | @ModifyExpressionValue(method = "method_6842", at = @At(value = "INVOKE", target = "Ljava/util/Random;nextFloat()F")) 36 | private float peepoPractice$changeTargetHeight(float randomFloat) { 37 | if (CategoryPreferences.NO_EARLY_FLYAWAY.getBoolValue()) { 38 | return 0.0F; 39 | } 40 | return randomFloat; 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/main/java/me/falu/peepopractice/mixin/world/entity/ai/PiglinBrainMixin.java: -------------------------------------------------------------------------------- 1 | package me.falu.peepopractice.mixin.world.entity.ai; 2 | 3 | import com.google.common.collect.Lists; 4 | import com.llamalad7.mixinextras.injector.ModifyReturnValue; 5 | import me.falu.peepopractice.core.category.preferences.CategoryPreferences; 6 | import me.falu.peepopractice.core.loot.PiglinBarterState; 7 | import net.minecraft.entity.EntityType; 8 | import net.minecraft.entity.LivingEntity; 9 | import net.minecraft.entity.mob.PiglinBrain; 10 | import net.minecraft.entity.mob.PiglinEntity; 11 | import net.minecraft.item.ItemStack; 12 | import net.minecraft.server.MinecraftServer; 13 | import net.minecraft.server.world.ServerWorld; 14 | import org.spongepowered.asm.mixin.Mixin; 15 | import org.spongepowered.asm.mixin.injection.At; 16 | 17 | import java.util.List; 18 | import java.util.Random; 19 | 20 | @Mixin(PiglinBrain.class) 21 | public abstract class PiglinBrainMixin { 22 | @ModifyReturnValue(method = "method_29276", at = @At("RETURN")) 23 | private static boolean peepoPractice$danceOnPlayerKill(boolean bl, LivingEntity livingEntity, LivingEntity livingEntity2) { 24 | return bl || livingEntity2.getType().equals(EntityType.PLAYER); 25 | } 26 | 27 | @ModifyReturnValue(method = "getBarteredItem", at = @At("RETURN")) 28 | private static List peepoPractice$guaranteeTrades(List barteredItems, PiglinEntity piglin) { 29 | if (CategoryPreferences.RANKED_LOOT_TABLE.getBoolValue()) { 30 | MinecraftServer server = piglin.getServer(); 31 | if (server == null || barteredItems.isEmpty()) { 32 | return barteredItems; 33 | } 34 | ServerWorld overworld = server.getOverworld(); 35 | PiglinBarterState piglinBarterState = overworld.getPersistentStateManager().getOrCreate(PiglinBarterState::new, "piglin_barters"); 36 | return Lists.newArrayList(piglinBarterState.guaranteeItem(piglin, barteredItems.get(0), new Random())); 37 | } 38 | return barteredItems; 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/main/java/me/falu/peepopractice/mixin/world/entity/category/BlazeEntityMixin.java: -------------------------------------------------------------------------------- 1 | package me.falu.peepopractice.mixin.world.entity.category; 2 | 3 | import me.falu.peepopractice.core.category.preferences.CategoryPreferences; 4 | import net.minecraft.entity.EntityType; 5 | import net.minecraft.entity.LivingEntity; 6 | import net.minecraft.entity.mob.BlazeEntity; 7 | import net.minecraft.item.ItemStack; 8 | import net.minecraft.item.Items; 9 | import net.minecraft.world.World; 10 | import org.spongepowered.asm.mixin.Mixin; 11 | import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; 12 | 13 | @Mixin(BlazeEntity.class) 14 | public abstract class BlazeEntityMixin extends MobEntityMixin { 15 | protected BlazeEntityMixin(EntityType entityType, World world) { 16 | super(entityType, world); 17 | } 18 | 19 | @Override 20 | protected void peepoPractice$onDropLoot(CallbackInfo ci) { 21 | if (CategoryPreferences.GOOD_BLAZE_RATES.getBoolValue()) { 22 | this.dropStack(new ItemStack(Items.BLAZE_ROD)); 23 | ci.cancel(); 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/main/java/me/falu/peepopractice/mixin/world/entity/category/MobEntityMixin.java: -------------------------------------------------------------------------------- 1 | package me.falu.peepopractice.mixin.world.entity.category; 2 | 3 | import net.minecraft.entity.EntityType; 4 | import net.minecraft.entity.LivingEntity; 5 | import net.minecraft.entity.mob.MobEntity; 6 | import net.minecraft.world.World; 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 | 12 | @Mixin(MobEntity.class) 13 | public abstract class MobEntityMixin extends LivingEntity { 14 | protected MobEntityMixin(EntityType entityType, World world) { 15 | super(entityType, world); 16 | } 17 | 18 | @SuppressWarnings("CancellableInjectionUsage") 19 | @Inject(method = "dropLoot", at = @At("HEAD"), cancellable = true) 20 | protected void peepoPractice$onDropLoot(CallbackInfo ci) { } 21 | } 22 | -------------------------------------------------------------------------------- /src/main/java/me/falu/peepopractice/mixin/world/entity/category/ZombifiedPiglinEntityMixin.java: -------------------------------------------------------------------------------- 1 | package me.falu.peepopractice.mixin.world.entity.category; 2 | 3 | import me.falu.peepopractice.core.category.preferences.CategoryPreferences; 4 | import net.minecraft.entity.EntityType; 5 | import net.minecraft.entity.SpawnReason; 6 | import net.minecraft.entity.mob.ZombifiedPiglinEntity; 7 | import net.minecraft.predicate.entity.LocationPredicate; 8 | import net.minecraft.server.world.ServerWorld; 9 | import net.minecraft.util.math.BlockPos; 10 | import net.minecraft.world.WorldAccess; 11 | import net.minecraft.world.gen.feature.StructureFeature; 12 | import org.spongepowered.asm.mixin.Mixin; 13 | import org.spongepowered.asm.mixin.Unique; 14 | import org.spongepowered.asm.mixin.injection.At; 15 | import org.spongepowered.asm.mixin.injection.Inject; 16 | import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; 17 | 18 | import java.util.Random; 19 | 20 | @Mixin(ZombifiedPiglinEntity.class) 21 | public class ZombifiedPiglinEntityMixin { 22 | @Unique private static final LocationPredicate BASTION_PREDICATE = LocationPredicate.feature(StructureFeature.BASTION_REMNANT); 23 | 24 | @Inject(method = "canSpawn(Lnet/minecraft/entity/EntityType;Lnet/minecraft/world/WorldAccess;Lnet/minecraft/entity/SpawnReason;Lnet/minecraft/util/math/BlockPos;Ljava/util/Random;)Z", at = @At("RETURN"), cancellable = true) 25 | private static void noBastionPigmen(EntityType type, WorldAccess world, SpawnReason spawnReason, BlockPos pos, Random random, CallbackInfoReturnable cir) { 26 | if (!CategoryPreferences.ZOMBIE_PIGMEN.getBoolValue() && world instanceof ServerWorld) { 27 | if (BASTION_PREDICATE.test((ServerWorld) world, pos.getX(), pos.getY(), pos.getZ())) { 28 | cir.setReturnValue(false); 29 | } 30 | } 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/main/java/me/falu/peepopractice/mixin/world/item/BoatItemMixin.java: -------------------------------------------------------------------------------- 1 | package me.falu.peepopractice.mixin.world.item; 2 | 3 | import me.falu.peepopractice.PeepoPractice; 4 | import me.falu.peepopractice.core.category.properties.event.EnterVehicleSplitEvent; 5 | import net.minecraft.entity.EntityType; 6 | import net.minecraft.item.BoatItem; 7 | import net.minecraft.item.ItemStack; 8 | import org.spongepowered.asm.mixin.Mixin; 9 | import org.spongepowered.asm.mixin.injection.At; 10 | import org.spongepowered.asm.mixin.injection.Redirect; 11 | 12 | @Mixin(BoatItem.class) 13 | public class BoatItemMixin { 14 | @Redirect(method = "use", at = @At(value = "INVOKE", target = "Lnet/minecraft/item/ItemStack;decrement(I)V")) 15 | private void peepoPractice$keepBoat(ItemStack instance, int amount) { 16 | if (PeepoPractice.CATEGORY.hasSplitEvent()) { 17 | if (PeepoPractice.CATEGORY.getSplitEvent() instanceof EnterVehicleSplitEvent) { 18 | EnterVehicleSplitEvent event = (EnterVehicleSplitEvent) PeepoPractice.CATEGORY.getSplitEvent(); 19 | if (event.hasVehicle() && event.getVehicle().equals(EntityType.BOAT) && event.shouldKeepItem()) { 20 | return; 21 | } 22 | } 23 | } 24 | instance.decrement(amount); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/main/java/me/falu/peepopractice/mixin/world/item/MinecartItemMixin.java: -------------------------------------------------------------------------------- 1 | package me.falu.peepopractice.mixin.world.item; 2 | 3 | import me.falu.peepopractice.PeepoPractice; 4 | import me.falu.peepopractice.core.category.properties.event.EnterVehicleSplitEvent; 5 | import net.minecraft.entity.EntityType; 6 | import net.minecraft.item.ItemStack; 7 | import net.minecraft.item.MinecartItem; 8 | import org.spongepowered.asm.mixin.Mixin; 9 | import org.spongepowered.asm.mixin.injection.At; 10 | import org.spongepowered.asm.mixin.injection.Redirect; 11 | 12 | @Mixin(MinecartItem.class) 13 | public class MinecartItemMixin { 14 | @Redirect(method = "useOnBlock", at = @At(value = "INVOKE", target = "Lnet/minecraft/item/ItemStack;decrement(I)V")) 15 | private void peepoPractice$keepBoat(ItemStack instance, int amount) { 16 | if (PeepoPractice.CATEGORY.hasSplitEvent()) { 17 | if (PeepoPractice.CATEGORY.getSplitEvent() instanceof EnterVehicleSplitEvent) { 18 | EnterVehicleSplitEvent event = (EnterVehicleSplitEvent) PeepoPractice.CATEGORY.getSplitEvent(); 19 | if (event.hasVehicle() && event.getVehicle().equals(EntityType.MINECART) && event.shouldKeepItem()) { 20 | return; 21 | } 22 | } 23 | } 24 | instance.decrement(amount); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/main/java/me/falu/peepopractice/owner/GenerationShutdownOwner.java: -------------------------------------------------------------------------------- 1 | package me.falu.peepopractice.owner; 2 | 3 | public interface GenerationShutdownOwner { 4 | void peepoPractice$shutdown(); 5 | } 6 | -------------------------------------------------------------------------------- /src/main/resources/assets/peepopractice/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/faluhub/peepoPractice/2b1e3095a43f04551710fd3094829a83b9d2c6da/src/main/resources/assets/peepopractice/icon.png -------------------------------------------------------------------------------- /src/main/resources/assets/peepopractice/models/item/random_axe.json: -------------------------------------------------------------------------------- 1 | { 2 | "parent": "item/generated", 3 | "textures": { 4 | "layer0": "peepopractice:item/random_axe" 5 | } 6 | } -------------------------------------------------------------------------------- /src/main/resources/assets/peepopractice/models/item/random_hoe.json: -------------------------------------------------------------------------------- 1 | { 2 | "parent": "item/generated", 3 | "textures": { 4 | "layer0": "peepopractice:item/random_hoe" 5 | } 6 | } -------------------------------------------------------------------------------- /src/main/resources/assets/peepopractice/models/item/random_pickaxe.json: -------------------------------------------------------------------------------- 1 | { 2 | "parent": "item/generated", 3 | "textures": { 4 | "layer0": "peepopractice:item/random_pickaxe" 5 | } 6 | } -------------------------------------------------------------------------------- /src/main/resources/assets/peepopractice/models/item/random_shovel.json: -------------------------------------------------------------------------------- 1 | { 2 | "parent": "item/generated", 3 | "textures": { 4 | "layer0": "peepopractice:item/random_shovel" 5 | } 6 | } -------------------------------------------------------------------------------- /src/main/resources/assets/peepopractice/models/item/random_sword.json: -------------------------------------------------------------------------------- 1 | { 2 | "parent": "item/generated", 3 | "textures": { 4 | "layer0": "peepopractice:item/random_sword" 5 | } 6 | } -------------------------------------------------------------------------------- /src/main/resources/assets/peepopractice/textures/category/bastion_split.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/faluhub/peepoPractice/2b1e3095a43f04551710fd3094829a83b9d2c6da/src/main/resources/assets/peepopractice/textures/category/bastion_split.png -------------------------------------------------------------------------------- /src/main/resources/assets/peepopractice/textures/category/end_game_split.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/faluhub/peepoPractice/2b1e3095a43f04551710fd3094829a83b9d2c6da/src/main/resources/assets/peepopractice/textures/category/end_game_split.png -------------------------------------------------------------------------------- /src/main/resources/assets/peepopractice/textures/category/end_split.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/faluhub/peepoPractice/2b1e3095a43f04551710fd3094829a83b9d2c6da/src/main/resources/assets/peepopractice/textures/category/end_split.png -------------------------------------------------------------------------------- /src/main/resources/assets/peepopractice/textures/category/fortress_split.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/faluhub/peepoPractice/2b1e3095a43f04551710fd3094829a83b9d2c6da/src/main/resources/assets/peepopractice/textures/category/fortress_split.png -------------------------------------------------------------------------------- /src/main/resources/assets/peepopractice/textures/category/igloo_split.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/faluhub/peepoPractice/2b1e3095a43f04551710fd3094829a83b9d2c6da/src/main/resources/assets/peepopractice/textures/category/igloo_split.png -------------------------------------------------------------------------------- /src/main/resources/assets/peepopractice/textures/category/island_leave_split.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/faluhub/peepoPractice/2b1e3095a43f04551710fd3094829a83b9d2c6da/src/main/resources/assets/peepopractice/textures/category/island_leave_split.png -------------------------------------------------------------------------------- /src/main/resources/assets/peepopractice/textures/category/mapless_split.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/faluhub/peepoPractice/2b1e3095a43f04551710fd3094829a83b9d2c6da/src/main/resources/assets/peepopractice/textures/category/mapless_split.png -------------------------------------------------------------------------------- /src/main/resources/assets/peepopractice/textures/category/nether_split.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/faluhub/peepoPractice/2b1e3095a43f04551710fd3094829a83b9d2c6da/src/main/resources/assets/peepopractice/textures/category/nether_split.png -------------------------------------------------------------------------------- /src/main/resources/assets/peepopractice/textures/category/post_blind_split.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/faluhub/peepoPractice/2b1e3095a43f04551710fd3094829a83b9d2c6da/src/main/resources/assets/peepopractice/textures/category/post_blind_split.png -------------------------------------------------------------------------------- /src/main/resources/assets/peepopractice/textures/category/raid_split.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/faluhub/peepoPractice/2b1e3095a43f04551710fd3094829a83b9d2c6da/src/main/resources/assets/peepopractice/textures/category/raid_split.png -------------------------------------------------------------------------------- /src/main/resources/assets/peepopractice/textures/category/ravine_enter_split.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/faluhub/peepoPractice/2b1e3095a43f04551710fd3094829a83b9d2c6da/src/main/resources/assets/peepopractice/textures/category/ravine_enter_split.png -------------------------------------------------------------------------------- /src/main/resources/assets/peepopractice/textures/category/stronghold_split.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/faluhub/peepoPractice/2b1e3095a43f04551710fd3094829a83b9d2c6da/src/main/resources/assets/peepopractice/textures/category/stronghold_split.png -------------------------------------------------------------------------------- /src/main/resources/assets/peepopractice/textures/category/uneasy_alliance_split.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/faluhub/peepoPractice/2b1e3095a43f04551710fd3094829a83b9d2c6da/src/main/resources/assets/peepopractice/textures/category/uneasy_alliance_split.png -------------------------------------------------------------------------------- /src/main/resources/assets/peepopractice/textures/gear.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/faluhub/peepoPractice/2b1e3095a43f04551710fd3094829a83b9d2c6da/src/main/resources/assets/peepopractice/textures/gear.png -------------------------------------------------------------------------------- /src/main/resources/assets/peepopractice/textures/item/random_axe.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/faluhub/peepoPractice/2b1e3095a43f04551710fd3094829a83b9d2c6da/src/main/resources/assets/peepopractice/textures/item/random_axe.png -------------------------------------------------------------------------------- /src/main/resources/assets/peepopractice/textures/item/random_hoe.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/faluhub/peepoPractice/2b1e3095a43f04551710fd3094829a83b9d2c6da/src/main/resources/assets/peepopractice/textures/item/random_hoe.png -------------------------------------------------------------------------------- /src/main/resources/assets/peepopractice/textures/item/random_pickaxe.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/faluhub/peepoPractice/2b1e3095a43f04551710fd3094829a83b9d2c6da/src/main/resources/assets/peepopractice/textures/item/random_pickaxe.png -------------------------------------------------------------------------------- /src/main/resources/assets/peepopractice/textures/item/random_shovel.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/faluhub/peepoPractice/2b1e3095a43f04551710fd3094829a83b9d2c6da/src/main/resources/assets/peepopractice/textures/item/random_shovel.png -------------------------------------------------------------------------------- /src/main/resources/assets/peepopractice/textures/item/random_sword.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/faluhub/peepoPractice/2b1e3095a43f04551710fd3094829a83b9d2c6da/src/main/resources/assets/peepopractice/textures/item/random_sword.png -------------------------------------------------------------------------------- /src/main/resources/assets/peepopractice/textures/peepopagman.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/faluhub/peepoPractice/2b1e3095a43f04551710fd3094829a83b9d2c6da/src/main/resources/assets/peepopractice/textures/peepopagman.png -------------------------------------------------------------------------------- /src/main/resources/assets/peepopractice/textures/peepopauseman.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/faluhub/peepoPractice/2b1e3095a43f04551710fd3094829a83b9d2c6da/src/main/resources/assets/peepopractice/textures/peepopauseman.png -------------------------------------------------------------------------------- /src/main/resources/assets/peepopractice/textures/widepeepohappy.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/faluhub/peepoPractice/2b1e3095a43f04551710fd3094829a83b9d2c6da/src/main/resources/assets/peepopractice/textures/widepeepohappy.png -------------------------------------------------------------------------------- /src/main/resources/data/minecraft/tags/blocks/valid_spawn.json: -------------------------------------------------------------------------------- 1 | { 2 | "replace": false, 3 | "values": [ 4 | "minecraft:grass_block", 5 | "minecraft:podzol", 6 | "minecraft:netherrack", 7 | "minecraft:soul_soil", 8 | "minecraft:soul_sand", 9 | "minecraft:basalt" 10 | ] 11 | } -------------------------------------------------------------------------------- /src/main/resources/datapacks/aa_endgame.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/faluhub/peepoPractice/2b1e3095a43f04551710fd3094829a83b9d2c6da/src/main/resources/datapacks/aa_endgame.zip -------------------------------------------------------------------------------- /src/main/resources/fabric.mod.json: -------------------------------------------------------------------------------- 1 | { 2 | "schemaVersion": 1, 3 | "id": "peepopractice", 4 | "version": "${version}", 5 | "name": "PeepoPractice", 6 | "description": "The best and most advanced MCSR Practice mod!", 7 | "authors": [ 8 | "falu" 9 | ], 10 | "contributors": [ 11 | "RedLime", 12 | "KingContaria" 13 | ], 14 | "contact": { 15 | "homepage": "https://github.com/faluhub/peepoPractice" 16 | }, 17 | "license": "MIT", 18 | "icon": "assets/peepopractice/icon.png", 19 | "environment": "client", 20 | "entrypoints": { 21 | "main": [ 22 | "me.falu.peepopractice.PeepoPractice" 23 | ] 24 | }, 25 | "mixins": [ 26 | "peepopractice.mixins.json" 27 | ], 28 | "accessWidener": "peepopractice.accesswidener", 29 | "depends": { 30 | "minecraft": "1.16.1", 31 | "speedrunigt": ">=7.0", 32 | "fabricloader": ">=0.15.0" 33 | }, 34 | "recommends": { 35 | "speedrunigt": ">=13.2" 36 | }, 37 | "breaks": { 38 | "worldpreview": "*", 39 | "mcsrranked": "*", 40 | "standardsettings": "<1.2.2", 41 | "stronghold-trainer": "*", 42 | "areessgee": "*", 43 | "atum": "*", 44 | "fast_reset": "*", 45 | "seedqueue": "*" 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /src/main/resources/peepopractice.accesswidener: -------------------------------------------------------------------------------- 1 | accessWidener v1 named 2 | 3 | accessible method net/minecraft/client/font/TextRenderer draw (Ljava/lang/String;FFILnet/minecraft/util/math/Matrix4f;ZZ)I 4 | accessible method net/minecraft/client/gui/widget/EntryListWidget getMaxScroll ()I 5 | accessible method net/minecraft/client/gui/widget/EntryListWidget getRowBottom (I)I 6 | accessible method net/minecraft/world/biome/layer/BiomeLayers isOcean (I)Z 7 | accessible method net/minecraft/client/gui/screen/world/MoreOptionsDialog setGeneratorOptions (Lnet/minecraft/world/gen/GeneratorOptions;)V 8 | accessible method net/minecraft/client/world/GeneratorType createFixedBiomeOptions (Lnet/minecraft/world/gen/GeneratorOptions;Lnet/minecraft/client/world/GeneratorType;Lnet/minecraft/world/biome/Biome;)Lnet/minecraft/world/gen/GeneratorOptions; 9 | accessible method net/minecraft/entity/mob/PiglinBrain getBarteredItem (Lnet/minecraft/entity/mob/PiglinEntity;)Ljava/util/List; 10 | accessible method net/minecraft/client/gui/DrawableHelper fillGradient (Lnet/minecraft/client/util/math/MatrixStack;IIIIII)V 11 | accessible method net/minecraft/server/network/ServerPlayerEntity getPermissionLevel ()I 12 | accessible method net/minecraft/world/gen/chunk/ChunkGenerator generateStrongholdPositions ()V 13 | accessible method net/minecraft/util/thread/ThreadExecutor runTasks ()V 14 | 15 | accessible field net/minecraft/state/property/EnumProperty values Lcom/google/common/collect/ImmutableSet; 16 | accessible field net/minecraft/client/gui/widget/EntryListWidget renderHeader Z 17 | accessible field net/minecraft/client/gui/widget/EntryListWidget renderSelection Z 18 | accessible field net/minecraft/client/render/debug/DebugRenderer showChunkBorder Z 19 | accessible field net/minecraft/client/world/GeneratorType SINGLE_BIOME_SURFACE Lnet/minecraft/client/world/GeneratorType; 20 | accessible field net/minecraft/client/gui/screen/world/MoreOptionsDialog generatorType Ljava/util/Optional; 21 | accessible field net/minecraft/world/gen/chunk/FlatChunkGeneratorConfig STRUCTURE_TO_FEATURES Ljava/util/Map; 22 | accessible field net/minecraft/client/network/ClientAdvancementManager advancementProgresses Ljava/util/Map; 23 | accessible field net/minecraft/client/gui/widget/ButtonWidget onPress Lnet/minecraft/client/gui/widget/ButtonWidget$PressAction; 24 | accessible field net/minecraft/world/gen/chunk/ChunkGenerator seed J 25 | accessible field net/minecraft/world/gen/chunk/ChunkGenerator strongholdPositions Ljava/util/List; 26 | accessible field net/minecraft/client/options/KeyBinding categoryOrderMap Ljava/util/Map; 27 | accessible field net/minecraft/client/MinecraftClient openProfilerSection Ljava/lang/String; 28 | accessible field net/minecraft/server/world/ServerWorld inEntityTick Z 29 | accessible field net/minecraft/world/storage/SerializingRegionBasedStorage worker Lnet/minecraft/world/storage/StorageIoWorker; 30 | accessible field net/minecraft/world/storage/VersionedChunkStorage worker Lnet/minecraft/world/storage/StorageIoWorker; 31 | -------------------------------------------------------------------------------- /src/main/resources/peepopractice.mixins.json: -------------------------------------------------------------------------------- 1 | { 2 | "required": true, 3 | "minVersion": "0.8", 4 | "package": "me.falu.peepopractice.mixin", 5 | "compatibilityLevel": "JAVA_8", 6 | "client": [ 7 | "compat.timer.InGameTimerUtilsMixin", 8 | "compat.timer.PracticeTimerManagerMixin", 9 | "compat.LocateCommandMixin", 10 | "compat.StandardSettingsMixin", 11 | 12 | "gui.hud.InGameHudMixin", 13 | 14 | "gui.renderer.ItemRendererMixin", 15 | 16 | "gui.screen.CreateWorldScreenMixin", 17 | "gui.screen.GameMenuScreenMixin", 18 | "gui.screen.LevelLoadingScreenMixin", 19 | "gui.screen.OpenToLanScreenMixin", 20 | "gui.screen.ScreenMixin", 21 | "gui.screen.TitleScreenMixin", 22 | 23 | "gui.widget.SliderWidgetMixin", 24 | 25 | "gui.MoreOptionsDialogMixin", 26 | "gui.RecipeBookMixin", 27 | 28 | "storage.FileResourcePackProviderMixin", 29 | "storage.StorageIoWorkerMixin", 30 | "storage.ThreadedAnvilChunkStorageMixin", 31 | 32 | "structure.category.BastionRemnantFeatureConfigMixin", 33 | "structure.category.DungeonFeatureMixin", 34 | "structure.category.PortalRoomMixin", 35 | 36 | "structure.ChunkGeneratorMixin", 37 | "structure.StructureFeatureMixin", 38 | "structure.StructurePoolBasedGeneratorMixin", 39 | "structure.StructureStartMixin", 40 | 41 | "world.biome.MultiNoiseBiomeSourceMixin", 42 | "world.biome.VanillaLayeredBiomeSourceMixin", 43 | 44 | "world.category.CarverMixin", 45 | "world.category.IglooGeneratorMixin", 46 | "world.category.RuleStructureProcessorMixin", 47 | 48 | "world.entity.ai.HoldingPatternPhaseMixin", 49 | "world.entity.ai.PiglinBrainMixin", 50 | 51 | "world.entity.category.BlazeEntityMixin", 52 | "world.entity.category.MobEntityMixin", 53 | "world.entity.category.ZombifiedPiglinEntityMixin", 54 | 55 | "world.entity.ClientPlayerEntityMixin", 56 | "world.entity.EyeOfEnderEntityMixin", 57 | "world.entity.MobEntityMixin", 58 | "world.entity.ServerPlayerEntityMixin", 59 | "world.entity.ThrownItemEntityMixin", 60 | 61 | "world.item.BoatItemMixin", 62 | "world.item.MinecartItemMixin", 63 | 64 | "world.ChestBlockEntityMixin", 65 | "world.LootableContainerBlockEntityMixin", 66 | "world.MinecraftServerMixin", 67 | "world.PlayerManagerMixin", 68 | "world.ServerWorldMixin", 69 | "world.SpikeCacheMixin", 70 | "world.UnderwaterCaveCarverMixin", 71 | 72 | "ClientAdvancementManagerMixin", 73 | "ClientPlayerInteractionManagerMixin", 74 | "ClientPlayNetworkHandlerMixin", 75 | "GameOptionsMixin", 76 | "MinecraftClientMixin", 77 | "ServerPlayNetworkHandlerMixin" 78 | ], 79 | "injectors": { 80 | "defaultRequire": 1 81 | } 82 | } --------------------------------------------------------------------------------