Made network cards configurable as to what faces they connect to. Closes #53.

This commit is contained in:
Florian Nücke
2022-01-10 18:19:57 +01:00
parent 44b2bfdc1d
commit 60871d30aa
15 changed files with 504 additions and 4 deletions

View File

@@ -0,0 +1,294 @@
package li.cil.oc2.client.gui;
import com.mojang.blaze3d.platform.GlStateManager;
import com.mojang.blaze3d.systems.RenderSystem;
import com.mojang.blaze3d.vertex.PoseStack;
import com.mojang.blaze3d.vertex.VertexConsumer;
import com.mojang.math.Quaternion;
import com.mojang.math.Vector3f;
import li.cil.oc2.client.gui.widget.Texture;
import li.cil.oc2.client.renderer.ModRenderType;
import li.cil.oc2.common.item.Items;
import li.cil.oc2.common.item.NetworkInterfaceCardItem;
import li.cil.oc2.common.network.Network;
import li.cil.oc2.common.network.message.NetworkInterfaceCardConfigurationMessage;
import net.minecraft.client.Minecraft;
import net.minecraft.client.gui.screens.Screen;
import net.minecraft.client.renderer.MultiBufferSource;
import net.minecraft.client.renderer.block.model.ItemTransforms;
import net.minecraft.client.renderer.entity.ItemRenderer;
import net.minecraft.client.renderer.texture.OverlayTexture;
import net.minecraft.client.resources.model.BakedModel;
import net.minecraft.client.resources.sounds.SimpleSoundInstance;
import net.minecraft.core.Direction;
import net.minecraft.network.chat.Component;
import net.minecraft.network.chat.TranslatableComponent;
import net.minecraft.sounds.SoundEvents;
import net.minecraft.util.Mth;
import net.minecraft.world.InteractionHand;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.inventory.InventoryMenu;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.phys.AABB;
import net.minecraft.world.phys.Vec3;
import javax.annotation.Nullable;
import static li.cil.oc2.common.util.TranslationUtils.key;
import static li.cil.oc2.common.util.TranslationUtils.text;
public final class NetworkInterfaceCardScreen extends Screen {
private static final String SIDE_STATE_TEXT = key("gui.{mod}.network_interface_card.side_state");
private static final Component CONNECTIVITY_ENABLED_TEXT = text("gui.{mod}.network_interface_card.connectivity.enabled");
private static final Component CONNECTIVITY_DISABLED_TEXT = text("gui.{mod}.network_interface_card.connectivity.disabled");
private static final Component INFO_TEXT = text("gui.{mod}.network_interface_card.info");
public static final int UI_WIDTH = Sprites.NETWORK_INTERFACE_CARD_SCREEN.width;
public static final int UI_HEIGHT = Sprites.NETWORK_INTERFACE_CARD_SCREEN.height;
public static final int BLOCK_LEFT = UI_WIDTH / 2;
public static final int BLOCK_TOP = 53;
public static final int INFO_TEXT_LEFT = 8;
public static final int INFO_TEXT_TOP = 104;
public static final int INFO_TEXT_WIDTH = UI_WIDTH - 16;
public static final int MAX_BLOCK_PITCH = 30;
///////////////////////////////////////////////////////////////////
private final Player player;
private final InteractionHand hand;
private final ComputerBlockItemRenderer computerBlockItemRenderer = new ComputerBlockItemRenderer();
private Vector3f blockRotation = new Vector3f(-30, 45, 0);
private int left, top;
@Nullable private Direction focusedSide;
private boolean isDraggingBlock, hasDraggedBlock;
private double dragStartX, dragStartY;
///////////////////////////////////////////////////////////////////
public NetworkInterfaceCardScreen(final Player player, final InteractionHand hand) {
super(Items.NETWORK_INTERFACE_CARD.get().getDescription());
this.player = player;
this.hand = hand;
}
///////////////////////////////////////////////////////////////////
@Override
protected void init() {
super.init();
left = (width - UI_WIDTH) / 2;
top = (height - UI_HEIGHT) / 2;
}
@Override
public void tick() {
super.tick();
final ItemStack heldItem = player.getItemInHand(hand);
if (!heldItem.is(Items.NETWORK_INTERFACE_CARD.get())) {
onClose();
}
}
@Override
public boolean mouseClicked(final double mouseX, final double mouseY, final int button) {
final boolean result = super.mouseClicked(mouseX, mouseY, button);
if (!result && isMouseInBlockArea(mouseX, mouseY) && button == 0) {
isDraggingBlock = true;
hasDraggedBlock = false;
dragStartX = mouseX;
dragStartY = mouseY;
}
return result;
}
@Override
public boolean mouseReleased(final double mouseX, final double mouseY, final int button) {
if (isDraggingBlock && button == 0) {
isDraggingBlock = false;
if (!hasDraggedBlock && focusedSide != null) {
final NetworkInterfaceCardConfigurationMessage message = new NetworkInterfaceCardConfigurationMessage(hand, focusedSide, !getConfiguration(focusedSide));
Network.INSTANCE.sendToServer(message);
Minecraft.getInstance().getSoundManager()
.play(SimpleSoundInstance.forUI(SoundEvents.UI_BUTTON_CLICK, 1));
}
}
return super.mouseReleased(mouseX, mouseY, button);
}
@Override
public boolean mouseDragged(final double mouseX, final double mouseY, final int activeButton, final double deltaX, final double deltaY) {
if (isDraggingBlock && activeButton == 0) {
if (!hasDraggedBlock) {
final double dx = mouseX - dragStartX;
final double dy = mouseY - dragStartY;
final double delta = Math.sqrt(dx * dx + dy * dy);
hasDraggedBlock = delta > 3;
}
if (hasDraggedBlock) {
blockRotation = new Vector3f(
Mth.clamp(blockRotation.x() - (float) deltaY, -MAX_BLOCK_PITCH, MAX_BLOCK_PITCH),
Mth.wrapDegrees(blockRotation.y() + (float) deltaX),
blockRotation.z()
);
}
}
return true;
}
@Override
public void render(final PoseStack stack, final int mouseX, final int mouseY, final float partialTicks) {
renderBackground(stack);
Sprites.NETWORK_INTERFACE_CARD_SCREEN.draw(stack, left, top);
super.render(stack, mouseX, mouseY, partialTicks);
final int blockX = left + BLOCK_LEFT;
final int blockY = top + BLOCK_TOP;
focusedSide = computerBlockItemRenderer.getFocusedSide(blockX - mouseX, blockY - mouseY, blockRotation);
computerBlockItemRenderer.render(blockX, blockY, blockRotation);
if (focusedSide != null) {
final Component enabledComponent = getConfiguration(focusedSide) ? CONNECTIVITY_ENABLED_TEXT : CONNECTIVITY_DISABLED_TEXT;
final TranslatableComponent tooltip = new TranslatableComponent(SIDE_STATE_TEXT, enabledComponent);
renderTooltip(stack, tooltip, mouseX, mouseY);
}
font.drawWordWrap(INFO_TEXT, left + INFO_TEXT_LEFT, top + INFO_TEXT_TOP, INFO_TEXT_WIDTH, 0xAAAAAA);
}
@Override
public boolean isPauseScreen() {
return false;
}
///////////////////////////////////////////////////////////////////
private boolean isMouseInBlockArea(final double mouseX, final double mouseY) {
return mouseX >= left + 37 && mouseX <= left + (37 + 102) &&
mouseY >= top + 10 && mouseY <= top + (10 + 102);
}
private boolean getConfiguration(@Nullable final Direction side) {
return side != null && NetworkInterfaceCardItem.getSideConfiguration(player.getItemInHand(hand), side);
}
///////////////////////////////////////////////////////////////////
private final class ComputerBlockItemRenderer {
public static final int BLOCK_RENDER_SIZE = 48;
private final ItemStack computerItemStack = new ItemStack(Items.COMPUTER.get());
private final ItemRenderer itemRenderer = Minecraft.getInstance().getItemRenderer();
private final BakedModel model = itemRenderer.getModel(computerItemStack, null, null, 0);
@Nullable
private Direction getFocusedSide(final float mouseX, final float mouseY, final Vector3f rotation) {
// Rotate ray inversely around block to represent visual block rotation.
final Quaternion quaternion = Quaternion.fromXYZDegrees(rotation);
quaternion.conj();
// Move ray in screen space to mouse position.
final float relMouseX = -mouseX / (float) BLOCK_RENDER_SIZE;
final float relMouseY = -mouseY / (float) BLOCK_RENDER_SIZE;
final Vector3f source = new Vector3f();
source.add(relMouseX, relMouseY, 1);
source.transform(quaternion);
final Vector3f target = new Vector3f();
target.add(relMouseX, relMouseY, -1);
target.transform(quaternion);
// Intersect rotated ray with bounding box representing block.
final AABB aabb = new AABB(-0.5, -0.5, -0.5, 0.5, 0.5, 0.5);
return aabb.clip(new Vec3(source), new Vec3(target))
.map(hit -> Direction.getNearest(hit.x, -hit.y(), hit.z()))
.filter(side -> side != Direction.SOUTH)
.orElse(null);
}
public void render(final int x, final int y, final Vector3f rotation) {
RenderSystem.setShaderTexture(0, InventoryMenu.BLOCK_ATLAS);
RenderSystem.enableBlend();
RenderSystem.blendFunc(GlStateManager.SourceFactor.SRC_ALPHA, GlStateManager.DestFactor.ONE_MINUS_SRC_ALPHA);
RenderSystem.setShaderColor(1, 1, 1, 1);
final Vector3f renderRotation = rotation.copy();
renderRotation.add(0, 180, 0);
final PoseStack stack = RenderSystem.getModelViewStack();
stack.pushPose();
stack.translate(x, y, 0);
stack.mulPose(Quaternion.fromXYZDegrees(renderRotation));
stack.scale(BLOCK_RENDER_SIZE, -BLOCK_RENDER_SIZE, BLOCK_RENDER_SIZE);
RenderSystem.applyModelViewMatrix();
final MultiBufferSource.BufferSource bufferSource = Minecraft.getInstance().renderBuffers().bufferSource();
renderBlock(bufferSource);
renderOverlays(stack, bufferSource);
bufferSource.endBatch();
stack.popPose();
RenderSystem.applyModelViewMatrix();
}
private void renderBlock(final MultiBufferSource.BufferSource bufferSource) {
itemRenderer.render(computerItemStack, ItemTransforms.TransformType.NONE, false, new PoseStack(), bufferSource, 0xF000F0, OverlayTexture.NO_OVERLAY, model);
}
private void renderOverlays(final PoseStack poseStack, final MultiBufferSource.BufferSource bufferSource) {
for (final Direction side : Direction.values()) {
// South face of computers is the front face (screen) and there's no connectivity allowed there.
if (side == Direction.SOUTH) {
continue;
}
poseStack.pushPose();
poseStack.setIdentity();
poseStack.translate(-side.getStepX() * 0.51, side.getStepY() * 0.51, -side.getStepZ() * 0.51);
final Vector3f sideRotation = switch (side) {
case DOWN -> new Vector3f(-90, 0, 0);
case UP -> new Vector3f(90, 0, 0);
case NORTH -> new Vector3f(0, 180, 0);
case WEST -> new Vector3f(0, -90, 0);
case EAST -> new Vector3f(0, 90, 0);
default -> throw new IllegalStateException("Unexpected value: " + side);
};
poseStack.mulPose(Quaternion.fromXYZDegrees(sideRotation));
poseStack.translate(-0.5, -0.5, 0);
if (getConfiguration(side)) {
renderOverlay(poseStack, bufferSource, Textures.BLOCK_FACE_ENABLED_TEXTURE);
} else {
renderOverlay(poseStack, bufferSource, Textures.BLOCK_FACE_DISABLED_TEXTURE);
}
if (side == focusedSide) {
renderOverlay(poseStack, bufferSource, Textures.BLOCK_FACE_FOCUSED_TEXTURE);
}
poseStack.popPose();
}
}
private void renderOverlay(final PoseStack poseStack, final MultiBufferSource.BufferSource bufferSource, final Texture texture) {
final VertexConsumer buffer = bufferSource.getBuffer(ModRenderType.getOverlay(texture.location));
buffer.vertex(poseStack.last().pose(), 0, 0, 0).uv(0, 0).endVertex();
buffer.vertex(poseStack.last().pose(), 0, 1, 0).uv(0, 1).endVertex();
buffer.vertex(poseStack.last().pose(), 1, 1, 0).uv(1, 1).endVertex();
buffer.vertex(poseStack.last().pose(), 1, 0, 0).uv(1, 0).endVertex();
}
}
}

