Skip to content

Commit

Permalink
username cache
Browse files Browse the repository at this point in the history
  • Loading branch information
KrLite committed Mar 10, 2024
1 parent 381c031 commit deb4f63
Show file tree
Hide file tree
Showing 3 changed files with 199 additions and 0 deletions.
2 changes: 2 additions & 0 deletions src/main/java/net/krlite/knowledges/KnowledgesClient.java
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
import net.krlite.knowledges.api.entrypoint.DataProvider;
import net.krlite.knowledges.api.entrypoint.TagProvider;
import net.krlite.knowledges.api.representable.PacketByteBufWritable;
import net.krlite.knowledges.config.modmenu.cache.UsernameCache;
import net.krlite.knowledges.impl.component.AbstractInfoComponent;
import net.krlite.knowledges.config.KnowledgesConfig;
import net.krlite.knowledges.manager.KnowledgesComponentManager;
Expand All @@ -40,6 +41,7 @@ public class KnowledgesClient implements ClientModInitializer {
public static final KnowledgesTagManager TAGS = new KnowledgesTagManager();

public static final KnowledgesHud HUD = new KnowledgesHud();
public static final UsernameCache CACHE_USERNAME = new UsernameCache();

static {
AutoConfig.register(KnowledgesConfig.class, PartitioningSerializer.wrap(Toml4jConfigSerializer::new));
Expand Down
130 changes: 130 additions & 0 deletions src/main/java/net/krlite/knowledges/config/modmenu/cache/Cache.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,130 @@
package net.krlite.knowledges.config.modmenu.cache;

import com.google.common.base.Charsets;
import com.google.common.collect.ImmutableMap;
import com.google.common.reflect.TypeToken;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import net.fabricmc.loader.api.FabricLoader;
import net.krlite.knowledges.KnowledgesClient;
import net.krlite.knowledges.KnowledgesCommon;

import java.io.BufferedReader;
import java.io.File;
import java.io.IOException;
import java.lang.reflect.Type;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.*;
import java.util.function.Consumer;

public abstract class Cache<K, V> {
public static final Path PATH = FabricLoader.getInstance().getConfigDir().resolve(KnowledgesCommon.ID).resolve("cache");

private final Map<K, V> map = new HashMap<>();
private final Gson gson = new GsonBuilder().setPrettyPrinting().create();
private final File file;
private boolean loading = false;

protected Cache(String fileName) {
this.file = PATH.resolve(fileName + ".json").toFile();
}

protected abstract boolean validate(V value);

public Map<K, V> asMap() {
return ImmutableMap.copyOf(map);
}

public boolean containsKey(K key) {
return asMap().containsKey(key);
}

public void put(K key, V value) {
if (validate(value)) {
V prev = map.put(key, value);
if (!loading && !Objects.equals(prev, value)) {
save();
}
}
}

public void putIfAbsent(K key, V value) {
if (containsKey(key)) return;
put(key, value);
}

public Optional<V> get(K key) {
return Optional.ofNullable(containsKey(key) ? asMap().get(key) : null);
}

public V getOrDefault(K key, V defaultValue) {
return get(key).orElse(defaultValue);
}

public Optional<V> remove(K key) {
V prev = map.remove(key);
if (prev != null) {
save();
}

return Optional.ofNullable(prev);
}

public void operate(Consumer<Map<K, V>> mapConsumer) {
mapConsumer.accept(map);
}

protected void save() {
if (!file.exists()) {
file.mkdirs();
}

new SaveThread(file, gson.toJson(asMap())).start();
}

protected void load() {
if (!file.exists()) {
save();
return;
}

loading = true;
try (final BufferedReader reader = Files.newBufferedReader(file.toPath(), Charsets.UTF_8)) {
Type type = new TypeToken<Map<UUID, String>>() {}.getType();
Map<K, V> tempMap = gson.fromJson(reader, type);

if (tempMap != null) {
map.clear();
tempMap.forEach(this::put);
}
} catch (Exception e) {
KnowledgesClient.LOGGER.error("Error caching! Deleting file {}", file.getPath());
file.delete();
} finally {
loading = false;
}
}

private static class SaveThread extends Thread {
private final File file;
private final String data;

public SaveThread(File file, String data) {
this.file = file;
this.data = data;
}

@Override
public void run() {
try {
// Make sure we don't save when another thread is still saving
synchronized (file) {
Files.writeString(file.toPath(), data);
}
} catch (IOException e) {
KnowledgesClient.LOGGER.error("Failed saving cache!");
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
package net.krlite.knowledges.config.modmenu.cache;

import com.mojang.authlib.GameProfile;
import com.mojang.authlib.yggdrasil.ProfileResult;
import net.krlite.knowledges.KnowledgesClient;
import net.minecraft.client.MinecraftClient;

import java.util.*;

public class UsernameCache extends Cache<UUID, String> {
private final Set<UUID> downloading = Collections.synchronizedSet(new HashSet<>());

public UsernameCache() {
super("username");
}

@Override
protected boolean validate(String value) {
return !value.isEmpty() && !value.contains("§");
}

@Override
public Optional<String> get(UUID key) {
if (!containsKey(key)) {
download(key);
}

return super.get(key);
}

private void download(UUID uuid) {
if (downloading.contains(uuid)) return;
new DownloadThread(this, uuid).start();
}

private static class DownloadThread extends Thread {
private final UsernameCache callback;
private final UUID uuid;

public DownloadThread(UsernameCache callback, UUID uuid) {
this.callback = callback;
this.uuid = uuid;

callback.downloading.add(uuid);
}

@Override
public void run() {
try {
ProfileResult profileResult = MinecraftClient.getInstance().getSessionService().fetchProfile(uuid, true);
if (profileResult == null) {
return;
}

GameProfile profile = profileResult.profile();
if (profile.getName() == null || profile.getName().equals("???")) {
return;
}

callback.put(profile.getId(), profile.getName());
callback.downloading.remove(uuid);
} catch (Exception e) {
KnowledgesClient.LOGGER.error("Error downloading player profile!", e);
}
}
}
}

0 comments on commit deb4f63

Please sign in to comment.