From e583e3ad5a0e7eb4681ec900a6801295734cf2b5 Mon Sep 17 00:00:00 2001 From: jchung01 Date: Mon, 16 Mar 2026 12:08:39 -0700 Subject: [PATCH 1/4] Fix client-side caching of PARTY_ENABLE prop - Cache in PropertyContainer instead of per-property --- .../api/properties/IPropertyListener.java | 5 +++++ .../api/properties/IPropertyType.java | 4 ---- .../api/properties/basic/PropertyTypeBase.java | 17 ----------------- .../questing/party/PartyManager.java | 16 +++------------- .../storage/PropertyContainer.java | 18 +++++++++++++++++- 5 files changed, 25 insertions(+), 35 deletions(-) diff --git a/src/main/java/betterquesting/api/properties/IPropertyListener.java b/src/main/java/betterquesting/api/properties/IPropertyListener.java index 3cc584f56..3ee2fd351 100644 --- a/src/main/java/betterquesting/api/properties/IPropertyListener.java +++ b/src/main/java/betterquesting/api/properties/IPropertyListener.java @@ -1,5 +1,10 @@ package betterquesting.api.properties; +/** + * A listener to run when a property's value changes. + * @param The property type + */ +@FunctionalInterface public interface IPropertyListener { void propertyChanged(IPropertyType prop, T newValue); } diff --git a/src/main/java/betterquesting/api/properties/IPropertyType.java b/src/main/java/betterquesting/api/properties/IPropertyType.java index 833423551..bf86e3a62 100644 --- a/src/main/java/betterquesting/api/properties/IPropertyType.java +++ b/src/main/java/betterquesting/api/properties/IPropertyType.java @@ -11,8 +11,4 @@ public interface IPropertyType { T readValue(NBTBase nbt); NBTBase writeValue(T value); - - void addListener(IPropertyListener listener); - - void notifyListeners(T newValue); } diff --git a/src/main/java/betterquesting/api/properties/basic/PropertyTypeBase.java b/src/main/java/betterquesting/api/properties/basic/PropertyTypeBase.java index 634863ce1..bdf25fb74 100644 --- a/src/main/java/betterquesting/api/properties/basic/PropertyTypeBase.java +++ b/src/main/java/betterquesting/api/properties/basic/PropertyTypeBase.java @@ -1,16 +1,11 @@ package betterquesting.api.properties.basic; -import java.util.ArrayList; -import java.util.List; - -import betterquesting.api.properties.IPropertyListener; import betterquesting.api.properties.IPropertyType; import net.minecraft.util.ResourceLocation; public abstract class PropertyTypeBase implements IPropertyType { private final ResourceLocation key; private final T def; - private final List> listeners = new ArrayList<>(); public PropertyTypeBase(ResourceLocation key, T def) { this.key = key; @@ -26,16 +21,4 @@ public ResourceLocation getKey() { public T getDefault() { return def; } - - @Override - public void addListener(IPropertyListener listener) { - listeners.add(listener); - } - - @Override - public void notifyListeners(T newValue) { - for (var listener : listeners) { - listener.propertyChanged(this, newValue); - } - } } diff --git a/src/main/java/betterquesting/questing/party/PartyManager.java b/src/main/java/betterquesting/questing/party/PartyManager.java index 936a995ee..bd3b63297 100644 --- a/src/main/java/betterquesting/questing/party/PartyManager.java +++ b/src/main/java/betterquesting/questing/party/PartyManager.java @@ -1,8 +1,6 @@ package betterquesting.questing.party; import betterquesting.api.enums.EnumPartyStatus; -import betterquesting.api.properties.IPropertyListener; -import betterquesting.api.properties.IPropertyType; import betterquesting.api.properties.NativeProps; import betterquesting.api.questing.party.IParty; import betterquesting.api.questing.party.IPartyDatabase; @@ -11,8 +9,6 @@ import betterquesting.storage.QuestSettings; import net.minecraft.nbt.NBTTagCompound; import net.minecraft.nbt.NBTTagList; -import net.minecraftforge.fml.common.FMLCommonHandler; -import net.minecraftforge.fml.relauncher.Side; import javax.annotation.Nonnull; import javax.annotation.Nullable; @@ -20,12 +16,13 @@ import java.util.List; import java.util.UUID; -public class PartyManager extends SimpleDatabase implements IPartyDatabase, IPropertyListener { +public class PartyManager extends SimpleDatabase implements IPartyDatabase { public static final PartyManager INSTANCE; static { INSTANCE = new PartyManager(); - NativeProps.PARTY_ENABLE.addListener(INSTANCE); + QuestSettings.INSTANCE.addPropertyListener(NativeProps.PARTY_ENABLE, + (partyEnabledProp, isEnabled) -> PartyManager.INSTANCE.partyEnabled = isEnabled); } private final HashMap partyCache = new HashMap<>(); @@ -107,11 +104,4 @@ public synchronized void reset() { super.reset(); partyCache.clear(); } - - @Override - public void propertyChanged(IPropertyType prop, Boolean newValue) { - if (prop == NativeProps.PARTY_ENABLE && FMLCommonHandler.instance().getEffectiveSide() == Side.SERVER) { - partyEnabled = newValue; - } - } } diff --git a/src/main/java/betterquesting/storage/PropertyContainer.java b/src/main/java/betterquesting/storage/PropertyContainer.java index e939f3c84..775088bb0 100644 --- a/src/main/java/betterquesting/storage/PropertyContainer.java +++ b/src/main/java/betterquesting/storage/PropertyContainer.java @@ -1,11 +1,15 @@ package betterquesting.storage; import betterquesting.api.properties.IPropertyContainer; +import betterquesting.api.properties.IPropertyListener; import betterquesting.api.properties.IPropertyReducible; import betterquesting.api.properties.IPropertyType; import betterquesting.api2.storage.INBTSaveLoad; import com.google.common.collect.BiMap; import com.google.common.collect.HashBiMap; +import com.google.common.collect.Multimap; +import com.google.common.collect.MultimapBuilder; + import net.minecraft.nbt.NBTBase; import net.minecraft.nbt.NBTTagCompound; import net.minecraft.util.ResourceLocation; @@ -19,6 +23,8 @@ public class PropertyContainer implements IPropertyContainer, INBTSaveLoad> id2PropertyMap = HashBiMap.create(); // property.getKey() -> property + @SuppressWarnings("UnstableApiUsage") + private final Multimap> propListeners = MultimapBuilder.hashKeys().arrayListValues().build(); @Override public synchronized T getProperty(IPropertyType prop) { @@ -59,13 +65,19 @@ public synchronized void removeProperty(IPropertyType prop) { if (jProp.isEmpty()) nbtInfo.removeTag(prop.getKey().getNamespace()); } + @SuppressWarnings("unchecked") @Override public synchronized void setProperty(IPropertyType prop, T value) { if (prop == null || value == null) return; id2PropertyMap.put(prop.getKey(), prop); NBTTagCompound dom = getDomain(prop.getKey()); - prop.notifyListeners(value); + if (propListeners.containsKey(prop.getKey())) { + for (IPropertyListener listener : propListeners.get(prop.getKey())) { + ((IPropertyListener) listener).propertyChanged(prop, value); + } + } + dom.setTag(prop.getKey().getPath(), prop.writeValue(value)); nbtInfo.setTag(prop.getKey().getNamespace(), dom); } @@ -76,6 +88,10 @@ public synchronized void removeAllProps() { for (String key : keys) nbtInfo.removeTag(key); } + public synchronized void addPropertyListener(IPropertyType prop, IPropertyListener listener) { + propListeners.put(prop.getKey(), listener); + } + @Deprecated @Override public synchronized NBTTagCompound writeToNBT(NBTTagCompound nbt) { From 8f3f24d1954da98922b4f6f836c6f37b0368ec8a Mon Sep 17 00:00:00 2001 From: jchung01 Date: Mon, 16 Mar 2026 12:49:14 -0700 Subject: [PATCH 2/4] Change launchwrapper version --- build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index 09eec4b5c..3f6f3d0e4 100644 --- a/build.gradle +++ b/build.gradle @@ -565,7 +565,7 @@ dependencies { compileOnlyApi 'org.jetbrains:annotations:24.1.0' annotationProcessor 'org.jetbrains:annotations:24.1.0' - patchedMinecraft('net.minecraft:launchwrapper:1.17.2') { + patchedMinecraft('net.minecraft:launchwrapper:1.12') { transitive = false } From e554ef032fa30fa02cf8c271ebb0022849103194 Mon Sep 17 00:00:00 2001 From: jchung01 Date: Mon, 16 Mar 2026 13:28:05 -0700 Subject: [PATCH 3/4] Make cached prop thread-safe --- .../java/betterquesting/questing/party/PartyManager.java | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/main/java/betterquesting/questing/party/PartyManager.java b/src/main/java/betterquesting/questing/party/PartyManager.java index bd3b63297..2053826b6 100644 --- a/src/main/java/betterquesting/questing/party/PartyManager.java +++ b/src/main/java/betterquesting/questing/party/PartyManager.java @@ -15,6 +15,7 @@ import java.util.HashMap; import java.util.List; import java.util.UUID; +import java.util.concurrent.atomic.AtomicBoolean; public class PartyManager extends SimpleDatabase implements IPartyDatabase { public static final PartyManager INSTANCE; @@ -22,12 +23,12 @@ public class PartyManager extends SimpleDatabase implements IPartyDataba static { INSTANCE = new PartyManager(); QuestSettings.INSTANCE.addPropertyListener(NativeProps.PARTY_ENABLE, - (partyEnabledProp, isEnabled) -> PartyManager.INSTANCE.partyEnabled = isEnabled); + (partyEnabledProp, isEnabled) -> PartyManager.INSTANCE.partyEnabled.set(isEnabled)); } private final HashMap partyCache = new HashMap<>(); // Cache PARTY_ENABLED prop due to frequent checks when creating ParticipantInfo in tick handler. - private boolean partyEnabled; + private final AtomicBoolean partyEnabled = new AtomicBoolean(false); @Override public synchronized IParty createNew(int id) { @@ -39,7 +40,7 @@ public synchronized IParty createNew(int id) { @Nullable @Override public synchronized DBEntry getParty(@Nonnull UUID uuid) { - if (!partyEnabled) + if (!partyEnabled.get()) return null; // We're merely preventing access. Not erasing data Integer cachedID = partyCache.get(uuid); From 6b860fe279647183ead3fe326451e6af136a3272 Mon Sep 17 00:00:00 2001 From: jchung01 Date: Tue, 17 Mar 2026 10:10:52 -0700 Subject: [PATCH 4/4] Simplify listener to Consumer --- .../api/properties/IPropertyListener.java | 10 ---------- .../betterquesting/questing/party/PartyManager.java | 3 +-- .../betterquesting/storage/PropertyContainer.java | 11 ++++++----- 3 files changed, 7 insertions(+), 17 deletions(-) delete mode 100644 src/main/java/betterquesting/api/properties/IPropertyListener.java diff --git a/src/main/java/betterquesting/api/properties/IPropertyListener.java b/src/main/java/betterquesting/api/properties/IPropertyListener.java deleted file mode 100644 index 3ee2fd351..000000000 --- a/src/main/java/betterquesting/api/properties/IPropertyListener.java +++ /dev/null @@ -1,10 +0,0 @@ -package betterquesting.api.properties; - -/** - * A listener to run when a property's value changes. - * @param The property type - */ -@FunctionalInterface -public interface IPropertyListener { - void propertyChanged(IPropertyType prop, T newValue); -} diff --git a/src/main/java/betterquesting/questing/party/PartyManager.java b/src/main/java/betterquesting/questing/party/PartyManager.java index 2053826b6..fd3322e76 100644 --- a/src/main/java/betterquesting/questing/party/PartyManager.java +++ b/src/main/java/betterquesting/questing/party/PartyManager.java @@ -22,8 +22,7 @@ public class PartyManager extends SimpleDatabase implements IPartyDataba static { INSTANCE = new PartyManager(); - QuestSettings.INSTANCE.addPropertyListener(NativeProps.PARTY_ENABLE, - (partyEnabledProp, isEnabled) -> PartyManager.INSTANCE.partyEnabled.set(isEnabled)); + QuestSettings.INSTANCE.addPropertyListener(NativeProps.PARTY_ENABLE, PartyManager.INSTANCE.partyEnabled::set); } private final HashMap partyCache = new HashMap<>(); diff --git a/src/main/java/betterquesting/storage/PropertyContainer.java b/src/main/java/betterquesting/storage/PropertyContainer.java index 775088bb0..a8959ea73 100644 --- a/src/main/java/betterquesting/storage/PropertyContainer.java +++ b/src/main/java/betterquesting/storage/PropertyContainer.java @@ -1,7 +1,6 @@ package betterquesting.storage; import betterquesting.api.properties.IPropertyContainer; -import betterquesting.api.properties.IPropertyListener; import betterquesting.api.properties.IPropertyReducible; import betterquesting.api.properties.IPropertyType; import betterquesting.api2.storage.INBTSaveLoad; @@ -17,14 +16,16 @@ import java.util.ArrayList; import java.util.List; import java.util.Objects; +import java.util.function.Consumer; public class PropertyContainer implements IPropertyContainer, INBTSaveLoad { private final NBTTagCompound nbtInfo = new NBTTagCompound(); // For reducing nbt // To hold nbt values if the properties are not used (ex: the addon is temporarily removed), we cache and use only used properties to reduce nbt. private final BiMap> id2PropertyMap = HashBiMap.create(); // property.getKey() -> property + /* Key is property type's key, value is consumer of the property's new value. */ @SuppressWarnings("UnstableApiUsage") - private final Multimap> propListeners = MultimapBuilder.hashKeys().arrayListValues().build(); + private final Multimap> propListeners = MultimapBuilder.hashKeys().arrayListValues().build(); @Override public synchronized T getProperty(IPropertyType prop) { @@ -73,8 +74,8 @@ public synchronized void setProperty(IPropertyType prop, T value) { NBTTagCompound dom = getDomain(prop.getKey()); if (propListeners.containsKey(prop.getKey())) { - for (IPropertyListener listener : propListeners.get(prop.getKey())) { - ((IPropertyListener) listener).propertyChanged(prop, value); + for (Consumer listener : propListeners.get(prop.getKey())) { + ((Consumer) listener).accept(value); } } @@ -88,7 +89,7 @@ public synchronized void removeAllProps() { for (String key : keys) nbtInfo.removeTag(key); } - public synchronized void addPropertyListener(IPropertyType prop, IPropertyListener listener) { + public synchronized void addPropertyListener(IPropertyType prop, Consumer listener) { propListeners.put(prop.getKey(), listener); }