Compare commits

...
Author SHA1 Message Date
MeAlam1 433a66124b Fucking JavaDocs 2026-09-27 18:05:15 +02:00
MeAlam1 4209ca3845 License 2026-09-27 17:44:25 +02:00
MeAlam1 6edd81c0f5 I think all weird Merge Conflicts are gone now 2026-09-27 17:19:57 +02:00
MeAlam1 7939b4ca71 Weird formatting 2026-09-27 17:14:21 +02:00
MeAlam1 0cc22b6675 Fixed some more weird MR Conflict inconsistancies 2026-09-27 17:05:08 +02:00
MeAlam1 0405eea167 Fixing Merge Conflicts 2026-09-27 16:56:06 +02:00
MeAlam1 17d07774d2 merge conflicts 2026-09-27 16:51:48 +02:00
MeAlam1 d71373c237 Merge branch 'main' into hello_world-but_worse 2026-09-27 16:41:23 +02:00
Polymeta a0e732dc6e Merge branch 'perf/build-speed' into 'main'
Speed up the Gradle build

See merge request cable-mc/cobblemon!2429
2026-09-24 14:35:25 +00:00
terrainwax ea20c2aa4c CI: don't upload dev-shadow jars 2026-09-24 10:43:07 +02:00
terrainwax da5272c473 Speed up the Gradle build
- Exclude assets/data (~250MB) from sources jars
- Limit licenser to .kt/.java so it doesn't scan ~29k resource files
- Enable the Gradle daemon and build cache
- The versioning plugin runs a full JGit status (~28s on this repo) in every project replace by ou own helper that use git local or CI env variable
2026-09-24 10:04:35 +02:00
Polymeta a785e574e0 Merge branch 'feature/data-registries' into 'main'
26.1.2 backport of dataregistry

See merge request cable-mc/cobblemon!2285
2026-09-22 21:08:14 +00:00
Polymeta 4b236b0268 Merge branch 'feature/remember-your-tm' into 'main'
load features a lot sooner to avoid form switching

Closes #2085

See merge request cable-mc/cobblemon!2406
2026-09-22 21:07:44 +00:00
Polymeta 1cd86ae5d6 small fixes
Signed-off-by: Polymeta <4726322-polymeta@users.noreply.gitlab.com>
2026-09-22 16:43:33 +01:00
Polymeta b7489a0b14 tidy up changelog
Signed-off-by: Polymeta <4726322-polymeta@users.noreply.gitlab.com>
2026-09-22 15:32:25 +01:00
Polymeta f7554425dc Merge branch 'tm-shelf-mipmap-correction' into 'main'
Fix mip map of TM shelf disc textures to be 16x16

Closes #2081

