99
1010public class SpendThresholdRuntime {
1111
12- private final Map <String , Long > cooldownUntil = new HashMap <>();
13-
14- private final Map <String , Long > lastActivityTick = new HashMap <>();
15-
16- private final Map <String , Long > lastDecayTick = new HashMap <>();
12+ private static final class KeyState {
13+ long cooldownUntil ;
14+ long lastActivityTick ;
15+ long lastDecayTick ;
16+ int lastProgressIntSent = Integer .MIN_VALUE ;
17+ SpendThresholdSpec spec ;
18+ }
1719
18- private final Map <String , Integer > lastProgressIntSent = new HashMap <>();
20+ private final Map <String , KeyState > states = new HashMap <>();
1921
2022 private final java .util .EnumMap <ResourceType , Set <String >> activeByResource = new java .util .EnumMap <>(ResourceType .class );
21- private final Map < String , SpendThresholdSpec > specByKey = new HashMap <>();
23+ private final java . util . EnumMap < ResourceType , Set < String >> activeByResourceReadOnly = new java . util . EnumMap <>(ResourceType . class );
2224
2325 public void startCooldown (String key , long now , int cooldownTicks ) {
2426 if (cooldownTicks <= 0 ) return ;
25- cooldownUntil .put (key , now + cooldownTicks );
27+ if (key == null || key .isEmpty ()) return ;
28+ KeyState ks = states .computeIfAbsent (key , __ -> new KeyState ());
29+ ks .cooldownUntil = now + cooldownTicks ;
2630 }
2731
2832 public boolean isCoolingDown (String key , long now ) {
29- Long until = cooldownUntil .get (key );
30- return until != null && now < until ;
33+ if (key == null || key .isEmpty ()) return false ;
34+ KeyState ks = states .get (key );
35+ return ks != null && now < ks .cooldownUntil ;
3136 }
3237
3338 public int cooldownRemainingTicks (String key , long now ) {
34- Long until = cooldownUntil .get (key );
35- if (until == null ) return 0 ;
39+ if (key == null || key .isEmpty ()) return 0 ;
40+ KeyState ks = states .get (key );
41+ long until = (ks == null ) ? 0L : ks .cooldownUntil ;
3642 long rem = until - now ;
3743 return (int ) Math .max (0 , rem );
3844 }
3945
4046 // === Activity/Decay tracking ===
4147 public void markActivity (String key , long now ) {
4248 if (key == null || key .isEmpty ()) return ;
43- lastActivityTick .put (key , now );
44- lastDecayTick .remove (key );
49+ KeyState ks = states .computeIfAbsent (key , __ -> new KeyState ());
50+ ks .lastActivityTick = now ;
51+ ks .lastDecayTick = 0L ;
4552 }
4653
4754 public long getLastActivity (String key ) {
48- return lastActivityTick .getOrDefault (key , 0L );
55+ if (key == null || key .isEmpty ()) return 0L ;
56+ KeyState ks = states .get (key );
57+ return ks == null ? 0L : ks .lastActivityTick ;
4958 }
5059
5160 public long getLastDecay (String key ) {
52- return lastDecayTick .getOrDefault (key , 0L );
61+ if (key == null || key .isEmpty ()) return 0L ;
62+ KeyState ks = states .get (key );
63+ return ks == null ? 0L : ks .lastDecayTick ;
5364 }
5465
5566 public void markDecay (String key , long now ) {
5667 if (key == null || key .isEmpty ()) return ;
57- lastDecayTick .put (key , now );
68+ KeyState ks = states .computeIfAbsent (key , __ -> new KeyState ());
69+ ks .lastDecayTick = now ;
5870 }
5971
6072 public boolean progressIntChanged (String key , int intProgress ) {
61- Integer prev = lastProgressIntSent .get (key );
62- if (prev == null || prev .intValue () != intProgress ) {
63- lastProgressIntSent .put (key , intProgress );
73+ if (key == null || key .isEmpty ()) return false ;
74+ KeyState ks = states .get (key );
75+ int prev = (ks == null ) ? Integer .MIN_VALUE : ks .lastProgressIntSent ;
76+ if (prev != intProgress ) {
77+ if (ks == null ) ks = states .computeIfAbsent (key , __ -> new KeyState ());
78+ ks .lastProgressIntSent = intProgress ;
6479 return true ;
6580 }
6681 return false ;
@@ -69,29 +84,50 @@ public boolean progressIntChanged(String key, int intProgress) {
6984 // === Active key index ===
7085 public void markActive (ResourceType rt , String key , SpendThresholdSpec spec ) {
7186 if (rt == null || key == null || key .isEmpty () || spec == null ) return ;
72- activeByResource .computeIfAbsent (rt , __ -> new HashSet <>()).add (key );
73- specByKey .put (key , spec );
87+ Set <String > set = activeByResource .get (rt );
88+ if (set == null ) {
89+ set = new HashSet <>();
90+ activeByResource .put (rt , set );
91+ activeByResourceReadOnly .put (rt , java .util .Collections .unmodifiableSet (set ));
92+ }
93+ set .add (key );
94+ KeyState ks = states .computeIfAbsent (key , __ -> new KeyState ());
95+ ks .spec = spec ;
7496 }
7597
7698 public void removeActive (ResourceType rt , String key ) {
7799 if (rt == null || key == null || key .isEmpty ()) return ;
78100 var set = activeByResource .get (rt );
79101 if (set != null ) {
80102 set .remove (key );
81- if (set .isEmpty ()) activeByResource .remove (rt );
103+ if (set .isEmpty ()) {
104+ activeByResource .remove (rt );
105+ activeByResourceReadOnly .remove (rt );
106+ }
107+ }
108+ KeyState ks = states .get (key );
109+ if (ks != null ) {
110+ ks .spec = null ;
111+ ks .lastActivityTick = 0L ;
112+ ks .lastDecayTick = 0L ;
113+ ks .lastProgressIntSent = Integer .MIN_VALUE ;
82114 }
83- specByKey .remove (key );
84- lastActivityTick .remove (key );
85- lastDecayTick .remove (key );
86- lastProgressIntSent .remove (key );
87115 }
88116
89117 public Set <String > getActiveKeys (ResourceType rt ) {
90118 var s = activeByResource .get (rt );
91- return s == null ? java .util .Set .of () : java .util .Set .copyOf (s );
119+ if (s == null || s .isEmpty ()) return java .util .Set .of ();
120+ var view = activeByResourceReadOnly .get (rt );
121+ if (view == null ) {
122+ view = java .util .Collections .unmodifiableSet (s );
123+ activeByResourceReadOnly .put (rt , view );
124+ }
125+ return view ;
92126 }
93127
94128 public SpendThresholdSpec getSpec (String key ) {
95- return specByKey .get (key );
129+ if (key == null || key .isEmpty ()) return null ;
130+ KeyState ks = states .get (key );
131+ return ks == null ? null : ks .spec ;
96132 }
97133}
0 commit comments