Skip to content

Commit

Permalink
Made predetermined Captchas Data oriented (#584)
Browse files Browse the repository at this point in the history
Added a Predetermined Captcha class that loads predetermined captchas
from user Datapacks.

---------

Co-authored-by: kirderf1 <[email protected]>
  • Loading branch information
ThalliumSulfate and kirderf1 authored Aug 15, 2024
1 parent 1a72692 commit 85e02ad
Show file tree
Hide file tree
Showing 3 changed files with 175 additions and 20 deletions.
57 changes: 37 additions & 20 deletions src/main/java/com/mraof/minestuck/alchemy/CardCaptchas.java
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
import com.google.common.collect.BiMap;
import com.google.common.collect.HashBiMap;
import com.mraof.minestuck.Minestuck;
import com.mraof.minestuck.item.MSItems;
import com.mraof.minestuck.world.storage.MSExtraData;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.resources.ResourceLocation;
Expand All @@ -12,6 +11,8 @@
import net.minecraft.world.item.Item;
import net.minecraftforge.fml.common.Mod;
import net.minecraftforge.registries.ForgeRegistries;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

import javax.annotation.Nonnull;
import javax.annotation.Nullable;
Expand All @@ -32,12 +33,12 @@ public final class CardCaptchas

public static final String AVAILABLE_CHARACTERS = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789!?";
public static final String EMPTY_CARD_CAPTCHA = "00000000";

private final BiMap<Item, String> captchasMap = HashBiMap.create();
private static final Logger LOGGER = LogManager.getLogger();

public CardCaptchas()
{
this.setupPredeterminedCaptchas();

}

/**
Expand All @@ -49,8 +50,20 @@ public static String getCaptcha(Item item, MinecraftServer server)
MSExtraData data = MSExtraData.get(server);
CardCaptchas captchas = data.getCardCaptchas();

if(captchas.captchasMap.containsKey(item))
if(PredeterminedCardCaptchas.getData().containsKey(item))
{
if(captchas.captchasMap.containsKey(item))
{
LOGGER.warn("Conflict: Item {} already has Code '{}', removing generated Code '{}' from data.", item, PredeterminedCardCaptchas.getData().get(item), captchas.captchasMap.get(item));
captchas.captchasMap.remove(item);
data.setDirty();
}
return PredeterminedCardCaptchas.getData().get(item);
}
else if(captchas.captchasMap.containsKey(item))
{
return captchas.captchasMap.get(item);
}
else
{
String captcha = captchas.createCaptchaForItem(item, server.overworld().getSeed());
Expand All @@ -63,7 +76,23 @@ public static String getCaptcha(Item item, MinecraftServer server)
@Nullable
public static Item getItemFromCaptcha(String captcha, MinecraftServer server)
{
return MSExtraData.get(server).getCardCaptchas().captchasMap.inverse().get(captcha);
MSExtraData data = MSExtraData.get(server);
CardCaptchas captchas = data.getCardCaptchas();

if(PredeterminedCardCaptchas.getData().inverse().containsKey(captcha))
{
if(captchas.captchasMap.inverse().containsKey(captcha))
{
LOGGER.warn("Conflict: Code '{}' already has assigned to Item {}, removing code '{}' reference to Item {}.", captcha, PredeterminedCardCaptchas.getData().inverse().get(captcha), captcha, captchas.captchasMap.inverse().get(captcha));
captchas.captchasMap.inverse().remove(captcha);
data.setDirty();
}
return PredeterminedCardCaptchas.getData().inverse().get(captcha);
}
else
{
return captchas.captchasMap.inverse().get(captcha);
}
}

public CompoundTag serialize()
Expand Down Expand Up @@ -92,20 +121,13 @@ public void deserialize(CompoundTag tag)
}
}



private void setupPredeterminedCaptchas()
{
predetermineCaptcha(MSItems.GENERIC_OBJECT.get(), EMPTY_CARD_CAPTCHA);
predetermineCaptcha(MSItems.SORD.get(), "SUPRePIC");
}

/**
* Creates a captcha from the registry name of the item and then adds it to a BiMap.
* There is some simple collision detection and backup captchas for redundancy
*/
private String createCaptchaForItem(Item item, long seed)
{

ResourceLocation itemId = Objects.requireNonNull(ForgeRegistries.ITEMS.getKey(item));

RandomSource itemRandom = RandomSource.create(seed)
Expand All @@ -117,17 +139,12 @@ private String createCaptchaForItem(Item item, long seed)
String cutHash = shuffledHash.substring(shuffledHash.length() - 16); //last 16 characters of hash
String captcha = captchaFromHash(cutHash);

if(captchasMap.containsValue(captcha))
if(captchasMap.containsValue(captcha) || PredeterminedCardCaptchas.getData().containsValue(captcha))
return generateBackupCaptcha(itemRandom);

return captcha;
}

private void predetermineCaptcha(Item item, String captcha)
{
captchasMap.put(item, captcha);
}

private static String createHash(String registryName)
{
StringBuilder hexString = new StringBuilder();
Expand Down Expand Up @@ -216,7 +233,7 @@ private String generateBackupCaptcha(RandomSource random)
String captchaString = captcha.toString();

//checks to make sure the captcha has not been created before
if(!captchasMap.containsValue(captchaString))
if(!captchasMap.containsValue(captchaString) && !PredeterminedCardCaptchas.getData().containsValue(captchaString))
{
return captchaString;
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,134 @@
package com.mraof.minestuck.alchemy;

import com.google.common.collect.BiMap;
import com.google.common.collect.HashBiMap;
import com.google.common.collect.ImmutableBiMap;
import com.google.gson.JsonElement;
import com.google.gson.JsonParser;
import com.mojang.serialization.Codec;
import com.mojang.serialization.JsonOps;
import com.mraof.minestuck.Minestuck;
import net.minecraft.MethodsReturnNonnullByDefault;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.server.packs.resources.ResourceManager;
import net.minecraft.server.packs.resources.SimplePreparableReloadListener;
import net.minecraft.util.profiling.ProfilerFiller;
import net.minecraft.world.item.Item;
import net.minecraftforge.event.AddReloadListenerEvent;
import net.minecraftforge.event.server.ServerStoppedEvent;
import net.minecraftforge.eventbus.api.SubscribeEvent;
import net.minecraftforge.fml.common.Mod;
import net.minecraftforge.registries.ForgeRegistries;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

import javax.annotation.Nullable;
import javax.annotation.ParametersAreNonnullByDefault;
import java.io.IOException;
import java.io.Reader;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;

@ParametersAreNonnullByDefault
@MethodsReturnNonnullByDefault
@Mod.EventBusSubscriber(modid = Minestuck.MOD_ID, bus = Mod.EventBusSubscriber.Bus.FORGE)
public class PredeterminedCardCaptchas
{
@Nullable
private static BiMap<Item, String> predefinedCardMap;

private static void setData(BiMap<Item, String> predefinedCards)
{
PredeterminedCardCaptchas.predefinedCardMap = predefinedCards;
}

public static BiMap<Item, String> getData()
{
return Objects.requireNonNull(PredeterminedCardCaptchas.predefinedCardMap, "Tried to get an instance of Predetermined Captchas too early.");
}

@SubscribeEvent
public static void onServerStopped(ServerStoppedEvent event)
{
predefinedCardMap = null;
}

@SubscribeEvent
public static void onResourceReload(AddReloadListenerEvent event)
{
event.addListener(new Loader());
}

private final static class Loader extends SimplePreparableReloadListener<Map<String, Item>>
{
private static final Logger LOGGER = LogManager.getLogger();
public static final String PATH = "minestuck/captcha_codes.json";
@Override
protected Map<String, Item> prepare(ResourceManager resourceManager, ProfilerFiller profiler)
{
BiMap<String, Item> captchaData = HashBiMap.create();

for(String namespace : resourceManager.getNamespaces())
{
parseCaptchaCodesFromNamespace(resourceManager, namespace).ifPresent(parsedData -> processCaptchaData(captchaData, parsedData));
}
return captchaData;
}

@Override
protected void apply(Map<String, Item> data, ResourceManager resourceManager, ProfilerFiller profiler)
{
ImmutableBiMap.Builder<Item, String> predefinedCards = ImmutableBiMap.builder();

for(Map.Entry<String, Item> entrySet : data.entrySet())
{
predefinedCards.put(entrySet.getValue(), entrySet.getKey());
}

PredeterminedCardCaptchas.setData(predefinedCards.build());
}

private static final Codec<Map<String, Item>> PREDEFINED_CAPTCHAS_CODEC = Codec.unboundedMap(Codec.STRING, ForgeRegistries.ITEMS.getCodec());

private static Optional<Map<String, Item>> parseCaptchaCodesFromNamespace(ResourceManager resourceManager, String namespace)
{
ResourceLocation rs = new ResourceLocation(namespace, PATH);

return resourceManager.getResource(rs).flatMap(resource -> {
try(Reader reader = resource.openAsReader())
{
JsonElement json = JsonParser.parseReader(reader);
return PREDEFINED_CAPTCHAS_CODEC.parse(JsonOps.INSTANCE, json)
.resultOrPartial(message -> LOGGER.error("Problem parsing json: {}, reason: {}", rs, message));

} catch(IOException ignored)
{
return Optional.empty();
}
});
}

private static void processCaptchaData(BiMap<String, Item> incompleteMap, Map<String, Item> parsedMap)
{
for(Map.Entry<String, Item> entry : parsedMap.entrySet())
{
String captcha = entry.getKey();
Item item = entry.getValue();

if(incompleteMap.containsValue(item))
{
LOGGER.error("Item {} already has an existing Code of '{}'!", item , incompleteMap.inverse().get(item));
}
else if(incompleteMap.containsKey(captcha))
{
LOGGER.error("Code '{}' is already assigned to Item: {}!", captcha, incompleteMap.get(captcha));
}
else
{
incompleteMap.put(entry.getKey(), entry.getValue());
}
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
{
"00000000": "minestuck:generic_object",
"SUPRePIC": "minestuck:sord"
}

0 comments on commit 85e02ad

Please sign in to comment.