mirror of
https://github.com/GTNewHorizons/StructureLib.git
synced 2026-11-13 16:54:01 +00:00
Compare commits
3
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4fdea65537 | ||
|
|
89057ebb2d | ||
|
|
d829ae466b |
No files matched your search
@@ -3,7 +3,6 @@
|
|||||||
dependencies {
|
dependencies {
|
||||||
api("com.github.GTNewHorizons:GTNHLib:0.11.21:dev")
|
api("com.github.GTNewHorizons:GTNHLib:0.11.21:dev")
|
||||||
implementation("com.github.GTNewHorizons:NotEnoughItems:2.8.110-GTNH:dev")
|
implementation("com.github.GTNewHorizons:NotEnoughItems:2.8.110-GTNH:dev")
|
||||||
|
|
||||||
testImplementation(platform('org.junit:junit-bom:5.9.2'))
|
testImplementation(platform('org.junit:junit-bom:5.9.2'))
|
||||||
testImplementation('org.junit.jupiter:junit-jupiter')
|
testImplementation('org.junit.jupiter:junit-jupiter')
|
||||||
testRuntimeOnly ('org.junit.platform:junit-platform-launcher')
|
testRuntimeOnly ('org.junit.platform:junit-platform-launcher')
|
||||||
|
|||||||
@@ -0,0 +1,88 @@
|
|||||||
|
package com.gtnewhorizon.structurelib;
|
||||||
|
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
import javax.annotation.Nullable;
|
||||||
|
|
||||||
|
import net.minecraft.block.Block;
|
||||||
|
import net.minecraft.entity.player.EntityPlayer;
|
||||||
|
import net.minecraft.entity.player.EntityPlayerMP;
|
||||||
|
import net.minecraft.item.ItemStack;
|
||||||
|
import net.minecraft.util.ChatComponentTranslation;
|
||||||
|
import net.minecraft.util.MovingObjectPosition;
|
||||||
|
import net.minecraft.world.World;
|
||||||
|
|
||||||
|
import com.gtnewhorizon.structurelib.alignment.constructable.ChannelDataAccessor;
|
||||||
|
import com.gtnewhorizon.structurelib.net.SetChannelDataMessage;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handler for middle-click channel picking. When the player holds a hologram projector on the cursor and middle-clicks
|
||||||
|
* a registered indicator item, the corresponding channel is set on the projector. The same applies in world when the
|
||||||
|
* projector is held in hand and a registered indicator block is middle-clicked. Shift+middle-click wipes all channels.
|
||||||
|
* Was made side neutral to support server-side validation at some point if necessary.
|
||||||
|
*/
|
||||||
|
public final class ChannelPickHandler {
|
||||||
|
|
||||||
|
private ChannelPickHandler() {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handle a middle-click event while holding a hologram projector on the cursor.
|
||||||
|
*/
|
||||||
|
public static boolean handleMiddleClick(EntityPlayer player, ItemStack holoStack, @Nullable ItemStack targetStack,
|
||||||
|
final boolean isSneaking) {
|
||||||
|
return handlePick(player, holoStack, targetStack, isSneaking, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean handleWorldPick(World world, EntityPlayer player, ItemStack holoStack,
|
||||||
|
@Nullable MovingObjectPosition hit, final boolean isSneaking) {
|
||||||
|
ItemStack targetStack = null;
|
||||||
|
|
||||||
|
if (hit != null) {
|
||||||
|
Block block = world.getBlock(hit.blockX, hit.blockY, hit.blockZ);
|
||||||
|
if (!block.isAir(world, hit.blockX, hit.blockY, hit.blockZ)) {
|
||||||
|
targetStack = block.getPickBlock(hit, world, hit.blockX, hit.blockY, hit.blockZ, player);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// A null target still goes through so that shift+middle-click wipes even when aiming at nothing
|
||||||
|
return handlePick(player, holoStack, targetStack, isSneaking, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static boolean handlePick(EntityPlayer player, ItemStack holoStack, @Nullable ItemStack targetStack,
|
||||||
|
final boolean isSneaking, final boolean onCursor) {
|
||||||
|
// Clear channels on shift+middle-click. The "press again to confirm" gesture is gated client-side
|
||||||
|
// (see ChannelWipeConfirmation) before this method is ever called.
|
||||||
|
if (isSneaking) {
|
||||||
|
ChannelDataAccessor.wipeChannelData(holoStack);
|
||||||
|
sendChannelData(player, holoStack, onCursor);
|
||||||
|
player.addChatMessage(new ChatComponentTranslation("structurelib.pickchannel.cleared"));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (targetStack == null) return false; // nothing hovered, cancel silently
|
||||||
|
|
||||||
|
Collection<Map.Entry<String, Integer>> channels = ChannelDescription.iterate(targetStack);
|
||||||
|
if (channels.isEmpty()) {
|
||||||
|
player.addChatMessage(new ChatComponentTranslation("structurelib.pickchannel.nomapping"));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Assuming there's only 1 channel per item for now (same behavior as drag and drop)
|
||||||
|
Map.Entry<String, Integer> first = channels.iterator().next();
|
||||||
|
ChannelDataAccessor.setChannelData(holoStack, first.getKey(), first.getValue());
|
||||||
|
sendChannelData(player, holoStack, onCursor);
|
||||||
|
player.addChatMessage(
|
||||||
|
new ChatComponentTranslation("structurelib.pickchannel.success", first.getKey(), first.getValue()));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Player is the one triggering this update, so should already be in-sync.
|
||||||
|
*/
|
||||||
|
private static void sendChannelData(EntityPlayer player, ItemStack holoStack, boolean onCursor) {
|
||||||
|
if (!(player instanceof EntityPlayerMP)) {
|
||||||
|
StructureLib.net.sendToServer(new SetChannelDataMessage(holoStack, onCursor));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,33 @@
|
|||||||
|
package com.gtnewhorizon.structurelib;
|
||||||
|
|
||||||
|
import net.minecraft.client.Minecraft;
|
||||||
|
import net.minecraft.entity.player.EntityPlayer;
|
||||||
|
import net.minecraft.util.ChatComponentTranslation;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Client-side-only confirmation gate for "wipe all channels" shift+middle-click gesture.
|
||||||
|
*/
|
||||||
|
public final class ChannelWipeConfirmation {
|
||||||
|
|
||||||
|
private static final long CONFIRM_WINDOW_MS = 5000;
|
||||||
|
private static long armedAtMs = -1;
|
||||||
|
|
||||||
|
private ChannelWipeConfirmation() {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Call this before invoking {@link ChannelPickHandler} whenever the wipe gesture (shift+middle-click) is detected.
|
||||||
|
*
|
||||||
|
* @return true if this is a confirming second press within the window and the wipe should proceed now; false if
|
||||||
|
* this press only armed the confirmation.
|
||||||
|
*/
|
||||||
|
public static boolean confirm(EntityPlayer player) {
|
||||||
|
long now = Minecraft.getSystemTime();
|
||||||
|
if (now - armedAtMs > CONFIRM_WINDOW_MS) {
|
||||||
|
armedAtMs = now;
|
||||||
|
player.addChatMessage(new ChatComponentTranslation("structurelib.pickchannel.confirmwipe"));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
armedAtMs = -1;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -30,6 +30,7 @@ import net.minecraft.util.IChatComponent;
|
|||||||
import net.minecraft.util.IIcon;
|
import net.minecraft.util.IIcon;
|
||||||
import net.minecraft.util.Vec3;
|
import net.minecraft.util.Vec3;
|
||||||
import net.minecraft.world.World;
|
import net.minecraft.world.World;
|
||||||
|
import net.minecraftforge.client.event.MouseEvent;
|
||||||
import net.minecraftforge.client.event.RenderWorldLastEvent;
|
import net.minecraftforge.client.event.RenderWorldLastEvent;
|
||||||
import net.minecraftforge.common.MinecraftForge;
|
import net.minecraftforge.common.MinecraftForge;
|
||||||
import net.minecraftforge.common.config.ConfigElement;
|
import net.minecraftforge.common.config.ConfigElement;
|
||||||
@@ -40,6 +41,7 @@ import org.apache.commons.lang3.tuple.Pair;
|
|||||||
import org.lwjgl.opengl.GL11;
|
import org.lwjgl.opengl.GL11;
|
||||||
|
|
||||||
import com.gtnewhorizon.structurelib.entity.fx.WeightlessParticleFX;
|
import com.gtnewhorizon.structurelib.entity.fx.WeightlessParticleFX;
|
||||||
|
import com.gtnewhorizon.structurelib.item.ItemConstructableTrigger;
|
||||||
import com.gtnewhorizon.structurelib.net.RegistryOrderSyncMessage;
|
import com.gtnewhorizon.structurelib.net.RegistryOrderSyncMessage;
|
||||||
import com.gtnewhorizon.structurelib.net.SetChannelDataMessage;
|
import com.gtnewhorizon.structurelib.net.SetChannelDataMessage;
|
||||||
|
|
||||||
@@ -53,6 +55,8 @@ import cpw.mods.fml.common.eventhandler.SubscribeEvent;
|
|||||||
import cpw.mods.fml.common.gameevent.TickEvent.ClientTickEvent;
|
import cpw.mods.fml.common.gameevent.TickEvent.ClientTickEvent;
|
||||||
import cpw.mods.fml.common.gameevent.TickEvent.Phase;
|
import cpw.mods.fml.common.gameevent.TickEvent.Phase;
|
||||||
import cpw.mods.fml.common.network.FMLNetworkEvent;
|
import cpw.mods.fml.common.network.FMLNetworkEvent;
|
||||||
|
import cpw.mods.fml.relauncher.Side;
|
||||||
|
import cpw.mods.fml.relauncher.SideOnly;
|
||||||
|
|
||||||
public class ClientProxy extends CommonProxy {
|
public class ClientProxy extends CommonProxy {
|
||||||
|
|
||||||
@@ -560,6 +564,35 @@ public class ClientProxy extends CommonProxy {
|
|||||||
|
|
||||||
public static class ForgeEventHandler {
|
public static class ForgeEventHandler {
|
||||||
|
|
||||||
|
@SubscribeEvent
|
||||||
|
@SideOnly(Side.CLIENT)
|
||||||
|
public void onMouseEvent(MouseEvent event) {
|
||||||
|
final EntityPlayer player = Minecraft.getMinecraft().thePlayer;
|
||||||
|
|
||||||
|
if (player == null || player.isDead) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
final ItemStack heldItem = player.getHeldItem();
|
||||||
|
|
||||||
|
if (heldItem == null || !(heldItem.getItem() instanceof ItemConstructableTrigger)) return;
|
||||||
|
|
||||||
|
if (event.button != 2 || !event.buttonstate) return;
|
||||||
|
|
||||||
|
// Sink the middle click as soon as a projector is held, so vanilla pick block does not run
|
||||||
|
event.setCanceled(true);
|
||||||
|
|
||||||
|
final boolean isSneaking = player.isSneaking();
|
||||||
|
if (isSneaking && !ChannelWipeConfirmation.confirm(player)) return;
|
||||||
|
|
||||||
|
ChannelPickHandler.handleWorldPick(
|
||||||
|
player.worldObj,
|
||||||
|
player,
|
||||||
|
heldItem,
|
||||||
|
Minecraft.getMinecraft().objectMouseOver,
|
||||||
|
isSneaking);
|
||||||
|
}
|
||||||
|
|
||||||
@SubscribeEvent
|
@SubscribeEvent
|
||||||
public void onEntityJoinWorld(EntityJoinWorldEvent e) {
|
public void onEntityJoinWorld(EntityJoinWorldEvent e) {
|
||||||
if (e.world.isRemote && e.entity instanceof EntityClientPlayerMP) {
|
if (e.world.isRemote && e.entity instanceof EntityClientPlayerMP) {
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ import net.minecraftforge.common.MinecraftForge;
|
|||||||
import com.gtnewhorizon.structurelib.net.ErrorHintParticleMessage;
|
import com.gtnewhorizon.structurelib.net.ErrorHintParticleMessage;
|
||||||
import com.gtnewhorizon.structurelib.net.UpdateHintParticleMessage;
|
import com.gtnewhorizon.structurelib.net.UpdateHintParticleMessage;
|
||||||
|
|
||||||
|
import cpw.mods.fml.common.event.FMLInitializationEvent;
|
||||||
import cpw.mods.fml.common.event.FMLLoadCompleteEvent;
|
import cpw.mods.fml.common.event.FMLLoadCompleteEvent;
|
||||||
import cpw.mods.fml.common.event.FMLPreInitializationEvent;
|
import cpw.mods.fml.common.event.FMLPreInitializationEvent;
|
||||||
import cpw.mods.fml.common.eventhandler.SubscribeEvent;
|
import cpw.mods.fml.common.eventhandler.SubscribeEvent;
|
||||||
@@ -64,6 +65,8 @@ public class CommonProxy {
|
|||||||
|
|
||||||
public void preInit(FMLPreInitializationEvent e) {}
|
public void preInit(FMLPreInitializationEvent e) {}
|
||||||
|
|
||||||
|
public void init(FMLInitializationEvent e) {}
|
||||||
|
|
||||||
public long getOverworldTime() {
|
public long getOverworldTime() {
|
||||||
return MinecraftServer.getServer().getEntityWorld().getTotalWorldTime();
|
return MinecraftServer.getServer().getEntityWorld().getTotalWorldTime();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -33,6 +33,7 @@ import com.gtnewhorizon.structurelib.util.XSTR;
|
|||||||
|
|
||||||
import cpw.mods.fml.common.Mod;
|
import cpw.mods.fml.common.Mod;
|
||||||
import cpw.mods.fml.common.SidedProxy;
|
import cpw.mods.fml.common.SidedProxy;
|
||||||
|
import cpw.mods.fml.common.event.FMLInitializationEvent;
|
||||||
import cpw.mods.fml.common.event.FMLInterModComms;
|
import cpw.mods.fml.common.event.FMLInterModComms;
|
||||||
import cpw.mods.fml.common.event.FMLLoadCompleteEvent;
|
import cpw.mods.fml.common.event.FMLLoadCompleteEvent;
|
||||||
import cpw.mods.fml.common.event.FMLPreInitializationEvent;
|
import cpw.mods.fml.common.event.FMLPreInitializationEvent;
|
||||||
@@ -128,6 +129,11 @@ public class StructureLib {
|
|||||||
DevelopHelper.onPreInit();
|
DevelopHelper.onPreInit();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Mod.EventHandler
|
||||||
|
public void init(FMLInitializationEvent e) {
|
||||||
|
proxy.init(e);
|
||||||
|
}
|
||||||
|
|
||||||
@Mod.EventHandler
|
@Mod.EventHandler
|
||||||
public void loadComplete(FMLLoadCompleteEvent e) {
|
public void loadComplete(FMLLoadCompleteEvent e) {
|
||||||
proxy.loadComplete(e);
|
proxy.loadComplete(e);
|
||||||
|
|||||||
+6
-1
@@ -146,7 +146,12 @@ public class ChannelDataAccessor {
|
|||||||
*/
|
*/
|
||||||
public static void wipeChannelData(ItemStack masterStack) {
|
public static void wipeChannelData(ItemStack masterStack) {
|
||||||
if (masterStack == null) throw new IllegalArgumentException();
|
if (masterStack == null) throw new IllegalArgumentException();
|
||||||
if (masterStack.stackTagCompound != null) masterStack.stackTagCompound.removeTag(SECONDARY_HINT_TAG);
|
if (masterStack.stackTagCompound != null) {
|
||||||
|
masterStack.stackTagCompound.removeTag(SECONDARY_HINT_TAG);
|
||||||
|
if (masterStack.stackTagCompound.hasNoTags()) {
|
||||||
|
masterStack.stackTagCompound = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
package com.gtnewhorizon.structurelib.core;
|
package com.gtnewhorizon.structurelib.core;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
@@ -7,7 +8,9 @@ import java.util.Set;
|
|||||||
|
|
||||||
import com.gtnewhorizon.gtnhmixins.IEarlyMixinLoader;
|
import com.gtnewhorizon.gtnhmixins.IEarlyMixinLoader;
|
||||||
|
|
||||||
|
import cpw.mods.fml.relauncher.FMLLaunchHandler;
|
||||||
import cpw.mods.fml.relauncher.IFMLLoadingPlugin;
|
import cpw.mods.fml.relauncher.IFMLLoadingPlugin;
|
||||||
|
import cpw.mods.fml.relauncher.Side;
|
||||||
|
|
||||||
@IFMLLoadingPlugin.MCVersion("1.7.10")
|
@IFMLLoadingPlugin.MCVersion("1.7.10")
|
||||||
public class StructureLibCore implements IFMLLoadingPlugin, IEarlyMixinLoader {
|
public class StructureLibCore implements IFMLLoadingPlugin, IEarlyMixinLoader {
|
||||||
@@ -44,6 +47,11 @@ public class StructureLibCore implements IFMLLoadingPlugin, IEarlyMixinLoader {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public List<String> getMixins(Set<String> loadedCoreMods) {
|
public List<String> getMixins(Set<String> loadedCoreMods) {
|
||||||
return Arrays.asList("blockChangeNotifier.MixinForgeHooks", "blockChangeNotifier.MixinWorld");
|
List<String> mixins = new ArrayList<>(
|
||||||
|
Arrays.asList("blockChangeNotifier.MixinForgeHooks", "blockChangeNotifier.MixinWorld"));
|
||||||
|
if (FMLLaunchHandler.side() == Side.CLIENT) {
|
||||||
|
mixins.add("MixinGuiContainer_PickChannel");
|
||||||
|
}
|
||||||
|
return mixins;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
+64
@@ -0,0 +1,64 @@
|
|||||||
|
package com.gtnewhorizon.structurelib.mixins.early;
|
||||||
|
|
||||||
|
import net.minecraft.client.Minecraft;
|
||||||
|
import net.minecraft.client.gui.GuiScreen;
|
||||||
|
import net.minecraft.client.gui.inventory.GuiContainer;
|
||||||
|
import net.minecraft.entity.player.EntityPlayer;
|
||||||
|
import net.minecraft.inventory.Slot;
|
||||||
|
import net.minecraft.item.ItemStack;
|
||||||
|
|
||||||
|
import org.spongepowered.asm.mixin.Mixin;
|
||||||
|
import org.spongepowered.asm.mixin.gen.Invoker;
|
||||||
|
import org.spongepowered.asm.mixin.injection.At;
|
||||||
|
import org.spongepowered.asm.mixin.injection.Inject;
|
||||||
|
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
||||||
|
|
||||||
|
import com.gtnewhorizon.structurelib.ChannelPickHandler;
|
||||||
|
import com.gtnewhorizon.structurelib.ChannelWipeConfirmation;
|
||||||
|
import com.gtnewhorizon.structurelib.StructureLib;
|
||||||
|
import com.gtnewhorizon.structurelib.item.ItemConstructableTrigger;
|
||||||
|
|
||||||
|
import codechicken.nei.NEIClientConfig;
|
||||||
|
import codechicken.nei.guihook.GuiContainerManager;
|
||||||
|
import cpw.mods.fml.common.Loader;
|
||||||
|
|
||||||
|
@Mixin(value = GuiContainer.class, priority = 100)
|
||||||
|
public abstract class MixinGuiContainer_PickChannel {
|
||||||
|
|
||||||
|
@Inject(method = "mouseClicked(III)V", at = @At("HEAD"), cancellable = true)
|
||||||
|
private void slib$onMouseClicked(int mouseX, int mouseY, int mouseButton, CallbackInfo ci) {
|
||||||
|
if (mouseButton != 2) return;
|
||||||
|
|
||||||
|
EntityPlayer player = Minecraft.getMinecraft().thePlayer;
|
||||||
|
if (player == null) return;
|
||||||
|
|
||||||
|
ItemStack cursorStack = player.inventory.getItemStack();
|
||||||
|
if (cursorStack == null || !(cursorStack.getItem() instanceof ItemConstructableTrigger)) return;
|
||||||
|
|
||||||
|
// Sink middle click as soon as projector is picked up
|
||||||
|
ci.cancel();
|
||||||
|
|
||||||
|
ItemStack hoveredStack = null;
|
||||||
|
try {
|
||||||
|
Slot slot = slib$invokeGetSlotAtPosition(mouseX, mouseY);
|
||||||
|
if (slot != null) {
|
||||||
|
hoveredStack = slot.getStack();
|
||||||
|
}
|
||||||
|
|
||||||
|
// try NEI
|
||||||
|
if (hoveredStack == null && Loader.isModLoaded("NotEnoughItems") && !NEIClientConfig.isHidden()) {
|
||||||
|
hoveredStack = GuiContainerManager.getStackMouseOver((GuiContainer) (Object) this);
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
StructureLib.LOGGER.error("Error while resolving hovered slot for channel pick", e);
|
||||||
|
}
|
||||||
|
|
||||||
|
boolean isSneaking = GuiScreen.isShiftKeyDown();
|
||||||
|
if (isSneaking && !ChannelWipeConfirmation.confirm(player)) return;
|
||||||
|
|
||||||
|
ChannelPickHandler.handleMiddleClick(player, cursorStack, hoveredStack, isSneaking);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Invoker("getSlotAtPosition")
|
||||||
|
protected abstract Slot slib$invokeGetSlotAtPosition(int x, int y);
|
||||||
|
}
|
||||||
@@ -23,15 +23,22 @@ import io.netty.buffer.ByteBuf;
|
|||||||
public class SetChannelDataMessage implements IMessage {
|
public class SetChannelDataMessage implements IMessage {
|
||||||
|
|
||||||
private final List<Map.Entry<String, Integer>> data = new ArrayList<>();
|
private final List<Map.Entry<String, Integer>> data = new ArrayList<>();
|
||||||
|
private boolean onCursor;
|
||||||
|
|
||||||
public SetChannelDataMessage() {}
|
public SetChannelDataMessage() {}
|
||||||
|
|
||||||
public SetChannelDataMessage(ItemStack trigger) {
|
public SetChannelDataMessage(ItemStack trigger) {
|
||||||
|
this(trigger, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
public SetChannelDataMessage(ItemStack trigger, final boolean onCursor) {
|
||||||
|
this.onCursor = onCursor;
|
||||||
ChannelDataAccessor.iterateChannelData(trigger).forEach(data::add);
|
ChannelDataAccessor.iterateChannelData(trigger).forEach(data::add);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void fromBytes(ByteBuf buf) {
|
public void fromBytes(ByteBuf buf) {
|
||||||
|
onCursor = buf.readBoolean();
|
||||||
data.clear();
|
data.clear();
|
||||||
int size = ByteBufUtils.readVarShort(buf);
|
int size = ByteBufUtils.readVarShort(buf);
|
||||||
for (int i = 0; i < size; i++) {
|
for (int i = 0; i < size; i++) {
|
||||||
@@ -41,6 +48,7 @@ public class SetChannelDataMessage implements IMessage {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void toBytes(ByteBuf buf) {
|
public void toBytes(ByteBuf buf) {
|
||||||
|
buf.writeBoolean(onCursor);
|
||||||
ByteBufUtils.writeVarShort(buf, data.size());
|
ByteBufUtils.writeVarShort(buf, data.size());
|
||||||
for (Entry<String, Integer> e : data) {
|
for (Entry<String, Integer> e : data) {
|
||||||
ByteBufUtils.writeUTF8String(buf, e.getKey());
|
ByteBufUtils.writeUTF8String(buf, e.getKey());
|
||||||
@@ -53,11 +61,11 @@ public class SetChannelDataMessage implements IMessage {
|
|||||||
@Override
|
@Override
|
||||||
public IMessage onMessage(SetChannelDataMessage message, MessageContext ctx) {
|
public IMessage onMessage(SetChannelDataMessage message, MessageContext ctx) {
|
||||||
EntityPlayerMP player = ctx.getServerHandler().playerEntity;
|
EntityPlayerMP player = ctx.getServerHandler().playerEntity;
|
||||||
ItemStack heldItem = player.getHeldItem();
|
ItemStack target = message.onCursor ? player.inventory.getItemStack() : player.getHeldItem();
|
||||||
if (heldItem != null && heldItem.getItem() instanceof ItemConstructableTrigger) {
|
if (target != null && target.getItem() instanceof ItemConstructableTrigger) {
|
||||||
ChannelDataAccessor.wipeChannelData(heldItem);
|
ChannelDataAccessor.wipeChannelData(target);
|
||||||
for (Entry<String, Integer> e : message.data) {
|
for (Entry<String, Integer> e : message.data) {
|
||||||
ChannelDataAccessor.setChannelData(heldItem, e.getKey(), e.getValue());
|
ChannelDataAccessor.setChannelData(target, e.getKey(), e.getValue());
|
||||||
}
|
}
|
||||||
// since this is a set all channel request from the client, we would assume client already know
|
// since this is a set all channel request from the client, we would assume client already know
|
||||||
// what this would look like on the client, so no sync
|
// what this would look like on the client, so no sync
|
||||||
@@ -65,7 +73,7 @@ public class SetChannelDataMessage implements IMessage {
|
|||||||
StructureLib.LOGGER.warn(
|
StructureLib.LOGGER.warn(
|
||||||
"{} trying to set channel data on {}, which is not a hologram projector!",
|
"{} trying to set channel data on {}, which is not a hologram projector!",
|
||||||
player.getUniqueID(),
|
player.getUniqueID(),
|
||||||
heldItem);
|
target);
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -67,7 +67,7 @@ public interface IStructureDefinition<T> {
|
|||||||
* @throws java.util.NoSuchElementException if the given structure piece is not found and the moon phase perfectly
|
* @throws java.util.NoSuchElementException if the given structure piece is not found and the moon phase perfectly
|
||||||
* matches
|
* matches
|
||||||
*/
|
*/
|
||||||
IStructureElement<? super T>[] getStructureFor(String name);
|
IStructureElement<T>[] getStructureFor(String name);
|
||||||
|
|
||||||
boolean isContainedInStructure(String name, int offsetA, int offsetB, int offsetC);
|
boolean isContainedInStructure(String name, int offsetA, int offsetB, int offsetC);
|
||||||
|
|
||||||
@@ -356,7 +356,7 @@ public interface IStructureDefinition<T> {
|
|||||||
* @return true if iteration completed successfully.
|
* @return true if iteration completed successfully.
|
||||||
* @param <T> type of context object.
|
* @param <T> type of context object.
|
||||||
*/
|
*/
|
||||||
static <T> boolean iterate(T object, ItemStack trigger, IStructureElement<? super T>[] elements, World world,
|
static <T> boolean iterate(T object, ItemStack trigger, IStructureElement<T>[] elements, World world,
|
||||||
ExtendedFacing extendedFacing, int basePositionX, int basePositionY, int basePositionZ, int basePositionA,
|
ExtendedFacing extendedFacing, int basePositionX, int basePositionY, int basePositionZ, int basePositionA,
|
||||||
int basePositionB, int basePositionC, boolean hintsOnly, Boolean checkBlocksIfNotNullForceCheckAllIfTrue) {
|
int basePositionB, int basePositionC, boolean hintsOnly, Boolean checkBlocksIfNotNullForceCheckAllIfTrue) {
|
||||||
if (!world.isRemote && hintsOnly) {
|
if (!world.isRemote && hintsOnly) {
|
||||||
|
|||||||
@@ -28,9 +28,6 @@ import com.gtnewhorizon.structurelib.util.ItemStackPredicate.NBTMode;
|
|||||||
/**
|
/**
|
||||||
* Use StructureUtility to instantiate. These are the building blocks for your {@link IStructureDefinition}. It
|
* Use StructureUtility to instantiate. These are the building blocks for your {@link IStructureDefinition}. It
|
||||||
* represents what a particular element can be and how this particular element should be autoplaced.
|
* represents what a particular element can be and how this particular element should be autoplaced.
|
||||||
*
|
|
||||||
* @param <T> Type of the context object. {@link IStructureElement} is contravariant over T, so parameters and fields of
|
|
||||||
* type {@link IStructureElement} should be declared as {@code IStructureElement<? super T>}
|
|
||||||
*/
|
*/
|
||||||
public interface IStructureElement<T> {
|
public interface IStructureElement<T> {
|
||||||
|
|
||||||
@@ -216,7 +213,7 @@ public interface IStructureElement<T> {
|
|||||||
* Forget the messed up class dependency graph for now. this is just so convenient.
|
* Forget the messed up class dependency graph for now. this is just so convenient.
|
||||||
*/
|
*/
|
||||||
default IStructureElementNoPlacement<T> noPlacement() {
|
default IStructureElementNoPlacement<T> noPlacement() {
|
||||||
return new IStructureElementNoPlacement<>() {
|
return new IStructureElementNoPlacement<T>() {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean check(T t, World world, int x, int y, int z) {
|
public boolean check(T t, World world, int x, int y, int z) {
|
||||||
|
|||||||
@@ -21,11 +21,11 @@ import com.google.common.collect.Iterables;
|
|||||||
*/
|
*/
|
||||||
public interface IStructureElementChain<T> extends IStructureElement<T> {
|
public interface IStructureElementChain<T> extends IStructureElement<T> {
|
||||||
|
|
||||||
IStructureElement<? super T>[] fallbacks();
|
IStructureElement<T>[] fallbacks();
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
default boolean check(T t, World world, int x, int y, int z) {
|
default boolean check(T t, World world, int x, int y, int z) {
|
||||||
for (IStructureElement<? super T> fallback : fallbacks()) {
|
for (IStructureElement<T> fallback : fallbacks()) {
|
||||||
if (fallback.check(t, world, x, y, z)) {
|
if (fallback.check(t, world, x, y, z)) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@@ -35,7 +35,7 @@ public interface IStructureElementChain<T> extends IStructureElement<T> {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
default boolean couldBeValid(T t, World world, int x, int y, int z, ItemStack trigger) {
|
default boolean couldBeValid(T t, World world, int x, int y, int z, ItemStack trigger) {
|
||||||
for (IStructureElement<? super T> fallback : fallbacks()) {
|
for (IStructureElement<T> fallback : fallbacks()) {
|
||||||
if (fallback.couldBeValid(t, world, x, y, z, trigger)) {
|
if (fallback.couldBeValid(t, world, x, y, z, trigger)) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@@ -45,7 +45,7 @@ public interface IStructureElementChain<T> extends IStructureElement<T> {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
default boolean spawnHint(T t, World world, int x, int y, int z, ItemStack trigger) {
|
default boolean spawnHint(T t, World world, int x, int y, int z, ItemStack trigger) {
|
||||||
for (IStructureElement<? super T> fallback : fallbacks()) {
|
for (IStructureElement<T> fallback : fallbacks()) {
|
||||||
if (fallback.spawnHint(t, world, x, y, z, trigger)) {
|
if (fallback.spawnHint(t, world, x, y, z, trigger)) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@@ -55,7 +55,7 @@ public interface IStructureElementChain<T> extends IStructureElement<T> {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
default boolean placeBlock(T t, World world, int x, int y, int z, ItemStack trigger) {
|
default boolean placeBlock(T t, World world, int x, int y, int z, ItemStack trigger) {
|
||||||
for (IStructureElement<? super T> fallback : fallbacks()) {
|
for (IStructureElement<T> fallback : fallbacks()) {
|
||||||
if (fallback.placeBlock(t, world, x, y, z, trigger)) {
|
if (fallback.placeBlock(t, world, x, y, z, trigger)) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@@ -67,7 +67,7 @@ public interface IStructureElementChain<T> extends IStructureElement<T> {
|
|||||||
@Override
|
@Override
|
||||||
default List<String> getDescription(T context) {
|
default List<String> getDescription(T context) {
|
||||||
Set<String> descriptions = new LinkedHashSet<>();
|
Set<String> descriptions = new LinkedHashSet<>();
|
||||||
for (IStructureElement<? super T> fallback : fallbacks()) {
|
for (IStructureElement<T> fallback : fallbacks()) {
|
||||||
List<String> desc = fallback.getDescription(context);
|
List<String> desc = fallback.getDescription(context);
|
||||||
if (desc != null) {
|
if (desc != null) {
|
||||||
descriptions.addAll(desc);
|
descriptions.addAll(desc);
|
||||||
@@ -82,7 +82,7 @@ public interface IStructureElementChain<T> extends IStructureElement<T> {
|
|||||||
AutoPlaceEnvironment env) {
|
AutoPlaceEnvironment env) {
|
||||||
Predicate<ItemStack> predicate = null;
|
Predicate<ItemStack> predicate = null;
|
||||||
List<Iterable<ItemStack>> is = new ArrayList<>();
|
List<Iterable<ItemStack>> is = new ArrayList<>();
|
||||||
for (IStructureElement<? super T> fallback : fallbacks()) {
|
for (IStructureElement<T> fallback : fallbacks()) {
|
||||||
BlocksToPlace e = fallback.getBlocksToPlace(t, world, x, y, z, trigger, env);
|
BlocksToPlace e = fallback.getBlocksToPlace(t, world, x, y, z, trigger, env);
|
||||||
if (e == null) continue;
|
if (e == null) continue;
|
||||||
if (predicate == null) predicate = e.getPredicate();
|
if (predicate == null) predicate = e.getPredicate();
|
||||||
@@ -99,7 +99,7 @@ public interface IStructureElementChain<T> extends IStructureElement<T> {
|
|||||||
EntityPlayerMP actor, Consumer<IChatComponent> chatter) {
|
EntityPlayerMP actor, Consumer<IChatComponent> chatter) {
|
||||||
boolean allContinue = true;
|
boolean allContinue = true;
|
||||||
List<IChatComponent> bufferedNoise = new ArrayList<>();
|
List<IChatComponent> bufferedNoise = new ArrayList<>();
|
||||||
for (IStructureElement<? super T> fallback : fallbacks()) {
|
for (IStructureElement<T> fallback : fallbacks()) {
|
||||||
PlaceResult result = fallback.survivalPlaceBlock(t, world, x, y, z, trigger, s, actor, bufferedNoise::add);
|
PlaceResult result = fallback.survivalPlaceBlock(t, world, x, y, z, trigger, s, actor, bufferedNoise::add);
|
||||||
switch (result) {
|
switch (result) {
|
||||||
case REJECT_CONTINUE:
|
case REJECT_CONTINUE:
|
||||||
@@ -124,7 +124,7 @@ public interface IStructureElementChain<T> extends IStructureElement<T> {
|
|||||||
AutoPlaceEnvironment env) {
|
AutoPlaceEnvironment env) {
|
||||||
boolean allContinue = true;
|
boolean allContinue = true;
|
||||||
List<IChatComponent> bufferedNoise = new ArrayList<>();
|
List<IChatComponent> bufferedNoise = new ArrayList<>();
|
||||||
for (IStructureElement<? super T> fallback : fallbacks()) {
|
for (IStructureElement<T> fallback : fallbacks()) {
|
||||||
PlaceResult result = fallback
|
PlaceResult result = fallback
|
||||||
.survivalPlaceBlock(t, world, x, y, z, trigger, env.withChatter(bufferedNoise::add));
|
.survivalPlaceBlock(t, world, x, y, z, trigger, env.withChatter(bufferedNoise::add));
|
||||||
switch (result) {
|
switch (result) {
|
||||||
|
|||||||
@@ -10,36 +10,36 @@ import net.minecraft.world.World;
|
|||||||
/**
|
/**
|
||||||
* Use StructureUtility to instantiate
|
* Use StructureUtility to instantiate
|
||||||
*/
|
*/
|
||||||
interface IStructureNavigate extends IStructureElement<Object> {
|
interface IStructureNavigate<T> extends IStructureElement<T> {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
default boolean check(Object t, World world, int x, int y, int z) {
|
default boolean check(T t, World world, int x, int y, int z) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
default boolean couldBeValid(Object t, World world, int x, int y, int z, ItemStack trigger) {
|
default boolean couldBeValid(T t, World world, int x, int y, int z, ItemStack trigger) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
default boolean spawnHint(Object t, World world, int x, int y, int z, ItemStack trigger) {
|
default boolean spawnHint(T t, World world, int x, int y, int z, ItemStack trigger) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
default boolean placeBlock(Object t, World world, int x, int y, int z, ItemStack trigger) {
|
default boolean placeBlock(T t, World world, int x, int y, int z, ItemStack trigger) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
default PlaceResult survivalPlaceBlock(Object t, World world, int x, int y, int z, ItemStack trigger, IItemSource s,
|
default PlaceResult survivalPlaceBlock(T t, World world, int x, int y, int z, ItemStack trigger, IItemSource s,
|
||||||
EntityPlayerMP actor, Consumer<IChatComponent> chatter) {
|
EntityPlayerMP actor, Consumer<IChatComponent> chatter) {
|
||||||
return PlaceResult.SKIP;
|
return PlaceResult.SKIP;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
default PlaceResult survivalPlaceBlock(Object t, World world, int x, int y, int z, ItemStack trigger,
|
default PlaceResult survivalPlaceBlock(T t, World world, int x, int y, int z, ItemStack trigger,
|
||||||
AutoPlaceEnvironment env) {
|
AutoPlaceEnvironment env) {
|
||||||
return PlaceResult.SKIP;
|
return PlaceResult.SKIP;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -21,42 +21,40 @@ public interface IStructureWalker<T> {
|
|||||||
* @param c The relative C position of the element in structure-local coordinates (pre transform)
|
* @param c The relative C position of the element in structure-local coordinates (pre transform)
|
||||||
* @return False to stop iterating, true to continue.
|
* @return False to stop iterating, true to continue.
|
||||||
*/
|
*/
|
||||||
boolean visit(IStructureElement<? super T> element, World world, int x, int y, int z, int a, int b, int c);
|
boolean visit(IStructureElement<T> element, World world, int x, int y, int z, int a, int b, int c);
|
||||||
|
|
||||||
default boolean blockNotLoaded(IStructureElement<? super T> element, World world, int x, int y, int z, int a, int b,
|
default boolean blockNotLoaded(IStructureElement<T> element, World world, int x, int y, int z, int a, int b,
|
||||||
int c) {
|
int c) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
static <T> IStructureWalker<T> ignoreBlockUnloaded(IStructureWalker<T> walker) {
|
static <T> IStructureWalker<T> ignoreBlockUnloaded(IStructureWalker<T> walker) {
|
||||||
return new IStructureWalker<>() {
|
return new IStructureWalker<T>() {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean visit(IStructureElement<? super T> element, World world, int x, int y, int z, int a, int b,
|
public boolean visit(IStructureElement<T> element, World world, int x, int y, int z, int a, int b, int c) {
|
||||||
int c) {
|
|
||||||
return walker.visit(element, world, x, y, z, a, b, c);
|
return walker.visit(element, world, x, y, z, a, b, c);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean blockNotLoaded(IStructureElement<? super T> element, World world, int x, int y, int z, int a,
|
public boolean blockNotLoaded(IStructureElement<T> element, World world, int x, int y, int z, int a, int b,
|
||||||
int b, int c) {
|
int c) {
|
||||||
return walker.visit(element, world, x, y, z, a, b, c);
|
return walker.visit(element, world, x, y, z, a, b, c);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
static <T> IStructureWalker<T> skipBlockUnloaded(IStructureWalker<T> walker) {
|
static <T> IStructureWalker<T> skipBlockUnloaded(IStructureWalker<T> walker) {
|
||||||
return new IStructureWalker<>() {
|
return new IStructureWalker<T>() {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean visit(IStructureElement<? super T> element, World world, int x, int y, int z, int a, int b,
|
public boolean visit(IStructureElement<T> element, World world, int x, int y, int z, int a, int b, int c) {
|
||||||
int c) {
|
|
||||||
return walker.visit(element, world, x, y, z, a, b, c);
|
return walker.visit(element, world, x, y, z, a, b, c);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean blockNotLoaded(IStructureElement<? super T> element, World world, int x, int y, int z, int a,
|
public boolean blockNotLoaded(IStructureElement<T> element, World world, int x, int y, int z, int a, int b,
|
||||||
int b, int c) {
|
int c) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -13,14 +13,14 @@ import net.minecraft.world.World;
|
|||||||
|
|
||||||
class LazyStructureElement<T> implements IStructureElementDeferred<T> {
|
class LazyStructureElement<T> implements IStructureElementDeferred<T> {
|
||||||
|
|
||||||
private Function<? super T, ? extends IStructureElement<? super T>> to;
|
private Function<T, IStructureElement<T>> to;
|
||||||
private IStructureElement<? super T> elem;
|
private IStructureElement<T> elem;
|
||||||
|
|
||||||
public LazyStructureElement(Function<? super T, ? extends IStructureElement<? super T>> to) {
|
public LazyStructureElement(Function<T, IStructureElement<T>> to) {
|
||||||
this.to = to;
|
this.to = to;
|
||||||
}
|
}
|
||||||
|
|
||||||
private IStructureElement<? super T> get(T t) {
|
private IStructureElement<T> get(T t) {
|
||||||
if (to != null) {
|
if (to != null) {
|
||||||
elem = to.apply(t);
|
elem = to.apply(t);
|
||||||
to = null;
|
to = null;
|
||||||
|
|||||||
@@ -16,17 +16,17 @@ import com.gtnewhorizon.structurelib.util.Vec3Impl;
|
|||||||
|
|
||||||
public class StructureDefinition<T> implements IStructureDefinition<T> {
|
public class StructureDefinition<T> implements IStructureDefinition<T> {
|
||||||
|
|
||||||
private final Map<Character, IStructureElement<? super T>> elements;
|
private final Map<Character, IStructureElement<T>> elements;
|
||||||
private final Map<String, String> shapes;
|
private final Map<String, String> shapes;
|
||||||
private final Map<String, IStructureElement<? super T>[]> structures;
|
private final Map<String, IStructureElement<T>[]> structures;
|
||||||
private final Map<String, Set<Vec3Impl>> occupiedSpaces;
|
private final Map<String, Set<Vec3Impl>> occupiedSpaces;
|
||||||
|
|
||||||
public static <B> Builder<B> builder() {
|
public static <B> Builder<B> builder() {
|
||||||
return new Builder<>();
|
return new Builder<>();
|
||||||
}
|
}
|
||||||
|
|
||||||
private StructureDefinition(Map<Character, IStructureElement<? super T>> elements, Map<String, String> shapes,
|
private StructureDefinition(Map<Character, IStructureElement<T>> elements, Map<String, String> shapes,
|
||||||
Map<String, IStructureElement<? super T>[]> structures, Map<String, Set<Vec3Impl>> occupiedSpaces) {
|
Map<String, IStructureElement<T>[]> structures, Map<String, Set<Vec3Impl>> occupiedSpaces) {
|
||||||
this.elements = elements;
|
this.elements = elements;
|
||||||
this.shapes = shapes;
|
this.shapes = shapes;
|
||||||
this.structures = structures;
|
this.structures = structures;
|
||||||
@@ -40,7 +40,7 @@ public class StructureDefinition<T> implements IStructureDefinition<T> {
|
|||||||
private static final char C = '\uC000';
|
private static final char C = '\uC000';
|
||||||
private char d = '\uD000';
|
private char d = '\uD000';
|
||||||
private final Map<Vec3Impl, Character> navigates;
|
private final Map<Vec3Impl, Character> navigates;
|
||||||
private final Map<Character, IStructureElement<? super T>> elements;
|
private final Map<Character, IStructureElement<T>> elements;
|
||||||
private final Map<String, String> shapes;
|
private final Map<String, String> shapes;
|
||||||
private final Map<String, Set<Vec3Impl>> occupiedSpaces;
|
private final Map<String, Set<Vec3Impl>> occupiedSpaces;
|
||||||
|
|
||||||
@@ -51,7 +51,7 @@ public class StructureDefinition<T> implements IStructureDefinition<T> {
|
|||||||
occupiedSpaces = new HashMap<>();
|
occupiedSpaces = new HashMap<>();
|
||||||
}
|
}
|
||||||
|
|
||||||
public Map<Character, IStructureElement<? super T>> getElements() {
|
public Map<Character, IStructureElement<T>> getElements() {
|
||||||
return elements;
|
return elements;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -181,18 +181,18 @@ public class StructureDefinition<T> implements IStructureDefinition<T> {
|
|||||||
* @deprecated use the unboxed version
|
* @deprecated use the unboxed version
|
||||||
*/
|
*/
|
||||||
@Deprecated
|
@Deprecated
|
||||||
public Builder<T> addElement(Character name, IStructureElement<? super T> structurePiece) {
|
public Builder<T> addElement(Character name, IStructureElement<T> structurePiece) {
|
||||||
elements.putIfAbsent(name, structurePiece);
|
elements.putIfAbsent(name, structurePiece);
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Builder<T> addElement(char name, IStructureElement<? super T> structurePiece) {
|
public Builder<T> addElement(char name, IStructureElement<T> structurePiece) {
|
||||||
elements.putIfAbsent(name, structurePiece);
|
elements.putIfAbsent(name, structurePiece);
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public IStructureDefinition<T> build() {
|
public IStructureDefinition<T> build() {
|
||||||
Map<String, IStructureElement<? super T>[]> structures = compileStructureMap();
|
Map<String, IStructureElement<T>[]> structures = compileStructureMap();
|
||||||
return new StructureDefinition<>(
|
return new StructureDefinition<>(
|
||||||
new HashMap<>(elements),
|
new HashMap<>(elements),
|
||||||
new HashMap<>(shapes),
|
new HashMap<>(shapes),
|
||||||
@@ -201,7 +201,7 @@ public class StructureDefinition<T> implements IStructureDefinition<T> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
private Map<String, IStructureElement<? super T>[]> compileElementSetMap() {
|
private Map<String, IStructureElement<T>[]> compileElementSetMap() {
|
||||||
Set<Integer> missing = findMissing();
|
Set<Integer> missing = findMissing();
|
||||||
if (missing.isEmpty()) {
|
if (missing.isEmpty()) {
|
||||||
return shapes.entrySet().stream().collect(
|
return shapes.entrySet().stream().collect(
|
||||||
@@ -217,7 +217,7 @@ public class StructureDefinition<T> implements IStructureDefinition<T> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
private Map<String, IStructureElement<? super T>[]> compileStructureMap() {
|
private Map<String, IStructureElement<T>[]> compileStructureMap() {
|
||||||
Set<Integer> missing = findMissing();
|
Set<Integer> missing = findMissing();
|
||||||
if (missing.isEmpty()) {
|
if (missing.isEmpty()) {
|
||||||
return shapes.entrySet().stream().collect(
|
return shapes.entrySet().stream().collect(
|
||||||
@@ -238,7 +238,7 @@ public class StructureDefinition<T> implements IStructureDefinition<T> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public Map<Character, IStructureElement<? super T>> getElements() {
|
public Map<Character, IStructureElement<T>> getElements() {
|
||||||
return elements;
|
return elements;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -246,13 +246,13 @@ public class StructureDefinition<T> implements IStructureDefinition<T> {
|
|||||||
return shapes;
|
return shapes;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Map<String, IStructureElement<? super T>[]> getStructures() {
|
public Map<String, IStructureElement<T>[]> getStructures() {
|
||||||
return structures;
|
return structures;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public IStructureElement<? super T>[] getStructureFor(String name) {
|
public IStructureElement<T>[] getStructureFor(String name) {
|
||||||
IStructureElement<? super T>[] elements = structures.get(name);
|
IStructureElement<T>[] elements = structures.get(name);
|
||||||
if (elements == null) throw new NoSuchElementException(name);
|
if (elements == null) throw new NoSuchElementException(name);
|
||||||
return elements;
|
return elements;
|
||||||
}
|
}
|
||||||
|
|||||||
File diff suppressed because it is too large.
Load diff
+1
-1
@@ -37,7 +37,7 @@ class SurvivalBuildStructureWalker<T> implements IStructureWalker<T> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean visit(IStructureElement<? super T> element, World world, int x, int y, int z, int a, int b, int c) {
|
public boolean visit(IStructureElement<T> element, World world, int x, int y, int z, int a, int b, int c) {
|
||||||
env.offsetABC[0] = a;
|
env.offsetABC[0] = a;
|
||||||
env.offsetABC[1] = b;
|
env.offsetABC[1] = b;
|
||||||
env.offsetABC[2] = c;
|
env.offsetABC[2] = c;
|
||||||
|
|||||||
@@ -0,0 +1,64 @@
|
|||||||
|
package com.gtnewhorizon.structurelib.util;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.Iterator;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.NavigableMap;
|
||||||
|
import java.util.Spliterator;
|
||||||
|
import java.util.TreeMap;
|
||||||
|
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();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -44,7 +44,11 @@ item.structurelib.constructableTrigger.gui.info=Many multiblocks support some de
|
|||||||
channels.structurelib.show_errors=When set, will highlight parts of the multiblock that is definitely wrong
|
channels.structurelib.show_errors=When set, will highlight parts of the multiblock that is definitely wrong
|
||||||
|
|
||||||
structurelib.tooltip.channelvalue=§7Value §b%d§r§7 for subchannel §b%s
|
structurelib.tooltip.channelvalue=§7Value §b%d§r§7 for subchannel §b%s
|
||||||
structurelib.tooltip.indicator_dnd=Drop into GUI to select this value for this channel
|
structurelib.tooltip.indicator_dnd=Drop into GUI or middle-click to select this value for this channel
|
||||||
|
structurelib.pickchannel.success=§aSet §b%s§r§a to §b%d§r
|
||||||
|
structurelib.pickchannel.cleared=§aAll channels cleared§r
|
||||||
|
structurelib.pickchannel.confirmwipe=§6Sneak+Middle-click again to clear all channels
|
||||||
|
structurelib.pickchannel.nomapping=§cNo channel mapping found for this item
|
||||||
|
|
||||||
structurelib.autoplace.built_stat=Built %s blocks
|
structurelib.autoplace.built_stat=Built %s blocks
|
||||||
structurelib.autoplace.complete=§aThis multiblock has completed!
|
structurelib.autoplace.complete=§aThis multiblock has completed!
|
||||||
|
|||||||
Reference in new issue
Block a user