Skip to content

Commit

Permalink
Finalize cache implementation, remove provider
Browse files Browse the repository at this point in the history
  • Loading branch information
bibi-reden committed Mar 23, 2024
1 parent b47fd33 commit 555dceb
Show file tree
Hide file tree
Showing 13 changed files with 287 additions and 318 deletions.
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
package com.edelweiss.playerex.mixin.cache;

import com.edelweiss.playerex.cache.PlayerEXCacheData;
import com.edelweiss.playerex.cache.PlayerEXCacheInternal;
import com.edelweiss.playerex.cache.PlayerEXCache;
import com.mojang.datafixers.DataFixer;
import com.mojang.serialization.Dynamic;
import com.mojang.serialization.Lifecycle;
Expand All @@ -23,7 +23,7 @@
@Mixin(LevelProperties.class)
abstract class LevelPropertiesMixin implements PlayerEXCacheData {
@Unique
private final PlayerEXCacheInternal playerEXCache = new PlayerEXCacheInternal();
private final PlayerEXCache playerEXCache = new PlayerEXCache();

@Inject(method = "updateProperties", at = @At("HEAD"))
private void playerex_updateProperties(DynamicRegistryManager registryManager, NbtCompound levelNbt, NbtCompound playerNbt, CallbackInfo ci) {
Expand All @@ -37,7 +37,7 @@ private static void playerex_readProperties(Dynamic<NbtElement> dynamic, DataFix
}

@Override
public PlayerEXCacheInternal playerEXCache() {
public PlayerEXCache playerEXCache() {
return this.playerEXCache;
}
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
package com.edelweiss.playerex.mixin.cache;

import com.edelweiss.playerex.cache.PlayerEXCacheInternal;
import com.edelweiss.playerex.cache.PlayerEXCache;
import net.minecraft.network.ClientConnection;
import net.minecraft.server.MinecraftServer;
import net.minecraft.server.PlayerManager;
Expand All @@ -27,25 +27,19 @@ abstract class PlayerManagerMixin {

@Inject(method = "onPlayerConnect", at = @At("TAIL"))
private void playerex_onPlayerConnect(ClientConnection connection, ServerPlayerEntity player, CallbackInfo ci) {
PlayerEXCacheInternal.Companion.ifCachePresent(this.server, null, playerCache -> {
playerCache.uncache(player);
return null;
});
var cache = PlayerEXCache.Companion.get(this.server);
if (cache != null) cache.uncache(player);
}

@Inject(method = "remove", at = @At("HEAD"))
private void playerex_remove(ServerPlayerEntity player, CallbackInfo ci) {
PlayerEXCacheInternal.Companion.ifCachePresent(this.server, null, playerCache -> {
playerCache.cache(player);
return null;
});
var cache = PlayerEXCache.Companion.get(this.server);
if (cache != null) cache.cache(player);
}

@Inject(method = "disconnectAllPlayers", at = @At("HEAD"))
private void playerex_disconnectAllPlayers(CallbackInfo ci) {
PlayerEXCacheInternal.Companion.ifCachePresent(this.server, null, playerCache -> {
for (ServerPlayerEntity player : this.players) { playerCache.cache(player); }
return null;
});
var cache = PlayerEXCache.Companion.get(this.server);
if (cache != null) for (ServerPlayerEntity player : this.players) { cache.cache(player); }
}
}
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
package com.edelweiss.playerex.mixin.cache;

import com.edelweiss.playerex.cache.PlayerEXCacheData;
import com.edelweiss.playerex.cache.PlayerEXCacheInternal;
import com.edelweiss.playerex.cache.PlayerEXCache;
import net.minecraft.world.level.ServerWorldProperties;
import net.minecraft.world.level.UnmodifiableLevelProperties;
import org.jetbrains.annotations.NotNull;
Expand All @@ -17,7 +17,7 @@ abstract class UnmodifiableLevelPropertiesMixin implements PlayerEXCacheData {

@NotNull
@Override
public PlayerEXCacheInternal playerEXCache() {
public PlayerEXCache playerEXCache() {
return ((PlayerEXCacheData) this.worldProperties).playerEXCache();
}
}
2 changes: 0 additions & 2 deletions src/main/kotlin/com/edelweiss/playerex/PlayerEXComponents.kt
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,10 @@ import net.minecraft.entity.LivingEntity

class PlayerEXComponents : EntityComponentInitializer, LevelComponentInitializer, WorldComponentInitializer {
companion object {
val livingEntities: ComponentKey<LivingEntityComponent> = ComponentRegistry.getOrCreate(PlayerEXAPI.id("living_entities"), LivingEntityComponent::class.java)
val playerEntities: ComponentKey<PlayerEntityComponent> = ComponentRegistry.getOrCreate(PlayerEXAPI.id("player_entities"), PlayerEntityComponent::class.java)
}

override fun registerEntityComponentFactories(registry: EntityComponentFactoryRegistry) {
registry.registerFor(LivingEntity::class.java, livingEntities) { entity -> LivingEntityComponent(entity) }
registry.registerForPlayers(playerEntities) { player -> PlayerEntityComponent(player) }
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
package com.edelweiss.playerex

import com.edelweiss.playerex.cache.PlayerEXCacheAPI
import com.edelweiss.playerex.commands.PlayerEXCacheCommands
import com.edelweiss.playerex.commands.PlayerEXCommands
import net.fabricmc.api.ModInitializer
import net.fabricmc.fabric.api.command.v2.CommandRegistrationCallback
import net.minecraft.server.MinecraftServer
import org.slf4j.LoggerFactory

object PlayerEXDirectorsCut : ModInitializer {
Expand Down
4 changes: 2 additions & 2 deletions src/main/kotlin/com/edelweiss/playerex/api/PlayerEXAPI.kt
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
package com.edelweiss.playerex.api

import com.edelweiss.playerex.PlayerEXDirectorsCut
import com.edelweiss.playerex.cache.PlayerEXCache
import com.edelweiss.playerex.cache.PlayerEXCacheAPI
import com.edelweiss.playerex.values.LevelValue
import net.minecraft.util.Identifier

/** Singleton used for main API access for PlayerEX. */
object PlayerEXAPI {
val LEVEL_VALUE = PlayerEXCache.register(LevelValue())
val LEVEL_VALUE = PlayerEXCacheAPI.register(LevelValue())


/**
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
package com.edelweiss.playerex.cache

enum class PlayerCacheKeys(val key: String) {
UUID("uuid"),
Name("name"),
Keys("keys"),
object PlayerCacheKeys {
const val UUID = "uuid"
const val NAME = "name"
const val KEYS = "keys"
}
208 changes: 186 additions & 22 deletions src/main/kotlin/com/edelweiss/playerex/cache/PlayerEXCache.kt
Original file line number Diff line number Diff line change
@@ -1,40 +1,204 @@
package com.edelweiss.playerex.cache

import com.edelweiss.playerex.PlayerEXDirectorsCut
import com.google.common.collect.BiMap
import com.google.common.collect.HashBiMap
import net.minecraft.nbt.NbtCompound
import net.minecraft.nbt.NbtList
import net.minecraft.server.MinecraftServer
import net.minecraft.server.network.ServerPlayerEntity
import net.minecraft.util.Identifier
import java.util.UUID

interface PlayerEXCache {
/** This is the singleton class that implements the cache. */
class PlayerEXCache(
private val cache: MutableMap<UUID, MutableMap<CachedPlayerValue<*>, *>> = mutableMapOf(),
val playerNameToUUID: BiMap<String, UUID> = HashBiMap.create()
) {
companion object {
/** Registers the provided cached value to the server. */
private val cacheKeys = mutableMapOf<Identifier, CachedPlayerValue<*>>()

@Suppress("UNCHECKED_CAST")
fun <V>register(key: CachedPlayerValue<V>): CachedPlayerValue<V> {
PlayerEXDirectorsCut.LOGGER.debug("Cached Value has been registered: <{} :: #id: {}>", key, key.id())
return PlayerEXCacheInternal.register(key)
return cacheKeys.computeIfAbsent(key.id()) { key } as CachedPlayerValue<V>
}

fun keys(): Set<Identifier> = cacheKeys.keys

fun getKey(id: Identifier): CachedPlayerValue<*>? = cacheKeys[id]

/** Tries to obtain the cache from the `LevelProperties` of the server, and if it is not found, it will be null. */
fun get(server: MinecraftServer): PlayerEXCache? {
val worldProperties = server.overworld.levelProperties
return (worldProperties as PlayerEXCacheData?)?.playerEXCache()
}
}

private fun isValidPlayerData(player: ServerPlayerEntity, function: (UUID, String) -> Unit): Boolean {
val profile = player.gameProfile ?: return false
if (profile.id == null || profile.name == null) return false
function(profile.id, profile.name)
return true
}

@Suppress("UNCHECKED_CAST")
private fun <V>getFromCache(uuid: UUID, key: CachedPlayerValue<V>): V? = this.cache[uuid]?.get(key) as V?

/** Gets a cached value from a players `UUID` and a cached value key.*/
internal fun <V>get(server: MinecraftServer, uuid: UUID, key: CachedPlayerValue<V>): V? {
val player = server.playerManager.getPlayer(uuid)
return if (player == null) this.getFromCache(uuid, key) else key.get(player)
}

/** Gets a cached value from a players name and a cached value key. */
internal fun <V>get(server: MinecraftServer, playerName: String, key: CachedPlayerValue<V>): V? {
if (playerName.isEmpty()) return null
val player = server.playerManager.getPlayer(playerName)
if (player == null) {
val uuid = this.playerNameToUUID[playerName]?: return null
return this.getFromCache(uuid, key)
}
return key.get(player)
}

/** Collect all the player ids from the server into a `Set`. */
fun playerIDs(server: MinecraftServer): Collection<UUID> {
val set = HashSet<UUID>(this.playerNameToUUID.values)
for (player in server.playerManager.playerList) {
val uuid = player?.gameProfile?.id ?: continue
set.add(uuid)
}
return set
}

/** Get access to the cache object. Should only be used on the server. */
fun <T>getCache(server: MinecraftServer, fallback: T, function: (PlayerEXCache) -> T): T {
val provider = PlayerEXCacheProvider(server)
if (provider.isEmpty()) return fallback
return function(provider)
/** Collects all the player names from the server into a `Set`. */
fun playerNames(server: MinecraftServer): Collection<String> {
val set = HashSet<String>(this.playerNameToUUID.keys)
for (player in server.playerManager.playerList) {
val playerName = player?.gameProfile?.name
if (!playerName.isNullOrEmpty()) set.add(playerName)
}
return set
}

/** Fetches the last cached value if offline, otherwise will fetch the current value from the player. */
fun <V>get(uuid: UUID, key: CachedPlayerValue<V>) {}
fun writeToNbt(): NbtList {
val list = NbtList()
val uuidToPlayerNames = this.playerNameToUUID.inverse()

for (uuid in this.cache.keys) {
val data = this.cache[uuid] ?: continue

/** Fetches the last cached value if offline, otherwise will fetch the current value from the player. */
fun <V>get(playerName: String, key: CachedPlayerValue<V>) {}
val entry = NbtCompound()
val keys = NbtCompound()

entry.putUuid(PlayerCacheKeys.UUID, uuid)
entry.putString(PlayerCacheKeys.NAME, uuidToPlayerNames.getOrDefault(uuid, ""))

for (key in data.keys) {
val innerEntry = NbtCompound()
key.writeToNbt(innerEntry, data[key])
keys.put(key.id().toString(), innerEntry)
}

entry.put(PlayerCacheKeys.KEYS, keys)
list.add(entry)
}

return list
}

/** Returns all offline & online player UUIDs. */
fun playerIDs(): Collection<UUID>
fun readFromNbt(list: NbtList) {
if (list.isEmpty()) return

/** Returns all offline & online player names. */
fun playerNames(): Collection<String>
this.cache.clear()
this.playerNameToUUID.clear()

/** Checks if the player with the UUID is in the cache or not. */
fun isPlayerCached(uuid: UUID): Boolean
for (index in list.indices) {
val entry = list.getCompound(index)
val keysCompound = entry.getCompound(PlayerCacheKeys.KEYS)
val uuid = entry.getUuid(PlayerCacheKeys.UUID)
val name = entry.getString(PlayerCacheKeys.NAME)

/** Checks if the player with the name is in the cache or not. */
fun isPlayerCached(playerName: String): Boolean
if (name.isEmpty()) continue

val data = mutableMapOf<CachedPlayerValue<*>, Any>()

for (id in keysCompound.keys) {
val key = cacheKeys[Identifier(id)]
val value = key?.readFromNbt(keysCompound.getCompound(id)) ?: continue
data[key] = value
}

this.cache[uuid] = data
this.playerNameToUUID[name] = uuid
}
}

fun isPlayerCached(uuid: UUID) = this.cache.containsKey(uuid)

fun isPlayerCached(name: String) = this.playerNameToUUID.containsKey(name)

fun cache(player: ServerPlayerEntity): Boolean = this.isValidPlayerData(player) { uuid, playerName ->
val value = mutableMapOf<CachedPlayerValue<*>, Any?>()
cacheKeys.values.forEach { key -> value[key] = key.get(player) }
this.cache[uuid] = value
this.playerNameToUUID[playerName] = uuid
}

/** Un-caches the player based on the provided `ServerPlayerEntity`. */
fun uncache(player: ServerPlayerEntity) = this.isValidPlayerData(player) { uuid, _ ->
this.cache.remove(uuid)
this.playerNameToUUID.inverse().remove(uuid)
}

/**
* Attempts to un-cache the player using a `UUID` and a given key.
*
* This could fail if the UUID is not in the cache, or the key removed is not present in the cache.
*/
fun uncache(uuid: UUID, key: CachedPlayerValue<*>): Boolean {
if (cacheKeys.containsKey(key.id())) return false

val value = this.cache[uuid] ?: return false

if (value.remove(key) != null) {
if (value.isEmpty()) {
this.cache.remove(uuid)
this.playerNameToUUID.inverse().remove(uuid)
}
return true
}
return false
}

/**
* Attempts to un-cache the player using their name and a given key.
*
* This could fail if the player name is not in the cache, or the key removed is not present in the cache.
*/
fun uncache(playerName: String, key: CachedPlayerValue<*>): Boolean {
if (playerName.isEmpty()) return false
return this.uncache(this.playerNameToUUID[playerName] ?: return false, key)
}


/**
* Attempts to un-cache the player using a `UUID`.
*
* This could fail if the UUID is not in the cache.
*/
fun uncache(uuid: UUID): Boolean {
this.cache.remove(uuid)
return this.playerNameToUUID.inverse().remove(uuid) != null
}

/**
* Attempts to un-cache the player using their name.
*
* This could fail if the UUID is not in the cache.
*/
fun uncache(playerName: String): Boolean {
if (playerName.isEmpty()) return false
val uuid = this.playerNameToUUID[playerName] ?: return false
return this.uncache(uuid)
}
}
Loading

0 comments on commit 555dceb

Please sign in to comment.