See merge request cable-mc/cobblemon!2398
2026-09-22 14:26:41 +00:00
Polymeta 858d2e908a I tidied up too well
Signed-off-by: Polymeta <4726322-polymeta@users.noreply.gitlab.com>
2026-09-21 22:12:18 +01:00
Polymeta b61e0b0c82 tidy up changelog
Signed-off-by: Polymeta <4726322-polymeta@users.noreply.gitlab.com>
2026-09-21 22:11:43 +01:00
Polymeta 4d9a6e71c9 use correct texture for upgrade and dubious disc
Signed-off-by: Polymeta <4726322-polymeta@users.noreply.gitlab.com>
2026-09-21 22:07:52 +01:00
Plastered_Crab a0010de504 Merge remote-tracking branch 'origin/tm-shelf-mipmap-correction' into tm-shelf-mipmap-correction
# Conflicts:
#	common/src/main/resources/assets/cobblemon/textures/block/disc_shelf/dubious_disc.png
#	common/src/main/resources/assets/cobblemon/textures/block/disc_shelf/music_disc_11.png
#	common/src/main/resources/assets/cobblemon/textures/block/disc_shelf/music_disc_13.png
#	common/src/main/resources/assets/cobblemon/textures/block/disc_shelf/music_disc_5.png
#	common/src/main/resources/assets/cobblemon/textures/block/disc_shelf/music_disc_blocks.png
#	common/src/main/resources/assets/cobblemon/textures/block/disc_shelf/music_disc_cat.png
#	common/src/main/resources/assets/cobblemon/textures/block/disc_shelf/music_disc_chirp.png
#	common/src/main/resources/assets/cobblemon/textures/block/disc_shelf/music_disc_creator.png
#	common/src/main/resources/assets/cobblemon/textures/block/disc_shelf/music_disc_creator_music_box.png
#	common/src/main/resources/assets/cobblemon/textures/block/disc_shelf/music_disc_far.png
#	common/src/main/resources/assets/cobblemon/textures/block/disc_shelf/music_disc_mall.png
#	common/src/main/resources/assets/cobblemon/textures/block/disc_shelf/music_disc_mellohi.png
#	common/src/main/resources/assets/cobblemon/textures/block/disc_shelf/music_disc_otherside.png
#	common/src/main/resources/assets/cobblemon/textures/block/disc_shelf/music_disc_pigstep.png
#	common/src/main/resources/assets/cobblemon/textures/block/disc_shelf/music_disc_precipice.png
#	common/src/main/resources/assets/cobblemon/textures/block/disc_shelf/music_disc_relic.png
#	common/src/main/resources/assets/cobblemon/textures/block/disc_shelf/music_disc_stal.png
#	common/src/main/resources/assets/cobblemon/textures/block/disc_shelf/music_disc_strad.png
#	common/src/main/resources/assets/cobblemon/textures/block/disc_shelf/music_disc_unknown.png
#	common/src/main/resources/assets/cobblemon/textures/block/disc_shelf/music_disc_wait.png
#	common/src/main/resources/assets/cobblemon/textures/block/disc_shelf/music_disc_ward.png
#	common/src/main/resources/assets/cobblemon/textures/block/disc_shelf/technical_machine.png
#	common/src/main/resources/assets/cobblemon/textures/block/disc_shelf/technical_machine_base.png
#	common/src/main/resources/assets/cobblemon/textures/block/disc_shelf/technical_machine_overlay.png
#	common/src/main/resources/assets/cobblemon/textures/block/disc_shelf/upgrade.png
2026-09-21 10:34:36 -07:00
Plastered_Crab cb73a30e23 changeloguwu 2026-09-21 10:18:50 -07:00
Polymeta 9a7536b068 you were not supposed to be there
Signed-off-by: Polymeta <4726322-polymeta@users.noreply.gitlab.com>
2026-09-21 14:42:23 +01:00
Polymeta bd973e702e per type texture for disc shelf tms
Signed-off-by: Polymeta <4726322-polymeta@users.noreply.gitlab.com>
2026-09-21 14:40:24 +01:00
Plastered_CrabandPolymeta ede1851d34 Fix mip map of TM shelf disc textures to be 16x16 2026-09-21 14:28:14 +01:00
Polymeta 32b0699235 load features a lot sooner to avoid form switching
Signed-off-by: Polymeta <4726322-polymeta@users.noreply.gitlab.com>
2026-09-21 10:13:59 +01:00
Polymeta e9c6580230 fix merge conflicts
Signed-off-by: Polymeta <4726322-polymeta@users.noreply.gitlab.com>
2026-09-21 10:13:27 +01:00
Polymeta f893eb48fd fix some errors I forgot about
Signed-off-by: Polymeta <4726322-polymeta@users.noreply.gitlab.com>
2026-09-21 09:38:51 +01:00
Polymeta 232e2f18bf MR feedback
Signed-off-by: Polymeta <4726322-polymeta@users.noreply.gitlab.com>
2026-09-21 09:38:51 +01:00
Polymeta c266c99710 26.1.2 backport of dataregistry
Signed-off-by: Polymeta <4726322-polymeta@users.noreply.gitlab.com>
2026-09-21 09:38:49 +01:00
Plastered_Crab 4354c3fb03 Fix mip map of TM shelf disc textures to be 16x16 2026-09-10 11:56:30 -07:00
MeAlam1 fc2335f5d5 Fixed Biomes 2026-09-08 14:04:46 +02:00
MeAlam1 7d8329b311 Update from Main 2026-09-08 13:19:56 +02:00
MeAlam1 9b46757d14 Merge branch 'main' into hello_world-but_worse 2026-09-08 13:19:28 +02:00
MeAlam1 fe5970180d Fixed the merge confilicts, Biomes are still broken tho 2026-09-06 15:52:50 +02:00
MeAlam1 89b81d6882 Update from main (Merge Conflicts will be fixed in the upcoming commits) 2026-09-06 15:01:08 +02:00
MeAlam1 2eb6042d06 Created a simple way to add Custom Biome Data Parsers 2026-04-16 00:04:06 +02:00
MeAlam1 5ec2fc7937 Revert Change
https://minecraft.wiki/w/World_generation
2026-04-15 23:46:09 +02:00
MeAlam1 f57093b835 DATA DRIVEN BIOMES WORK + COMPATIBILITY 2026-04-14 19:04:58 +02:00
MeAlam1 03bb782dc4 Big Boy cleanup 2026-04-14 18:14:11 +02:00
Aram d296fa81dc Cleanup 2026-04-13 15:09:20 +02:00
Aram 1621bada06 Fixed JSON Loading 2026-04-10 15:56:58 +02:00
Aram 873ccc8109 JSON Loading Works!!!! 2026-04-10 15:01:06 +02:00
Aram eb4257cc26 Biome Registry Order is broken 2026-04-10 14:28:09 +02:00
Aram dfcb988f1e Got some issues with JSON loading, but ill work on it when i got time 2026-04-09 16:59:10 +02:00
Aram 2ca2a77333 Cobblemon Biomes are now accessible in World Generation 2026-04-09 15:39:43 +02:00
MeAlam1 b468e4eb6c Forgor 2026-04-09 02:01:55 +02:00
MeAlam1 c39397413f Final Renaming of Methods 2026-04-09 01:54:31 +02:00
MeAlam1 b3fe03194a Cleanup of all the Code 2026-04-09 01:52:33 +02:00
MeAlam1 d2eafe7b41 Added all Mixin Docs 2026-04-09 01:09:41 +02:00
MeAlam1 7f78c15f6b Hello World be Worlding 2026-04-08 23:02:29 +02:00
MeAlam1 0cd1f8a2fd Commit 2026-04-08 16:10:51 +02:00
MeAlam1 d6c2da3d90 Update CobblemonBiomes.kt 2026-04-07 22:10:23 +02:00
MeAlam1 ba84883eae Evolved to Registry is already frozen ERROR 2026-04-06 23:18:06 +02:00
MeAlam1 fcfdfb270d Slowly Start working on Biome Registry 2026-03-26 14:13:46 +01:00
MeAlam1 96d1d9d552 Started implementing custom biomes 2026-03-26 00:36:33 +01:00
MeAlam1 0d1de548a0 Test Hello *World* 2026-03-25 23:06:41 +01:00
120 changed files with 1291 additions and 263 deletions
+3
View File
@@ -157,6 +157,9 @@ build:
paths:
- neoforge/build/libs
- fabric/build/libs
exclude:
- neoforge/build/libs/*-dev-shadow.jar
- fabric/build/libs/*-dev-shadow.jar
publish:
stage: deploy
+5
View File
@@ -59,6 +59,8 @@
- Updated various recipes to display in Recipe Groups within the Recipe Book
- Updated most recipes to display more logically/consistently within the Recipe Book's categories
- Make ride rotations less jittery in multiplayer.
- Changes tm shelf discs to use textures instead of tinting and overlays to line up with item display
- Blank TM can now be inserted into the Disc Shelf
### Fixes
- Correctly grants "They're Minerals!" advancement when planting a type gem
@@ -81,8 +83,11 @@
- Fixed Diglett line not having their faint animations
- Fixed Kingambit moving its legs while walking
- Various Structure fixes
- Fixed shiny pokemon under certain circumstances forgetting taught TMs
### Developer
- Fixed wrong order of operations in the code flow prior to the `FOSSIL_REVIVED` event causing pokemon to appear without entity link
- Fixed TestPartySlot command not correctly comparing the provided spec with the target Pokémon
### Molang & Datapacks
- A new `negate` variant for evolution requirements has been added, which will negate (invert) the provided inner condition.
+5 -2
View File
@@ -1,3 +1,6 @@
import utilities.gitBranch
import utilities.gitShortCommit
plugins {
id("cobblemon.root-conventions")
}
@@ -6,6 +9,6 @@ version = "${project.property("mod_version")}+${project.property("mc_version")}"
val isSnapshot = project.property("snapshot")?.equals("true") == true
if (isSnapshot) {
val fixedBranchName = versioning.info.branch.substringAfter("/")
version = "$version-${fixedBranchName}-${versioning.info.build}"
val fixedBranchName = gitBranch().substringAfter("/")
version = "$version-${fixedBranchName}-${gitShortCommit()}"
}
+15 -6
View File
@@ -1,9 +1,8 @@
import utilities.isSnapshot
import utilities.version
import java.time.OffsetDateTime
import java.time.ZoneOffset
import java.time.format.DateTimeFormatter
import utilities.gitBranch
import utilities.gitCommit
/*
*
@@ -72,6 +71,14 @@ tasks.withType<Test> {
}
}
// Git info lives in its own output dir (not processResources) so a new commit only rewrites this one file.
val writeGitInfo by tasks.registering(WriteProperties::class) {
destinationFile = layout.buildDirectory.file("generated/gitInfo/cobblemon-git.properties")
property("commit", gitCommit())
property("branch", gitBranch())
}
sourceSets.main { output.dir(mapOf("builtBy" to writeGitInfo), layout.buildDirectory.dir("generated/gitInfo")) }
tasks.processResources {
exclude("data/**/README.md")
}
@@ -98,11 +105,13 @@ sourceSets {
property("modid", "cobblemon")
property("version", project.version())
property("isSnapshot", if(rootProject.isSnapshot()) "true" else "false")
property("gitCommit", versioning.info.commit)
property("branch", versioning.info.branch)
System.getProperty("buildNumber")?.let { property("buildNumber", it) }
property("timestamp", OffsetDateTime.now(ZoneOffset.UTC).format(DateTimeFormatter.ofPattern("MM/dd/yyyy hh:mm:ss")) + " UTC")
}
}
}
}
// The platform jars are built from :common:jar, so the remapped common jars are only needed by `publish`
tasks.assemble {
setDependsOn(dependsOn.filterNot { (it as? TaskProvider<*>)?.name in setOf("remapJar", "remapSourcesJar") })
}
@@ -0,0 +1,18 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.duck.biome;
import net.minecraft.core.Holder;
import net.minecraft.world.level.biome.Biome;
import java.util.List;
public interface BiomeSourceDuck {
void addPossibleBiomes(List<Holder<Biome>> biomes);
}
@@ -8,14 +8,21 @@
package com.cobblemon.mod.common.mixin;
import com.cobblemon.mod.common.Cobblemon;
import com.cobblemon.mod.common.duck.MinecraftServerDuck;
import com.llamalad7.mixinextras.injector.wrapoperation.Operation;
import com.llamalad7.mixinextras.injector.wrapoperation.WrapOperation;
import net.minecraft.core.LayeredRegistryAccess;
import net.minecraft.core.RegistryAccess;
import net.minecraft.resources.ResourceKey;
import net.minecraft.server.MinecraftServer;
import net.minecraft.server.RegistryLayer;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.world.level.Level;
import org.spongepowered.asm.mixin.Final;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow;
import org.spongepowered.asm.mixin.injection.At;
import java.util.Map;
import java.util.Optional;
@@ -23,18 +30,41 @@ import java.util.Optional;
@Mixin(MinecraftServer.class)
public class MinecraftServerMixin implements MinecraftServerDuck {
@Shadow
@Final
private Map<ResourceKey<Level>, ServerLevel> levels;
@Shadow
@Final
private Map<ResourceKey<Level>, ServerLevel> levels;
@Override
public Optional<ResourceKey<Level>> cobblemon$getLevelKey(final Level level) {
return this.levels.entrySet()
.stream()
// Reference equality is intended here, the level we're handed is the same one as in the
// map so we don't need to check value semantics.
.filter(entry -> entry.getValue() == level)
.map(Map.Entry::getKey)
.findFirst();
}
@Override
public Optional<ResourceKey<Level>> cobblemon$getLevelKey(final Level level) {
return this.levels.entrySet()
.stream()
// Reference equality is intended here, the level we're handed is the same one as in the
// map so we don't need to check value semantics.
.filter(entry -> entry.getValue() == level)
.map(Map.Entry::getKey)
.findFirst();
}
/**
* Wraps the earliest {@code compositeAccess()} call in {@link MinecraftServer}'s constructor
* so Cobblemon can capture the server registry access as soon as it exists.
*
* <p>This sets {@link Cobblemon#registryAccess} before downstream initialization
* depends on biome lookups, while still delegating to the original call unchanged.</p>
*
* @author MeAlam
* @since April 9th 2026
*/
@WrapOperation(method = "<init>", at = @At(
value = "INVOKE",
target = "Lnet/minecraft/core/LayeredRegistryAccess;compositeAccess()Lnet/minecraft/core/RegistryAccess$Frozen;",
ordinal = 0
))
private RegistryAccess.Frozen cobblemon$captureRegistryAccess
(LayeredRegistryAccess<RegistryLayer> instance, Operation<RegistryAccess.Frozen> original) {
Cobblemon.registryAccess = instance.compositeAccess();
return original.call(instance);
}
}
@@ -0,0 +1,51 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.mixin.biome;
import net.minecraft.data.worldgen.BootstrapContext;
import net.minecraft.data.worldgen.biome.BiomeData;
import net.minecraft.world.level.biome.Biome;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
/**
* TODO (MeAlam):<br>
* This mixin is currently disabled, as it does not appear to be required at this time.
* It is retained for potential future use if biome registration issues arise.
*
* <p>
* When enabled, this mixin registers biomes into the bootstrap context
* of Minecraft's {@code BiomeData} class.
* </p>
*
* <p>
* Example usage:
* <pre>{@code
* context.register(MY_BIOME_KEY, createMyBiome());
*
* // e.g.
* context.register(
* CobblemonBiomes.TEST_BIOME,
* CobblemonBiomes.createTestBiome()
* );
* }</pre>
*
* @author MeAlam
* @since April 9th 2026
*/
@Mixin(BiomeData.class)
public class BiomeDataMixin {
@Inject(method = "bootstrap", at = @At("TAIL"))
private static void cobblemon$registerBiomes(BootstrapContext<Biome> context, CallbackInfo ci) {
}
}
@@ -0,0 +1,68 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.mixin.biome;
import com.cobblemon.mod.common.duck.biome.BiomeSourceDuck;
import it.unimi.dsi.fastutil.objects.ObjectLinkedOpenHashSet;
import net.minecraft.core.Holder;
import net.minecraft.world.level.biome.Biome;
import net.minecraft.world.level.biome.BiomeResolver;
import net.minecraft.world.level.biome.BiomeSource;
import org.spongepowered.asm.mixin.Final;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Mutable;
import org.spongepowered.asm.mixin.Shadow;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
import java.util.function.Supplier;
import java.util.stream.Collectors;
/**
* TODO (MeAlam):<br>
* This mixin is currently disabled, as it does not appear to be required at this time.
* It is retained for potential future use if biome injection becomes necessary.
*
* <p>
* When enabled, this mixin extends the {@code BiomeSource} by modifying the
* {@code possibleBiomes} supplier to include additional biomes at runtime.
* It merges existing biomes with new ones while ensuring uniqueness.
* </p>
*
* <p>
* Example usage:
* <pre>{@code
* ((BiomeSourceDuck) biomeSource).addPossibleBiomes(List.of(
* biomeHolderA,
* biomeHolderB
* ));
*
* // Existing and new biomes are merged with duplicates removed.
* }</pre>
*
* @author MeAlam
* @since April 9th 2026
*/
@Mixin(BiomeSource.class)
public abstract class BiomeSourceMixin implements BiomeResolver, BiomeSourceDuck {
@Mutable
@Final
@Shadow
private Supplier<Set<Holder<Biome>>> possibleBiomes;
@Override
public void addPossibleBiomes(List<Holder<Biome>> biomes) {
List<Holder<Biome>> combined = new ArrayList<>();
combined.addAll(this.possibleBiomes.get());
combined.addAll(biomes);
this.possibleBiomes = () -> new ObjectLinkedOpenHashSet<>(combined.stream().distinct().collect(Collectors.toList()));
}
}
@@ -0,0 +1,123 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.mixin.biome;
import com.cobblemon.mod.common.Cobblemon;
import com.cobblemon.mod.common.util.biome.RegistryExtensions;
import com.cobblemon.mod.common.world.biome.CobblemonBiome;
import com.cobblemon.mod.common.world.biome.CobblemonBiomeLoader;
import com.llamalad7.mixinextras.injector.wrapoperation.Operation;
import com.llamalad7.mixinextras.injector.wrapoperation.WrapOperation;
import com.mojang.datafixers.util.Either;
import com.mojang.datafixers.util.Pair;
import net.minecraft.core.Holder;
import net.minecraft.core.HolderGetter;
import net.minecraft.core.RegistryAccess;
import net.minecraft.core.registries.Registries;
import net.minecraft.resources.ResourceKey;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.world.level.biome.*;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow;
import org.spongepowered.asm.mixin.Unique;
import org.spongepowered.asm.mixin.injection.At;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.function.Function;
/**
* Ensures compatibility with Biolith by applying this mixin before its biome modifications.
*
* <p>
* Biolith uses mixins (see
* <a href="https://github.com/TerraformersMC/Biolith/blob/1.21.1/common/src/main/java/com/terraformersmc/biolith/impl/mixin/MixinMultiNoiseBiomeSource.java">
* MixinMultiNoiseBiomeSource
* </a>)
* with a priority of {@code 900}, allowing it to run before Terrablender.
* </p>
*
* <p>
* Because Biolith includes functionality to disable vanilla biomes, this mixin must
* execute before Biolith’s transformations to ensure compatibility and prevent
* unintended biome removal or conflicts.
* </p>
*
* @author MeAlam
* @since April 9th 2026
*/
@Mixin(value = MultiNoiseBiomeSource.class, priority = 850)
public abstract class MultiNoiseBiomeSourceMixin extends BiomeSource {
@Shadow
protected abstract Climate.ParameterList<Holder<Biome>> parameters();
@Unique
private Climate.ParameterList<Holder.Reference<Biome>> cobblemon$parameters;
@WrapOperation(
method = "parameters",
at = @At(value = "INVOKE", target = "Lcom/mojang/datafixers/util/Either;map(Ljava/util/function/Function;Ljava/util/function/Function;)Ljava/lang/Object;"),
remap = false
)
private Object cobblemon$injectBiomes(
Either<Climate.ParameterList<RegistryAccess.RegistryEntry<Biome>>,
RegistryAccess.RegistryEntry<MultiNoiseBiomeSourceParameterList>> instance,
Function<Climate.ParameterList<RegistryAccess.RegistryEntry<Biome>>,
Climate.ParameterList<RegistryAccess.RegistryEntry<Biome>>> leftMap,
Function<RegistryAccess.RegistryEntry<MultiNoiseBiomeSourceParameterList>,
Climate.ParameterList<RegistryAccess.RegistryEntry<Biome>>> rightMap,
Operation<Object> original) {
synchronized (this) {
if (cobblemon$parameters == null) {
/* NOTE:
* Minecraft also does this exact cast on the return of this operation, so we know it's safe.
*/
@SuppressWarnings("unchecked")
Climate.ParameterList<Holder.Reference<Biome>> originalEntries =
(Climate.ParameterList<Holder.Reference<Biome>>) original.call(instance, leftMap, rightMap);
Map<ResourceLocation, CobblemonBiome> loadedBiomes = CobblemonBiomeLoader.getLoadedBiomes();
List<Pair<Climate.ParameterPoint, Holder.Reference<Biome>>> parameterList =
new ArrayList<>(originalEntries.values().size() + loadedBiomes.size());
parameterList.addAll(originalEntries.values());
HolderGetter<Biome> biomeEntryGetter = RegistryExtensions.getRegistryLookup(Registries.BIOME).orElseThrow();
int appended = 0;
int skipped = 0;
for (Map.Entry<ResourceLocation, CobblemonBiome> loadedEntry : loadedBiomes.entrySet()) {
ResourceKey<Biome> biomeKey = ResourceKey.create(Registries.BIOME, loadedEntry.getKey());
Optional<Holder.Reference<Biome>> biomeHolder = biomeEntryGetter.get(biomeKey);
if (biomeHolder.isEmpty()) {
skipped++;
Cobblemon.LOGGER.warn("MEALAM Skipping cobblemon biome climate entry for missing biome {}", loadedEntry.getKey());
continue;
}
parameterList.add(new Pair<>(loadedEntry.getValue().getParameters(), biomeHolder.get()));
appended++;
}
Cobblemon.LOGGER.info(
"MEALAM Biome source climate entries: base={}, appended={}, skipped={}.",
originalEntries.values().size(),
appended,
skipped
);
cobblemon$parameters = new Climate.ParameterList<>(parameterList);
}
return cobblemon$parameters;
}
}
}
@@ -0,0 +1,67 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.mixin.biome;
import com.cobblemon.mod.common.Cobblemon;
import com.cobblemon.mod.common.CobblemonBiomes;
import com.cobblemon.mod.common.world.biome.CobblemonBiomeLoader;
import com.mojang.serialization.Decoder;
import net.minecraft.core.WritableRegistry;
import net.minecraft.core.registries.Registries;
import net.minecraft.resources.RegistryDataLoader;
import net.minecraft.resources.RegistryOps;
import net.minecraft.resources.ResourceKey;
import net.minecraft.server.packs.resources.ResourceManager;
import net.minecraft.world.level.biome.Biome;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
import java.util.Map;
/**
* Registers Cobblemon biomes during datapack registry loading.
*
* <p>Injected into {@link RegistryDataLoader} at the tail of
* {@code loadContentsFromManager}.</p>
*
* <p>At this point, vanilla biome JSON has already been decoded, but registries are not frozen
* yet, making this the safest point to add biome entries.</p>
*
* <p>Only applies when the currently loaded registry is {@link Registries#BIOME}.</p>
*
* @author MeAlam
* @since April 9th 2026
*/
@Mixin(RegistryDataLoader.class)
public class RegistryDataLoaderMixin {
@Inject(method = "loadContentsFromManager", at = @At("TAIL"))
private static <E> void cobblemon$registerBiomes(
ResourceManager resourceManager,
RegistryOps.RegistryInfoLookup registryInfoLookup,
WritableRegistry<E> registry,
Decoder<E> codec,
Map<ResourceKey<?>, Exception> loadingErrors,
CallbackInfo ci
) {
if (!registry.key().equals(Registries.BIOME)) {
return;
}
@SuppressWarnings("unchecked")
WritableRegistry<Biome> biomeRegistry = (WritableRegistry<Biome>) registry;
CobblemonBiomes.register(
CobblemonBiomeLoader.load(resourceManager, registryInfoLookup)
);
CobblemonBiomes.INSTANCE.setRegistry(biomeRegistry);
Cobblemon.implementation.registerBiomes();
}
}
@@ -8,8 +8,12 @@ object CobblemonBuildDetails {
const val VERSION = "{{ version }}"
const val SNAPSHOT = {{ isSnapshot }}
const val GIT_COMMIT = "{{ gitCommit | default("Not Specified") }}"
const val BRANCH = "{{ branch | default("Not Specified") }}"
// Read at runtime rather than templated in, so a new commit doesn't invalidate compiled code.
private val git = java.util.Properties().apply {
CobblemonBuildDetails::class.java.getResourceAsStream("/cobblemon-git.properties")?.use(::load)
}
@JvmField val GIT_COMMIT: String = git.getProperty("commit", "Not Specified")
@JvmField val BRANCH: String = git.getProperty("branch", "Not Specified")
fun smallCommitHash(): String {
return this.GIT_COMMIT.substring(0, 7)
@@ -137,6 +137,7 @@ import kotlin.reflect.jvm.isAccessible
import kotlin.reflect.jvm.javaField
import net.minecraft.client.Minecraft
import net.minecraft.commands.synchronization.SingletonArgumentInfo
import net.minecraft.core.RegistryAccess
import net.minecraft.resources.ResourceKey
import net.minecraft.resources.ResourceLocation
import net.minecraft.server.level.ServerPlayer
@@ -182,7 +183,10 @@ object Cobblemon {
var seasonResolver: SeasonResolver = TagSeasonResolver
var wallpapers = mutableMapOf<UUID, Set<ResourceLocation>>()
val serverPlayerStructs = mutableMapOf<UUID, ObjectValue<Player>>()
@JvmField
var registryAccess: RegistryAccess.Frozen = RegistryAccess.EMPTY
val serverPlayerStructs = mutableMapOf<UUID, ObjectValue<Player>>()
val statistics: CobblemonStats = CobblemonStats
@@ -0,0 +1,37 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common
import com.cobblemon.mod.common.platform.PlatformRegistry
import com.cobblemon.mod.common.world.biome.CobblemonBiome
import com.cobblemon.mod.common.util.cobblemonResource
import net.minecraft.core.Registry
import net.minecraft.core.registries.Registries
import net.minecraft.resources.ResourceKey
import net.minecraft.resources.ResourceLocation
import net.minecraft.world.level.biome.Biome
object CobblemonBiomes : PlatformRegistry<Registry<Biome>, ResourceKey<Registry<Biome>>, Biome>() {
override var registry: Registry<Biome>? = null
override val resourceKey: ResourceKey<Registry<Biome>> = Registries.BIOME
@JvmField
val TEST_BIOME: ResourceKey<Biome> = registerKey("test_biome")
@JvmStatic
fun register(data: Map<ResourceLocation, CobblemonBiome>) {
data.forEach { (identifier, cobblemonBiome) ->
create(identifier, cobblemonBiome.biome)
}
}
private fun registerKey(key: String): ResourceKey<Biome> {
return ResourceKey.create(Registries.BIOME, cobblemonResource(key))
}
}
@@ -20,7 +20,7 @@ import net.minecraft.world.level.block.entity.BlockEntityType
object CobblemonBlockEntities : PlatformRegistry<Registry<BlockEntityType<*>>, ResourceKey<Registry<BlockEntityType<*>>>, BlockEntityType<*>>() {
override val registry: Registry<BlockEntityType<*>> = BuiltInRegistries.BLOCK_ENTITY_TYPE
override var registry: Registry<BlockEntityType<*>>? = BuiltInRegistries.BLOCK_ENTITY_TYPE
override val resourceKey: ResourceKey<Registry<BlockEntityType<*>>> = Registries.BLOCK_ENTITY_TYPE
@JvmField
@@ -59,7 +59,7 @@ import net.minecraft.world.level.material.PushReaction
@Suppress("SameParameterValue", "HasPlatformType", "MemberVisibilityCanBePrivate", "unused")
object CobblemonBlocks : PlatformRegistry<Registry<Block>, ResourceKey<Registry<Block>>, Block>() {
override val registry: Registry<Block> = BuiltInRegistries.BLOCK
override var registry: Registry<Block>? = BuiltInRegistries.BLOCK
override val resourceKey: ResourceKey<Registry<Block>> = Registries.BLOCK
val APRICORN_BLOCK_SET_TYPE = BlockSetType("apricorn")
@@ -30,7 +30,7 @@ import net.minecraft.world.entity.ai.attributes.AttributeSupplier
object CobblemonEntities : PlatformRegistry<Registry<EntityType<*>>, ResourceKey<Registry<EntityType<*>>>, EntityType<*>>() {
override val registry: Registry<EntityType<*>> = BuiltInRegistries.ENTITY_TYPE
override var registry: Registry<EntityType<*>>? = BuiltInRegistries.ENTITY_TYPE
override val resourceKey: ResourceKey<Registry<EntityType<*>>> = Registries.ENTITY_TYPE
@JvmField
@@ -12,6 +12,7 @@ import com.cobblemon.mod.common.api.net.NetworkPacket
import com.mojang.brigadier.arguments.ArgumentType
import kotlin.reflect.KClass
import net.minecraft.commands.synchronization.ArgumentTypeInfo
import net.minecraft.core.HolderLookup
import net.minecraft.resources.ResourceKey
import net.minecraft.resources.ResourceLocation
import net.minecraft.server.MinecraftServer
@@ -122,6 +123,8 @@ interface CobblemonImplementation {
* TODO
*/
fun registerMobEffects()
fun registerBiomes()
/**
@@ -168,12 +171,16 @@ interface CobblemonImplementation {
* TODO
*
* @param identifier
* @param reloader
* @param reloaderFactory
* @param type
* @param dependencies
*/
fun registerResourceReloader(identifier: ResourceLocation, reloader: PreparableReloadListener, type: PackType, dependencies: Collection<ResourceLocation>)
fun registerResourceReloader(
identifier: ResourceLocation,
type: PackType,
dependencies: Collection<ResourceLocation>,
reloaderFactory: (HolderLookup.Provider) -> PreparableReloadListener
)
/**
* TODO
*
@@ -114,7 +114,7 @@ object CobblemonItemComponents : PlatformRegistry<Registry<DataComponentType<*>>
Registry.register(BuiltInRegistries.DATA_COMPONENT_TYPE, ResourceLocation.parse("cobblemon:held_item_effect"), HELD_ITEM_EFFECT)
}
override val registry = BuiltInRegistries.DATA_COMPONENT_TYPE
override var registry = BuiltInRegistries.DATA_COMPONENT_TYPE
override val resourceKey = Registries.DATA_COMPONENT_TYPE
}
@@ -68,7 +68,7 @@ import net.minecraft.world.level.block.Block
@Suppress("unused", "SameParameterValue")
object CobblemonItems : PlatformRegistry<Registry<Item>, ResourceKey<Registry<Item>>, Item>() {
override val registry: Registry<Item> = BuiltInRegistries.ITEM
override var registry: Registry<Item>? = BuiltInRegistries.ITEM
override val resourceKey: ResourceKey<Registry<Item>> = Registries.ITEM
@JvmField
@@ -23,8 +23,7 @@ object CobblemonMenuType : PlatformRegistry<Registry<MenuType<*>>, ResourceKey<R
val COOKING_POT = MenuType.register("cooking_pot_menu", ::CookingPotMenu)
val TM_MACHINE = MenuType.register("tm_machine_menu", ::TMMachineMenu)
override val registry: Registry<MenuType<*>>
get() = BuiltInRegistries.MENU
override var registry: Registry<MenuType<*>>? = BuiltInRegistries.MENU
override val resourceKey: ResourceKey<Registry<MenuType<*>>>
get() = Registries.MENU
}
@@ -19,7 +19,7 @@ import net.minecraft.resources.ResourceKey
import net.minecraft.world.effect.MobEffect
object CobblemonMobEffects : PlatformRegistry<Registry<MobEffect>, ResourceKey<Registry<MobEffect>>, MobEffect>() {
override val registry: Registry<MobEffect> = BuiltInRegistries.MOB_EFFECT
override var registry: Registry<MobEffect>? = BuiltInRegistries.MOB_EFFECT
override val resourceKey: ResourceKey<Registry<MobEffect>> = Registries.MOB_EFFECT
@JvmField
@@ -20,7 +20,7 @@ import net.minecraft.world.level.block.Block
import net.minecraft.world.level.block.state.BlockState
object CobblemonPoiTypes: PlatformRegistry<Registry<PoiType>, ResourceKey<Registry<PoiType>>, PoiType>() {
override val registry: Registry<PoiType> = BuiltInRegistries.POINT_OF_INTEREST_TYPE
override var registry: Registry<PoiType>? = BuiltInRegistries.POINT_OF_INTEREST_TYPE
override val resourceKey: ResourceKey<Registry<PoiType>> = Registries.POINT_OF_INTEREST_TYPE
@JvmField
@@ -27,8 +27,7 @@ object CobblemonRecipeSerializers : PlatformRegistry<Registry<RecipeSerializer<*
val BREWING_STAND: RecipeSerializer<*> = register("cobblemon:brewing_stand", BrewingStandRecipe.Serializer())
override val registry: Registry<RecipeSerializer<*>>
get() = BuiltInRegistries.RECIPE_SERIALIZER
override var registry: Registry<RecipeSerializer<*>>? = BuiltInRegistries.RECIPE_SERIALIZER
override val resourceKey: ResourceKey<Registry<RecipeSerializer<*>>>
get() = Registries.RECIPE_SERIALIZER
@@ -27,8 +27,7 @@ object CobblemonRecipeTypes : PlatformRegistry<Registry<RecipeType<*>>, Resource
val BREWING_STAND: RecipeType<BrewingStandRecipe> = register("cobblemon:brewing_stand")
override val registry: Registry<RecipeType<*>>
get() = BuiltInRegistries.RECIPE_TYPE
override var registry: Registry<RecipeType<*>>? = BuiltInRegistries.RECIPE_TYPE
override val resourceKey: ResourceKey<Registry<RecipeType<*>>>
get() = Registries.RECIPE_TYPE
@@ -20,7 +20,7 @@ import net.minecraft.world.level.block.SoundType
object CobblemonSounds : PlatformRegistry<Registry<SoundEvent>, ResourceKey<Registry<SoundEvent>>, SoundEvent>() {
override val registry: Registry<SoundEvent> = BuiltInRegistries.SOUND_EVENT
override var registry: Registry<SoundEvent>? = BuiltInRegistries.SOUND_EVENT
override val resourceKey: ResourceKey<Registry<SoundEvent>> = Registries.SOUND_EVENT
@JvmField
@@ -20,7 +20,7 @@ import net.minecraft.world.entity.ai.village.poi.PoiType
import net.minecraft.world.entity.npc.VillagerProfession
object CobblemonVillagerProfessions: PlatformRegistry<Registry<VillagerProfession>, ResourceKey<Registry<VillagerProfession>>, VillagerProfession>() {
override val registry: Registry<VillagerProfession> = BuiltInRegistries.VILLAGER_PROFESSION
override var registry: Registry<VillagerProfession>? = BuiltInRegistries.VILLAGER_PROFESSION
override val resourceKey: ResourceKey<Registry<VillagerProfession>> = Registries.VILLAGER_PROFESSION
@JvmField
@@ -95,6 +95,6 @@ object CobblemonCriteria : PlatformRegistry<Registry<CriterionTrigger<*>>, Resou
@JvmField
val LEARN_TM_COUNT = this.create("tms_learned", SimpleCriterionTrigger(CountableCriterion.CODEC))
override val registry = BuiltInRegistries.TRIGGER_TYPES
override var registry = BuiltInRegistries.TRIGGER_TYPES
override val resourceKey = Registries.TRIGGER_TYPE
}
@@ -21,7 +21,7 @@ object CobblemonEntitySubPredicates : PlatformRegistry<Registry<MapCodec<out Ent
@JvmStatic
val POKE_POBBER = this.create("poke_bobber", FishingBobberPredicate.CODEC)
override val registry: Registry<MapCodec<out EntitySubPredicate>> = BuiltInRegistries.ENTITY_SUB_PREDICATE_TYPE
override var registry: Registry<MapCodec<out EntitySubPredicate>>? = BuiltInRegistries.ENTITY_SUB_PREDICATE_TYPE
override val resourceKey: ResourceKey<Registry<MapCodec<out EntitySubPredicate>>> = Registries.ENTITY_SUB_PREDICATE_TYPE
@@ -44,8 +44,15 @@ interface JsonDataRegistry<T> : DataRegistry {
*/
val resourcePath: String
override fun reload(manager: ResourceManager) {
val data = hashMapOf<ResourceLocation, T>()
/**
* Traverses JSON resources for this registry and maps them to their resolved identifiers.
* Returning null from [parse] skips the resource.
*/
fun <R> loadJsonData(
manager: ResourceManager,
parse: (reader: BufferedReader, sourceIdentifier: ResourceLocation, resolvedIdentifier: ResourceLocation) -> R?
): Map<ResourceLocation, R> {
val data = hashMapOf<ResourceLocation, R>()
manager.listResources(resourcePath) { path -> path.endsWith(JSON_EXTENSION) }.forEach { (identifier, resource) ->
if (identifier.namespace == "pixelmon") {
return@forEach
@@ -54,11 +61,21 @@ interface JsonDataRegistry<T> : DataRegistry {
resource.open().use { stream ->
stream.bufferedReader().use { reader ->
val resolvedIdentifier = ResourceLocation.fromNamespaceAndPath(identifier.namespace, File(identifier.path).nameWithoutExtension)
data[resolvedIdentifier] = parse(reader, resolvedIdentifier)
parse(reader, identifier, resolvedIdentifier)?.let { parsed ->
data[resolvedIdentifier] = parsed
}
}
}
}
return data
}
override fun reload(manager: ResourceManager) {
val data = loadJsonData(manager) { reader, _, resolvedIdentifier ->
parse(reader, resolvedIdentifier)
}
reload(data)
}
@@ -0,0 +1,93 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.api.data
import com.cobblemon.mod.common.util.endsWith
import com.google.gson.Gson
import com.google.gson.reflect.TypeToken
import net.minecraft.core.HolderLookup
import java.io.BufferedReader
import java.io.File
import java.util.concurrent.ExecutionException
import net.minecraft.resources.ResourceLocation
import net.minecraft.server.packs.PackType
import net.minecraft.server.packs.resources.ResourceManager
/**
* A [DataRegistry] that consumes JSON files.
* Every deserialized instance is attached to an [Identifier].
* For example a file under data/mymod/[resourcePath]/entry.json would be backed by the identifier modid:entry.
*
* @param T The type of the data consumed by this registry.
*
* @author Licious
* @since August 5th, 2022
*/
interface ServerJsonDataRegistry<T> : DataRegistry {
/**
* The [TypeToken] of type [T].
*/
val typeToken: TypeToken<T>
/**
* The folder location for the data this registry will consume.
*/
val resourcePath: String
override val type: PackType
get() = PackType.SERVER_DATA
override fun reload(manager: ResourceManager) {
//NO-OP, handled by reload(manager, registry)
}
fun reload(manager: ResourceManager, registry: HolderLookup.Provider) {
val gson = setupParser(registry)
val data = hashMapOf<ResourceLocation, T>()
manager.listResources(resourcePath) { path -> path.endsWith(JSON_EXTENSION) }.forEach { (identifier, resource) ->
if (identifier.namespace == "pixelmon") {
return@forEach
}
resource.open().use { stream ->
stream.bufferedReader().use { reader ->
val resolvedIdentifier = ResourceLocation.fromNamespaceAndPath(identifier.namespace, File(identifier.path).nameWithoutExtension)
data[resolvedIdentifier] = parse(reader, resolvedIdentifier, gson)
}
}
}
reload(data, registry)
}
fun parse(reader: BufferedReader, identifier: ResourceLocation, gson: Gson): T {
return try {
gson.fromJson(reader, typeToken.type)
} catch (exception: Exception) {
throw ExecutionException("Error loading JSON for data: $identifier", exception)
}
}
/**
* Allows the creation of [Gson] instances which have adapters that are registry-aware, e.g. [com.cobblemon.mod.common.pokemon.evolution.adapters.LegacyItemConditionWrapperAdapter]
*/
fun setupParser(registry: HolderLookup.Provider): Gson
/**
* Reloads this registry from the deserialized data.
*
* @param data A map of the data associating an instance to the respective identifier from the [ResourceManager].
*/
fun reload(data: Map<ResourceLocation, T>, registry: HolderLookup.Provider)
companion object {
const val JSON_EXTENSION = ".json"
}
}
@@ -40,7 +40,7 @@ object Fossils: JsonDataRegistry<Fossil> {
.registerTypeAdapter(ResourceLocation::class.java, IdentifierAdapter)
.registerTypeAdapter(PokemonProperties::class.java, pokemonPropertiesShortAdapter)
.registerTypeAdapter(TypeToken.getParameterized(RegistryLikeCondition::class.java, Item::class.java).type, ItemLikeConditionAdapter)
.registerTypeAdapter(ItemPredicate::class.java, LegacyItemConditionWrapperAdapter)
.registerTypeAdapter(ItemPredicate::class.java, LegacyItemConditionWrapperAdapter())
.create()
override val typeToken: TypeToken<Fossil> = TypeToken.get(Fossil::class.java)
@@ -10,7 +10,7 @@ package com.cobblemon.mod.common.api.interaction
import com.cobblemon.mod.common.Cobblemon
import com.cobblemon.mod.common.api.conditional.RegistryLikeCondition
import com.cobblemon.mod.common.api.data.JsonDataRegistry
import com.cobblemon.mod.common.api.data.ServerJsonDataRegistry
import com.cobblemon.mod.common.api.molang.ExpressionLike
import com.cobblemon.mod.common.api.pokemon.PokemonProperties
import com.cobblemon.mod.common.api.pokemon.requirement.Requirement
@@ -26,6 +26,7 @@ import com.google.gson.Gson
import com.google.gson.GsonBuilder
import com.google.gson.reflect.TypeToken
import net.minecraft.advancements.critereon.ItemPredicate
import net.minecraft.core.HolderLookup
import net.minecraft.resources.ResourceLocation
import net.minecraft.server.level.ServerPlayer
import net.minecraft.server.packs.PackType
@@ -36,28 +37,31 @@ import net.minecraft.world.level.block.Block
import net.minecraft.world.level.levelgen.structure.Structure
import net.minecraft.world.level.material.Fluid
object PokemonInteractions : JsonDataRegistry<PokemonInteractionSet> {
object PokemonInteractions : ServerJsonDataRegistry<PokemonInteractionSet> {
override val id = cobblemonResource("pokemon_interactions")
override val type = PackType.SERVER_DATA
override fun setupParser(registry: HolderLookup.Provider): Gson {
return GsonBuilder()
.registerTypeAdapter(ResourceLocation::class.java, IdentifierAdapter)
.registerTypeAdapter(PokemonProperties::class.java, pokemonPropertiesShortAdapter)
.registerTypeAdapter(ItemPredicate::class.java, LegacyItemConditionWrapperAdapter(registry))
.registerTypeAdapter(Requirement::class.java, CobblemonRequirementAdapter)
.registerTypeAdapter(InteractionEffect::class.java, InteractionEffectAdapter)
.registerTypeAdapter(ExpressionLike::class.java, ExpressionLikeAdapter)
.registerTypeAdapter(TypeToken.getParameterized(RegistryLikeCondition::class.java, Biome::class.java).type, BiomeLikeConditionAdapter)
.registerTypeAdapter(TypeToken.getParameterized(RegistryLikeCondition::class.java, Block::class.java).type, BlockLikeConditionAdapter)
.registerTypeAdapter(TypeToken.getParameterized(RegistryLikeCondition::class.java, Item::class.java).type, ItemLikeConditionAdapter)
.registerTypeAdapter(TypeToken.getParameterized(RegistryLikeCondition::class.java, Structure::class.java).type, StructureLikeConditionAdapter)
.registerTypeAdapter(TypeToken.getParameterized(RegistryLikeCondition::class.java, Fluid::class.java).type, FluidLikeConditionAdapter)
.registerTypeAdapter(IntRange::class.java, IntRangeAdapter)
.registerTypeAdapterFactory(LowerCaseEnumTypeAdapterFactory())
.setPrettyPrinting()
.create()
}
override val observable = SimpleObservable<PokemonInteractions>()
override val typeToken: TypeToken<PokemonInteractionSet> = TypeToken.get(PokemonInteractionSet::class.java)
override val resourcePath = "pokemon_interactions"
override val gson: Gson = GsonBuilder()
.registerTypeAdapter(ResourceLocation::class.java, IdentifierAdapter)
.registerTypeAdapter(PokemonProperties::class.java, pokemonPropertiesShortAdapter)
.registerTypeAdapter(ItemPredicate::class.java, LegacyItemConditionWrapperAdapter)
.registerTypeAdapter(Requirement::class.java, CobblemonRequirementAdapter)
.registerTypeAdapter(InteractionEffect::class.java, InteractionEffectAdapter)
.registerTypeAdapter(ExpressionLike::class.java, ExpressionLikeAdapter)
.registerTypeAdapter(TypeToken.getParameterized(RegistryLikeCondition::class.java, Biome::class.java).type, BiomeLikeConditionAdapter)
.registerTypeAdapter(TypeToken.getParameterized(RegistryLikeCondition::class.java, Block::class.java).type, BlockLikeConditionAdapter)
.registerTypeAdapter(TypeToken.getParameterized(RegistryLikeCondition::class.java, Item::class.java).type, ItemLikeConditionAdapter)
.registerTypeAdapter(TypeToken.getParameterized(RegistryLikeCondition::class.java, Structure::class.java).type, StructureLikeConditionAdapter)
.registerTypeAdapter(TypeToken.getParameterized(RegistryLikeCondition::class.java, Fluid::class.java).type, FluidLikeConditionAdapter)
.registerTypeAdapter(IntRange::class.java, IntRangeAdapter)
.registerTypeAdapterFactory(LowerCaseEnumTypeAdapterFactory())
.setPrettyPrinting()
.create()
val interactions = mutableListOf<PokemonInteractionSet>()
@@ -65,7 +69,7 @@ object PokemonInteractions : JsonDataRegistry<PokemonInteractionSet> {
PokemonInteractionsSyncPacket(interactions).sendToPlayer(player)
}
override fun reload(data: Map<ResourceLocation, PokemonInteractionSet>) {
override fun reload(data: Map<ResourceLocation, PokemonInteractionSet>, registry: HolderLookup.Provider) {
interactions.clear()
val split = data.entries
.partition {
@@ -87,4 +91,4 @@ object PokemonInteractions : JsonDataRegistry<PokemonInteractionSet> {
.flatMap { it.interactions }
.firstOrNull(interactionCheck)
}
}
}
@@ -128,7 +128,7 @@ object PokemonSpecies : JsonDataRegistry<Species> {
.registerTypeAdapter(TypeToken.getParameterized(RegistryLikeCondition::class.java, Fluid::class.java).type, FluidLikeConditionAdapter)
.registerTypeAdapter(EggGroup::class.java, EggGroupAdapter)
.registerTypeAdapter(MobEffect::class.java, RegistryElementAdapter<MobEffect>(BuiltInRegistries::MOB_EFFECT))
.registerTypeAdapter(ItemPredicate::class.java, LegacyItemConditionWrapperAdapter)
.registerTypeAdapter(ItemPredicate::class.java, LegacyItemConditionWrapperAdapter())
.registerTypeAdapter(RidingBehaviourSettings::class.java, RidingBehaviourSettingsAdapter)
.registerTypeAdapter(RideSoundSettingsList::class.java, RideSoundSettingsListAdapter)
.registerTypeAdapter(ObtainableItemCondition::class.java, ObtainableItemConditionAdapter)
@@ -128,6 +128,7 @@ class DiscShelfBlockEntity(pos: BlockPos, state: BlockState) : BlockEntity(Cobbl
fun isValidItem(stack: ItemStack): Boolean {
return stack.item is TechnicalMachineItem
|| stack.item == CobblemonItems.BLANK_TM
|| stack.item == CobblemonItems.UPGRADE
|| stack.item == CobblemonItems.DUBIOUS_DISC
|| stack.has(DataComponents.JUKEBOX_PLAYABLE)
@@ -31,6 +31,7 @@ import com.cobblemon.mod.common.client.sound.instances.CancellableSoundInstance
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
import com.cobblemon.mod.common.item.PokeBallItem
import com.cobblemon.mod.common.pokemon.Pokemon
import com.cobblemon.mod.common.pokemon.activestate.SentOutState
import com.cobblemon.mod.common.util.DataKeys
import com.cobblemon.mod.common.util.giveOrDropItemStack
import com.cobblemon.mod.common.util.lang
@@ -235,6 +236,7 @@ class FossilMultiblockStructure (
entity.setPos(fixedPosition.center.subtract(0.0, 0.5, 0.0))
// TODO: Find a correct way to set the new entity's Yaw rotation. (Face away from the machine)
if (world.addFreshEntity(entity)) {
pokemon.state = SentOutState(entity)
CobblemonEvents.FOSSIL_REVIVED.post(FossilRevivedEvent(pokemon, null))
return true
} else {
@@ -8,9 +8,9 @@
package com.cobblemon.mod.common.client.render.block
import com.cobblemon.mod.common.CobblemonItemComponents
import com.cobblemon.mod.common.block.entity.DiscShelfBlockEntity
import com.cobblemon.mod.common.CobblemonItems
import com.cobblemon.mod.common.client.render.color.TechnicalMachineItemColorProvider
import com.cobblemon.mod.common.util.cobblemonResource
import com.mojang.blaze3d.vertex.PoseStack
import com.mojang.blaze3d.vertex.VertexConsumer
@@ -37,15 +37,15 @@ class DiscShelfBlockEntityRenderer(ctx: BlockEntityRendererProvider.Context) :
private val slotHeightPixels = 2
private val textureSize = 16f
private val slotUMax = slotWidthPixels / textureSize
private val slotVMax = slotHeightPixels / textureSize
private val leftMargin = 1f
private val rightMargin = 1f
private val topMargin = 1f
private val interRowSpacing = 0f
private val tmOverlayDepthOffset = -0.0002f
private val slotBrightnessMultiplier = 0.65f
private val tmBaseTexture = cobblemonResource("textures/block/disc_shelf/technical_machine_base.png")
private val tmOverlayTexture = cobblemonResource("textures/block/disc_shelf/technical_machine_overlay.png")
private val musicDiscUnknownTexture = cobblemonResource("textures/block/disc_shelf/music_disc_unknown.png")
override fun render(
@@ -98,35 +98,19 @@ class DiscShelfBlockEntityRenderer(ctx: BlockEntityRendererProvider.Context) :
poseStack.translate(posX, posY, -0.001f)
if (item.item == CobblemonItems.TECHNICAL_MACHINE) {
val baseColor = TechnicalMachineItemColorProvider.getColor(item, 0)
val overlayColor = TechnicalMachineItemColorProvider.getColor(item, 1)
val elementId = item.components.get(CobblemonItemComponents.TM_MOVE)!!.move.elementalType.showdownId
val texture = cobblemonResource("textures/block/disc_shelf/${elementId}_tm.png")
renderSlotQuad(
poseStack,
buffer,
tmBaseTexture,
baseColor,
texture,
0xFFFFFF,
packedLight,
slotWidth,
slotHeight,
tint = true,
useTextRenderType = true
)
poseStack.pushPose()
poseStack.translate(0.0, 0.0, tmOverlayDepthOffset.toDouble())
renderSlotQuad(
poseStack,
buffer,
tmOverlayTexture,
overlayColor,
packedLight,
slotWidth,
slotHeight,
tint = true,
useTextRenderType = true
)
poseStack.popPose()
} else {
val tex = getItemTexture(item)
renderSlotQuad(
@@ -176,13 +160,15 @@ class DiscShelfBlockEntityRenderer(ctx: BlockEntityRendererProvider.Context) :
val g = (gBase * slotBrightnessMultiplier).coerceIn(0f, 1f)
val b = (bBase * slotBrightnessMultiplier).coerceIn(0f, 1f)
consumer.addVertex(matrix, 0f, 0f, 0f).setColor(r, g, b, a).setUv(0f, 1f)
// The quad faces -Z, so a viewer looking at it sees +X on their left.
// U runs from slotUMax at x = 0 to 0 at x = width so the art is not mirrored.
consumer.addVertex(matrix, 0f, 0f, 0f).setColor(r, g, b, a).setUv(slotUMax, slotVMax)
.setUv1(0, 0).setUv2(light and 0xFFFF, light shr 16).setNormal(0f, 0f, -1f)
consumer.addVertex(matrix, 0f, height, 0f).setColor(r, g, b, a).setUv(0f, 0f)
consumer.addVertex(matrix, 0f, height, 0f).setColor(r, g, b, a).setUv(slotUMax, 0f)
.setUv1(0, 0).setUv2(light and 0xFFFF, light shr 16).setNormal(0f, 0f, -1f)
consumer.addVertex(matrix, width, height, 0f).setColor(r, g, b, a).setUv(1f, 0f)
consumer.addVertex(matrix, width, height, 0f).setColor(r, g, b, a).setUv(0f, 0f)
.setUv1(0, 0).setUv2(light and 0xFFFF, light shr 16).setNormal(0f, 0f, -1f)
consumer.addVertex(matrix, width, 0f, 0f).setColor(r, g, b, a).setUv(1f, 1f)
consumer.addVertex(matrix, width, 0f, 0f).setColor(r, g, b, a).setUv(0f, slotVMax)
.setUv1(0, 0).setUv2(light and 0xFFFF, light shr 16).setNormal(0f, 0f, -1f)
}
@@ -1,27 +0,0 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.client.render.color
import com.cobblemon.mod.common.api.types.ElementalTypes
import com.cobblemon.mod.common.item.components.TMMoveComponent.Companion.getTMMove
import net.minecraft.client.color.item.ItemColor
import net.minecraft.util.FastColor
import net.minecraft.world.item.ItemStack
object TechnicalMachineItemColorProvider : ItemColor {
override fun getColor(itemStack: ItemStack, layer: Int): Int {
val moveType = getTMMove(itemStack)?.elementalType ?: ElementalTypes.NORMAL
val color = when (layer) {
0 -> moveType.primaryColor
1 -> moveType.secondaryColor
else -> 0xFFFFFF
}
return FastColor.ARGB32.opaque(color)
}
}
@@ -9,7 +9,6 @@
package com.cobblemon.mod.common.command
import com.cobblemon.mod.common.api.permission.CobblemonPermissions
import com.cobblemon.mod.common.api.pokemon.PokemonPropertyExtractor
import com.cobblemon.mod.common.command.argument.PokemonPropertiesArgumentType
import com.cobblemon.mod.common.util.party
import com.cobblemon.mod.common.util.permission
@@ -45,7 +44,7 @@ object TestPartySlotCommand {
val player = context.player(PLAYER)
val slot = IntegerArgumentType.getInteger(context, SLOT)
val properties = PokemonPropertiesArgumentType.getPokemonProperties(context, PROPERTIES)
return if (player.party().get(slot - 1)?.createPokemonProperties(PokemonPropertyExtractor.ALL)
?.let { properties.isSubSetOf(it) } == true) Command.SINGLE_SUCCESS else NO_SUCCESS
val target = player.party().get(slot - 1) ?: return NO_SUCCESS
return if (properties.matches(target)) Command.SINGLE_SUCCESS else NO_SUCCESS
}
}
@@ -15,6 +15,7 @@ import com.cobblemon.mod.common.api.berry.Berries
import com.cobblemon.mod.common.api.cooking.Seasonings
import com.cobblemon.mod.common.api.data.DataProvider
import com.cobblemon.mod.common.api.data.DataRegistry
import com.cobblemon.mod.common.api.data.ServerJsonDataRegistry
import com.cobblemon.mod.common.api.dialogue.Dialogues
import com.cobblemon.mod.common.api.events.CobblemonEvents
import com.cobblemon.mod.common.api.fishing.PokeRods
@@ -51,6 +52,8 @@ import com.cobblemon.mod.common.pokemon.properties.PropertiesCompletionProvider
import com.cobblemon.mod.common.util.cobblemonResource
import com.cobblemon.mod.common.util.ifClient
import com.cobblemon.mod.common.util.server
import net.minecraft.core.HolderLookup
import com.cobblemon.mod.common.world.biome.CobblemonBiomeLoader
import java.util.UUID
import net.minecraft.resources.ResourceLocation
import net.minecraft.server.level.ServerPlayer
@@ -71,8 +74,7 @@ object CobblemonDataProvider : DataProvider {
// client is public only so we can use it cross-module. maybe a more elegant solution exists, but this works
@ApiStatus.Internal
val clientResourceReloader: PreparableReloadListener = SimpleResourceReloader(PackType.CLIENT_RESOURCES)
private val serverResourceReloader: PreparableReloadListener = SimpleResourceReloader(PackType.SERVER_DATA)
val clientResourceReloader: PreparableReloadListener = ClientResourceReloader()
fun registerDefaults() {
this.register(CobblemonScripts, reloadable = true)
@@ -90,6 +92,7 @@ object CobblemonDataProvider : DataProvider {
this.register(PokeBalls, reloadable = false)
this.register(PropertiesCompletionProvider, reloadable = false)
this.register(CobblemonPartyPools, reloadable = false)
this.register(CobblemonBiomeLoader, reloadable = true)
this.register(CobblemonPartyCompositions, reloadable = false)
this.register(SpawnDetailPresets, reloadable = true)
this.register(CobblemonSpawnRules, reloadable = true)
@@ -126,9 +129,9 @@ object CobblemonDataProvider : DataProvider {
}
ifClient {
Cobblemon.implementation.registerResourceReloader(cobblemonResource("client_resources"), clientResourceReloader, PackType.CLIENT_RESOURCES, emptyList())
Cobblemon.implementation.registerResourceReloader(cobblemonResource("client_resources"), PackType.CLIENT_RESOURCES, emptyList()) { clientResourceReloader }
}
Cobblemon.implementation.registerResourceReloader(cobblemonResource("data_resources"), serverResourceReloader, PackType.SERVER_DATA, emptyList())
Cobblemon.implementation.registerResourceReloader(cobblemonResource("data_resources"), PackType.SERVER_DATA, emptyList()) { ServerResourceReloader(it) }
}
override fun <T : DataRegistry> register(registry: T, reloadable: Boolean): T {
@@ -167,12 +170,25 @@ object CobblemonDataProvider : DataProvider {
}
}
private class SimpleResourceReloader(private val type: PackType) : ResourceManagerReloadListener {
private class ClientResourceReloader : ResourceManagerReloadListener {
override fun onResourceManagerReload(manager: ResourceManager) {
registries.filter { it.type == PackType.CLIENT_RESOURCES }.forEach { it.reload(manager) }
}
}
private class ServerResourceReloader(private val registryAccess: HolderLookup.Provider) : ResourceManagerReloadListener {
override fun onResourceManagerReload(manager: ResourceManager) {
// Check for a server running, this is due to the create a world screen triggering datapack reloads, these are fine to happen as many times as needed as players may be in the process of adding their datapacks.
val reloadAllowed = server()?.isReady != true
registries.filter { it.type == this.type && (reloadAllowed || it in reloadableRegistries) }
.forEach { it.reload(manager) }
registries
.filter { it.type == PackType.SERVER_DATA && (reloadAllowed || it in reloadableRegistries) }
.forEach {
if (it is ServerJsonDataRegistry<*>) {
it.reload(manager, registryAccess)
} else {
it.reload(manager)
}
}
}
}
}
@@ -17,7 +17,7 @@ import net.minecraft.resources.ResourceKey
object CobblemonParticles : PlatformRegistry<Registry<ParticleType<*>>, ResourceKey<Registry<ParticleType<*>>>, ParticleType<*>>() {
override val registry: Registry<ParticleType<*>> = BuiltInRegistries.PARTICLE_TYPE
override var registry: Registry<ParticleType<*>>? = BuiltInRegistries.PARTICLE_TYPE
override val resourceKey: ResourceKey<Registry<ParticleType<*>>> = Registries.PARTICLE_TYPE
val SNOWSTORM_PARTICLE_TYPE = this.create("snowstorm", SnowstormParticleType())
@@ -26,66 +26,58 @@ import net.minecraft.resources.ResourceLocation
* @author Licious
* @since February 11th, 2023
*/
abstract class PlatformRegistry<R : Registry<T>, K : ResourceKey<R>, T> {
abstract class PlatformRegistry<R : Registry<T>, K : ResourceKey<R>, T>(
open var registry: R? = null,
open val resourceKey: K? = null
) {
protected val queue = hashMapOf<ResourceLocation, T>()
/**
* The vanilla [Registry].
*/
abstract val registry: R
/**
* Creates a new entry in this registry.
*
* @param E The type of the entry.
* @param name The name of the entry, this will be an [ResourceLocation.path].
* @param entry The entry being added.
* @return The entry created.
*/
open fun <E : T> create(name: String, entry: E): E {
val resourceLocation = cobblemonResource(name)
return this.create(resourceLocation, entry)
}
/**
* The vanilla [ResourceKey].
*/
abstract val resourceKey: K
/**
* Creates a new entry in this registry. Introduced for sidemod usage
*
* @param E The type of the entry.
* @param resourceLocation The name of the entry, this will be an [ResourceLocation.path].
* @param entry The entry being added.
* @return The entry created.
*/
open fun <E : T> create(resourceLocation: ResourceLocation, entry: E): E {
this.queue[resourceLocation] = entry
if (entry is BagItemLike) {
BagItems.bagItems.add(
priority = Priority.NORMAL,
value = entry
)
}
return entry
}
protected val queue = hashMapOf<ResourceLocation, T>()
/**
* Handles the registration of this registry into the platform specific one.
*
* @param consumer The consumer that will handle the logic for registering every entry in this registry into the platform specific one.
*/
open fun register(consumer: (ResourceLocation, T) -> Unit) {
this.queue.forEach(consumer)
}
/**
* Creates a new entry in this registry.
*
* @param E The type of the entry.
* @param name The name of the entry, this will be an [ResourceLocation.path].
* @param entry The entry being added.
* @return The entry created.
*/
open fun <E : T> create(name: String, entry: E): E {
val resourceLocation = cobblemonResource(name)
return this.create(resourceLocation, entry)
}
/**
* Creates a new entry in this registry. Introduced for sidemod usage
*
* @param E The type of the entry.
* @param name The name of the entry, this will be an [ResourceLocation.path].
* @param entry The entry being added.
* @return The entry created.
*/
open fun <E : T> create(resourceLocation: ResourceLocation, entry: E): E {
this.queue[resourceLocation] = entry
if (entry is BagItemLike) {
BagItems.bagItems.add(
priority = Priority.NORMAL,
value = entry
)
}
return entry
}
/**
* Handles the registration of this registry into the platform specific one.
*
* @param consumer The consumer that will handle the logic for registering every entry in this registry into the platform specific one.
*/
open fun register(consumer: (ResourceLocation, T) -> Unit) {
this.queue.forEach(consumer)
}
/**
* Returns a collection of every entry in this registry.
*
* @return The entries of this registry.
*/
open fun all(): Collection<T> = this.queue.values.toList()
/**
* Returns a collection of every entry in this registry.
*
* @return The entries of this registry.
*/
open fun all(): Collection<T> = this.queue.values.toList()
}
@@ -9,6 +9,7 @@
package com.cobblemon.mod.common.pokemon.evolution.adapters
import com.cobblemon.mod.common.util.adapters.CodecBackedAdapter
import com.cobblemon.mod.common.util.adapters.RegistryAccessResolver
import com.google.gson.JsonDeserializationContext
import com.google.gson.JsonDeserializer
import com.google.gson.JsonElement
@@ -19,6 +20,7 @@ import net.minecraft.advancements.critereon.ItemCustomDataPredicate
import net.minecraft.advancements.critereon.ItemPredicate
import net.minecraft.advancements.critereon.ItemSubPredicates
import net.minecraft.advancements.critereon.NbtPredicate
import net.minecraft.core.HolderLookup
import net.minecraft.core.registries.BuiltInRegistries
import net.minecraft.core.registries.Registries
import net.minecraft.resources.ResourceLocation
@@ -26,13 +28,16 @@ import net.minecraft.tags.TagKey
import java.lang.reflect.Type
// This is an ugly adapter because breaking changes are scary and people would ;(
object LegacyItemConditionWrapperAdapter : JsonDeserializer<ItemPredicate>, JsonSerializer<ItemPredicate> {
class LegacyItemConditionWrapperAdapter(val registryAccess: HolderLookup.Provider = RegistryAccessResolver.getAccess())
: JsonDeserializer<ItemPredicate>, JsonSerializer<ItemPredicate> {
private const val TAG_PREFIX = "#"
private const val LEGACY_ITEM = "item"
private const val LEGACY_NBT = "nbt"
companion object {
private const val TAG_PREFIX = "#"
private const val LEGACY_ITEM = "item"
private const val LEGACY_NBT = "nbt"
}
private val codecAdapter = CodecBackedAdapter(ItemPredicate.CODEC)
private val codecAdapter = CodecBackedAdapter(ItemPredicate.CODEC, registryAccess)
override fun deserialize(json: JsonElement, typeOfT: Type, context: JsonDeserializationContext): ItemPredicate {
if (json.isJsonPrimitive) {
@@ -64,7 +69,9 @@ object LegacyItemConditionWrapperAdapter : JsonDeserializer<ItemPredicate>, Json
{ id ->
if (isTag) {
val tag = TagKey.create(Registries.ITEM, id)
builder.of(tag)
registryAccess.lookup(Registries.ITEM)
.map { builder.of(tag) }
.orElseGet { builder }
} else {
BuiltInRegistries.ITEM.getOptional(id)
.map { builder.of(it) }
@@ -75,4 +82,4 @@ object LegacyItemConditionWrapperAdapter : JsonDeserializer<ItemPredicate>, Json
)
}
}
}
@@ -52,6 +52,10 @@ fun Collection<JsonElement>.toJsonArray(): JsonArray {
return array
}
fun JsonElement.asJsonObjectOrNull(): JsonObject? =
if (isJsonObject) asJsonObject else null
fun JsonObject.isEmpty() = size() <= 0
fun JsonObject.isNotEmpty() = size() > 0
@@ -10,16 +10,19 @@ package com.cobblemon.mod.common.util.adapters
import com.google.gson.*
import com.mojang.serialization.Codec
import com.mojang.serialization.JsonOps
import net.minecraft.core.HolderLookup
import net.minecraft.resources.RegistryOps
import java.lang.reflect.Type
class CodecBackedAdapter<T>(val codec: Codec<T>) : JsonDeserializer<T>, JsonSerializer<T> {
class CodecBackedAdapter<T>(val codec: Codec<T>, val registryAccess: HolderLookup.Provider = RegistryAccessResolver.getAccess()) : JsonDeserializer<T>, JsonSerializer<T> {
override fun deserialize(
jElement: JsonElement,
typeOfT: Type,
context: JsonDeserializationContext
): T {
val ops = RegistryOpsProvider.getOpsWithDefaultFallback()
val ops = RegistryOps.create(JsonOps.INSTANCE, registryAccess)
val result = ops.withDecoder(codec).apply(jElement)
return result.result().orElseThrow {
IllegalStateException("Failed to deserialize $jElement: ${result.error().orElse(null)}")
@@ -31,7 +34,7 @@ class CodecBackedAdapter<T>(val codec: Codec<T>) : JsonDeserializer<T>, JsonSeri
typeOfSrc: Type?,
context: JsonSerializationContext?
): JsonElement {
val ops = RegistryOpsProvider.getOpsWithDefaultFallback()
val ops = RegistryOps.create(JsonOps.INSTANCE, registryAccess)
val result = ops.withEncoder(codec).apply(src)
return result.result().orElseThrow {
IllegalStateException("Failed to serialize $src: ${result.error().orElse(null)}")
@@ -15,27 +15,33 @@ import com.google.gson.JsonElement
import com.mojang.serialization.DynamicOps
import com.mojang.serialization.JsonOps
import net.minecraft.client.Minecraft
import net.minecraft.core.RegistryAccess
import net.minecraft.resources.RegistryOps
/**
* Utility for obtaining RegistryOps with the correct client/server registry access.
* Used when manually encoding/decoding registry-backed values (e.g., items, predicates).
* Utility for getting RegistryAccess with the correct client/server registry access.
* Used when as a fallback when manually encoding/decoding registry-backed values (e.g., items, predicates).
*/
object RegistryOpsProvider {
fun getOpsWithDefaultFallback(): DynamicOps<JsonElement> {
object RegistryAccessResolver {
fun getAccess(): RegistryAccess {
try {
Cobblemon.implementation
} catch (e: UninitializedPropertyAccessException) {
return RegistryAccess.EMPTY
}
if (Cobblemon.implementation.environment() == Environment.CLIENT) {
val minecraft = Minecraft.getInstance()
if (minecraft != null && minecraft.level != null)
return RegistryOps.create(JsonOps.INSTANCE, minecraft.level!!.registryAccess())
return minecraft.level!!.registryAccess()
}
if (Cobblemon.implementation.environment() == Environment.SERVER) {
val server = server()
if (server != null)
return RegistryOps.create(JsonOps.INSTANCE, server.registryAccess())
return server.registryAccess()
}
return JsonOps.INSTANCE
return RegistryAccess.EMPTY
}
}
@@ -0,0 +1,35 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.util.biome
import com.cobblemon.mod.common.Cobblemon
import net.minecraft.core.HolderLookup
import net.minecraft.core.Registry
import net.minecraft.core.RegistryAccess
import net.minecraft.resources.ResourceKey
import java.util.*
object RegistryExtensions {
@JvmStatic
fun <T> getRegistryLookup(
registryKey: ResourceKey<out Registry<T>>
): Optional<HolderLookup.RegistryLookup<T>> {
return Cobblemon.registryAccess.getRegistryLookup(registryKey)
}
}
fun <T> RegistryAccess?.getRegistryLookup(
registryKey: ResourceKey<out Registry<T>>
): Optional<HolderLookup.RegistryLookup<T>> {
if (this == null || this == RegistryAccess.EMPTY) {
return Optional.empty()
}
return this.lookup(registryKey)
}
@@ -13,6 +13,7 @@ import com.cobblemon.mod.common.api.abilities.Abilities
import com.cobblemon.mod.common.api.abilities.Ability
import com.cobblemon.mod.common.api.moves.BenchedMoves
import com.cobblemon.mod.common.api.moves.MoveSet
import com.cobblemon.mod.common.api.pokemon.feature.SpeciesFeatures
import com.cobblemon.mod.common.pokemon.EVs
import com.cobblemon.mod.common.pokemon.FormData
import com.cobblemon.mod.common.pokemon.Gender
@@ -21,20 +22,22 @@ import com.cobblemon.mod.common.pokemon.Pokemon
import com.cobblemon.mod.common.pokemon.Species
import com.cobblemon.mod.common.util.DataKeys
import com.cobblemon.mod.common.util.codec.CodecUtils
import com.cobblemon.mod.common.util.codec.internal.ClientPokemonP1.Companion.FEATURES
import com.cobblemon.mod.common.util.codec.internal.ClientPokemonP1.Companion.FEATURE_ID
import com.mojang.serialization.Codec
import com.mojang.serialization.MapCodec
import com.mojang.serialization.codecs.RecordCodecBuilder
import java.util.Optional
import java.util.UUID
import net.minecraft.core.UUIDUtil
import net.minecraft.network.chat.Component
import net.minecraft.network.chat.ComponentSerialization
import net.minecraft.nbt.CompoundTag
import net.minecraft.nbt.StringTag
import net.minecraft.nbt.Tag
internal class PokemonP1(
val uuid: UUID,
val species: Species,
val form: FormData,
val nickname: Optional<Component>,
val features: List<CompoundTag>,
val level: Int,
val experience: Int,
val friendship: Int,
@@ -55,7 +58,22 @@ internal class PokemonP1(
other.ability = this.ability
other.species = this.species
other.form = this.form
this.nickname.ifPresent { other.nickname = it.copy() }
this.features.forEach { featureNbt ->
val featureId = featureNbt.getString(FEATURE_ID)
if (featureId.isEmpty()) {
return@forEach
}
val speciesFeatureProviders = SpeciesFeatures.getFeaturesFor(other.species)
val feature = speciesFeatureProviders.firstNotNullOfOrNull { provider -> provider(featureNbt) } ?: return@forEach
if (
featureNbt.contains("keys", Tag.TAG_STRING.toInt()) &&
!featureNbt.getList("keys", Tag.TAG_STRING.toInt()).contains(StringTag.valueOf(featureId))
) {
return@forEach
}
other.features.removeIf { it.name == feature.name }
other.features.add(feature)
}
other.level = this.level
other.experience = this.experience
other.setFriendship(this.friendship)
@@ -91,7 +109,7 @@ internal class PokemonP1(
UUIDUtil.LENIENT_CODEC.fieldOf(DataKeys.POKEMON_UUID).forGetter(PokemonP1::uuid),
Species.BY_IDENTIFIER_CODEC.fieldOf(DataKeys.POKEMON_SPECIES_IDENTIFIER).forGetter(PokemonP1::species),
Codec.STRING.fieldOf(DataKeys.POKEMON_FORM_ID).forGetter { pokemon -> pokemon.form.formOnlyShowdownId() },
ComponentSerialization.CODEC.optionalFieldOf(DataKeys.POKEMON_NICKNAME).forGetter(PokemonP1::nickname),
Codec.list(CompoundTag.CODEC).optionalFieldOf(FEATURES, emptyList()).forGetter(PokemonP1::features),
CodecUtils.dynamicIntRange(1) { Cobblemon.config.maxPokemonLevel }.fieldOf(DataKeys.POKEMON_LEVEL).forGetter(PokemonP1::level),
Codec.intRange(0, Int.MAX_VALUE).fieldOf(DataKeys.POKEMON_EXPERIENCE).forGetter(PokemonP1::experience),
CodecUtils.dynamicIntRange(0) { Cobblemon.config.maxPokemonFriendship }.fieldOf(DataKeys.POKEMON_FRIENDSHIP).forGetter(PokemonP1::friendship),
@@ -104,9 +122,9 @@ internal class PokemonP1(
Codec.FLOAT.fieldOf(DataKeys.POKEMON_SCALE_MODIFIER).forGetter(PokemonP1::scaleModifier),
Codec.BOOL.fieldOf(DataKeys.POKEMON_SHINY).forGetter(PokemonP1::shiny),
Ability.CODEC.fieldOf(DataKeys.POKEMON_ABILITY).forGetter(PokemonP1::ability)
).apply(instance) { uuid, species, formId, nickname, level, experience, friendship, currentHealth, gender, ivs, evs, moveSet, benchedMoves, scaleModifier, shiny, ability ->
).apply(instance) { uuid, species, formId, features, level, experience, friendship, currentHealth, gender, ivs, evs, moveSet, benchedMoves, scaleModifier, shiny, ability ->
val form = species.forms.firstOrNull { it.formOnlyShowdownId() == formId } ?: species.standardForm
PokemonP1(uuid, species, form, nickname, level, experience, friendship, currentHealth, gender, ivs, evs, moveSet, benchedMoves, scaleModifier, shiny, ability)
PokemonP1(uuid, species, form, features, level, experience, friendship, currentHealth, gender, ivs, evs, moveSet, benchedMoves, scaleModifier, shiny, ability)
}
}
@@ -114,7 +132,11 @@ internal class PokemonP1(
pokemon.uuid,
pokemon.species,
pokemon.form,
Optional.ofNullable(pokemon.nickname),
pokemon.features.map { feature ->
val nbt = CompoundTag()
nbt.putString(FEATURE_ID, feature.name)
feature.saveToNBT(nbt)
},
pokemon.level,
pokemon.experience,
pokemon.friendship,
@@ -14,15 +14,12 @@ import com.cobblemon.mod.common.api.riding.stats.RidingStat
import com.cobblemon.mod.common.pokemon.OriginalTrainerType
import com.cobblemon.mod.common.pokemon.Pokemon
import com.cobblemon.mod.common.util.DataKeys
import com.cobblemon.mod.common.util.codec.internal.ClientPokemonP1.Companion.FEATURES
import com.cobblemon.mod.common.util.codec.internal.ClientPokemonP1.Companion.FEATURE_ID
import com.cobblemon.mod.common.util.codec.optionalFieldOfWithDefault
import com.mojang.serialization.Codec
import com.mojang.serialization.MapCodec
import com.mojang.serialization.codecs.RecordCodecBuilder
import net.minecraft.nbt.CompoundTag
import net.minecraft.nbt.StringTag
import net.minecraft.nbt.Tag
import net.minecraft.network.chat.Component
import net.minecraft.network.chat.ComponentSerialization
import net.minecraft.resources.ResourceLocation
import net.minecraft.world.item.ItemStack
import java.util.*
@@ -31,7 +28,7 @@ internal data class PokemonP3(
val originalTrainerType: OriginalTrainerType,
val originalTrainer: Optional<String>,
val forcedAspects: Set<String>,
val features: List<CompoundTag>,
val nickname: Optional<Component>,
val heldItemVisible: Optional<Boolean>,
val canDropHeldItem: Optional<Boolean>,
val cosmeticItem: ItemStack,
@@ -51,22 +48,7 @@ internal data class PokemonP3(
this.originalTrainer.ifPresent { other.originalTrainer = it }
other.refreshOriginalTrainer()
other.forcedAspects = this.forcedAspects
this.features.forEach { featureNbt ->
val featureId = featureNbt.getString(FEATURE_ID)
if (featureId.isEmpty()) {
return@forEach
}
val speciesFeatureProviders = SpeciesFeatures.getFeaturesFor(other.species)
val feature = speciesFeatureProviders.firstNotNullOfOrNull { provider -> provider(featureNbt) } ?: return@forEach
if (
featureNbt.contains("keys", Tag.TAG_STRING.toInt()) &&
!featureNbt.getList("keys", Tag.TAG_STRING.toInt()).contains(StringTag.valueOf(featureId))
) {
return@forEach
}
other.features.removeIf { it.name == feature.name }
other.features.add(feature)
}
this.nickname.ifPresent { other.nickname = it.copy() }
this.heldItemVisible.ifPresent { other.heldItemVisible = it }
this.canDropHeldItem.ifPresent { other.canDropHeldItem = it }
this.cosmeticItem.let { other.cosmeticItem = it }
@@ -92,7 +74,7 @@ internal data class PokemonP3(
OriginalTrainerType.CODEC.optionalFieldOfWithDefault(DataKeys.POKEMON_ORIGINAL_TRAINER_TYPE, OriginalTrainerType.NONE).forGetter(PokemonP3::originalTrainerType),
Codec.STRING.optionalFieldOf(DataKeys.POKEMON_ORIGINAL_TRAINER).forGetter(PokemonP3::originalTrainer),
Codec.list(Codec.STRING).optionalFieldOf(DataKeys.POKEMON_FORCED_ASPECTS, emptyList()).forGetter { it.forcedAspects.toMutableList() },
Codec.list(CompoundTag.CODEC).optionalFieldOf(FEATURES, emptyList()).forGetter(PokemonP3::features),
ComponentSerialization.CODEC.optionalFieldOf(DataKeys.POKEMON_NICKNAME).forGetter(PokemonP3::nickname),
Codec.BOOL.optionalFieldOf(DataKeys.HELD_ITEM_VISIBLE).forGetter(PokemonP3::heldItemVisible),
Codec.BOOL.optionalFieldOf(DataKeys.HELD_ITEM_AI_DROPPABLE).forGetter(PokemonP3::canDropHeldItem),
ItemStack.CODEC.optionalFieldOf(DataKeys.POKEMON_COSMETIC_ITEM).forGetter { Optional.ofNullable(it.cosmeticItem.takeIf { !it.isEmpty }) },
@@ -109,7 +91,7 @@ internal data class PokemonP3(
originalTrainerType,
originalTrainer,
forcedAspects,
features,
nickname,
heldItemVisible,
canDropHeldItem,
cosmeticItem,
@@ -125,7 +107,7 @@ internal data class PokemonP3(
originalTrainerType,
originalTrainer,
forcedAspects.toSet(),
features,
nickname,
heldItemVisible,
canDropHeldItem,
cosmeticItem.orElse(ItemStack.EMPTY),
@@ -146,11 +128,7 @@ internal data class PokemonP3(
pokemon.originalTrainerType,
Optional.ofNullable(pokemon.originalTrainer),
pokemon.forcedAspects,
pokemon.features.map { feature ->
val nbt = CompoundTag()
nbt.putString(FEATURE_ID, feature.name)
feature.saveToNBT(nbt)
},
Optional.ofNullable(pokemon.nickname),
Optional.ofNullable(pokemon.heldItemVisible),
Optional.ofNullable(pokemon.canDropHeldItem),
pokemon.cosmeticItem,
@@ -0,0 +1,19 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.world.biome
import com.google.gson.JsonElement
import net.minecraft.world.level.biome.Biome
import net.minecraft.world.level.biome.Climate
data class CobblemonBiome(
val biome: Biome,
val parameters: Climate.ParameterPoint,
val customData: Map<String, JsonElement> = emptyMap()
)
@@ -0,0 +1,75 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.world.biome
import com.cobblemon.mod.common.Cobblemon
import com.cobblemon.mod.common.api.data.JsonDataRegistry
import com.cobblemon.mod.common.api.reactive.SimpleObservable
import com.cobblemon.mod.common.util.cobblemonResource
import com.cobblemon.mod.common.world.biome.parse.CobblemonBiomeJsonParser
import com.google.gson.Gson
import com.google.gson.GsonBuilder
import com.google.gson.JsonElement
import com.google.gson.reflect.TypeToken
import com.mojang.serialization.JsonOps
import net.minecraft.resources.RegistryOps
import net.minecraft.resources.ResourceLocation
import net.minecraft.server.level.ServerPlayer
import net.minecraft.server.packs.PackType
import net.minecraft.server.packs.resources.ResourceManager
import java.io.Reader
object CobblemonBiomeLoader : JsonDataRegistry<CobblemonBiome> {
override val gson: Gson = GsonBuilder().setPrettyPrinting().create()
override val typeToken: TypeToken<CobblemonBiome> = TypeToken.get(CobblemonBiome::class.java)
override val resourcePath = "biomes"
override val id = cobblemonResource(resourcePath)
override val type = PackType.SERVER_DATA
override val observable = SimpleObservable<CobblemonBiomeLoader>()
private var loadedBiomes: Map<ResourceLocation, CobblemonBiome> = emptyMap()
@JvmStatic
fun load(
manager: ResourceManager,
registryInfoLookup: RegistryOps.RegistryInfoLookup
): Map<ResourceLocation, CobblemonBiome> {
val ops = RegistryOps.create(JsonOps.INSTANCE, registryInfoLookup)
val result = loadJsonData(manager) { reader, _, _ ->
parseBiome(reader, ops)
}
loadedBiomes = result.toMap()
observable.emit(this)
Cobblemon.LOGGER.info("MEALAM Loaded {} cobblemon biomes.", result.size)
return result
}
private fun parseBiome(
reader: Reader,
ops: RegistryOps<JsonElement>
): CobblemonBiome? = CobblemonBiomeJsonParser.parse(reader, ops)
override fun reload(manager: ResourceManager) = observable.emit(this)
override fun reload(data: Map<ResourceLocation, CobblemonBiome>) = Unit
override fun sync(player: ServerPlayer) = Unit
@JvmStatic
fun getLoadedBiomes(): Map<ResourceLocation, CobblemonBiome> = loadedBiomes
@JvmStatic
fun getLoadedBiome(location: ResourceLocation): CobblemonBiome =
loadedBiomes[location]
?: error("MEALAM Unknown location: $location")
}
@@ -0,0 +1,43 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.world.biome.parse
import com.cobblemon.mod.common.Cobblemon
import com.google.gson.JsonElement
import com.google.gson.JsonObject
import com.mojang.serialization.JsonOps
import net.minecraft.resources.RegistryOps
import net.minecraft.world.level.biome.Climate
object ClimateParametersSectionParser : CobblemonBiomeSectionParser {
override val id = "parameters"
override fun parse(
root: JsonObject,
ops: RegistryOps<JsonElement>,
builder: CobblemonBiomeParseBuilder
): Boolean {
val params = root.getAsJsonObject("parameters")
if (params == null) {
Cobblemon.LOGGER.error("MEALAM Missing 'parameters' section in biome JSON")
return false
}
val result = Climate.ParameterPoint.CODEC.parse(JsonOps.INSTANCE, params)
val parameterPoint = result.result().orElse(null)
if (parameterPoint == null) {
val errorMessage = result.error().map { it.message() }.orElse("unknown error")
Cobblemon.LOGGER.error("MEALAM Failed to parse climate parameters: {}", errorMessage)
return false
}
builder.parameters = parameterPoint
return true
}
}
@@ -0,0 +1,45 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.world.biome.parse
import com.cobblemon.mod.common.util.asJsonObjectOrNull
import com.cobblemon.mod.common.world.biome.CobblemonBiome
import com.google.gson.JsonElement
import com.google.gson.JsonParser
import net.minecraft.resources.RegistryOps
import java.io.Reader
object CobblemonBiomeJsonParser {
private val sectionParsers = mutableListOf(
VanillaBiomeSectionParser,
ClimateParametersSectionParser,
CustomDataSectionParser(setOf("parameters"))
)
fun parse(reader: Reader, ops: RegistryOps<JsonElement>): CobblemonBiome? {
val root = JsonParser.parseReader(reader).asJsonObjectOrNull() ?: return null
val builder = CobblemonBiomeParseBuilder()
for (parser in getParsers()) {
if (!parser.parse(root, ops, builder) && parser.required) {
return null
}
}
return builder.buildOrNull()
}
fun register(parser: CobblemonBiomeSectionParser) {
sectionParsers.add(parser)
}
private fun getParsers(): List<CobblemonBiomeSectionParser> = sectionParsers.toList()
}
@@ -0,0 +1,27 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.world.biome.parse
import com.cobblemon.mod.common.world.biome.CobblemonBiome
import com.google.gson.JsonElement
import net.minecraft.world.level.biome.Biome
import net.minecraft.world.level.biome.Climate
class CobblemonBiomeParseBuilder {
var biome: Biome? = null
var parameters: Climate.ParameterPoint? = null
val customData: MutableMap<String, JsonElement> = linkedMapOf()
fun buildOrNull(): CobblemonBiome? {
val parsedBiome = biome ?: return null
val parsedParameters = parameters ?: return null
return CobblemonBiome(parsedBiome, parsedParameters, customData.toMap())
}
}
@@ -0,0 +1,26 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.world.biome.parse
import com.google.gson.JsonElement
import com.google.gson.JsonObject
import net.minecraft.resources.RegistryOps
interface CobblemonBiomeSectionParser {
val id: String
val required: Boolean
get() = true
fun parse(
root: JsonObject,
ops: RegistryOps<JsonElement>,
builder: CobblemonBiomeParseBuilder
): Boolean
}
@@ -0,0 +1,34 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.world.biome.parse
import com.google.gson.JsonElement
import com.google.gson.JsonObject
import net.minecraft.resources.RegistryOps
class CustomDataSectionParser(
private val reservedKeys: Set<String>
) : CobblemonBiomeSectionParser {
override val id = "custom_data"
override val required = false
override fun parse(
root: JsonObject,
ops: RegistryOps<JsonElement>,
builder: CobblemonBiomeParseBuilder
): Boolean {
for ((key, value) in root.entrySet()) {
if (key !in reservedKeys) {
builder.customData[key] = value.deepCopy()
}
}
return true
}
}
@@ -0,0 +1,36 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.world.biome.parse
import com.cobblemon.mod.common.Cobblemon
import com.google.gson.JsonElement
import com.google.gson.JsonObject
import net.minecraft.resources.RegistryOps
import net.minecraft.world.level.biome.Biome
object VanillaBiomeSectionParser : CobblemonBiomeSectionParser {
override val id = "vanilla_biome"
override fun parse(
root: JsonObject,
ops: RegistryOps<JsonElement>,
builder: CobblemonBiomeParseBuilder
): Boolean {
val result = Biome.DIRECT_CODEC.parse(ops, root)
val biome = result.result().orElse(null)
if (biome == null) {
val errorMessage = result.error().map { it.message() }.orElse("unknown error")
Cobblemon.LOGGER.error("MEALAM Failed to parse vanilla biome: {}", errorMessage)
return false
}
builder.biome = biome
return true
}
}
@@ -18,7 +18,7 @@ import net.minecraft.world.level.levelgen.feature.Feature
object CobblemonFeatures : PlatformRegistry<Registry<Feature<*>>, ResourceKey<Registry<Feature<*>>>, Feature<*>>() {
override val registry: Registry<Feature<*>> = BuiltInRegistries.FEATURE
override var registry: Registry<Feature<*>>? = BuiltInRegistries.FEATURE
override val resourceKey: ResourceKey<Registry<Feature<*>>> = Registries.FEATURE
@JvmField
Binary file not shown.

After

Width:  |  Height:  |  Size: 406 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 406 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 406 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 406 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 381 B

After

Width:  |  Height:  |  Size: 211 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 408 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 406 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 123 B

After

Width:  |  Height:  |  Size: 103 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 406 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 406 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 408 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 408 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 406 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 406 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 408 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 377 B

After

Width:  |  Height:  |  Size: 414 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 377 B

After

Width:  |  Height:  |  Size: 403 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 380 B

After

Width:  |  Height:  |  Size: 410 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 374 B

After

Width:  |  Height:  |  Size: 401 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 408 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 377 B

After

Width:  |  Height:  |  Size: 405 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 377 B

After

Width:  |  Height:  |  Size: 404 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 377 B

After

Width:  |  Height:  |  Size: 406 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 377 B

After

Width:  |  Height:  |  Size: 407 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 377 B

After

Width:  |  Height:  |  Size: 403 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 404 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 405 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 377 B

After

Width:  |  Height:  |  Size: 411 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 377 B

After

Width:  |  Height:  |  Size: 402 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 377 B

After

Width:  |  Height:  |  Size: 411 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 377 B

After

Width:  |  Height:  |  Size: 411 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 377 B

After

Width:  |  Height:  |  Size: 410 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 377 B

After

Width:  |  Height:  |  Size: 411 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 374 B

After

Width:  |  Height:  |  Size: 412 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 374 B

After

Width:  |  Height:  |  Size: 399 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 394 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 377 B

After

Width:  |  Height:  |  Size: 405 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 377 B

After

Width:  |  Height:  |  Size: 412 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 377 B

After

Width:  |  Height:  |  Size: 411 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 408 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 407 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 406 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 406 B

Some files were not shown because too many files have changed in this diff Show More