customizable registries

Signed-off-by: Glease <4586901+Glease@users.noreply.github.com>
This commit is contained in:
Glease committed 2024-04-20 13:45:10 +08:00
1 parent e5f3ad873e
commit fa1dca35b9
9 files changed
+365 -69

No files matched your search

@@ -2,10 +2,12 @@ package com.gtnewhorizon.structurelib;
import static com.gtnewhorizon.structurelib.StructureLib.RANDOM;
import java.lang.ref.WeakReference;
import java.util.*;
import net.minecraft.block.Block;
import net.minecraft.client.Minecraft;
import net.minecraft.client.entity.EntityClientPlayerMP;
import net.minecraft.client.gui.GuiNewChat;
import net.minecraft.client.particle.EntityFX;
import net.minecraft.client.renderer.Tessellator;
@@ -23,18 +25,25 @@ import net.minecraft.util.Vec3;
import net.minecraft.world.World;
import net.minecraftforge.client.event.RenderWorldLastEvent;
import net.minecraftforge.common.MinecraftForge;
import net.minecraftforge.event.entity.EntityJoinWorldEvent;
import net.minecraftforge.event.world.WorldEvent;
import org.apache.commons.lang3.tuple.Pair;
import org.lwjgl.opengl.GL11;
import com.gtnewhorizon.structurelib.entity.fx.WeightlessParticleFX;
import com.gtnewhorizon.structurelib.net.RegistryOrderSyncMessage;
import com.gtnewhorizon.structurelib.net.SetChannelDataMessage;
import cpw.mods.fml.client.event.ConfigChangedEvent;
import cpw.mods.fml.common.FMLCommonHandler;
import cpw.mods.fml.common.event.FMLLoadCompleteEvent;
import cpw.mods.fml.common.event.FMLPreInitializationEvent;
import cpw.mods.fml.common.eventhandler.EventPriority;
import cpw.mods.fml.common.eventhandler.SubscribeEvent;
import cpw.mods.fml.common.gameevent.TickEvent.ClientTickEvent;
import cpw.mods.fml.common.gameevent.TickEvent.Phase;
import cpw.mods.fml.common.network.FMLNetworkEvent;
public class ClientProxy extends CommonProxy {
@@ -239,6 +248,7 @@ public class ClientProxy extends CommonProxy {
public void preInit(FMLPreInitializationEvent e) {
FMLCommonHandler.instance().bus().register(new FMLEventHandler());
MinecraftForge.EVENT_BUS.register(new ForgeEventHandler());
DebugHelper.addDebug();
}
static void markTextureUsed(IIcon icon) {
@@ -246,6 +256,31 @@ public class ClientProxy extends CommonProxy {
((IStructureCompat) StructureLib.COMPAT).markTextureUsed(icon);
}
static void sendRegistryOrderToServer() {
if (FMLEventHandler.configSent) return;
FMLEventHandler.configSent = true;
for (String s : SortedRegistry.ALL_REGISTRIES.keySet()) {
Pair<List<String>, List<String>> registryOrder = ConfigurationHandler.INSTANCE.getRegistryOrder(s);
if (registryOrder == null || registryOrder.getKey().isEmpty() && registryOrder.getValue().isEmpty()) continue;
StructureLib.net.sendToServer(new RegistryOrderSyncMessage(s, registryOrder.getKey(), registryOrder.getValue()));
}
}
@Override
public void loadComplete(FMLLoadCompleteEvent evt) {
super.loadComplete(evt);
for (Map.Entry<String, WeakReference<SortedRegistry<?>>> e : SortedRegistry.ALL_REGISTRIES.entrySet()) {
SortedRegistry<?> r = e.getValue().get();
if (r == null) continue;
Pair<List<String>, List<String>> p = ConfigurationHandler.INSTANCE.getRegistryOrder(e.getKey());
r.registerOrdering(getSPPlayerUUID(), p.getKey(), p.getValue());
}
}
public UUID getSPPlayerUUID() {
return EntityPlayer.func_146094_a(Minecraft.getMinecraft().getSession().func_148256_e());
}
private static class HintGroup {
private final List<HintParticleInfo> hints = new LinkedList<>();
@@ -434,12 +469,34 @@ public class ClientProxy extends CommonProxy {
public static class FMLEventHandler {
static boolean connected = false;
static boolean configSent = false;
private void resetPlayerLocation() {
lastPlayerPos.xCoord = Minecraft.getMinecraft().thePlayer.posX;
lastPlayerPos.yCoord = Minecraft.getMinecraft().thePlayer.posY;
lastPlayerPos.zCoord = Minecraft.getMinecraft().thePlayer.posZ;
}
@SubscribeEvent
public void onPlayerLogIn(FMLNetworkEvent.ClientConnectedToServerEvent e) {
connected = true;
configSent = false;
}
@SubscribeEvent
public void onPlayerLogOut(FMLNetworkEvent.ClientDisconnectionFromServerEvent e) {
connected = false;
}
@SubscribeEvent(priority = EventPriority.LOWEST)
public void onConfigChange(ConfigChangedEvent.PostConfigChangedEvent e) {
if (connected && e.modID.equals(StructureLibAPI.MOD_ID)) {
configSent = false;
sendRegistryOrderToServer();
}
}
@SubscribeEvent
public void onClientTick(ClientTickEvent e) {
if (e.phase == Phase.END && Minecraft.getMinecraft().thePlayer != null) {
@@ -483,6 +540,13 @@ public class ClientProxy extends CommonProxy {
public static class ForgeEventHandler {
@SubscribeEvent
public void onEntityJoinWorld(EntityJoinWorldEvent e) {
if (e.world.isRemote && e.entity instanceof EntityClientPlayerMP) {
sendRegistryOrderToServer();
}
}
@SubscribeEvent
public void onWorldLoad(WorldEvent.Load e) {
if (e.world.isRemote) {
@@ -15,7 +15,7 @@ import net.minecraft.world.World;
import com.gtnewhorizon.structurelib.net.ErrorHintParticleMessage;
import com.gtnewhorizon.structurelib.net.UpdateHintParticleMessage;
import cpw.mods.fml.common.event.FMLLoadCompleteEvent;
import cpw.mods.fml.common.event.FMLPreInitializationEvent;
public class CommonProxy {
@@ -70,6 +70,10 @@ public class CommonProxy {
}
}
public void loadComplete(FMLLoadCompleteEvent e) {
ConfigurationHandler.INSTANCE.loadRegistryOrder();
}
private final Map<EntityPlayerMP, Map<Object, Long>> throttleMap = new WeakHashMap<>();
public void addThrottledChat(Object throttleKey, EntityPlayer player, IChatComponent text, short intervalRequired,
@@ -1,15 +1,24 @@
package com.gtnewhorizon.structurelib;
import java.io.File;
import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import net.minecraftforge.common.config.ConfigCategory;
import net.minecraftforge.common.config.Configuration;
import net.minecraftforge.common.config.Property;
import com.google.common.collect.Iterables;
import com.google.common.collect.Lists;
import cpw.mods.fml.client.event.ConfigChangedEvent;
import cpw.mods.fml.common.FMLCommonHandler;
import cpw.mods.fml.common.eventhandler.SubscribeEvent;
import org.apache.commons.lang3.tuple.Pair;
public enum ConfigurationHandler {
@@ -22,6 +31,7 @@ public enum ConfigurationHandler {
private int hintTransparency;
private int autoPlaceBudget;
private int autoPlaceInterval;
private Map<String, Pair<List<String>, List<String>>> registryOrders;
ConfigurationHandler() {
FMLCommonHandler.instance().bus().register(this);
@@ -86,8 +96,36 @@ public enum ConfigurationHandler {
+ "Note this relates to the wall clock, not in game ticks.\n"
+ "Value smaller than default is likely to be perceived as no minimal interval whatsoever.");
loadRegistryOrder();
saveConfig();
}
void loadRegistryOrder() {
registryOrders = new HashMap<>();
for (Map.Entry<String, WeakReference<SortedRegistry<?>>> e : SortedRegistry.ALL_REGISTRIES.entrySet()) {
SortedRegistry<?> r = e.getValue().get();
if (r == null) continue;
Property pOrder = config
.get("registries." + e.getKey(), "ordering", Iterables.toArray(r.getCurrentOrdering(), String.class), "stuff not in this list will be automatically available after all entries listed here in their natural order, unless explicitly disabled in disabled config below.");
Property pDisable = config.get("registries." + e.getKey(), "disabled", new String[0], "stuff in this list will be disabled");
List<String> all = Lists.newArrayList(r.getCurrentOrdering());
List<String> curVal = new ArrayList<>(Arrays.asList(pOrder.getStringList()));
List<String> disabled = new ArrayList<>(Arrays.asList(pDisable.getStringList()));
curVal.removeAll(disabled);
all.removeAll(disabled);
curVal.removeIf(s -> !all.remove(s));
curVal.addAll(all);
pOrder.set(curVal.toArray(new String[0]));
registryOrders.put(e.getKey(), Pair.of(curVal, disabled));
}
saveConfig();
}
private void saveConfig() {
if (config.hasChanged()) {
config.save();
config.load();
}
}
@@ -122,6 +160,10 @@ public enum ConfigurationHandler {
return autoPlaceInterval;
}
public Pair<List<String>, List<String>> getRegistryOrder(String name) {
return registryOrders.get(name);
}
Configuration getConfig() {
return config;
}
@@ -0,0 +1,151 @@
package com.gtnewhorizon.structurelib;
import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.NavigableMap;
import java.util.Spliterator;
import java.util.TreeMap;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.Consumer;
import javax.annotation.Nullable;
import net.minecraft.entity.player.EntityPlayerMP;
import com.gtnewhorizon.structurelib.net.RegistryOrderSyncMessage;
import cpw.mods.fml.common.FMLCommonHandler;
import cpw.mods.fml.common.eventhandler.SubscribeEvent;
import cpw.mods.fml.common.gameevent.PlayerEvent;
import org.apache.commons.lang3.tuple.Pair;
public class SortedRegistry<V> implements Iterable<V> {
static final ConcurrentHashMap<String, WeakReference<SortedRegistry<?>>> ALL_REGISTRIES = new ConcurrentHashMap<>();
static {
RegistryOrderSyncMessage.Handler.setAllRegistries(ALL_REGISTRIES);
}
private final NavigableMap<String, V> store = new TreeMap<>();
/**
* Store stuff in an array for faster iteration access than a red black tree
*/
private List<V> baked = Collections.emptyList();
private final Map<UUID, Pair<List<String>, List<String>>> playerOrdering = new HashMap<>();
private final Map<UUID, List<V>> playerBaked = new HashMap<>();
/**
* Construct an unsynchronized registry
*/
public SortedRegistry() {
}
/**
* Construct a synchronized registry whose ordering will be synced to the server
*
* @param name network name
*/
public SortedRegistry(String name) {
WeakReference<SortedRegistry<?>> oldValue = ALL_REGISTRIES.put(name, new WeakReference<>(this));
if (oldValue != null && oldValue.get() != null) {
throw new IllegalArgumentException("duplicate name");
}
FMLCommonHandler.instance().bus().register(this);
}
@SubscribeEvent
public void onPlayerLogOut(PlayerEvent.PlayerLoggedOutEvent e) {
playerOrdering.remove(e.player.getUniqueID());
playerBaked.remove(e.player.getUniqueID());
}
public void register(String key, V val) {
if (key == null || val == null) throw new NullPointerException();
V old = store.putIfAbsent(key, val);
if (old != null) {
throw new IllegalArgumentException("Duplicate key: " + key);
}
baked = new ArrayList<>(store.values());
for (Map.Entry<UUID, Pair<List<String>, List<String>>> e : playerOrdering.entrySet()) {
makePlayerBaked(e.getKey(), e.getValue().getKey(), e.getValue().getValue());
}
}
public void registerOrdering(EntityPlayerMP player, List<String> ordering, List<String> disabled) {
registerOrdering(player.getUniqueID(), ordering, disabled);
}
public void registerOrdering(UUID playerID, List<String> ordering, List<String> disabled) {
playerOrdering.put(playerID, Pair.of(ordering, disabled));
makePlayerBaked(playerID, ordering, disabled);
}
private void makePlayerBaked(UUID player, List<String> ordering, List<String> disabled) {
TreeMap<String, V> copy = new TreeMap<>(store);
for (String s : disabled) {
copy.remove(s);
}
List<V> b = new ArrayList<>();
for (String k : ordering) {
V v = copy.remove(k);
if (v != null) {
b.add(v);
}
}
b.addAll(copy.values());
playerBaked.put(player, b);
}
public int size() {
return store.size();
}
public boolean isEmpty() {
return store.isEmpty();
}
public boolean containsKey(String key) {
return store.containsKey(key);
}
public boolean containsValue(V value) {
return store.containsValue(value);
}
public V get(String key) {
return store.get(key);
}
public Iterable<V> getPlayerOrdering(@Nullable EntityPlayerMP player) {
if (player == null) return baked;
List<V> playerPreference = playerBaked.get(player.getUniqueID());
if (playerPreference == null) return baked;
return playerPreference;
}
Iterable<String> getCurrentOrdering() {
return store.keySet();
}
@Override
public Iterator<V> iterator() {
return baked.iterator();
}
@Override
public void forEach(Consumer<? super V> action) {
baked.forEach(action);
}
@Override
public Spliterator<V> spliterator() {
return baked.spliterator();
}
}
@@ -17,13 +17,16 @@ import com.gtnewhorizon.structurelib.item.ItemConstructableTrigger;
import com.gtnewhorizon.structurelib.item.ItemFrontRotationTool;
import com.gtnewhorizon.structurelib.net.AlignmentMessage;
import com.gtnewhorizon.structurelib.net.ErrorHintParticleMessage;
import com.gtnewhorizon.structurelib.net.RegistryOrderSyncMessage;
import com.gtnewhorizon.structurelib.net.SetChannelDataMessage;
import com.gtnewhorizon.structurelib.net.UpdateHintParticleMessage;
import com.gtnewhorizon.structurelib.util.InventoryUtility;
import com.gtnewhorizon.structurelib.util.XSTR;
import cpw.mods.fml.common.Loader;
import cpw.mods.fml.common.Mod;
import cpw.mods.fml.common.SidedProxy;
import cpw.mods.fml.common.event.FMLLoadCompleteEvent;
import cpw.mods.fml.common.event.FMLPreInitializationEvent;
import cpw.mods.fml.common.event.FMLServerStartingEvent;
import cpw.mods.fml.common.network.NetworkRegistry;
@@ -65,6 +68,7 @@ public class StructureLib {
net.registerMessage(UpdateHintParticleMessage.Handler.class, UpdateHintParticleMessage.class, 2, Side.CLIENT);
net.registerMessage(SetChannelDataMessage.Handler.class, SetChannelDataMessage.class, 3, Side.SERVER);
net.registerMessage(ErrorHintParticleMessage.Handler.class, ErrorHintParticleMessage.class, 4, Side.CLIENT);
net.registerMessage(RegistryOrderSyncMessage.Handler.class, RegistryOrderSyncMessage.class, 5, Side.SERVER);
try {
DEBUG_MODE = Boolean.parseBoolean(System.getProperty("structurelib.debug"));
@@ -109,11 +113,17 @@ public class StructureLib {
proxy.preInit(e);
NetworkRegistry.INSTANCE.registerGuiHandler(instance(), new GuiHandler());
InventoryUtility.init();
if (Loader.isModLoaded(STRUCTURECOMPAT_MODID)) {
COMPAT = Loader.instance().getIndexedModList().get(STRUCTURECOMPAT_MODID).getMod();
}
}
@Mod.EventHandler
public void loadComplete(FMLLoadCompleteEvent e) {
proxy.loadComplete(e);
}
@Mod.EventHandler
public void serverStarting(FMLServerStartingEvent e) {
e.registerServerCommand(new CommandConfigureChannels());
@@ -0,0 +1,77 @@
package com.gtnewhorizon.structurelib.net;
import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ConcurrentHashMap;
import com.gtnewhorizon.structurelib.SortedRegistry;
import cpw.mods.fml.common.network.ByteBufUtils;
import cpw.mods.fml.common.network.simpleimpl.IMessage;
import cpw.mods.fml.common.network.simpleimpl.IMessageHandler;
import cpw.mods.fml.common.network.simpleimpl.MessageContext;
import io.netty.buffer.ByteBuf;
public class RegistryOrderSyncMessage implements IMessage {
private String name;
private List<String> order;
private List<String> disabled;
public RegistryOrderSyncMessage() {}
public RegistryOrderSyncMessage(String name, List<String> order, List<String> disabled) {
this.name = name;
this.order = order;
this.disabled = disabled;
}
@Override
public void fromBytes(ByteBuf buf) {
name = ByteBufUtils.readUTF8String(buf);
int size = ByteBufUtils.readVarInt(buf, 2);
order = new ArrayList<>(size);
for (int i = 0; i < size; i++) {
order.add(ByteBufUtils.readUTF8String(buf));
}
size = ByteBufUtils.readVarInt(buf, 2);
disabled = new ArrayList<>(size);
for (int i = 0; i < size; i++) {
disabled.add(ByteBufUtils.readUTF8String(buf));
}
}
@Override
public void toBytes(ByteBuf buf) {
ByteBufUtils.writeUTF8String(buf, name);
ByteBufUtils.writeVarInt(buf, order.size(), 2);
for (String s : order) {
ByteBufUtils.writeUTF8String(buf, s);
}
ByteBufUtils.writeVarInt(buf, disabled.size(), 2);
for (String s : disabled) {
ByteBufUtils.writeUTF8String(buf, s);
}
}
public static class Handler implements IMessageHandler<RegistryOrderSyncMessage, IMessage> {
private static ConcurrentHashMap<String, WeakReference<SortedRegistry<?>>> ALL_REGISTRIES;
public static void setAllRegistries(ConcurrentHashMap<String, WeakReference<SortedRegistry<?>>> r) {
if (ALL_REGISTRIES == null) ALL_REGISTRIES = r;
}
@Override
public IMessage onMessage(RegistryOrderSyncMessage message, MessageContext ctx) {
if (ALL_REGISTRIES == null) return null;
WeakReference<SortedRegistry<?>> ref = ALL_REGISTRIES.get(message.name);
if (ref == null) return null;
SortedRegistry<?> r = ref.get();
if (r == null) return null;
r.registerOrdering(ctx.getServerHandler().playerEntity, message.order, message.disabled);
return null;
}
}
}
@@ -623,8 +623,8 @@ public class StructureUtility {
* </pre>
*
* @param tierExtractor a function to extract tier info from a block. This function can return null and will never
* be passed a null block or an invalid block meta. If this function returns null, then the block
* would be considered invalid.
* be passed a null block or an invalid block meta. If this function returns null, then the
* block would be considered invalid.
* @param allKnownTiers A list of all known tiers as of calling. Can be empty or null. No hint will be spawned if
* empty or null. Cannot have null elements. First element denotes the most primitive tier.
* Last element denotes the most advanced tier. If not all tiers are available at definition
@@ -30,6 +30,7 @@ import net.minecraft.inventory.InventoryEnderChest;
import net.minecraft.item.ItemStack;
import com.gtnewhorizon.gtnhlib.util.map.ItemStackMap;
import com.gtnewhorizon.structurelib.SortedRegistry;
import com.gtnewhorizon.structurelib.util.InventoryUtility.ItemStackExtractor.APIType;
import com.gtnewhorizon.structurelib.util.ItemStackPredicate.NBTMode;
@@ -38,12 +39,13 @@ import com.gtnewhorizon.structurelib.util.ItemStackPredicate.NBTMode;
*/
public class InventoryUtility {
private static final SortedRegistry<ItemStackExtractor> stackExtractors = new SortedRegistry<>();
private static final SortedRegistry<ItemStackExtractor> stackExtractors = new SortedRegistry<>("stackExtractors");
private static final List<Predicate<? super EntityPlayerMP>> enableEnder = new CopyOnWriteArrayList<>();
/**
* The remove() of the Iterable returned must be implemented!
*/
private static final SortedRegistry<InventoryProvider<?>> inventoryProviders = new SortedRegistry<>();
private static final SortedRegistry<InventoryProvider<?>> inventoryProviders = new SortedRegistry<>(
"inventoryProviders");
static {
inventoryProviders.register("5000-main-inventory", new InventoryProvider<InventoryIterable<InventoryPlayer>>() {
@@ -102,8 +104,8 @@ public class InventoryUtility {
registerInventoryProvider(key, newInventoryProvider(extractor));
}
public static Iterator<? extends ItemStackExtractor> getStackExtractors() {
return stackExtractors.iterator();
public static Iterator<? extends ItemStackExtractor> getStackExtractors(EntityPlayerMP player) {
return stackExtractors.getPlayerOrdering(player).iterator();
}
public static <Inv extends IInventory> InventoryProvider<InventoryIterable<Inv>> newInventoryProvider(
@@ -165,7 +167,7 @@ public class InventoryUtility {
boolean simulate, int count) {
ItemStackCounterImpl store = new ItemStackCounterImpl();
int sum = 0;
for (InventoryProvider<?> provider : inventoryProviders) {
for (InventoryProvider<?> provider : inventoryProviders.getPlayerOrdering(player)) {
sum += takeFromPlayer(player, predicate, simulate, count - sum, store, provider, null);
if (sum >= count) return store.getStore();
}
@@ -186,7 +188,7 @@ public class InventoryUtility {
int count = filter.stackSize;
ItemStackPredicate predicate = ItemStackPredicate.from(filter, NBTMode.EXACT);
ItemStackCounterImpl store = new ItemStackCounterImpl();
for (InventoryProvider<?> provider : inventoryProviders) {
for (InventoryProvider<?> provider : inventoryProviders.getPlayerOrdering(player)) {
sum += takeFromPlayer(player, predicate, simulate, count - sum, store, provider, filter);
if (sum >= count) return sum;
}
@@ -212,7 +214,7 @@ public class InventoryUtility {
* @param predicate item stack filter
* @param simulate whether to do removal
* @param count let's hope int size is enough...
* @param recursive do recursive lookup using {@link #getStackExtractors() stack extractors}
* @param recursive do recursive lookup using {@link #getStackExtractors(EntityPlayerMP) stack extractors}
* @return amount taken. never negative nor bigger than count...
*/
public static Map<ItemStack, Integer> takeFromInventory(Iterable<ItemStack> inv, Predicate<ItemStack> predicate,
@@ -252,7 +254,7 @@ public class InventoryUtility {
if (found == count) return count;
}
if (!recursive) continue;
for (ItemStackExtractor f : stackExtractors) {
for (ItemStackExtractor f : stackExtractors.getPlayerOrdering(player)) {
boolean end = f.isAPIImplemented(APIType.IS_VALID_SOURCE);
if (!f.isValidSource(stack, player)) continue;
if (filter != null && f.isAPIImplemented(APIType.EXTRACT_ONE_STACK)) {
@@ -268,6 +270,10 @@ public class InventoryUtility {
return found;
}
public static void init() {
// dummy method to init static final fields
}
public interface OptimizedExtractor {
/**
@@ -1,58 +0,0 @@
package com.gtnewhorizon.structurelib.util;
import java.util.*;
import java.util.function.Consumer;
public class SortedRegistry<V> implements Iterable<V> {
private final NavigableMap<String, V> store = new TreeMap<>();
/**
* Store stuff in an array for faster iteration access than a red black tree
*/
private List<V> baked = Collections.emptyList();
public void register(String key, V val) {
if (key == null || val == null) throw new NullPointerException();
V old = store.putIfAbsent(key, val);
if (old != null) {
throw new IllegalArgumentException("Duplicate key: " + key);
}
// TODO allow a user to edit the order with a configuration file
baked = new ArrayList<>(store.values());
}
public int size() {
return store.size();
}
public boolean isEmpty() {
return store.isEmpty();
}
public boolean containsKey(String key) {
return store.containsKey(key);
}
public boolean containsValue(V value) {
return store.containsValue(value);
}
public V get(String key) {
return store.get(key);
}
@Override
public Iterator<V> iterator() {
return baked.iterator();
}
@Override
public void forEach(Consumer<? super V> action) {
baked.forEach(action);
}
@Override
public Spliterator<V> spliterator() {
return baked.spliterator();
}
}