Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

1.19.4 feature backports #347

Merged
merged 20 commits into from
Oct 1, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
64ce1e5
Implement Dynamic Registry Flags.
cocona20xx Jul 25, 2023
5d0d36a
Implement Dynamic Registry Flags p2
cocona20xx Jul 25, 2023
a4cb501
Expanded upon OPTIONAL flag javadocs to elaborate use-case
cocona20xx Jul 26, 2023
cefd32a
Expanded further on OPTIONAL javadocs
cocona20xx Jul 26, 2023
120c5cf
Implemented some changes suggested by Pyrofab
cocona20xx Jul 26, 2023
c8807ac
Add missing icon to data callback library (#331)
LostLuma Jul 28, 2023
1ccfa82
Commit most style changes
cocona20xx Jul 29, 2023
855a414
Make documentation for DynamicRegistryFlag more accurate
cocona20xx Jul 29, 2023
9613027
Improve the javadocs on DynamicRegistryFlag AGAIN
cocona20xx Jul 29, 2023
5789686
BUT WAIT, THERE'S MORE!
cocona20xx Jul 29, 2023
69f759b
Implement style change in DynamicRegistryFlagManager
cocona20xx Jul 30, 2023
55d23d2
Fix binary incompatibility hopefully?
cocona20xx Jul 31, 2023
f912a1c
Remove deprecation annotations on compat. method overloads in Dynamic…
cocona20xx Jul 31, 2023
bc6e2bb
Fix: Modify Tag path behavior in Dynamic Registries (#329)
cocona20xx Jul 31, 2023
954897c
Flip a boolean to sync with FAPI behavior
cocona20xx Sep 28, 2023
1b82fc7
Clean up the dynamic tag test
EnnuiL Jul 31, 2023
570d322
Add stopguard on registering dynamic registries
EnnuiL Aug 1, 2023
696a299
Ran the licenser. Dear gods that's a lotta copyright year changes wowie
cocona20xx Sep 28, 2023
69c8361
Revert "Ran the licenser. Dear gods that's a lotta copyright year cha…
cocona20xx Sep 28, 2023
662e6b0
Merge branch '1.19.4' into feature/backportmania-1.19.4
EnnuiL Sep 30, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
3 changes: 3 additions & 0 deletions library/core/registry/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,9 @@ qslModule {
testmodOnly("resource_loader")
testmodOnly("testing")
}
data {
testmodOnly("tags")
}
}
entrypoints {
client_init {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,57 @@
* This registry will be frozen at the same time as static registries.
*/
public final class DynamicMetaRegistry {
/**
* Registers a server-side dynamic registry.
* <p>
* Entries will be loaded from {@code "data/<namespace>/<registry_namespace>/<registry_path>"} for every datapack
* {@code namespace}, where {@code registry_namespace} and {@code registry_path}'s values are respectively
* {@code key.getLocation().getNamespace()} and {@code key.getLocation().getPath()}.
*
* @param <E> the type of elements in the dynamic registry
* @param key a {@link RegistryKey#ofRegistry(Identifier) key for the new dynamic registry}
* @param entryCodec the codec used to deserialize entries from datapacks
* @throws IllegalStateException if this registry of registries already got frozen
*/
public static <E> void register(RegistryKey<? extends Registry<E>> key, Codec<E> entryCodec, DynamicRegistryFlag... flags) {
DynamicMetaRegistryImpl.register(key, entryCodec, flags);
}

/**
* Registers a dynamic registry which contents get synced between the server and connected clients.
* <p>
* Entries will be loaded from {@code "data/<namespace>/<registry_namespace>/<registry_path>"} for every datapack
* {@code namespace}, where {@code registry_namespace} and {@code registry_path}'s values are respectively
* {@code key.getLocation().getNamespace()} and {@code key.getLocation().getPath()}.
*
* @param <E> the type of elements in the dynamic registry
* @param key a {@link RegistryKey#ofRegistry(Identifier) key for the new dynamic registry}
* @param entryCodec the codec used to both deserialize entries from datapacks and (de)serialize entries to and from packets
* @throws IllegalStateException if this registry of registries already got frozen
* @see #registerSynced(RegistryKey, Codec, Codec, DynamicRegistryFlag...)
*/
public static <E> void registerSynced(RegistryKey<? extends Registry<E>> key, Codec<E> entryCodec, DynamicRegistryFlag... flags) {
DynamicMetaRegistryImpl.registerSynced(key, entryCodec, entryCodec, flags);
}

/**
* Registers a dynamic registry which contents get synced between the server and connected clients.
* <p>
* Entries will be loaded from {@code "data/<namespace>/<registry_namespace>/<registry_path>"} for every datapack
* {@code namespace}, where {@code registry_namespace} and {@code registry_path}'s values are respectively
* {@code key.getLocation().getNamespace()} and {@code key.getLocation().getPath()}.
*
* @param <E> the type of elements in the dynamic registry
* @param key a {@link RegistryKey#ofRegistry(Identifier) key for the new dynamic registry}
* @param entryCodec the codec used to deserialize entries from datapacks
* @param syncCodec the codec used to (de)serialize entries to and from packets - may be the same as {@code entryCodec}
* @throws IllegalStateException if this registry of registries already got frozen
* @see #registerSynced(RegistryKey, Codec, DynamicRegistryFlag...)
*/
public static <E> void registerSynced(RegistryKey<? extends Registry<E>> key, Codec<E> entryCodec, Codec<E> syncCodec, DynamicRegistryFlag... flags) {
DynamicMetaRegistryImpl.registerSynced(key, entryCodec, syncCodec, flags);
}

/**
* Registers a server-side dynamic registry.
* <p>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
/*
* Copyright 2023 The Quilt Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package org.quiltmc.qsl.registry.api.dynamic;

import net.minecraft.registry.RegistryKey;
import net.minecraft.util.Identifier;

import org.quiltmc.qsl.registry.impl.dynamic.DynamicMetaRegistryImpl;
import org.quiltmc.qsl.registry.impl.dynamic.DynamicRegistryFlagManager;

/**
* Flags that can be set on dynamic registries to define their behavior.
* <p>
* All flags are off by default, and can be enabled using static methods in this class or on dynamic registry creation via varargs.
*
* @see org.quiltmc.qsl.registry.api.sync.RegistrySynchronization org.quiltmc.qsl.registry.api.sync.RegistrySynchronization,
* which contains similar flag setters/getters for static registries
*/
public enum DynamicRegistryFlag {
/**
* Indicates that this registry (and the entries within) do not necessarily need to be sent
* to (logical) clients for synchronization in multiplayer contexts.
* <p>
* <b>Note:</b> This flag is intended only for synchronized dynamic registries. On non-synced dynamic registries, this flag does nothing.
* <p>
* One use-case for this flag is for creating mods that are entirely compatible with vanilla, and thus do not
* require the dynamic registry to exist clientside to connect to the server.
* This allows for both vanilla clients/clients without the mod to connect <i>and</i> for clients with the mod
* supplying the registry to connect, with the latter being able to see the contents of the registry and possibly
* enable extra clientside features accordingly.
*/
OPTIONAL;

/**
* Enables a specific flag on a dynamic registry.
*
* @param registryId the value id ({@link RegistryKey#getValue()}) of the target dynamic registry
* @param flag the flag value to enable on the dynamic registry
*/
public static void setFlag(Identifier registryId, DynamicRegistryFlag flag) {
try {
DynamicRegistryFlagManager.setFlag(registryId, flag);
} catch (Exception e) {
logFlagModifyException(registryId, flag, e);
}
}

/**
* Checks if a dynamic registry has the {@link DynamicRegistryFlag#OPTIONAL} flag enabled on it.
*
* @param registryId the value id ({@link RegistryKey#getValue()}) of the dynamic registry to check
* @return whether the checked dynamic registry has the {@link DynamicRegistryFlag#OPTIONAL} flag enabled
*/
public static boolean isOptional(Identifier registryId) {
return DynamicRegistryFlagManager.isOptional(registryId);
}

/**
* Helper method for logging exceptions to avoid code duplication.
*/
private static void logFlagModifyException(Identifier registryId, DynamicRegistryFlag flag, Exception e) {
DynamicMetaRegistryImpl.LOGGER.error("Caught exception while attempting to enable flag {} on registry id {}: {}", flag.toString(), registryId, e.toString());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -22,31 +22,44 @@
import com.google.common.collect.ImmutableMap;
import com.mojang.serialization.Codec;
import org.jetbrains.annotations.ApiStatus;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import net.minecraft.registry.Registry;
import net.minecraft.registry.RegistryKey;
import net.minecraft.registry.RegistryLoader;
import net.minecraft.util.Identifier;

import org.quiltmc.qsl.registry.mixin.DynamicRegistrySyncAccessor;
import org.quiltmc.qsl.registry.api.dynamic.DynamicRegistryFlag;

@ApiStatus.Internal
public class DynamicMetaRegistryImpl {
private static boolean frozen;
private static final Set<Identifier> MODDED_REGISTRY_IDS = new HashSet<>();

public static final Logger LOGGER = LoggerFactory.getLogger("quilt_dynamic_registries");

public static boolean isModdedRegistryId(Identifier id) {
return MODDED_REGISTRY_IDS.contains(id);
}

public static <E> void register(RegistryKey<? extends Registry<E>> ref, Codec<E> entryCodec) {
public static <E> void register(RegistryKey<? extends Registry<E>> ref, Codec<E> entryCodec, DynamicRegistryFlag... flags) {
if (ref.getValue().getNamespace().equals(Identifier.DEFAULT_NAMESPACE)) {
throw new IllegalStateException("The '" + ref.getValue() + "' dynamic registry cannot have 'minecraft' as its identifier's namespace!");
}

if (frozen) throw new IllegalStateException("Registry is already frozen");

MODDED_REGISTRY_IDS.add(ref.getValue());
RegistryLoader.WORLDGEN_REGISTRIES.add(new RegistryLoader.DecodingData<>(ref, entryCodec));
for (DynamicRegistryFlag flag : flags) {
DynamicRegistryFlagManager.setFlag(ref.getValue(), flag);
}
}

public static <E> void registerSynced(RegistryKey<? extends Registry<E>> ref, Codec<E> entryCodec, Codec<E> syncCodec) {
register(ref, entryCodec);
public static <E> void registerSynced(RegistryKey<? extends Registry<E>> ref, Codec<E> entryCodec, Codec<E> syncCodec, DynamicRegistryFlag... flags) {
register(ref, entryCodec, flags);
var builder = ImmutableMap.<RegistryKey<? extends Registry<?>>, Object>builder().putAll(DynamicRegistrySyncAccessor.quilt$getSyncedCodecs());
DynamicRegistrySyncAccessor.quilt$invokeAddSyncedRegistry(builder, ref, syncCodec);
DynamicRegistrySyncAccessor.quilt$setSyncedCodecs(builder.build());
Expand All @@ -55,4 +68,8 @@ public static <E> void registerSynced(RegistryKey<? extends Registry<E>> ref, Co
public static void freeze() {
frozen = true;
}

static boolean isFrozen() {
return frozen;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
/*
* Copyright 2023 The Quilt Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package org.quiltmc.qsl.registry.impl.dynamic;

import org.jetbrains.annotations.ApiStatus;
import com.google.common.collect.Multimap;
import com.google.common.collect.MultimapBuilder;

import net.minecraft.util.Identifier;

import org.quiltmc.qsl.registry.api.dynamic.DynamicRegistryFlag;

@ApiStatus.Internal
public final class DynamicRegistryFlagManager {
private static final Multimap<Identifier, DynamicRegistryFlag> DYNAMIC_REGISTRY_FLAGS =
MultimapBuilder.hashKeys().enumSetValues(DynamicRegistryFlag.class).build();

public static void setFlag(Identifier registryId, DynamicRegistryFlag flag) throws IllegalStateException {
if (DynamicMetaRegistryImpl.isFrozen()) {
throw new IllegalStateException("Dynamic registries are frozen, and thus flags cannot be changed!");
}

if (!DynamicMetaRegistryImpl.isModdedRegistryId(registryId)) return;

DYNAMIC_REGISTRY_FLAGS.put(registryId, flag);
}

public static boolean isOptional(Identifier registryId) {
if (DYNAMIC_REGISTRY_FLAGS.containsKey(registryId)) {
return DYNAMIC_REGISTRY_FLAGS.get(registryId).contains(DynamicRegistryFlag.OPTIONAL);
}

return false;
}
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
/*
* Copyright 2016, 2017, 2018, 2019 FabricMC
* Copyright 2023 The Quilt Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
Expand All @@ -17,21 +18,53 @@
package org.quiltmc.qsl.registry.mixin;

import java.util.Map;
import java.util.stream.Stream;

import org.spongepowered.asm.mixin.Dynamic;
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 org.spongepowered.asm.mixin.Unique;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Redirect;

import net.minecraft.registry.DynamicRegistryManager;
import net.minecraft.registry.DynamicRegistrySync;
import net.minecraft.registry.Registry;
import net.minecraft.registry.RegistryKey;

import org.quiltmc.qsl.registry.impl.dynamic.DynamicRegistryFlagManager;

@Mixin(DynamicRegistrySync.class)
public abstract class DynamicRegistrySyncMixin {
@SuppressWarnings("unused") // makes the field mutable for use by the accessor
@Shadow
@Final
@Mutable
private static Map<RegistryKey<? extends Registry<?>>, ?> SYNCED_CODECS;
@Unique
private static boolean filterRegistryEntry(DynamicRegistryManager.RegistryEntry<?> entry) {
// OPTIONAL
if (DynamicRegistryFlagManager.isOptional(entry.key().getValue())) {
return entry.value().size() > 0;
}

return true; // If no flags apply, always return true
}

@Shadow
private static Stream<DynamicRegistryManager.RegistryEntry<?>> streamSyncedRegistries(DynamicRegistryManager registryManager) {
throw new IllegalStateException("Mixin injection failed.");
}

/**
* This redirect mixin's annotation was taken directly from Fabric API (and adapted for Quilt Mappings), the rest of this file was not.
*/
@Dynamic("method_45961: Codec.xmap in buildManagerCodec")
@Redirect(method = "method_45961",
at = @At(value = "INVOKE", target = "Lnet/minecraft/registry/DynamicRegistrySync;streamSyncedRegistries(Lnet/minecraft/registry/DynamicRegistryManager;)Ljava/util/stream/Stream;"))
private static Stream<DynamicRegistryManager.RegistryEntry<?>> filterNonSyncedEntries(DynamicRegistryManager drm) {
return streamSyncedRegistries(drm).filter(DynamicRegistrySyncMixin::filterRegistryEntry);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
/*
* Copyright 2023 The Quilt Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package org.quiltmc.qsl.registry.mixin;

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.CallbackInfoReturnable;

import net.minecraft.registry.Registry;
import net.minecraft.registry.RegistryKey;
import net.minecraft.registry.tag.TagManagerLoader;
import net.minecraft.util.Identifier;

import org.quiltmc.qsl.registry.impl.dynamic.DynamicMetaRegistryImpl;

@Mixin(TagManagerLoader.class)
public class TagManagerLoaderMixin {
@Inject(method = "getRegistryDirectory", at = @At("HEAD"), cancellable = true)
private static void quilt$replaceModdedDynamicRegistryTagPath(RegistryKey<? extends Registry<?>> registry, CallbackInfoReturnable<String> cir) {
Identifier id = registry.getValue();
if (DynamicMetaRegistryImpl.isModdedRegistryId(id)) {
cir.setReturnValue("tags/" + id.getNamespace() + "/" + id.getPath());
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
"ServerPlayerEntityMixin",
"ServerPlayNetworkHandlerAccessor",
"SimpleRegistryMixin",
"TagManagerLoaderMixin",
"patch.BeaconBlockEntityMixin",
"patch.MooshroomEntityMixin",
"patch.StatusEffectInstanceMixin",
Expand Down
Loading