Skip to content

Commit

Permalink
Added queue module. Closes #55.
Browse files Browse the repository at this point in the history
  • Loading branch information
fnuecke committed Mar 14, 2017
1 parent bd341fc commit 4dbe940
Show file tree
Hide file tree
Showing 15 changed files with 343 additions and 30 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,8 @@ public final class SerialProtocolDocumentationReference {
/**
* The name of the reference, i.e. the text of the link in the manual.
* <p>
* Note that this is run through the {@link net.minecraft.util.text.translation.I18n},
* so it is generally a good idea to use localized values here.
* Note that this is run through {@link net.minecraft.client.resources.I18n},
* so it is generally a good idea to use localizable values here.
*/
public final String name;

Expand Down
2 changes: 2 additions & 0 deletions src/main/java/li/cil/tis3d/client/renderer/TextureLoader.java
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ public final class TextureLoader {
public static final ResourceLocation LOCATION_MODULE_EXECUTION_OVERLAY_WAITING = new ResourceLocation(API.MOD_ID, "blocks/overlay/module_execution_waiting");
public static final ResourceLocation LOCATION_MODULE_INFRARED_OVERLAY = new ResourceLocation(API.MOD_ID, "blocks/overlay/module_infrared");
public static final ResourceLocation LOCATION_MODULE_KEYPAD_OVERLAY = new ResourceLocation(API.MOD_ID, "blocks/overlay/module_keypad");
public static final ResourceLocation LOCATION_MODULE_QUEUE_OVERLAY = new ResourceLocation(API.MOD_ID, "blocks/overlay/module_queue");
public static final ResourceLocation LOCATION_MODULE_RANDOM_OVERLAY = new ResourceLocation(API.MOD_ID, "blocks/overlay/module_random");
public static final ResourceLocation LOCATION_MODULE_REDSTONE_OVERLAY = new ResourceLocation(API.MOD_ID, "blocks/overlay/module_redstone");
public static final ResourceLocation LOCATION_MODULE_REDSTONE_BARS_OVERLAY = new ResourceLocation(API.MOD_ID, "blocks/overlay/module_redstone_bars");
Expand All @@ -53,6 +54,7 @@ public void onTextureStitchPre(final TextureStitchEvent.Pre event) {
map.registerSprite(LOCATION_MODULE_EXECUTION_OVERLAY_WAITING);
map.registerSprite(LOCATION_MODULE_INFRARED_OVERLAY);
map.registerSprite(LOCATION_MODULE_KEYPAD_OVERLAY);
map.registerSprite(LOCATION_MODULE_QUEUE_OVERLAY);
map.registerSprite(LOCATION_MODULE_RANDOM_OVERLAY);
map.registerSprite(LOCATION_MODULE_REDSTONE_OVERLAY);
map.registerSprite(LOCATION_MODULE_REDSTONE_BARS_OVERLAY);
Expand Down
2 changes: 2 additions & 0 deletions src/main/java/li/cil/tis3d/common/Constants.java
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ public final class Constants {
public static final String NAME_ITEM_MODULE_EXECUTION = "module_execution";
public static final String NAME_ITEM_MODULE_INFRARED = "module_infrared";
public static final String NAME_ITEM_MODULE_KEYPAD = "module_keypad";
public static final String NAME_ITEM_MODULE_QUEUE = "module_queue";
public static final String NAME_ITEM_MODULE_RANDOM = "module_random";
public static final String NAME_ITEM_MODULE_RANDOM_ACCESS_MEMORY = "module_random_access_memory";
public static final String NAME_ITEM_MODULE_READ_ONLY_MEMORY = "module_read_only_memory";
Expand All @@ -48,6 +49,7 @@ public final class Constants {
NAME_ITEM_MODULE_EXECUTION,
NAME_ITEM_MODULE_INFRARED,
NAME_ITEM_MODULE_KEYPAD,
NAME_ITEM_MODULE_QUEUE,
NAME_ITEM_MODULE_RANDOM,
NAME_ITEM_MODULE_RANDOM_ACCESS_MEMORY,
NAME_ITEM_MODULE_READ_ONLY_MEMORY,
Expand Down
22 changes: 3 additions & 19 deletions src/main/java/li/cil/tis3d/common/ProxyCommon.java
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,7 @@
import li.cil.tis3d.api.ModuleAPI;
import li.cil.tis3d.api.prefab.manual.ResourceContentProvider;
import li.cil.tis3d.client.manual.provider.GameRegistryPathProvider;
import li.cil.tis3d.common.api.CreativeTab;
import li.cil.tis3d.common.api.FontRendererAPIImpl;
import li.cil.tis3d.common.api.InfraredAPIImpl;
import li.cil.tis3d.common.api.ManualAPIImpl;
import li.cil.tis3d.common.api.ModuleAPIImpl;
import li.cil.tis3d.common.api.SerialAPIImpl;
import li.cil.tis3d.common.api.*;
import li.cil.tis3d.common.capabilities.CapabilityInfraredReceiver;
import li.cil.tis3d.common.entity.EntityInfraredPacket;
import li.cil.tis3d.common.event.TickHandlerInfraredPacket;
Expand All @@ -19,19 +14,7 @@
import li.cil.tis3d.common.integration.Integration;
import li.cil.tis3d.common.integration.redstone.RedstoneIntegration;
import li.cil.tis3d.common.item.ItemModule;
import li.cil.tis3d.common.module.ModuleAudio;
import li.cil.tis3d.common.module.ModuleBundledRedstone;
import li.cil.tis3d.common.module.ModuleDisplay;
import li.cil.tis3d.common.module.ModuleExecution;
import li.cil.tis3d.common.module.ModuleInfrared;
import li.cil.tis3d.common.module.ModuleKeypad;
import li.cil.tis3d.common.module.ModuleRandom;
import li.cil.tis3d.common.module.ModuleRandomAccessMemory;
import li.cil.tis3d.common.module.ModuleReadOnlyMemory;
import li.cil.tis3d.common.module.ModuleRedstone;
import li.cil.tis3d.common.module.ModuleSerialPort;
import li.cil.tis3d.common.module.ModuleStack;
import li.cil.tis3d.common.module.ModuleTerminal;
import li.cil.tis3d.common.module.*;
import li.cil.tis3d.common.network.Network;
import li.cil.tis3d.common.provider.SimpleModuleProvider;
import net.minecraft.block.Block;
Expand Down Expand Up @@ -109,6 +92,7 @@ public void onInit(final FMLInitializationEvent event) {
ModuleAPI.addProvider(new SimpleModuleProvider<>(Constants.NAME_ITEM_MODULE_EXECUTION, ModuleExecution::new));
ModuleAPI.addProvider(new SimpleModuleProvider<>(Constants.NAME_ITEM_MODULE_INFRARED, ModuleInfrared::new));
ModuleAPI.addProvider(new SimpleModuleProvider<>(Constants.NAME_ITEM_MODULE_KEYPAD, ModuleKeypad::new));
ModuleAPI.addProvider(new SimpleModuleProvider<>(Constants.NAME_ITEM_MODULE_QUEUE, ModuleQueue::new));
ModuleAPI.addProvider(new SimpleModuleProvider<>(Constants.NAME_ITEM_MODULE_RANDOM, ModuleRandom::new));
ModuleAPI.addProvider(new SimpleModuleProvider<>(Constants.NAME_ITEM_MODULE_RANDOM_ACCESS_MEMORY, ModuleRandomAccessMemory::new));
ModuleAPI.addProvider(new SimpleModuleProvider<>(Constants.NAME_ITEM_MODULE_READ_ONLY_MEMORY, ModuleReadOnlyMemory::new));
Expand Down
12 changes: 6 additions & 6 deletions src/main/java/li/cil/tis3d/common/block/BlockCasing.java
Original file line number Diff line number Diff line change
Expand Up @@ -37,12 +37,12 @@
* Block for the module casings.
*/
public final class BlockCasing extends Block {
public static final PropertyBool MODULE_X_NEG = PropertyBool.create("xneg");
public static final PropertyBool MODULE_X_POS = PropertyBool.create("xpos");
public static final PropertyBool MODULE_Y_NEG = PropertyBool.create("yneg");
public static final PropertyBool MODULE_Y_POS = PropertyBool.create("ypos");
public static final PropertyBool MODULE_Z_NEG = PropertyBool.create("zneg");
public static final PropertyBool MODULE_Z_POS = PropertyBool.create("zpos");
private static final PropertyBool MODULE_X_NEG = PropertyBool.create("xneg");
private static final PropertyBool MODULE_X_POS = PropertyBool.create("xpos");
private static final PropertyBool MODULE_Y_NEG = PropertyBool.create("yneg");
private static final PropertyBool MODULE_Y_POS = PropertyBool.create("ypos");
private static final PropertyBool MODULE_Z_NEG = PropertyBool.create("zneg");
private static final PropertyBool MODULE_Z_POS = PropertyBool.create("zpos");

// --------------------------------------------------------------------- //

Expand Down
7 changes: 7 additions & 0 deletions src/main/java/li/cil/tis3d/common/init/Items.java
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,14 @@
import li.cil.tis3d.common.item.ItemModule;
import net.minecraft.item.Item;
import net.minecraft.item.ItemStack;
import net.minecraft.item.crafting.ShapelessRecipes;
import net.minecraft.util.ResourceLocation;
import net.minecraftforge.fml.common.registry.ForgeRegistries;
import net.minecraftforge.fml.common.registry.GameRegistry;
import net.minecraftforge.oredict.ShapedOreRecipe;
import net.minecraftforge.oredict.ShapelessOreRecipe;

import java.util.Collections;
import java.util.HashMap;
import java.util.Map;

Expand Down Expand Up @@ -103,6 +105,11 @@ public static void addRecipes() {
'R', "dustRedstone",
'Q', "gemQuartz"));

GameRegistry.addRecipe(new ShapelessRecipes(new ItemStack(modules.get(Constants.NAME_ITEM_MODULE_QUEUE)),
Collections.singletonList(new ItemStack(modules.get(Constants.NAME_ITEM_MODULE_STACK)))));
GameRegistry.addRecipe(new ShapelessRecipes(new ItemStack(modules.get(Constants.NAME_ITEM_MODULE_STACK)),
Collections.singletonList(new ItemStack(modules.get(Constants.NAME_ITEM_MODULE_QUEUE)))));

GameRegistry.addRecipe(new ShapedOreRecipe(new ItemStack(key, 1),
"GI ",
"GI ",
Expand Down
266 changes: 266 additions & 0 deletions src/main/java/li/cil/tis3d/common/module/ModuleQueue.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,266 @@
package li.cil.tis3d.common.module;

import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import li.cil.tis3d.api.FontRendererAPI;
import li.cil.tis3d.api.machine.Casing;
import li.cil.tis3d.api.machine.Face;
import li.cil.tis3d.api.machine.Pipe;
import li.cil.tis3d.api.machine.Port;
import li.cil.tis3d.api.prefab.module.AbstractModuleRotatable;
import li.cil.tis3d.api.util.RenderUtil;
import li.cil.tis3d.client.renderer.TextureLoader;
import net.minecraft.client.Minecraft;
import net.minecraft.client.renderer.GlStateManager;
import net.minecraft.nbt.NBTTagCompound;
import net.minecraft.util.math.MathHelper;
import net.minecraftforge.fml.relauncher.Side;
import net.minecraftforge.fml.relauncher.SideOnly;

/**
* The queue module can be used to store a number of values to be retrieved
* later on. It operates as FIFO queue, providing the bottom element to all
* ports but a single value can only be read from one port.
* <p>
* While it is not full, it will receive data on all ports and push them back.
*/
public final class ModuleQueue extends AbstractModuleRotatable {
// --------------------------------------------------------------------- //
// Persisted data

private final short[] queue = new short[QUEUE_SIZE];
private int head = 0; // Highest element index, exclusive.
private int tail = 0; // Lowest element index, inclusive.

// --------------------------------------------------------------------- //
// Computed data

// NBT data names.
private static final String TAG_QUEUE = "queue";
private static final String TAG_HEAD = "head";
private static final String TAG_TAIL = "tail";

// Data packet types.
private static final byte DATA_TYPE_UPDATE = 0;

/**
* The number of elements the queue may store, plus one never used slot
* to allow easily differentiating empty and full queue states.
*/
private static final int QUEUE_SIZE = 17;

// --------------------------------------------------------------------- //

public ModuleQueue(final Casing casing, final Face face) {
super(casing, face);
}

// --------------------------------------------------------------------- //
// Module

@Override
public void step() {
stepOutput();
stepInput();
}

@Override
public void onDisabled() {
// Clear queue on shutdown.
head = tail = 0;

sendData();
}

@Override
public void onWriteComplete(final Port port) {
// Pop the bottom value (the one that was being written).
pop();

// If one completes, cancel all other writes to ensure a value is only
// written once.
cancelWrite();

// Start writing again right away to write as fast as possible.
stepOutput();
}

@Override
public void onData(final ByteBuf data) {
head = data.readByte();
tail = data.readByte();
for (int i = 0; i < queue.length; i++) {
queue[i] = data.readShort();
}
}

@SideOnly(Side.CLIENT)
@Override
public void render(final boolean enabled, final float partialTicks) {
if (!enabled) {
return;
}

rotateForRendering();
RenderUtil.ignoreLighting();

RenderUtil.drawQuad(RenderUtil.getSprite(TextureLoader.LOCATION_MODULE_QUEUE_OVERLAY));

// Render detailed state when player is close.
if (!isEmpty() && Minecraft.getMinecraft().player.getDistanceSqToCenter(getCasing().getPosition()) < 64) {
drawState();
}
}

@Override
public void readFromNBT(final NBTTagCompound nbt) {
super.readFromNBT(nbt);

final int[] queueNbt = nbt.getIntArray(TAG_QUEUE);
final int count = Math.min(queueNbt.length, queue.length);
for (int i = 0; i < count; i++) {
queue[i] = (short) queueNbt[i];
}

head = MathHelper.clamp(nbt.getInteger(TAG_HEAD), 0, QUEUE_SIZE - 1);
tail = MathHelper.clamp(nbt.getInteger(TAG_TAIL), 0, QUEUE_SIZE - 1);
}

@Override
public void writeToNBT(final NBTTagCompound nbt) {
super.writeToNBT(nbt);

final int[] queueNbt = new int[queue.length];
for (int i = 0; i < queue.length; i++) {
queueNbt[i] = queue[i];
}
nbt.setIntArray(TAG_QUEUE, queueNbt);

nbt.setInteger(TAG_HEAD, head);
nbt.setInteger(TAG_TAIL, tail);
}

// --------------------------------------------------------------------- //

/**
* Check whether the queue is currently empty, i.e. no more items can be retrieved.
*
* @return <tt>true</tt> if the queue is empty, <tt>false</tt> otherwise.
*/
private boolean isEmpty() {
return head == tail;
}

/**
* Check whether the queue is currently full, i.e. no more items can be stored.
*
* @return <tt>true</tt> if the queue is full, <tt>false</tt> otherwise.
*/
private boolean isFull() {
return (head + 1) % QUEUE_SIZE == tail;
}

/**
* Store the specified item on the queue.
*
* @param value the value to store on the queue.
* @throws ArrayIndexOutOfBoundsException if the queue is full.
*/
private void push(final short value) {
queue[head] = value;
head = (head + 1) % QUEUE_SIZE;

sendData();
}

/**
* Retrieve the value that's currently on top of the queue, i.e. the value
* that was last pushed to the queue.
*
* @return the value on top of the queue.
* @throws ArrayIndexOutOfBoundsException if the queue is empty.
*/
private short peek() {
return queue[tail];
}

/**
* Reduces the queue size by one.
*/
private void pop() {
tail = (tail + 1) % QUEUE_SIZE;

sendData();
}

/**
* Update the outputs of the queue, pushing the top value.
*/
private void stepOutput() {
// Don't try to write if the queue is empty.
if (isEmpty()) {
return;
}

for (final Port port : Port.VALUES) {
final Pipe sendingPipe = getCasing().getSendingPipe(getFace(), port);
if (!sendingPipe.isWriting()) {
sendingPipe.beginWrite(peek());
}
}
}

/**
* Update the inputs of the queue, pulling values onto the queue.
*/
private void stepInput() {
for (final Port port : Port.VALUES) {
// Stop reading if the queue is full.
if (isFull()) {
return;
}

// Continuously read from all ports, push back last received value.
final Pipe receivingPipe = getCasing().getReceivingPipe(getFace(), port);
if (!receivingPipe.isReading()) {
receivingPipe.beginRead();
}
if (receivingPipe.canTransfer()) {
// Store the value.
push(receivingPipe.read());

// Start reading again right away to read as fast as possible.
if (!isFull()) {
receivingPipe.beginRead();
}
}
}
}

private void sendData() {
final ByteBuf data = Unpooled.buffer();
data.writeByte(head);
data.writeByte(tail);
for (final short value : queue) {
data.writeShort(value);
}
getCasing().sendData(getFace(), data, DATA_TYPE_UPDATE);
}

@SideOnly(Side.CLIENT)
private void drawState() {
// Offset to start drawing at top left of inner area, slightly inset.
GlStateManager.translate(3 / 16f, 5 / 16f, 0);
GlStateManager.scale(1 / 128f, 1 / 128f, 1);
GlStateManager.translate(4.5f, 14.5f, 0);
GlStateManager.color(1f, 1f, 1f, 1f);

for (int i = tail, j = 0; i != head; i = (i + 1) % QUEUE_SIZE, j++) {
FontRendererAPI.drawString(String.format("%4X", queue[i]));
GlStateManager.translate(0, FontRendererAPI.getCharHeight() + 1, 0);
if ((j + 1) % 4 == 0) {
GlStateManager.translate((FontRendererAPI.getCharWidth() + 1) * 5, (FontRendererAPI.getCharHeight() + 1) * -4, 0);
}
}
}
}
Loading

0 comments on commit 4dbe940

Please sign in to comment.