View File

@@ -9,6 +9,7 @@ public final class Sprites {
public static final Sprite ROBOT_CONTAINER = new Sprite(ROBOT_CONTAINER_TEXTURE);
public static final Sprite TERMINAL_SCREEN = new Sprite(TERMINAL_SCREEN_TEXTURE);
public static final Sprite BUS_INTERFACE_SCREEN = new Sprite(BUS_INTERFACE_SCREEN_TEXTURE);
public static final Sprite NETWORK_INTERFACE_CARD_SCREEN = new Sprite(NETWORK_INTERFACE_CARD_SCREEN_TEXTURE);
public static final Sprite TERMINAL_FOCUSED = new Sprite(TERMINAL_FOCUSED_TEXTURE);
public static final Sprite SLOT_SELECTION = new Sprite(SLOT_SELECTION_TEXTURE, 18, 18, 0, 0);

View File

@@ -7,11 +7,15 @@ public final class Textures {
public static final Texture ROBOT_CONTAINER_TEXTURE = new Texture("textures/gui/widget/robot_container.png", 176, 197);
public static final Texture TERMINAL_SCREEN_TEXTURE = new Texture("textures/gui/widget/terminal_screen.png", 336, 208);
public static final Texture BUS_INTERFACE_SCREEN_TEXTURE = new Texture("textures/gui/widget/bus_interface_screen.png", 240, 30);
public static final Texture NETWORK_INTERFACE_CARD_SCREEN_TEXTURE = new Texture("textures/gui/widget/network_interface_card_screen.png", 176, 130);
public static final Texture TERMINAL_FOCUSED_TEXTURE = new Texture("textures/gui/overlay/terminal_focused.png", 336, 208);
public static final Texture SLOT_SELECTION_TEXTURE = new Texture("textures/gui/overlay/slot_selection.png", 18, 270);
public static final Texture INFO_ICON_TEXTURE = new Texture("textures/gui/overlay/slot_info.png", 28, 28);
public static final Texture WARN_ICON_TEXTURE = new Texture("textures/gui/overlay/slot_warn.png", 28, 28);
public static final Texture BLOCK_FACE_FOCUSED_TEXTURE = new Texture("textures/gui/overlay/block_face_focused.png", 16, 16);
public static final Texture BLOCK_FACE_ENABLED_TEXTURE = new Texture("textures/gui/overlay/block_face_enabled.png", 16, 16);
public static final Texture BLOCK_FACE_DISABLED_TEXTURE = new Texture("textures/gui/overlay/block_face_disabled.png", 16, 16);
public static final Texture HOTBAR_TEXTURE = new Texture("textures/gui/widget/hotbar.png", 224, 26);
public static final Texture SIDEBAR_2_TEXTURE = new Texture("textures/gui/widget/sidebar_2.png", 19, 34);

View File

@@ -47,6 +47,24 @@ public abstract class ModRenderType extends RenderType {
state);
}
public static RenderType getOverlay(final ResourceLocation location) {
final TextureStateShard texture = new TextureStateShard(location, false, true);
final RenderType.CompositeState state = RenderType.CompositeState.builder()
.setShaderState(RenderStateShard.POSITION_TEX_SHADER)
.setTextureState(texture)
.setOutputState(TRANSLUCENT_TARGET)
.setTransparencyState(ADDITIVE_TRANSPARENCY)
.createCompositeState(false);
return create(
API.MOD_ID + "/overlay",
DefaultVertexFormat.POSITION_TEX,
VertexFormat.Mode.QUADS,
256,
false,
true,
state);
}
///////////////////////////////////////////////////////////////////
private ModRenderType(final String name, final VertexFormat format, final VertexFormat.Mode drawMode, final int bufferSize, final boolean useDelegate, final boolean needsSorting, final Runnable setupTask, final Runnable clearTask) {

View File

@@ -12,6 +12,7 @@ import li.cil.oc2.common.bus.device.util.IdentityProxy;
import li.cil.oc2.common.bus.device.util.OptionalAddress;
import li.cil.oc2.common.bus.device.util.OptionalInterrupt;
import li.cil.oc2.common.capabilities.Capabilities;
import li.cil.oc2.common.item.NetworkInterfaceCardItem;
import li.cil.oc2.common.serialization.NBTSerialization;
import li.cil.oc2.common.util.NBTTagIds;
import li.cil.sedna.device.virtio.VirtIONetworkDevice;
@@ -51,7 +52,7 @@ public final class NetworkInterfaceCardItemDevice extends IdentityProxy<ItemStac
@Nonnull
@Override
public <T> LazyOptional<T> getCapability(final Capability<T> cap, @Nullable final Direction side) {
if (cap == Capabilities.NETWORK_INTERFACE && side != null) {
if (cap == Capabilities.NETWORK_INTERFACE && NetworkInterfaceCardItem.getSideConfiguration(identity, side)) {
return LazyOptional.of(() -> networkInterface).cast();
}

View File

@@ -63,7 +63,7 @@ public final class Items {
new FloppyItem(512 * Constants.KILOBYTE));
public static final RegistryObject<Item> REDSTONE_INTERFACE_CARD = register("redstone_interface_card");
public static final RegistryObject<Item> NETWORK_INTERFACE_CARD = register("network_interface_card");
public static final RegistryObject<Item> NETWORK_INTERFACE_CARD = register("network_interface_card", NetworkInterfaceCardItem::new);
public static final RegistryObject<Item> FILE_IMPORT_EXPORT_CARD = register("file_import_export_card");
public static final RegistryObject<Item> SOUND_CARD = register("sound_card");

View File

@@ -0,0 +1,111 @@
package li.cil.oc2.common.item;
import li.cil.oc2.client.gui.NetworkInterfaceCardScreen;
import li.cil.oc2.common.Constants;
import li.cil.oc2.common.util.ItemStackUtils;
import li.cil.oc2.common.util.NBTTagIds;
import net.minecraft.ChatFormatting;
import net.minecraft.client.Minecraft;
import net.minecraft.core.Direction;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.network.chat.Component;
import net.minecraft.world.InteractionHand;
import net.minecraft.world.InteractionResultHolder;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.item.TooltipFlag;
import net.minecraft.world.level.Level;
import net.minecraftforge.api.distmarker.Dist;
import net.minecraftforge.api.distmarker.OnlyIn;
import javax.annotation.Nullable;
import java.util.Arrays;
import java.util.List;
import static li.cil.oc2.common.util.TooltipUtils.withColor;
import static li.cil.oc2.common.util.TranslationUtils.text;
public final class NetworkInterfaceCardItem extends ModItem {
private static final String SIDE_CONFIGURATION_TAG_NAME = "sides";
private static final Component IS_CONFIGURED_TEXT = withColor(text("item.{mod}.network_interface_card.is_configured"), ChatFormatting.GREEN);
///////////////////////////////////////////////////////////////////
public static void setSideConfiguration(final ItemStack stack, final Direction side, final boolean enabled) {
final int index = side.get3DDataValue();
final CompoundTag tag = ItemStackUtils.getOrCreateModDataTag(stack);
final byte[] values;
if (tag.contains(SIDE_CONFIGURATION_TAG_NAME, NBTTagIds.TAG_BYTE_ARRAY) &&
tag.getByteArray(SIDE_CONFIGURATION_TAG_NAME).length == Constants.BLOCK_FACE_COUNT) {
values = tag.getByteArray(SIDE_CONFIGURATION_TAG_NAME);
} else {
values = new byte[Constants.BLOCK_FACE_COUNT];
Arrays.fill(values, (byte) 1);
}
values[index] = (byte) (enabled ? 1 : 0);
tag.putByteArray(SIDE_CONFIGURATION_TAG_NAME, values);
}
public static boolean getSideConfiguration(final ItemStack stack, @Nullable final Direction side) {
if (side == null) {
return false;
}
final int index = side.get3DDataValue();
final CompoundTag tag = ItemStackUtils.getModDataTag(stack);
if (tag.contains(SIDE_CONFIGURATION_TAG_NAME, NBTTagIds.TAG_BYTE_ARRAY)) {
final byte[] values = tag.getByteArray(SIDE_CONFIGURATION_TAG_NAME);
if (index < values.length) {
return values[index] != 0;
}
}
return true;
}
public static boolean hasConfiguration(final ItemStack stack) {
final byte[] values = ItemStackUtils.getModDataTag(stack).getByteArray(SIDE_CONFIGURATION_TAG_NAME);
for (final byte value : values) {
if (value == 0) {
return true;
}
}
return false;
}
///////////////////////////////////////////////////////////////////
@Override
public void appendHoverText(final ItemStack stack, @Nullable final Level level, final List<Component> tooltip, final TooltipFlag flag) {
super.appendHoverText(stack, level, tooltip, flag);
if (NetworkInterfaceCardItem.hasConfiguration(stack)) {
tooltip.add(IS_CONFIGURED_TEXT);
}
}
@Override
public InteractionResultHolder<ItemStack> use(final Level level, final Player player, final InteractionHand hand) {
final ItemStack itemStack = player.getItemInHand(hand);
if (player.getLevel().isClientSide()) {
if (itemStack.is(Items.NETWORK_INTERFACE_CARD.get())) {
openConfigurationScreen(player, hand);
}
}
return InteractionResultHolder.sidedSuccess(itemStack, player.getLevel().isClientSide());
}
///////////////////////////////////////////////////////////////////
@OnlyIn(Dist.CLIENT)
private void openConfigurationScreen(final Player player, final InteractionHand hand) {
Minecraft.getInstance().setScreen(new NetworkInterfaceCardScreen(player, hand));
}
}

View File

@@ -66,6 +66,8 @@ public final class Network {
registerMessage(ClientCanceledImportFileMessage.class, ClientCanceledImportFileMessage::new, NetworkDirection.PLAY_TO_SERVER);
registerMessage(BusCableFacadeMessage.class, BusCableFacadeMessage::new, NetworkDirection.PLAY_TO_CLIENT);
registerMessage(NetworkInterfaceCardConfigurationMessage.class, NetworkInterfaceCardConfigurationMessage::new, NetworkDirection.PLAY_TO_SERVER);
}
public static <T> void sendToClientsTrackingChunk(final T message, final LevelChunk chunk) {

View File

@@ -0,0 +1,61 @@
package li.cil.oc2.common.network.message;
import li.cil.oc2.common.item.Items;
import li.cil.oc2.common.item.NetworkInterfaceCardItem;
import net.minecraft.core.Direction;
import net.minecraft.network.FriendlyByteBuf;
import net.minecraft.server.level.ServerPlayer;
import net.minecraft.world.InteractionHand;
import net.minecraft.world.item.ItemStack;
import net.minecraftforge.network.NetworkEvent;
public final class NetworkInterfaceCardConfigurationMessage extends AbstractMessage {
private InteractionHand hand;
private Direction side;
private boolean value;
///////////////////////////////////////////////////////////////////
public NetworkInterfaceCardConfigurationMessage(final InteractionHand hand, final Direction side, final boolean value) {
this.hand = hand;
this.side = side;
this.value = value;
}
public NetworkInterfaceCardConfigurationMessage(final FriendlyByteBuf buffer) {
super(buffer);
}
///////////////////////////////////////////////////////////////////
@Override
public void fromBytes(final FriendlyByteBuf buffer) {
hand = buffer.readEnum(InteractionHand.class);
side = buffer.readEnum(Direction.class);
value = buffer.readBoolean();
}
@Override
public void toBytes(final FriendlyByteBuf buffer) {
buffer.writeEnum(hand);
buffer.writeEnum(side);
buffer.writeBoolean(value);
}
///////////////////////////////////////////////////////////////////
@Override
protected void handleMessage(final NetworkEvent.Context context) {
final ServerPlayer player = context.getSender();
if (player == null) {
return;
}
final ItemStack itemStack = player.getItemInHand(hand);
if (!itemStack.is(Items.NETWORK_INTERFACE_CARD.get())) {
return;
}
NetworkInterfaceCardItem.setSideConfiguration(itemStack, side, value);
}
}

View File

@@ -5,6 +5,8 @@ The network interface card (NIC) allows [computers](../block/computer.md) to sen
Computers *have to be shut down* before installing or removing this component. Installing it while the computer is running will have no effect, removing it may lead to system errors.
These cards can be configured to only connect to selected sides (use while holding). This allows using multiple cards to build a custom router, for example.
When using the default Linux distribution, this device will provide a regular ethernet device. Network setup can either be performed manually, or using the convenience script `setup-network.lua`. This script provides the option to either use a fixed-address setup, or a DHCP setup. For a DHCP setup, exactly one computer in the network must act as a DHCP server.
After initial setup, use the command `ifconfig` to see the currently used IP address.
After initial setup, use the command `ifconfig` to see the currently used IP address.

View File

@@ -27,6 +27,7 @@
"item.oc2.flash_memory": "Flash Memory",
"item.oc2.redstone_interface_card": "Redstone Interface Card",
"item.oc2.network_interface_card": "Network Interface Card",
"item.oc2.network_interface_card.is_configured": "Has connectivity configuration.",
"item.oc2.file_import_export_card": "File Import/Export Card",
"item.oc2.file_import_export_card.desc": "Provides an API to import and export files into and out of a virtual computer from and into your real file system.",
"item.oc2.robot": "Robot",
@@ -72,6 +73,11 @@
"gui.oc2.file_chooser.confirm_button.overwrite": "Overwrite",
"gui.oc2.file_chooser.cancel_button": "Cancel",
"gui.oc2.network_interface_card.side_state": "Connectivity: %s",
"gui.oc2.network_interface_card.connectivity.enabled": "Enabled",
"gui.oc2.network_interface_card.connectivity.disabled": "Disabled",
"gui.oc2.network_interface_card.info": "Drag to rotate. Click faces to toggle connectivity.",
"manual.oc2.home": "Home",
"manual.oc2.blocks": "Blocks",
"manual.oc2.items": "Items",
@@ -96,4 +102,4 @@
"subtitles.oc2.floppy_eject": "Floppy ejected",
"subtitles.oc2.floppy_insert": "Floppy inserted",
"subtitles.oc2.hdd": "Hard drive access"
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 KiB