aboutsummaryrefslogtreecommitdiff
path: root/src/main/java/moe/nea/caelo/optifine/OptifineCustomItemCache.kt
blob: ed5068eb92c6d4f5504395690f9ebfda333efb4e (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
package moe.nea.caelo.optifine

import moe.nea.caelo.CaeloCommand
import moe.nea.caelo.config.CConfig
import moe.nea.caelo.event.NeaTickEvent
import moe.nea.caelo.event.ResourceReloadEvent
import moe.nea.caelo.optifine.OptifineRegexCache.cache
import moe.nea.caelo.util.Histogram
import moe.nea.caelo.util.MC
import net.minecraft.item.ItemStack
import net.minecraftforge.fml.common.eventhandler.SubscribeEvent
import net.optifine.CustomItemProperties
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable
import java.lang.ref.ReferenceQueue
import java.lang.ref.WeakReference

object OptifineCustomItemCache {

	init {
		CaeloCommand.subcommand("opticache") { args ->
			val cache = cacheSizeHistory.lastOrNull() ?: CacheStats()
			MC.display("OptiCache stats:")
			MC.display("- History: §3${cacheSizeHistory.size}")
			MC.display("- Misses: §c${cache.cacheMisses}")
			MC.display("- Hits: §a${cache.cacheHits}")
			MC.display("- Insertions: §b${cache.insertions}")
			MC.display("- Evictions: §b${cache.removals}")
			MC.display("- Cache Size: §b${cache.size}")
			OptifineRegexCache.printStats()
		}
	}

	val referenceQueue = ReferenceQueue<ItemStack>()

	class CacheKeyReference(val cacheKey: CacheKey, itemStack: ItemStack) :
		WeakReference<ItemStack>(itemStack, referenceQueue)

	class CacheKey(itemStack: ItemStack, val type: Int) {
		val hashCode = System.identityHashCode(itemStack) * 31 + type
		val ref = CacheKeyReference(this, itemStack)

		override fun equals(other: Any?): Boolean {
			if (other === this) return true
			if (other !is CacheKey) return false
			return ref.get() === other.ref.get() && type == other.type
		}

		override fun hashCode(): Int {
			return hashCode
		}

		fun isPresent(): Boolean {
			return ref.get() != null
		}
	}

	data class CacheStats(
		var cacheHits: Int = 0,
		var cacheMisses: Int = 0,
		var insertions: Int = 0,
		var size: Int = 0,
		var removals: Int = 0,
	)

	private var map = mutableMapOf<CacheKey, CustomItemProperties?>()
	private val cacheSizeHistory = Histogram<CacheStats>(1000)
	private var cacheStats = CacheStats()

	@SubscribeEvent
	fun onResourcePackReload(resourceReload: ResourceReloadEvent) {
		map.clear()
	}

	@SubscribeEvent
	fun onTick(event: NeaTickEvent) {
		var removeCount = 0
		while (true) {
			val ref = referenceQueue.poll() as CacheKeyReference? ?: break
			removeCount++
			map.remove(ref.cacheKey)
		}
		cacheStats.size = map.size
		cacheStats.removals = removeCount
		cacheSizeHistory.append(cacheStats)
		cacheStats = CacheStats()
	}

	@JvmStatic
	fun retrieveCacheHit(
		itemStack: ItemStack,
		type: Int,
		cir: CallbackInfoReturnable<CustomItemProperties?>
	) {
		if (!CConfig.config.optiCache.citCache)
			return
		val key = CacheKey(itemStack, type)
		if (!map.containsKey(key)) {
			cacheStats.cacheMisses++
			return
		}
		cacheStats.cacheHits++
		cir.returnValue = map[key]
	}

	@JvmStatic
	fun storeCustomItemProperties(itemStack: ItemStack, type: Int, cip: CustomItemProperties) {
		map[CacheKey(itemStack, type)] = cip
		cacheStats.insertions++
	}

	@JvmStatic
	fun storeNoCustomItemProperties(itemStack: ItemStack, type: Int) {
		map[CacheKey(itemStack, type)] = null
		cacheStats.insertions++
	}
}